fix(translator): preserve thoughtSignature in non-stream gemini to claude conversion - #190
Open
warelik wants to merge 7 commits into
Open
fix(translator): preserve thoughtSignature in non-stream gemini to claude conversion#190warelik wants to merge 7 commits into
warelik wants to merge 7 commits into
Conversation
added 3 commits
August 20, 2026 13:13
…aude conversion Extract thoughtSignature / thought_signature and include signature in Claude thinking content blocks in ConvertGeminiResponseToClaudeNonStream. Classify parts with thoughtSignature as thinking to prevent reasoning text leakage and ensure parity with streaming converter. Fixes HTTP 400 (Invalid signature in thinking block) on multi-turn conversations with extended thinking. Refs router-for-me/CLIProxyAPI#5106
Do not classify non-thought parts carrying thoughtSignature as thinking to prevent swallowing visible text. Bind thinkingSignature strictly to parts where thought: true is set so functionCall signatures do not overwrite thinking signatures. Guard flushThinking to avoid emitting phantom empty thinking blocks on functionCall carriers. Handle signature-only finish chunks in streaming path without opening empty text blocks. Add regression tests for carrier text parts, functionCall carriers, signature precedence, and streaming parity. Refs router-for-me/CLIProxyAPI#5106
added 2 commits
August 20, 2026 18:33
Align Gemini->Claude streaming signature handling with antigravity reference. Open thinking block for standalone signature when none is active or when previous block is signed.
Contributor
Author
Follow-up: Preserve streaming thinking signatures across all response statesFollowing an audit of the streaming signature preservation logic, this follow-up aligns Gemini->Claude streaming translation with the reference implementation in Changes:
New regression tests:
|
…and tool calls Route thought signatures through carrier thinking blocks in streaming Gemini-to-Claude conversion when attached to visible text or tool calls.
Contributor
Author
Round Two Follow-Up Update: Stream Carrier Thinking Blocks for Visible Text and Tool Call SignaturesWe have completed round two of follow-ups on PR #190:
|
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.
Problem
signaturein non-streaming thinking blocks: InConvertGeminiResponseToClaudeNonStream, thinking blocks were constructed without preserving Gemini'sthoughtSignature/thought_signature. In multi-turn conversations with Anthropic API, returning thinking blocks lacking their signature causes HTTP 400 (Invalid signature in thinking block).ConvertGeminiResponseToClaude, signatures arriving withResponseType == 0(before thinking text),ResponseType == 1(visible text), or attached tofunctionCallparts were dropped.Fix
internal/translator/gemini/claude/gemini_claude_response.go:62: InConvertGeminiResponseToClaudeNonStream, capturedthinkingSignaturestrictly from parts whereisThoughtis true, resetting signatures upon flushing empty thoughts and emittingsignatureon the output Claude thinking block.internal/translator/gemini/claude/gemini_claude_response.go:210: InConvertGeminiResponseToClaude, introduced carrier block emission for standalone signatures, visible text with signatures, and tool calls with signatures viaappendPartSignature, isolating thinking blocks per signature and ensuringCurrentThinkingSignedstate is properly reset.Tests
TestConvertGeminiResponseToClaudeNonStream_ThoughtSignature: Verifies non-stream thinking block preservessignature.TestConvertGeminiResponseToClaudeNonStream_TextWithThoughtSignatureWithoutThoughtFlag: Verifies visible text with signature remains a text block.TestConvertGeminiResponseToClaudeNonStream_FunctionCallWithThoughtSignature: Verifies tool use with signature emitstool_usewithout phantom thinking block.TestConvertGeminiResponseToClaude_VisibleTextWithThoughtSignatureEmitsCarrierAndText: Verifies stream visible text with signature emits a carrier thinking block with signature delta followed by the text block.TestConvertGeminiResponseToClaude_FunctionCallWithThoughtSignatureEmitsCarrierAndTool: Verifies stream tool calls with signatures emit carrier thinking block followed by tool_use.TestConvertGeminiResponseToClaude_MultipleSignedThoughtChunksSplitBlocks: Verifies signed streaming thinking chunks cleanly isolate into separate blocks.Reverse bite-check
Reverting non-stream signature handling in
internal/translator/gemini/claude/gemini_claude_response.goreproduces the missing signature failure:Reverting streaming carrier signature emission reproduces the failure where visible text with signatures is swallowed or fails carrier creation:
Verification
TMPDIR=/Users/warelik/.cache/gotmp GOCACHE=/Users/warelik/.cache/gocache go build ./... TMPDIR=/Users/warelik/.cache/gotmp GOCACHE=/Users/warelik/.cache/gocache go vet ./internal/translator/gemini/claude/... gofmt -l internal/translator/gemini/claude/gemini_claude_response.go internal/translator/gemini/claude/gemini_claude_response_test.go TMPDIR=/Users/warelik/.cache/gotmp GOCACHE=/Users/warelik/.cache/gocache go test -v ./internal/translator/gemini/claude/...Output:
Tooling note
The
jbcontextsemantic-search CLI is installed but non-functional in this environment(
jbcontext searchfails withthe OS keychain is not accessible). The equivalentreview passes were performed with the repository's own tooling and manual inspection
instead; this is disclosed for transparency about how the change was reviewed.