fix(ai-gemini): read/write thoughtSignature at Part level for Gemini 3.x#459
fix(ai-gemini): read/write thoughtSignature at Part level for Gemini 3.x#459pemontto wants to merge 4 commits intoTanStack:mainfrom
Conversation
Gemini 3.x models emit thoughtSignature as a Part-level sibling of functionCall (per @google/genai Part type), not nested inside functionCall. The adapter was reading from functionCall.thoughtSignature (which does not exist in the SDK types) and writing it back nested, causing the API to reject subsequent tool-call turns with 400 INVALID_ARGUMENT: "Function call is missing a thought_signature". Read side: check part.thoughtSignature first, fall back to functionCall.thoughtSignature for Gemini 2.x compatibility. Write side: emit thoughtSignature as a Part-level sibling of functionCall instead of nesting it inside. Closes TanStack#403 Related: TanStack#218, TanStack#401, TanStack#404
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
✅ Files skipped from review due to trivial changes (1)
📝 WalkthroughWalkthroughReads Gemini Changes
Sequence Diagram(s)sequenceDiagram
participant Client as Client (UI / StreamProcessor)
participant Server as Server (chat() + Adapter)
participant Gemini as Gemini API
Client->>Server: Send history (may include ToolCall parts)
Server->>Server: Adapter formats messages\n(reads part.thoughtSignature || functionCall.thoughtSignature)
Server->>Gemini: POST chat request with Part-level `thoughtSignature`
Gemini-->>Server: Streaming response with parts[] (part.thoughtSignature or functionCall.thoughtSignature)
Server->>Server: processStreamChunks updates ToolCall state\n(prefer part.thoughtSignature, backfill if unset)
Server->>Client: Stream events / UIMessages (ToolCall start/updates)
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 inconclusive)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
Re: the pre-merge check about #403 scope. This PR fixes the server-side adapter (the The client-side |
|
Can confirm this also fixes the issue we were experiencing missing thought signature |
|
View your CI Pipeline Execution ↗ for commit 48cf4a2
☁️ Nx Cloud last updated this comment at |
|
View your CI Pipeline Execution ↗ for commit 48cf4a2
☁️ Nx Cloud last updated this comment at |
Per review feedback: use the @google/genai typed `Part` interface directly instead of `as any` casts. - Read side: `part.thoughtSignature` is properly typed on `Part`, so the cast is removed entirely. The Gemini 2.x fallback to `functionCall.thoughtSignature` is also removed since the SDK has never typed it there and Gemini has always emitted it at Part level. - Write side: construct a typed `Part` and conditionally assign `thoughtSignature`, avoiding the `as Part` cast on a spread literal. The only remaining `as any` in this area is the pre-existing functionResponse cast, which is unrelated to this fix.
|
Good call, addressed in ab05e4c. The
Only remaining All 66 tests still pass, typecheck clean. |
|
@pemontto After some further testing, it seems there are a few more places in the tool call life cycle where the provider meta data should be added to avoid more errors. See the following file locations: |
|
Good catch @mattsoltani, those locations are exactly what #404 (by @houmark, opened 2026-03-28) addresses. It threads That PR has been open for nearly a month without a maintainer review. Could we get it reviewed alongside this one? Without both, the adapter write-side fix here never actually has a signature to write because it's dropped upstream in the pipeline. If preferred, I'm happy to pull those changes into this PR (with attribution to @houmark) so it's a single comprehensive fix. Just let me know which you'd like. |
Summary
Gemini 3.x models emit
thoughtSignatureas a Part-level sibling offunctionCall(per the@google/genaiParttype definition), not nested insidefunctionCall. TheFunctionCallinterface has nothoughtSignatureproperty at all.The adapter was:
functionCall.thoughtSignature(wrong location, doesn't exist in SDK types)functionCall(wrong location, API ignores it there)This causes Gemini 3.x to reject subsequent tool-call turns with:
The
@google/genaiPart type (for reference)Changes
processStreamChunks): readspart.thoughtSignaturefirst, falls back tofunctionCall.thoughtSignaturefor Gemini 2.x compatibilityformatMessages): emitsthoughtSignatureas a Part-level sibling offunctionCallinstead of nesting it insideTest plan
thoughtSignaturefrom Gemini 3.x streaming response and round-trips it at the Part levelfunctionCall.thoughtSignaturefor Gemini 2.x wire formatgemini-3.1-pro-previewandgemini-3.1-flash-lite-previewsessions (multi-turn tool calling with thinking enabled)Closes #403
Related: #218, #401, #404
Summary by CodeRabbit
Bug Fixes
Tests
Chores