From 288015577246aab442e9e7c36033a5331ddebc77 Mon Sep 17 00:00:00 2001 From: Gioxa Date: Mon, 11 May 2026 20:22:07 +0700 Subject: [PATCH] fix(openai-responses): emit reasoning summary as delta.reasoning_content (#2159) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Integrated into release/v3.8.0 — emit reasoning summary as delta.reasoning_content for Chat Completions clients --- open-sse/translator/response/openai-responses.ts | 9 +++++++-- tests/unit/responses-translation-fixes.test.ts | 2 +- tests/unit/translator-resp-openai-responses.test.ts | 2 +- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/open-sse/translator/response/openai-responses.ts b/open-sse/translator/response/openai-responses.ts index 07ff889385..a5b5ceafdf 100644 --- a/open-sse/translator/response/openai-responses.ts +++ b/open-sse/translator/response/openai-responses.ts @@ -823,13 +823,18 @@ export function openaiResponsesToOpenAIResponse(chunk, state) { }; } - // Handle true reasoning summary ("Thought for 15s") + // Handle true reasoning summary ("Thought for 15s"). + // Emit as `delta.reasoning_content` — matches the shape used by the + // `reasoning_content_text.delta` branch above and is what Chat clients + // (OpenCode, Claude Code, Cursor, etc.) actually render in their thinking + // panel. A nested `delta.reasoning.summary` object is swallowed by most + // stream mergers and never reaches the user. if (eventType === "response.reasoning_summary_text.delta") { const reasoningDelta = data.delta || ""; if (!reasoningDelta) return null; const reasoningDeltaShape = state.copilotCompatibleReasoning ? { reasoning_text: reasoningDelta } - : { reasoning: { summary: reasoningDelta } }; + : { reasoning_content: reasoningDelta }; return { id: state.chatId, object: "chat.completion.chunk", diff --git a/tests/unit/responses-translation-fixes.test.ts b/tests/unit/responses-translation-fixes.test.ts index 1abf64d3eb..644c4882a4 100644 --- a/tests/unit/responses-translation-fixes.test.ts +++ b/tests/unit/responses-translation-fixes.test.ts @@ -406,7 +406,7 @@ test("Responses→Chat streaming: reasoning delta emits reasoning_content in Cha }; const result = openaiResponsesToOpenAIResponse(chunk, state); assert.ok(result, "should return a chunk"); - assert.equal(result.choices[0].delta.reasoning.summary, "thinking step..."); + assert.equal(result.choices[0].delta.reasoning_content, "thinking step..."); }); test("Responses→Chat streaming: Copilot mode emits reasoning_text for summary deltas", () => { diff --git a/tests/unit/translator-resp-openai-responses.test.ts b/tests/unit/translator-resp-openai-responses.test.ts index 78ba82d5bb..292c4b7d11 100644 --- a/tests/unit/translator-resp-openai-responses.test.ts +++ b/tests/unit/translator-resp-openai-responses.test.ts @@ -289,7 +289,7 @@ test("Responses -> OpenAI: tool-call delta, reasoning delta and completed usage assert.equal(added.choices[0].delta.tool_calls[0].function.name, "weather"); assert.equal(args.choices[0].delta.tool_calls[0].function.arguments, '{"city":"SP"}'); - assert.equal(reasoning.choices[0].delta.reasoning.summary, "Need weather info."); + assert.equal(reasoning.choices[0].delta.reasoning_content, "Need weather info."); assert.equal(completed.choices[0].finish_reason, "tool_calls"); assert.equal((completed as any).usage.prompt_tokens, 8); assert.equal((completed as any).usage.completion_tokens, 2);