Skip to content

fix: prevent warnings and skip-validator injection for valid Gemini 3 parallel function calls - #17649

Merged
lgrammel merged 4 commits into
mainfrom
bugfix/16298-2
Jul 22, 2026
Merged

fix: prevent warnings and skip-validator injection for valid Gemini 3 parallel function calls#17649
lgrammel merged 4 commits into
mainfrom
bugfix/16298-2

Conversation

@lgrammel

@lgrammel lgrammel commented Jul 21, 2026

Copy link
Copy Markdown
Collaborator

Background

Gemini 3 legitimately signs only the first standard function call in a parallel model response, but replaying later unsigned calls caused misleading warnings and unnecessary skip-validator injection.

Root Cause

The Google converter evaluated missing signatures per normalized tool-call part rather than recognizing that one assistant message represents a model response whose first standard parallel function call carries the shared signature.

Summary

The converter now tracks signed standard function calls across the entire assistant response, preserves state across mixed parts, excludes built-in server calls from seeding that state, retains fallback mitigation for genuinely missing signatures, and includes a patch changeset.

Testing

Regression coverage verifies three-call parallel responses, intervening text and server parts, signed-server/unsigned-function behavior, genuine missing-signature fallback, and provider namespace handling. Google Node and Edge suites, full workspace type checking, and focused formatting/lint checks pass.

End-to-end Validation

  • pnpm -C packages/google build && pnpm -C examples/ai-functions exec tsx src/stream-text/google/function-call-id.ts — live Gemini calls produced three parallel tool calls and completed replay with final text.
  • pnpm -C examples/ai-functions exec tsx -e '<inline Gemini 3.5 Flash parallel-call probe>' — observed [signed,unsigned,unsigned], an empty warnings array, and successful final text generation.

Related Issues

Fixes #16298

Closes #17620

Co-authored-by: mttzzz 16290052+mttzzz@users.noreply.github.com
Co-authored-by: lgrammel 205036+lgrammel@users.noreply.github.com

@lgrammel

Copy link
Copy Markdown
Collaborator Author

Bugfix review

Outcome: changes-required

Reproduction replay

Status: no-longer-reproduces

The exact original reproduction completed successfully and the original bug signal did not appear.

Fixes issue

Status: partially-addresses

The fix handles the reproduced consecutive standard function calls, but its batch boundaries do not fully match Google's per-model-response semantics. Google specifies that only the first functionCall in a parallel response carries the signature.

Concerns:

  • The state resets on every non-tool part, although an assistant message represents one model response and mixed parts do not necessarily begin another function-call step.
  • Any signed SDK tool-call seeds the state, including built-in server toolCall parts, rather than only a signed standard functionCall.

Side effects

Risk: medium

A signed built-in server tool call can cause a subsequent unsigned custom functionCall to be emitted without the existing fallback sentinel or warning. Google permits toolCall, toolResponse, and functionCall parts to coexist and requires their signature context to be preserved.

Concerns:

  • A focused converter probe with a signed server toolCall followed by an unsigned custom functionCall produced no sentinel and no warning, potentially allowing a replay request to fail validation.
  • The non-tool reset can reintroduce false warnings and sentinel injection for valid mixed-part parallel responses.

Performance

Risk: none

The implementation adds one message-local boolean and constant-time checks per content part, with no meaningful memory or runtime cost.

Backwards compatibility

Risk: medium

Stored data is not mutated, but replaying existing mixed built-in/custom histories may now omit the fallback sentinel where missing custom-function metadata previously received mitigation.

Concerns:

  • Persisted histories with a signed server tool invocation followed by a custom function call whose signature was dropped can change from mitigated replay to an unmitigated request.

Architecture

Risk: medium

The change is correctly localized to the Google provider, but it conflates the SDK's normalized tool-call type with Google's distinct functionCall and server toolCall wire concepts.

Concerns:

  • Parallel function-call state should be based on standard functionCall semantics, not every normalized tool-call part.
  • Using content adjacency as a step boundary conflicts with the existing architecture, where each assistant response message already represents a generated step.

Change scope

Status: minimal

The tracked diff is limited to the converter, focused regression tests, and the required patch changeset; no unrelated tracked changes were found.

Security

Risk: none

The change introduces no new parsing, network access, credential handling, or externally controlled execution path.

Testing

Status: needs-more

The added tests cover consecutive ordinary calls and the implemented reset, but omit mixed built-in/custom tool responses and encode a questionable non-tool boundary assumption.

Concerns:

  • Add coverage proving that a signed server toolCall does not suppress mitigation for the first unsigned custom functionCall.
  • Add coverage for parallel functionCall parts separated by other response-part types according to Google's per-response signature rule.
  • Reconsider the test asserting that every non-tool part necessarily ends a parallel function-call batch.

Verification

Inspected every tracked diff hunk and the response-message construction path, confirmed the package type-check, Node test suite, and Edge test suite pass, and ran a focused mixed server/custom converter probe that exposed the untested suppression case.

Relevant Documentation

@lgrammel lgrammel changed the title fix: prevent warnings and skip-validator injection for valid Gemini 3 parallel tool calls fix: prevent warnings and skip-validator injection for valid Gemini 3 parallel function calls Jul 22, 2026
@lgrammel

Copy link
Copy Markdown
Collaborator Author

Addressed PR comment 5036567100.

Bugfix review

Outcome: changes-required

Reproduction replay

Status: no-longer-reproduces

... 81 more line(s) omitted

Updated the tracking boundary to the full assistant/model response and restricted it to standard functionCall parts. Non-tool and server tool parts no longer reset valid parallel-call state, while a signed built-in toolCall cannot suppress mitigation for the first unsigned custom function call. Added focused coverage for text-separated calls, mixed server call/response parts, and the signed-server/unsigned-function case; all focused, package, type, and live validations pass.

@lgrammel

Copy link
Copy Markdown
Collaborator Author

Bugfix review

Outcome: approved

Reproduction replay

Status: no-longer-reproduces

The exact original reproduction completed successfully and the original bug signal did not appear.

Fixes issue

Status: fully-addresses

The converter now recognizes unsigned standard function calls following a signed standard function call in the same model response as valid Gemini 3 parallel calls, while preserving mitigation for genuinely missing first signatures.

Side effects

Risk: low

State is scoped to each assistant response and only signed standard function calls seed it; text and server-tool parts preserve the response boundary without incorrectly suppressing mitigation.

Performance

Risk: none

The implementation adds one response-local boolean and constant-time checks while processing existing content parts, with no material memory or runtime cost.

Backwards compatibility

Risk: low

Stored messages are not modified; replay serialization changes only by omitting unnecessary mitigation for valid signed-first parallel function-call responses.

Architecture

Risk: none

The provider-specific behavior remains localized in the Google message converter, distinguishes standard function calls from built-in server calls, and introduces no new dependencies, exports, or cross-package source access.

Change scope

Status: minimal

The tracked changes are limited to the Google converter, focused regression tests, and the required patch changeset.

Security

Risk: none

The change introduces no new parsing, network, credential, URL-handling, or execution paths.

Testing

Status: appropriate

Regression tests cover signed-first parallel calls, intervening text and server parts, server calls not seeding function-call state, missing-signature fallback, non-Gemini behavior, and provider namespaces.

Verification

Inspected every tracked diff hunk and the response-message construction path. The full Google Node and Edge suites each passed 700 tests, full workspace type checking passed, focused lint and formatting checks passed, and the diff had no whitespace errors.

Relevant Documentation

@lgrammel
lgrammel merged commit 5e5453c into main Jul 22, 2026
55 checks passed
@lgrammel
lgrammel deleted the bugfix/16298-2 branch July 22, 2026 12:18
lgrammel added a commit that referenced this pull request Jul 22, 2026
…emini 3 parallel function calls (#17745)

## Background

Gemini 3 legitimately signs only the first standard function call in a
parallel model response, but replaying later unsigned calls caused
misleading warnings and unnecessary skip-validator injection.

## Root Cause

The Google converter evaluated missing signatures per normalized
tool-call part rather than recognizing that one assistant message
represents a model response whose first standard parallel function call
carries the shared signature.

## Summary

The converter now tracks signed standard function calls across the
entire assistant response, preserves state across mixed parts, excludes
built-in server calls from seeding that state, retains fallback
mitigation for genuinely missing signatures, and includes a patch
changeset.

## Testing

Regression coverage verifies three-call parallel responses, intervening
text and server parts, signed-server/unsigned-function behavior, genuine
missing-signature fallback, and provider namespace handling. Google Node
and Edge suites, full workspace type checking, and focused
formatting/lint checks pass.

## End-to-end Validation

- `pnpm -C packages/google build && pnpm -C examples/ai-functions exec
tsx src/stream-text/google/function-call-id.ts` — live Gemini calls
produced three parallel tool calls and completed replay with final text.
- `pnpm -C examples/ai-functions exec tsx -e '<inline Gemini 3.5 Flash
parallel-call probe>'` — observed `[signed,unsigned,unsigned]`, an empty
warnings array, and successful final text generation.

## Related Issues

Fixes #16298

Closes #17620

Backport of #17649

Co-authored-by: mttzzz <16290052+mttzzz@users.noreply.github.com>
Co-authored-by: lgrammel <205036+lgrammel@users.noreply.github.com>
@github-actions

Copy link
Copy Markdown
Contributor

🚀 Published in:

Package Version
ai 7.0.35 github npm
@ai-sdk/amazon-bedrock 5.0.28 github npm
@ai-sdk/angular 3.0.35 github npm
@ai-sdk/azure 4.0.19 github npm
@ai-sdk/devtools 1.0.7 github npm
@ai-sdk/gateway 4.0.27 github npm
@ai-sdk/google 4.0.22 github npm
@ai-sdk/google-vertex 5.0.26 github npm
@ai-sdk/harness 1.0.40 github npm
@ai-sdk/harness-claude-code 1.0.41 github npm
@ai-sdk/harness-codex 1.0.42 github npm
@ai-sdk/harness-deepagents 1.0.39 github npm
@ai-sdk/harness-opencode 1.0.41 github npm
@ai-sdk/harness-pi 1.0.40 github npm
@ai-sdk/langchain 3.0.35 github npm
@ai-sdk/llamaindex 3.0.35 github npm
@ai-sdk/openai 4.0.18 github npm
@ai-sdk/otel 1.0.35 github npm
@ai-sdk/policy-opa 1.0.35 github npm
@ai-sdk/react 4.0.38 github npm
@ai-sdk/rsc 3.0.35 github npm
@ai-sdk/sandbox-just-bash 1.0.40 github npm
@ai-sdk/sandbox-vercel 1.0.40 github npm
@ai-sdk/svelte 5.0.35 github npm
@ai-sdk/tui 1.0.36 github npm
@ai-sdk/vue 4.0.35 github npm
@ai-sdk/workflow 1.0.35 github npm
@ai-sdk/workflow-harness 1.0.40 github npm

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Gemini 3 parallel function calls: converter injects skip_thought_signature_validator + warns every turn (only the first parallel call is signed)

1 participant