Skip to content

fix(ai-gemini): read/write thoughtSignature at Part level for Gemini 3.x#459

Open
pemontto wants to merge 1 commit intoTanStack:mainfrom
pemontto:fix/gemini-thought-signature-part-level
Open

fix(ai-gemini): read/write thoughtSignature at Part level for Gemini 3.x#459
pemontto wants to merge 1 commit intoTanStack:mainfrom
pemontto:fix/gemini-thought-signature-part-level

Conversation

@pemontto
Copy link
Copy Markdown

@pemontto pemontto commented Apr 16, 2026

Summary

Gemini 3.x models emit thoughtSignature as a Part-level sibling of functionCall (per the @google/genai Part type definition), not nested inside functionCall. The FunctionCall interface has no thoughtSignature property at all.

The adapter was:

  • Reading from functionCall.thoughtSignature (wrong location, doesn't exist in SDK types)
  • Writing it back nested inside functionCall (wrong location, API ignores it there)

This causes Gemini 3.x to reject subsequent tool-call turns with:

400 INVALID_ARGUMENT: "Function call is missing a thought_signature"

The @google/genai Part type (for reference)

export declare interface Part {
    functionCall?: FunctionCall;
    thoughtSignature?: string;  // <-- Part-level sibling
    // ...
}

export declare interface FunctionCall {
    id?: string;
    args?: Record<string, unknown>;
    name?: string;
    // no thoughtSignature here
}

Changes

  • Read side (processStreamChunks): reads part.thoughtSignature first, falls back to functionCall.thoughtSignature for Gemini 2.x compatibility
  • Write side (formatMessages): emits thoughtSignature as a Part-level sibling of functionCall instead of nesting it inside

Test plan

  • Existing tests pass (66/66)
  • Added test: reads Part-level thoughtSignature from Gemini 3.x streaming response and round-trips it at the Part level
  • Added test: falls back to functionCall.thoughtSignature for Gemini 2.x wire format
  • Verified fix against live gemini-3.1-pro-preview and gemini-3.1-flash-lite-preview sessions (multi-turn tool calling with thinking enabled)

Closes #403
Related: #218, #401, #404

Summary by CodeRabbit

  • Bug Fixes
    • Fixed Gemini 3.x adapter to correctly handle tool-call signature data, resolving API validation errors during subsequent tool-call turns while maintaining Gemini 2.x backward compatibility.

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
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 16, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 2be06704-8788-4ed7-9a00-be65ca2af992

📥 Commits

Reviewing files that changed from the base of the PR and between c780bc1 and 37fb4c1.

📒 Files selected for processing (3)
  • .changeset/fix-gemini-thought-signature-part-level.md
  • packages/typescript/ai-gemini/src/adapters/text.ts
  • packages/typescript/ai-gemini/tests/gemini-adapter.test.ts

📝 Walkthrough

Walkthrough

This PR fixes Gemini 3.x adapter behavior for thoughtSignature handling. It updates the adapter to read thoughtSignature from the Part level (with backward compatibility for Gemini 2.x nested placement) and emit it as a Part-level sibling to functionCall in request messages instead of nesting it under functionCall.

Changes

Cohort / File(s) Summary
Changesets
.changeset/fix-gemini-thought-signature-part-level.md
Documents patch fix for @tanstack/ai-gemini addressing Gemini 3.x API validation errors by moving thoughtSignature from nested under functionCall to Part-level placement with backward-compatible fallback for Gemini 2.x.
Gemini Adapter Implementation
packages/typescript/ai-gemini/src/adapters/text.ts
Updated processStreamChunks to derive thoughtSignature from streamed Part first with fallback to functionCall.thoughtSignature for older models. Modified formatMessages to emit thoughtSignature at Gemini Part level as a sibling to functionCall instead of nesting.
Test Coverage
packages/typescript/ai-gemini/tests/gemini-adapter.test.ts
Updated Gemini 3.x test to validate Part-level thoughtSignature reading and removed nested expectation. Added new Gemini 2.x test validating backward-compatible fallback behavior and verifying adapter emits thoughtSignature at Part level for subsequent tool-call turns.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 A whispered thought now finds its place,
No longer nested, showing its face,
Part-level siblings, standing tall,
Gemini flows through it all!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Linked Issues check ❓ Inconclusive This PR addresses the adapter-level implementation of thoughtSignature handling in Gemini 3.x (fixing the read/write paths in the adapter code). However, issue #403 identifies that the root cause also requires core framework changes to preserve providerMetadata through the client-side UIMessage pipeline. Verify whether core framework changes from issue #403 (ToolCallPart, InternalToolCallState, updateToolCallPart, handleToolCallStartEvent, buildAssistantMessages updates) are included or planned separately, as this adapter fix alone may not fully resolve the client-side pipeline issue.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately and concisely describes the main change: fixing how thoughtSignature is read/written at the Part level for Gemini 3.x models.
Description check ✅ Passed The description provides comprehensive context with the Part type definition, clear explanation of the read/write changes, test coverage, and verification against live sessions. All required checklist items are addressed.
Out of Scope Changes check ✅ Passed All changes are narrowly scoped to fixing the Gemini adapter's read/write behavior for thoughtSignature at the Part level: the changeset file, text.ts adapter logic, and gemini-adapter.test.ts. No unrelated changes detected.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@pemontto
Copy link
Copy Markdown
Author

Re: the pre-merge check about #403 scope.

This PR fixes the server-side adapter (the GeminiTextAdapter read/write paths), which is the direct cause of the 400 INVALID_ARGUMENT: "Function call is missing a thought_signature" errors on Gemini 3.x models.

The client-side providerMetadata pipeline issue (threading through ToolCallPart, InternalToolCallState, StreamProcessor, etc.) is a separate concern tracked in #404. That PR addresses whether providerMetadata survives the client-side UIMessage round-trip. Both fixes are needed for a complete solution, but they're independent: this adapter fix stops the API rejections, and #404 ensures the metadata persists through the client layer.

@mattsoltani
Copy link
Copy Markdown

mattsoltani commented Apr 17, 2026

Can confirm this also fixes the issue we were experiencing missing thought signature

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.

providerMetadata lost in client-side UIMessage pipeline — breaks Gemini 3 thoughtSignature on multi-turn tool calls

2 participants