Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 31 additions & 16 deletions src/core/assistant-message/NativeToolCallParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,17 @@
// Raw chunk tracking state (keyed by index from one API stream)
private static rawChunkTrackersByScope = new WeakMap<
object,
Map<number, { id: string; name: string; hasStarted: boolean; deltaBuffer: string[] }>
Map<
number,
{
id?: string
name: string
// Track whether the provider sent a name, including an empty name.
nameSeen: boolean
hasStarted: boolean
deltaBuffer: string[]
}
>
>()

public static createScope(): object {
Expand Down Expand Up @@ -124,49 +134,54 @@

let tracked = rawChunkTracker.get(index)

// Initialize new tool call tracking when we receive an id
if (id && !tracked) {
// Create the tracker on first sight of this index, independent of whether
// an id has arrived yet. Keying the lifecycle by index (not id) ensures any
// `arguments` that stream before the id is known are buffered rather than dropped.
if (!tracked) {
tracked = {
id,
name: name || "",
nameSeen: name !== undefined,

Check warning on line 144 in src/core/assistant-message/NativeToolCallParser.ts

View workflow job for this annotation

GitHub Actions / mutation-diff

Mutation test advisory

src/core/assistant-message/NativeToolCallParser.ts:144: Survived ConditionalExpression mutant (replacement: false). See the job summary for the complete list and resolution guidance.
hasStarted: false,
deltaBuffer: [],
}
rawChunkTracker.set(index, tracked)
}

if (!tracked) {
return events
// Record id and name as they arrive (they may come in separate chunks).
if (id) {
tracked.id = id
}

// Update name if present in chunk and not yet set
if (name) {
if (name !== undefined) {
tracked.name = name
Comment thread
awschmeder marked this conversation as resolved.
tracked.nameSeen = true
}

// Emit start event when we have the name
if (!tracked.hasStarted && tracked.name) {
// Emit start event only once both id and name are known. Using a local
// non-null id keeps emitted events typed as id: string.
if (!tracked.hasStarted && tracked.id && tracked.nameSeen) {
const startedId = tracked.id
events.push({
type: "tool_call_start",
id: tracked.id,
id: startedId,
name: tracked.name,
})
tracked.hasStarted = true

// Flush buffered deltas
// Flush buffered deltas accumulated during the pre-start window.
for (const bufferedDelta of tracked.deltaBuffer) {
events.push({
type: "tool_call_delta",
id: tracked.id,
id: startedId,
delta: bufferedDelta,
})
}
tracked.deltaBuffer = []
}

// Emit delta event for argument chunks
// Emit delta event for argument chunks, buffering until start is emitted.
if (args) {
if (tracked.hasStarted) {
if (tracked.hasStarted && tracked.id) {

Check warning on line 184 in src/core/assistant-message/NativeToolCallParser.ts

View workflow job for this annotation

GitHub Actions / mutation-diff

Mutation test advisory

src/core/assistant-message/NativeToolCallParser.ts:184: Survived LogicalOperator mutant (replacement: tracked.hasStarted || tracked.id). See the job summary for the complete list and resolution guidance.
events.push({
type: "tool_call_delta",
id: tracked.id,
Expand All @@ -190,7 +205,7 @@

if (rawChunkTracker) {
for (const [, tracked] of rawChunkTracker.entries()) {
if (tracked.hasStarted) {
if (tracked.hasStarted && tracked.id) {

Check warning on line 208 in src/core/assistant-message/NativeToolCallParser.ts

View workflow job for this annotation

GitHub Actions / mutation-diff

Mutation test advisory

src/core/assistant-message/NativeToolCallParser.ts:208: Survived LogicalOperator mutant (replacement: tracked.hasStarted || tracked.id). See the job summary for the complete list and resolution guidance.
events.push({
type: "tool_call_end",
id: tracked.id,
Expand Down
Loading
Loading