Skip to content

fix(core): forward providerMetadata on tool-result stream chunks#1202

Open
KeWang0622 wants to merge 1 commit intoVoltAgent:mainfrom
KeWang0622:fix/tool-output-provider-metadata
Open

fix(core): forward providerMetadata on tool-result stream chunks#1202
KeWang0622 wants to merge 1 commit intoVoltAgent:mainfrom
KeWang0622:fix/tool-output-provider-metadata

Conversation

@KeWang0622
Copy link
Copy Markdown

@KeWang0622 KeWang0622 commented Apr 12, 2026

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) attach providerMetadata containing thoughtSignature to tool-output stream events. The tool-resulttool-output-available and tool-errortool-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.

The fix adds providerMetadata forwarding to three cases (tool-result, tool-error, tool-output), matching the pattern already used by tool-call, text-*, reasoning-*, and source-* chunk types.

Changes

File What
packages/core/src/agent/streaming/guardrail-stream.ts Forward providerMetadata in tool-result, tool-error, and tool-output cases
packages/core/src/agent/streaming/guardrail-stream.spec.ts 2 regression tests: fullStream passthrough + UI stream conversion
.changeset/fix-tool-output-provider-metadata.md Patch changeset for @voltagent/core

Root cause

In convertFullStreamChunkToUIMessageStream, the tool-call case correctly forwards providerMetadata:

case "tool-call": {
  const typed = part as {
    // ...
    providerMetadata?: unknown;  // ✅ included
  };
  return {
    // ...
    ...(typed.providerMetadata != null ? { providerMetadata: typed.providerMetadata } : {}),  // ✅ forwarded
  };
}

But tool-result did not:

case "tool-result": {
  const typed = part as {
    // ...
    // ❌ providerMetadata was missing from the type assertion
  };
  return {
    // ...
    // ❌ providerMetadata was not forwarded
  };
}

When the AI SDK's createUIMessageStream received a tool-output-available chunk containing the unexpected providerMetadata key from Google's thinking model, zod's strict schema validation rejected it entirely, crashing the stream.

Test plan

  • 2 new regression tests added
    • preserves providerMetadata on tool-result events (Google Vertex thinking models) — validates fullStream passthrough
    • forwards providerMetadata to tool-output-available in UI stream (fixes #1195) — validates the UI stream conversion that was failing
  • All 7 guardrail-stream tests pass
  • Biome lint clean
  • Changeset included

Summary by cubic

Forwards providerMetadata on tool-result, tool-error, and tool-output stream 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).

  • Bug Fixes
    • Carry providerMetadata through convertFullStreamChunkToUIMessageStream for tool-resulttool-output-available, tool-errortool-output-error, and tool-output.
    • Add two regression tests (full stream passthrough and UI stream conversion).
    • Include a patch changeset for @voltagent/core.

Written for commit 6c20216. Summary will update on new commits.

Summary by CodeRabbit

  • Bug Fixes

    • Fixed provider metadata preservation during tool event stream conversions, resolving schema validation failures for Vertex thinking models. The metadata, including thoughtSignature, is now correctly maintained throughout the streaming pipeline.
  • Tests

    • Added integration tests verifying provider metadata is properly propagated for tool events in the streaming pipeline.

…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-bot
Copy link
Copy Markdown

changeset-bot bot commented Apr 12, 2026

🦋 Changeset detected

Latest commit: 6c20216

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@voltagent/core Patch

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

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 12, 2026

📝 Walkthrough

Walkthrough

A changeset entry documents a patch release for @voltagent/core that propagates providerMetadata from tool-result and tool-error events through the stream conversion pipeline, preserving provider-specific metadata like Google Vertex's thoughtSignature for UI message streams.

Changes

Cohort / File(s) Summary
Changeset Documentation
.changeset/fix-tool-output-provider-metadata.md
Release note documenting the addition of providerMetadata propagation from tool-result and tool-error chunks to their corresponding UI stream output chunks.
Test Coverage
packages/core/src/agent/streaming/guardrail-stream.spec.ts
Two new integration tests verifying that providerMetadata is preserved on tool events in both the full stream and UI stream output, covering tool-call, tool-result, and tool-output-available event paths.
Stream Conversion Logic
packages/core/src/agent/streaming/guardrail-stream.ts
Updated convertFullStreamChunkToUIMessageStream to conditionally spread providerMetadata from tool-related chunks (tool-result, tool-error, tool-output) into their corresponding UI message output chunks when present.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Poem

🐰 Metadata flows like morning dew,
Through tool-results, preserved and true,
From Vertex's thoughts to UI streams,
The rabbit hops through dev-land dreams!
Now provider secrets stay intact,
Schema validation's back on track. 🎉

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The PR title clearly and concisely summarizes the primary change: forwarding providerMetadata on tool-result stream chunks to fix Google Vertex thinking model compatibility.
Description check ✅ Passed The PR description is comprehensive and complete, covering the bug context, root cause with code examples, specific changes across three files, test plan verification, and changeset inclusion per template requirements.
Linked Issues check ✅ Passed The PR fully addresses #1195 objectives: forwards providerMetadata in tool-result/tool-error/tool-output conversions, maintains backward compatibility, preserves validation for required fields, and includes tests validating the fix.
Out of Scope Changes check ✅ Passed All changes are in scope: additions to guardrail-stream.ts and guardrail-stream.spec.ts focus solely on providerMetadata forwarding, and the changeset documents the fix without introducing unrelated modifications.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ 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.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (3)
packages/core/src/agent/streaming/guardrail-stream.spec.ts (2)

458-510: Consider adding parity tests for tool-error and raw tool-output metadata forwarding.

Line [458]-Line [510] verifies tool-result mapping, but this PR also changes tool-error and tool-output conversion 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 using as any.

In the test assertions for the #1195 fix, prefer typed narrowing over as any casts when filtering by the type discriminant. Since VoltAgentTextStreamPart uses 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 any with emitted.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: mention tool-output forwarding explicitly.

The implementation also forwards providerMetadata for tool-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

📥 Commits

Reviewing files that changed from the base of the PR and between 9d5ed63 and 6c20216.

📒 Files selected for processing (3)
  • .changeset/fix-tool-output-provider-metadata.md
  • packages/core/src/agent/streaming/guardrail-stream.spec.ts
  • packages/core/src/agent/streaming/guardrail-stream.ts

Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No issues found across 3 files

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.

[BUG] Tool calls do not work with google-vertex thinking models due to a zod schema mismatch

2 participants