Skip to content

fix(openai): preserve reasoning_content round-trip for thinking-mode models - #129

Merged
ranxianglei merged 1 commit into
masterfrom
2026-08-13_fix-openai-reasoning-content
Aug 12, 2026
Merged

ranxianglei merged 1 commit into
masterfrom
2026-08-13_fix-openai-reasoning-content

Conversation

@ranxianglei

Copy link
Copy Markdown
Owner

Problem

OpenAI-compatible reasoning models (DeepSeek-R1, GLM-4.6 thinking, Qwen-QwQ) emit reasoning_content (chain-of-thought) and require it be echoed back on subsequent requests. After every proxy-tool/compress re-request, the upstream returned HTTP 400:

The `reasoning_content` in the thinking mode must be passed back to the API.

bili's OpenAI adapter dropped it (grep reasoning_content dist = 0). The Anthropic (thinking) and Responses paths already handled this — only OpenAI was missing.

Fix

Mirror the existing Anthropic thinking handling for OpenAI reasoning_content:

  • src/bili-message.ts — add reasoningContent?: string sidecar field (next to thinkingSignature).
  • src/openai.ts
    • openaiToCore: when an incoming assistant message has non-empty reasoning_content, emit a {contentType:"reasoning", text, reasoningContent} coreMessage before content/tool-calls.
    • coreToOpenai: accumulate reasoning from contentType:"reasoning" coreMessages; flush() re-attaches reasoning_content on the output assistant object (handles text+reasoning, tool_calls+reasoning, reasoning-only).
  • src/loop/core.ts
    • ParsedStreamEvent + {kind:"reasoning"; delta; raw?}; CompressLoopAdapter + optional emitReasoning?(delta):Buffer.
    • runCompressLoop accumulates assistantReasoning; forwards to client (raw passthrough round-1 / emitReasoning round-2+); re-request reconstruction pushes the reasoning coreMessage before the text one so coreToOpenai re-emits it → no more 400.
  • src/loop/adapter-openai.ts — parseStream yields {kind:"reasoning", delta:delta.reasoning_content, raw} for non-empty reasoning_content (independent of tool_calls/content); emitReasoning rebuilds the delta:{reasoning_content} SSE chunk.

Tests

tests/openai-reasoning-content.test.ts (+155): round-trip / reasoning+tool_calls / no-reasoning-unaffected / parseStream captures reasoning.

342/342 tests pass, typecheck clean, build OK.

…models

OpenAI-compatible reasoning models (DeepSeek-R1, GLM-4.6 thinking, Qwen-QwQ)
emit reasoning_content (CoT) on assistant messages and require it be echoed
back. bili's OpenAI adapter dropped it (grep reasoning_content = 0), so every
re-request after a proxy tool/compress got HTTP 400 'The reasoning_content in
the thinking mode must be passed back to the API.'

Mirror the existing Anthropic thinking handling: openaiToCore parses
reasoning_content into a contentType:reasoning coreMessage; coreToOpenai
re-emits it; adapter-openai parseStream captures delta.reasoning_content and
forwards it + includes it in the re-request.
@ranxianglei
ranxianglei force-pushed the 2026-08-13_fix-openai-reasoning-content branch from 82c0c3e to e5edd10 Compare August 12, 2026 16:30
@ranxianglei
ranxianglei merged commit 85ac1b3 into master Aug 12, 2026
3 checks passed
@ranxianglei ranxianglei mentioned this pull request Aug 12, 2026
@ranxianglei
ranxianglei deleted the 2026-08-13_fix-openai-reasoning-content branch October 15, 2026 05:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant