fix: signature + correct field for streaming reasoning thinking blocks - #17
Open
angelz07 wants to merge 1 commit into
Open
fix: signature + correct field for streaming reasoning thinking blocks#17angelz07 wants to merge 1 commit into
angelz07 wants to merge 1 commit into
Conversation
…blocks Reasoning models exposed via OpenAI-style `reasoning_content` (e.g. LM Studio, o1/o3) produced empty responses in Claude Code. Two defects in the streaming path: 1. The thinking_delta used the field `text` instead of `thinking`, so the reasoning payload was malformed (the OpenRouter path already used `thinking`). 2. No `signature_delta` was ever emitted and the thinking block was closed only at stream end, after the text block. Claude Code discards thinking blocks that arrive without a signature, so the whole assistant turn was dropped. Add a reusable closeThinkingBlock() helper that emits a signature_delta followed by content_block_stop, and call it before any text/tool block starts and at stream end. This finalizes the thinking block in the order Claude Code expects (thinking -> signature -> stop -> text). The signature value is opaque for local/OpenAI-compatible providers (never validated against Anthropic). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Fix empty Claude Code responses from reasoning models exposed via the OpenAI-style
reasoning_contentfield (LM Studio local models, OpenAI o1/o3).Why
When a backend streams reasoning via
delta.reasoning_content, the proxy produced a thinking block that Claude Code silently discarded, leaving the user with an empty response even though the model returned valid text. Two defects instreamOpenAIToClaude:thinking_deltaused the fieldtextinstead ofthinking, so the reasoning payload was malformed. The OpenRouterreasoning_detailspath already usedthinkingcorrectly.signature_deltawas ever emitted, and the thinking block was closed only at stream end — after the text block. Claude Code discards thinking blocks that arrive without a signature (see the pitfall documented in CLAUDE.md), so the whole assistant turn was dropped.How
Add a reusable
closeThinkingBlock()helper that emits asignature_deltafollowed bycontent_block_stop, and call it before any text/tool block starts and once more at stream end. This finalizes the thinking block in the order Claude Code expects:The signature value is a fixed opaque string; it is never validated against Anthropic for local/OpenAI-compatible providers, and Claude Code only requires its presence to treat the block as valid thinking.
Testing
go test ./internal/server/...passes.thinking -> signature_delta -> stop -> textordering with the text content preserved, where before the thinking block had no signature and the wrong delta field.🤖 Generated with Claude Code