fix(core): forward providerMetadata on tool-result stream chunks#1202
fix(core): forward providerMetadata on tool-result stream chunks#1202KeWang0622 wants to merge 1 commit intoVoltAgent:mainfrom
Conversation
…eam chunks Google Vertex thinking models (e.g. gemini-3-flash-preview) attach providerMetadata containing thoughtSignature to tool-output stream events. The tool-result -> tool-output-available and tool-error -> tool-output-error conversions in convertFullStreamChunkToUIMessageStream were not forwarding this field, causing the AI SDK's UI message stream schema validation to reject the chunk with "unrecognized_keys" errors. This broke all tool calls when using @ai-sdk/google-vertex with thinking models. The fix adds providerMetadata forwarding to the tool-result, tool-error, and tool-output cases, matching the pattern already used by tool-call, text-*, reasoning-*, and source-* chunk types. Fixes VoltAgent#1195
🦋 Changeset detectedLatest commit: 6c20216 The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
📝 WalkthroughWalkthroughA changeset entry documents a patch release for Changes
Estimated code review effort🎯 2 (Simple) | ⏱️ ~12 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 |
There was a problem hiding this comment.
🧹 Nitpick comments (3)
packages/core/src/agent/streaming/guardrail-stream.spec.ts (2)
458-510: Consider adding parity tests fortool-errorand rawtool-outputmetadata forwarding.Line [458]-Line [510] verifies
tool-resultmapping, but this PR also changestool-errorandtool-outputconversion paths. Adding those two assertions would lock in all modified branches.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/core/src/agent/streaming/guardrail-stream.spec.ts` around lines 458 - 510, Add assertions in the existing test in guardrail-stream.spec.ts that mirror the tool-result checks for the other modified branches: emit parts with type "tool-error" (including providerMetadata) and a raw "tool-output" part, run them through the same buildPipeline / uiStream flow used in this test, then assert that uiChunks contains a "tool-error" chunk and a "tool-output" chunk whose providerMetadata equals the same google thoughtSignature object; reference the existing parts array, pipeline.createUIStream(), uiChunks collection, and the find assertions used for "tool-input-available" and "tool-output-available" to add equivalent checks for "tool-error" and raw "tool-output".
444-455: Improve type safety for discriminated union narrowing instead of usingas any.In the test assertions for the
#1195fix, prefer typed narrowing overas anycasts when filtering by thetypediscriminant. SinceVoltAgentTextStreamPartuses a discriminated union pattern, you can create a type predicate function to properly narrow the type, allowing TypeScript to infer the correct shape without losing type safety.For example, create a helper like:
const isToolCall = (part: VoltAgentTextStreamPart): part is VoltAgentTextStreamPart & { type: 'tool-call' } => part.type === 'tool-call';Then replace
emitted.find((c) => c.type === "tool-call") as anywithemitted.find(isToolCall).This maintains the TypeScript-first codebase principles and applies to all four assertions across both test cases (lines 444, 451, 497, 505).
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/core/src/agent/streaming/guardrail-stream.spec.ts` around lines 444 - 455, Create type predicate helpers to narrow the VoltAgentTextStreamPart discriminated union instead of using "as any": add functions like isToolCall(part: VoltAgentTextStreamPart): part is VoltAgentTextStreamPart & { type: 'tool-call' } and isToolResult(part: VoltAgentTextStreamPart): part is VoltAgentTextStreamPart & { type: 'tool-result' }, then replace occurrences of emitted.find((c) => c.type === "tool-call") as any and emitted.find((c) => c.type === "tool-result") as any with emitted.find(isToolCall) and emitted.find(isToolResult) respectively so TypeScript infers the concrete shapes for the assertions (apply to the four assertions referencing tool-call/tool-result)..changeset/fix-tool-output-provider-metadata.md (1)
5-10: Optional release-note clarity tweak: mentiontool-outputforwarding explicitly.The implementation also forwards
providerMetadatafortool-output; adding that to this note would align docs with shipped behavior.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In @.changeset/fix-tool-output-provider-metadata.md around lines 5 - 10, Release note should explicitly mention forwarding providerMetadata for tool-output; update the changelog text to state that convertFullStreamChunkToUIMessageStream now forwards providerMetadata when converting tool-result → tool-output-available and tool-error → tool-output-error (so tool-output chunks preserve providerMetadata like thoughtSignature). Mention both conversions and the providerMetadata field by name to align docs with the implemented behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In @.changeset/fix-tool-output-provider-metadata.md:
- Around line 5-10: Release note should explicitly mention forwarding
providerMetadata for tool-output; update the changelog text to state that
convertFullStreamChunkToUIMessageStream now forwards providerMetadata when
converting tool-result → tool-output-available and tool-error →
tool-output-error (so tool-output chunks preserve providerMetadata like
thoughtSignature). Mention both conversions and the providerMetadata field by
name to align docs with the implemented behavior.
In `@packages/core/src/agent/streaming/guardrail-stream.spec.ts`:
- Around line 458-510: Add assertions in the existing test in
guardrail-stream.spec.ts that mirror the tool-result checks for the other
modified branches: emit parts with type "tool-error" (including
providerMetadata) and a raw "tool-output" part, run them through the same
buildPipeline / uiStream flow used in this test, then assert that uiChunks
contains a "tool-error" chunk and a "tool-output" chunk whose providerMetadata
equals the same google thoughtSignature object; reference the existing parts
array, pipeline.createUIStream(), uiChunks collection, and the find assertions
used for "tool-input-available" and "tool-output-available" to add equivalent
checks for "tool-error" and raw "tool-output".
- Around line 444-455: Create type predicate helpers to narrow the
VoltAgentTextStreamPart discriminated union instead of using "as any": add
functions like isToolCall(part: VoltAgentTextStreamPart): part is
VoltAgentTextStreamPart & { type: 'tool-call' } and isToolResult(part:
VoltAgentTextStreamPart): part is VoltAgentTextStreamPart & { type:
'tool-result' }, then replace occurrences of emitted.find((c) => c.type ===
"tool-call") as any and emitted.find((c) => c.type === "tool-result") as any
with emitted.find(isToolCall) and emitted.find(isToolResult) respectively so
TypeScript infers the concrete shapes for the assertions (apply to the four
assertions referencing tool-call/tool-result).
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 403b2e30-f2ee-4e9a-a047-5fcf5154e722
📒 Files selected for processing (3)
.changeset/fix-tool-output-provider-metadata.mdpackages/core/src/agent/streaming/guardrail-stream.spec.tspackages/core/src/agent/streaming/guardrail-stream.ts
Summary
Fixes #1195 — Tool calls do not work with Google Vertex thinking models due to a zod schema mismatch.
Google Vertex thinking models (e.g.
gemini-3-flash-preview) attachproviderMetadatacontainingthoughtSignatureto tool-output stream events. Thetool-result→tool-output-availableandtool-error→tool-output-errorconversions inconvertFullStreamChunkToUIMessageStreamwere not forwarding this field, causing the AI SDK's UI message stream schema validation to reject the chunk withunrecognized_keyserrors.The fix adds
providerMetadataforwarding to three cases (tool-result,tool-error,tool-output), matching the pattern already used bytool-call,text-*,reasoning-*, andsource-*chunk types.Changes
packages/core/src/agent/streaming/guardrail-stream.tsproviderMetadataintool-result,tool-error, andtool-outputcasespackages/core/src/agent/streaming/guardrail-stream.spec.ts.changeset/fix-tool-output-provider-metadata.md@voltagent/coreRoot cause
In
convertFullStreamChunkToUIMessageStream, thetool-callcase correctly forwardsproviderMetadata:But
tool-resultdid not:When the AI SDK's
createUIMessageStreamreceived atool-output-availablechunk containing the unexpectedproviderMetadatakey from Google's thinking model, zod's strict schema validation rejected it entirely, crashing the stream.Test plan
preserves providerMetadata on tool-result events (Google Vertex thinking models)— validates fullStream passthroughforwards providerMetadata to tool-output-available in UI stream (fixes #1195)— validates the UI stream conversion that was failingSummary by cubic
Forwards
providerMetadataontool-result,tool-error, andtool-outputstream chunks to fix schema errors with Google Vertex thinking models. Restores tool calls and UI stream validation when using@ai-sdk/google-vertex(fixes #1195).providerMetadatathroughconvertFullStreamChunkToUIMessageStreamfortool-result→tool-output-available,tool-error→tool-output-error, andtool-output.@voltagent/core.Written for commit 6c20216. Summary will update on new commits.
Summary by CodeRabbit
Bug Fixes
Tests