fix(gemini-oauth): preserve thoughtSignature in Cloud Code SSE handler (#3214)#3215
fix(gemini-oauth): preserve thoughtSignature in Cloud Code SSE handler (#3214)#3215abbyshekit wants to merge 1 commit intomainfrom
Conversation
#3214) Closes #3214. Gemini 3.x rejects tool-call follow-ups with HTTP 400 INVALID_ARGUMENT: Function call is missing a thought_signature in functionCall parts. despite the prior fixes in #1565 (sentinel injection on every part) and #1752 (full roundtrip + synthetic fallback) being present in HEAD. @thomasmaerz traced it: the upstream Cloud Code SSE handler in src/llm/gemini_oauth.rs was silently discarding `thoughtSignature` when extracting `functionCall` parts. The synthetic sentinel "skip_thought_signature_validator" then replaced every real signature on the request side, and Gemini's INVALID_ARGUMENT validator (added for 3.x) rejected the request. Fix: extract `thoughtSignature` as a sibling of `functionCall` in the SSE part-handler. The roundtrip and synthetic-fallback layers added by #1565/#1752 are unchanged — they now have the real signature to work with for thinking-mode parts, and the synthetic fallback still kicks in when no signature was delivered. Refactored the inline part-extraction loop into a small helper (`extract_function_call_part`) so the behavior is unit-testable without spinning up a mock SSE stream. Three regression tests pin the contract: - _preserves_thought_signature: the #3214 happy path - _omits_signature_when_absent: the no-signature case keeps working unchanged (request-side fallback handles it) - _returns_none_for_text_only_part: defensive — text-only parts must not be misclassified as functionCall parts 3/3 pass on `cargo test`. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Code Review
This pull request introduces the extract_function_call_part helper function in src/llm/gemini_oauth.rs to ensure that thoughtSignature is preserved when extracting functionCall objects from Gemini SSE streams. This change addresses issue #3214, preventing INVALID_ARGUMENT errors in Gemini 3.x by maintaining the link between function calls and their associated signatures. Comprehensive unit tests have been added to verify the extraction logic for various scenarios, including cases with and without signatures. I have no feedback to provide.
|
Heads up — the underlying issue report was incorrect I filed issue #3214 which this PR addresses, but I've since confirmed the issue was authored with the wrong provider and auth path. My actual setup uses llm_backend=gemini (OpenAI-compatible endpoint with API key), not gemini_oauth / Cloud Code SSE. I'd suggest not merging this until the actual reproduction case is established against gemini_oauth. I'm sorry for the noise from the incorrect issue report. |
Summary
Closes #3214. Re-fixes the Gemini 3.x `INVALID_ARGUMENT — Function call is missing a thought_signature in functionCall parts` error that the prior fixes #1565 and #1752 were intended to address but did not, because both fixes worked on the request-builder layer while the upstream SSE parser was still dropping the real signature on the floor.
Root cause (verbatim from the reporter)
The SSE shape Cloud Code delivers:
```json
{
"functionCall": {"name": "list_dir", "args": {}},
"thoughtSignature": ""
}
```
Fix
Extract `thoughtSignature` as a sibling of `functionCall` in the SSE part-handler. Refactored the inline extraction into a small helper `extract_function_call_part` so the contract is unit-testable without standing up a mock SSE stream.
The roundtrip + synthetic-fallback layers added by #1565 / #1752 are unchanged — they now have the real signature to work with for thinking-mode parts, and the synthetic fallback still applies when no signature was delivered.
Tests
Three regression tests, all passing:
Test plan
🤖 Generated with Claude Code