fix(google): gate mixed tool use to the Gemini 3 Developer API + build tool config in chat()#6426
Merged
Merged
Conversation
…d tool config in chat() Combining built-in (provider) tools with function tools is only supported on the Gemini 3 Developer API — Vertex AI drops search tools when function declarations are present. Gate `include_server_side_tool_invocations` (and the provider-tool merge) on `not vertexai`, so Vertex falls back to single-type tool use with a warning instead of sending an unsupported request. Also moves all tool/tool_config construction out of `LLMStream._run()` and into `LLM.chat()`, where all of its inputs (tools, tool_choice, model, vertexai, cached_content) already live. This builds the `ToolConfig` once instead of mutating one built earlier, drops the build-then-pop cache dance (caching now simply skips building tools), and leaves `_run()` owning only what depends on the formatted context (system_instruction). `_run()` no longer mutates shared request state on retry. `create_tools_config` now returns `(tools, mixed)` and is the single source of truth for whether built-in and function tools are combined, so the flag can't drift from what's actually sent. The cache gate honors cached_content passed via either the constructor or extra_kwargs. Adds request-capture tests covering the Dev-API mixed path, the Vertex and non-Gemini-3 fallbacks, provider-tools-only, tool_choice=auto passthrough, and cache-via-extra_kwargs suppression.
theomonnom
approved these changes
Jul 14, 2026
The cache branch skipped building tools but didn't remove tools/tool_config a caller passed directly via extra_kwargs, so those were sent alongside cached_content and rejected by Gemini. Restore the old behavior of dropping them (from either source) when a cache is active.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Follow-up to #6416 (mixed built-in + function tool support). Two things: a correctness fix (Vertex) and a structural cleanup (move request shaping into
chat()).Why
include_server_side_tool_invocationson Vertex too, producing a request Vertex rejects.tool_configconstruction lived inLLMStream._run()even though all its inputs (tools,tool_choice, model,vertexai,cached_content) are known atchat()time. That forced a build-then-mutate pattern, a duplicated mixed-tools predicate, and a build-then-pop()cache dance.What changed
is_gemini_3_api = _is_gemini_3_model(model) and not client.vertexai. On Vertex, mixing falls back to single-type tool use with a warning instead of an unsupported request.tool_configconstruction from_run()intochat(). Builds theToolConfigonce;_run()now owns only what depends on the formatted context (system_instructiondropping) and no longer mutates shared request state on retry.if/elseinchat()(skip building tools when cached) instead of build-then-pop(). Honorscached_contentfrom either the constructor orextra_kwargs.create_tools_configreturns(tools, mixed)and is the single source of truth for whether built-in + function tools are actually combined, so the server-side-invocation flag can't drift from what's sent.Note on
tool_choice="auto"AUTOis passed through as-is alongside the circulation flag (noAUTO→VALIDATEDupgrade). Verified live against the Gemini 3 Developer API:AUTO+ built-in tools is accepted.Tests
New request-capture tests in
test_plugin_google_llm.pycovering: Dev-API mixed path, Vertex fallback, non-Gemini-3 fallback, provider-tools-only,tool_choice="auto"passthrough, and cache-via-extra_kwargssuppression. All 21 google LLM tests pass; ruff clean.🤖 Generated with Claude Code