fix(ai): preserve providerMetadata through client-side UIMessage pipeline#404
fix(ai): preserve providerMetadata through client-side UIMessage pipeline#404houmark wants to merge 1 commit intoTanStack:mainfrom
providerMetadata through client-side UIMessage pipeline#404Conversation
…line PR TanStack#401 added providerMetadata to ToolCall and ToolCallStartEvent and wired it through the server-side ToolCallManager. However, the client-side pipeline (StreamProcessor → UIMessage → ModelMessage) drops providerMetadata entirely, breaking Gemini 3+ thinking models on multi-turn tool calls. This commit carries providerMetadata through every step: - types.ts: add providerMetadata to ToolCallPart - stream/types.ts: add providerMetadata to InternalToolCallState - stream/message-updaters.ts: accept and forward providerMetadata - stream/processor.ts: pass chunk.providerMetadata into state and parts - messages.ts: include providerMetadata in buildAssistantMessages() and modelMessageToUIMessage() Fixes TanStack#403
|
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 (5)
📝 WalkthroughWalkthroughThis PR threads Changes
Estimated code review effort🎯 2 (Simple) | ⏱️ ~10 minutes Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 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 |
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
Summary
PR #401 added
providerMetadatatoToolCallandToolCallStartEventand wired it through the server-sideToolCallManager. However, the client-side pipeline (StreamProcessor→ UIMessage → ModelMessage) dropsproviderMetadataentirely, breaking Gemini 3+ thinking models on multi-turn tool calls.This PR completes the round-trip by carrying
providerMetadatathrough every step of the client-side pipeline.Fixes #403 (follow-up to #216, #401)
Changes (5 files, 26 lines added)
types.tsproviderMetadatatoToolCallPartinterfacestream/types.tsproviderMetadatatoInternalToolCallStatestream/message-updaters.tsproviderMetadatainupdateToolCallPart()stream/processor.tschunk.providerMetadataintoInternalToolCallStateandupdateToolCallPart()inhandleToolCallStartEvent(); include it ingetCompletedToolCalls()messages.tsproviderMetadataontoolCallsinbuildAssistantMessages()(UIMessage → ModelMessage); preserve it inmodelMessageToUIMessage()(ModelMessage → UIMessage)How it works
The data flow is now:
Test plan
preserves thoughtSignature in functionCall parts when sending history back to Gemini) continues to pass (server-side path)Summary by CodeRabbit