From 2bf7db92eec52f1ea4d8177c683a464c2606751d Mon Sep 17 00:00:00 2001 From: ardaaltinors Date: Wed, 25 Mar 2026 10:06:20 +0300 Subject: [PATCH 1/3] fix: include tool_calls in streaming response call logs --- open-sse/utils/stream.ts | 54 +++++++++++++++++++++++++++++++++------- 1 file changed, 45 insertions(+), 9 deletions(-) diff --git a/open-sse/utils/stream.ts b/open-sse/utils/stream.ts index 987d309dbd..0c5fb99daf 100644 --- a/open-sse/utils/stream.ts +++ b/open-sse/utils/stream.ts @@ -113,6 +113,9 @@ export function createSSEStream(options: StreamOptions = {}) { let usage: UsageTokenRecord | null = null; /** Passthrough (OpenAI CC shape): saw tool_calls in stream before finish_reason */ let passthroughHasToolCalls = false; + /** Passthrough: accumulate tool_calls deltas for call log responseBody */ + const passthroughToolCalls = new Map(); + let passthroughToolCallSeq = 0; // State for translate mode (accumulatedContent for call log response body) const state: TranslateState | null = @@ -268,9 +271,29 @@ export function createSSEStream(options: StreamOptions = {}) { } } - // T18: Track if we saw tool calls + // T18: Track if we saw tool calls & accumulate for call log if (delta?.tool_calls && delta.tool_calls.length > 0) { passthroughHasToolCalls = true; + for (const tc of delta.tool_calls) { + const key = tc?.id ? `id:${tc.id}` : Number.isInteger(tc?.index) ? `idx:${tc.index}` : `seq:${++passthroughToolCallSeq}`; + const existing = passthroughToolCalls.get(key); + const deltaArgs = typeof tc?.function?.arguments === "string" ? tc.function.arguments : ""; + if (!existing) { + passthroughToolCalls.set(key, { + id: tc?.id ?? null, + index: Number.isInteger(tc?.index) ? tc.index : passthroughToolCalls.size, + type: tc?.type || "function", + function: { + name: tc?.function?.name || "", + arguments: deltaArgs, + }, + }); + } else { + if (tc?.id) existing.id = existing.id || tc.id; + if (tc?.function?.name) existing.function.name = tc.function.name; + existing.function.arguments += deltaArgs; + } + } } const content = delta?.content || delta?.reasoning_content; @@ -516,13 +539,19 @@ export function createSSEStream(options: StreamOptions = {}) { const prompt = Number(u?.prompt_tokens ?? u?.input_tokens ?? 0); const completion = Number(u?.completion_tokens ?? u?.output_tokens ?? 0); const content = passthroughAccumulatedContent.trim() || ""; + const message: Record = { + role: "assistant", + content: content || null, + }; + if (passthroughToolCalls.size > 0) { + message.tool_calls = [...passthroughToolCalls.values()] + .sort((a, b) => a.index - b.index); + } const responseBody = { choices: [ { - message: { - role: "assistant", - content, - }, + message, + finish_reason: passthroughHasToolCalls ? "tool_calls" : "stop", }, ], usage: { @@ -643,13 +672,20 @@ export function createSSEStream(options: StreamOptions = {}) { const prompt = Number(u?.prompt_tokens ?? u?.input_tokens ?? 0); const completion = Number(u?.completion_tokens ?? u?.output_tokens ?? 0); const content = (state?.accumulatedContent ?? "").trim() || ""; + const message: Record = { + role: "assistant", + content: content || null, + }; + const hasToolCalls = state?.toolCalls?.size > 0; + if (hasToolCalls) { + message.tool_calls = [...state.toolCalls.values()] + .sort((a, b) => (a.index ?? 0) - (b.index ?? 0)); + } const responseBody = { choices: [ { - message: { - role: "assistant", - content, - }, + message, + finish_reason: hasToolCalls ? "tool_calls" : "stop", }, ], usage: { From ea924f3bbf25800856b3ee37e5109aa53585672b Mon Sep 17 00:00:00 2001 From: ardaaltinors Date: Wed, 25 Mar 2026 10:18:41 +0300 Subject: [PATCH 2/3] fix(stream): correct tool_calls delta keying and normalize shapes --- open-sse/utils/stream.ts | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/open-sse/utils/stream.ts b/open-sse/utils/stream.ts index 0c5fb99daf..1d3abd2391 100644 --- a/open-sse/utils/stream.ts +++ b/open-sse/utils/stream.ts @@ -275,7 +275,12 @@ export function createSSEStream(options: StreamOptions = {}) { if (delta?.tool_calls && delta.tool_calls.length > 0) { passthroughHasToolCalls = true; for (const tc of delta.tool_calls) { - const key = tc?.id ? `id:${tc.id}` : Number.isInteger(tc?.index) ? `idx:${tc.index}` : `seq:${++passthroughToolCallSeq}`; + // Key by index first — id only appears on the first delta in OpenAI streaming + const key = Number.isInteger(tc?.index) + ? `idx:${tc.index}` + : tc?.id + ? `id:${tc.id}` + : `seq:${++passthroughToolCallSeq}`; const existing = passthroughToolCalls.get(key); const deltaArgs = typeof tc?.function?.arguments === "string" ? tc.function.arguments : ""; if (!existing) { @@ -290,7 +295,7 @@ export function createSSEStream(options: StreamOptions = {}) { }); } else { if (tc?.id) existing.id = existing.id || tc.id; - if (tc?.function?.name) existing.function.name = tc.function.name; + if (tc?.function?.name && !existing.function.name) existing.function.name = tc.function.name; existing.function.arguments += deltaArgs; } } @@ -678,8 +683,15 @@ export function createSSEStream(options: StreamOptions = {}) { }; const hasToolCalls = state?.toolCalls?.size > 0; if (hasToolCalls) { + // Normalize shape — translators may store different structures message.tool_calls = [...state.toolCalls.values()] - .sort((a, b) => (a.index ?? 0) - (b.index ?? 0)); + .map((tc: any) => ({ + id: tc.id ?? null, + index: tc.index ?? tc.blockIndex ?? 0, + type: tc.type ?? "function", + function: tc.function ?? { name: tc.name ?? "", arguments: "" }, + })) + .sort((a, b) => a.index - b.index); } const responseBody = { choices: [ From 35538e6f777a15ff17263a7d4c22153cc7f525c1 Mon Sep 17 00:00:00 2001 From: ardaaltinors Date: Wed, 25 Mar 2026 10:57:09 +0300 Subject: [PATCH 3/3] refactor(stream): add ToolCall type, replace any, simplify ternary --- open-sse/utils/stream.ts | 50 +++++++++++++++++++++++++++------------- 1 file changed, 34 insertions(+), 16 deletions(-) diff --git a/open-sse/utils/stream.ts b/open-sse/utils/stream.ts index 1d3abd2391..e3e3898ee9 100644 --- a/open-sse/utils/stream.ts +++ b/open-sse/utils/stream.ts @@ -57,6 +57,13 @@ type TranslateState = ReturnType & { accumulatedContent?: string; }; +type ToolCall = { + id: string | null; + index: number; + type: string; + function: { name: string; arguments: string }; +}; + type UsageTokenRecord = Record; function getOpenAIIntermediateChunks(value: unknown): unknown[] { @@ -114,7 +121,7 @@ export function createSSEStream(options: StreamOptions = {}) { /** Passthrough (OpenAI CC shape): saw tool_calls in stream before finish_reason */ let passthroughHasToolCalls = false; /** Passthrough: accumulate tool_calls deltas for call log responseBody */ - const passthroughToolCalls = new Map(); + const passthroughToolCalls = new Map(); let passthroughToolCallSeq = 0; // State for translate mode (accumulatedContent for call log response body) @@ -276,13 +283,17 @@ export function createSSEStream(options: StreamOptions = {}) { passthroughHasToolCalls = true; for (const tc of delta.tool_calls) { // Key by index first — id only appears on the first delta in OpenAI streaming - const key = Number.isInteger(tc?.index) - ? `idx:${tc.index}` - : tc?.id - ? `id:${tc.id}` - : `seq:${++passthroughToolCallSeq}`; + let key: string; + if (Number.isInteger(tc?.index)) { + key = `idx:${tc.index}`; + } else if (tc?.id) { + key = `id:${tc.id}`; + } else { + key = `seq:${++passthroughToolCallSeq}`; + } const existing = passthroughToolCalls.get(key); - const deltaArgs = typeof tc?.function?.arguments === "string" ? tc.function.arguments : ""; + const deltaArgs = + typeof tc?.function?.arguments === "string" ? tc.function.arguments : ""; if (!existing) { passthroughToolCalls.set(key, { id: tc?.id ?? null, @@ -295,7 +306,8 @@ export function createSSEStream(options: StreamOptions = {}) { }); } else { if (tc?.id) existing.id = existing.id || tc.id; - if (tc?.function?.name && !existing.function.name) existing.function.name = tc.function.name; + if (tc?.function?.name && !existing.function.name) + existing.function.name = tc.function.name; existing.function.arguments += deltaArgs; } } @@ -549,8 +561,9 @@ export function createSSEStream(options: StreamOptions = {}) { content: content || null, }; if (passthroughToolCalls.size > 0) { - message.tool_calls = [...passthroughToolCalls.values()] - .sort((a, b) => a.index - b.index); + message.tool_calls = [...passthroughToolCalls.values()].sort( + (a, b) => a.index - b.index + ); } const responseBody = { choices: [ @@ -685,12 +698,17 @@ export function createSSEStream(options: StreamOptions = {}) { if (hasToolCalls) { // Normalize shape — translators may store different structures message.tool_calls = [...state.toolCalls.values()] - .map((tc: any) => ({ - id: tc.id ?? null, - index: tc.index ?? tc.blockIndex ?? 0, - type: tc.type ?? "function", - function: tc.function ?? { name: tc.name ?? "", arguments: "" }, - })) + .map( + (tc: Record): ToolCall => ({ + id: (tc.id as string) ?? null, + index: (tc.index as number) ?? (tc.blockIndex as number) ?? 0, + type: (tc.type as string) ?? "function", + function: (tc.function as ToolCall["function"]) ?? { + name: (tc.name as string) ?? "", + arguments: "", + }, + }) + ) .sort((a, b) => a.index - b.index); } const responseBody = {