fix(gemini): preserve toolConfig on native generate_content - #23493
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Greptile SummaryThis PR fixes a regression in the native Google/Vertex Key changes:
Confidence Score: 4/5
|
| Filename | Overview |
|---|---|
| litellm/google_genai/main.py | Adds _get_tool_config_from_kwargs helper, extracts and forwards tool_config through all four call paths (generate_content, agenerate_content_stream, generate_content_stream, setup_generate_content_call). Also backfills the missing system_instruction extraction in generate_content_stream, addressing the issue flagged in the previous review thread. |
| litellm/llms/custom_httpx/llm_http_handler.py | Adds tool_config parameter to both generate_content_handler and async_generate_content_handler, and correctly threads it into transform_generate_content_request in both the sync path and the _is_async delegation path. |
| litellm/llms/gemini/google_genai/transformation.py | Adds tool_config parameter to transform_generate_content_request and emits toolConfig into the request dict when the value is not None. Consistent with the system_instruction guard pattern. |
| litellm/llms/vertex_ai/google_genai/transformation.py | Adds tool_config to transform_generate_content_request, correctly guarded with if tool_config is not None. Also aligns system_instruction guard from falsy check to is not None — a pre-existing inconsistency addressed as a bonus improvement. |
| litellm/llms/base_llm/google_genai/transformation.py | Updates the abstract base class signature to include tool_config as an optional parameter, keeping it consistent with all concrete implementations. |
| tests/test_litellm/google_genai/test_google_genai_main.py | Adds test_generate_content_stream_forwards_system_instruction which asserts both tool_config and system_instruction are forwarded in the sync stream path. Fixes an existing test assertion that was using == as a statement instead of assert. No real network calls — all mocked. |
| tests/test_litellm/google_genai/test_google_genai_transformation.py | Adds parameterized test_transform_generate_content_request_preserves_tool_config covering both GoogleGenAIConfig and VertexAIGoogleGenAIConfig. Updates existing tests to pass tool_config=None explicitly after the new required param was added to the signature. |
| tests/proxy_unit_tests/test_google_gemini_proxy_request.py | Injects toolConfig into the sample payload and asserts it is present in the outbound HTTP request body, providing end-to-end regression coverage for the proxy path. |
Sequence Diagram
sequenceDiagram
participant Caller
participant generate_content / agenerate_content_stream / generate_content_stream
participant setup_generate_content_call
participant BaseLLMHTTPHandler
participant async_generate_content_handler
participant transform_generate_content_request
Caller->>generate_content / agenerate_content_stream / generate_content_stream: call(kwargs incl. toolConfig)
generate_content / agenerate_content_stream / generate_content_stream->>setup_generate_content_call: setup(..., **kwargs)
setup_generate_content_call->>transform_generate_content_request: transform(..., tool_config)
Note over setup_generate_content_call: request_body stored in SetupResult (unused by callers)
setup_generate_content_call-->>generate_content / agenerate_content_stream / generate_content_stream: SetupResult
generate_content / agenerate_content_stream / generate_content_stream->>generate_content / agenerate_content_stream / generate_content_stream: tool_config = _get_tool_config_from_kwargs(kwargs)
generate_content / agenerate_content_stream / generate_content_stream->>BaseLLMHTTPHandler: generate_content_handler(..., tool_config)
alt _is_async = True
BaseLLMHTTPHandler->>async_generate_content_handler: async_generate_content_handler(..., tool_config)
async_generate_content_handler->>transform_generate_content_request: transform(..., tool_config)
transform_generate_content_request-->>async_generate_content_handler: {contents, tools, toolConfig, ...}
else sync
BaseLLMHTTPHandler->>transform_generate_content_request: transform(..., tool_config)
transform_generate_content_request-->>BaseLLMHTTPHandler: {contents, tools, toolConfig, ...}
end
BaseLLMHTTPHandler->>BaseLLMHTTPHandler: HTTP POST with toolConfig in body
Last reviewed commit: 296ea95
There was a problem hiding this comment.
Pull request overview
This PR adds support for passing toolConfig through LiteLLM’s Google GenAI generate_content request flow (including Vertex AI’s Google GenAI-compatible path), and updates unit tests to validate the field is preserved end-to-end.
Changes:
- Thread
tool_configfromlitellm.google_genai.maininto the HTTP handler and providertransform_generate_content_request(...). - Add
toolConfigmapping in bothGoogleGenAIConfigandVertexAIGoogleGenAIConfigrequest transformations. - Extend tests to assert
toolConfigis preserved in transformed requests and outgoing HTTP JSON bodies.
Reviewed changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated 10 comments.
Show a summary per file
| File | Description |
|---|---|
litellm/google_genai/main.py |
Extracts toolConfig/tool_config from kwargs and forwards it into request construction + handler calls. |
litellm/llms/custom_httpx/llm_http_handler.py |
Adds tool_config plumbing into sync/async generate_content handler signatures and transform calls. |
litellm/llms/base_llm/google_genai/transformation.py |
Updates the base interface signature/docs to include tool_config. |
litellm/llms/gemini/google_genai/transformation.py |
Adds toolConfig into the Google GenAI request payload. |
litellm/llms/vertex_ai/google_genai/transformation.py |
Adds toolConfig into the Vertex AI Google GenAI-format request payload. |
tests/test_litellm/google_genai/test_google_genai_transformation.py |
Adds/updates tests to ensure toolConfig is preserved for both Google and Vertex configs. |
tests/proxy_unit_tests/test_google_gemini_proxy_request.py |
Verifies the outbound HTTP request JSON includes the provided toolConfig. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
bbb8928 to
864b247
Compare
864b247 to
db38fd9
Compare
db38fd9 to
f3108f0
Compare
f3108f0 to
296ea95
Compare
* bump: version 1.82.1 → 1.82.2 * fix(gemini): preserve toolConfig on native generate_content (#23493) * chore: regenerate poetry.lock to match pyproject.toml (#23514) Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> * fix(docs): correct Docker image tag in v1.82.0 release notes Add missing 'v' prefix to Docker image tag: main-1.82.0-stable → main-v1.82.0-stable Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: yuneng-jiang <yuneng.jiang@gmail.com> Co-authored-by: Emerson Gomes <emerson.gomes@thalesgroup.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
…#23505) * fix(gemini): preserve toolConfig on native generate_content (#23493) * fix(ui): use sanitizeNumeric for team_member_budget in team edit form The team edit form (TeamInfo.tsx) used Number() to convert the team_member_budget field value, which silently converts null/undefined/"" to 0. When an admin edits a team for any reason without touching the budget field, this sends team_member_budget=0 to the backend, creating a shared budget row with max_budget=0.0 that blocks all team members. Use sanitizeNumeric (already used for tpm_limit, rpm_limit, soft_budget in the same form) which correctly returns null for empty/null/undefined values, preventing accidental zero-budget creation. --------- Co-authored-by: Emerson Gomes <emerson.gomes@thalesgroup.com>
…#23568) * bump: version 1.82.1 → 1.82.2 * fix(gemini): preserve toolConfig on native generate_content (#23493) * chore: regenerate poetry.lock to match pyproject.toml (#23514) Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> * fix claude.md * ui logo (#23556) * fix(proxy): prevent OOM/Prisma connection loss from unbounded managed-object poll (#23472) * fix(proxy): cap managed-object poll size + expire stale rows + kill-switch flag to prevent OOM/Prisma connection loss * fix(constants): simplify PROXY_BATCH_POLLING_ENABLED readability * docs+test: document new polling env vars, add pagination+stale-cleanup tests * fix: exclude stale_expired from batch poll queries; fix update_many assertions in tests * fix: scope stale cleanup to file_purpose, fix file_object mocks, add CheckBatchCost tests * fix: avoid duplicate cost logging in fallback path; guard integer constants against zero/negative values * fix: cache _has_batch_processed_column; guard cleanup from aborting poll; narrow fallback except * fix: add complete/completed to primary query not_in; fix vacuous test assertion - Primary find_many was missing "complete" and "completed" in its not_in filter, creating asymmetry with the fallback query. A job whose status was set to "complete" but whose batch_processed flag update failed would be silently re-fetched and re-processed every cycle, emitting duplicate cost logs. - test_fallback_completion_update_omits_batch_processed patched _is_base64_encoded_unified_file_id to return None, causing an immediate continue — so update() was never called and the assertion looped over an empty list (vacuously true). Rewrote the test to mock the full completion pipeline, verify update() is called exactly once, and assert batch_processed is absent from the update data. - Added symmetric test (primary path) proving batch_processed IS included when the column exists. Made-with: Cursor * fix(huggingface): forward extra_headers to embedding handler (#23502) The huggingface branch in litellm.embedding() did not pass the headers kwarg to huggingface_embed.embedding(), silently dropping user-provided extra_headers like X-HF-Bill-To. Fixes #23502 Made-with: Cursor --------- Co-authored-by: yuneng-jiang <yuneng.jiang@gmail.com> Co-authored-by: Emerson Gomes <emerson.gomes@thalesgroup.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Co-authored-by: Ishaan Jaffer <ishaanjaffer0324@gmail.com>
Relevant issues
Fixes #23491
Pre-Submission checklist
Please complete all items before asking a LiteLLM maintainer to review your PR
tests/test_litellm/directory, Adding at least 1 test is a hard requirement - see detailsmake test-unit@greptileaiand received a Confidence Score of at least 4/5 before requesting a maintainer reviewCI (LiteLLM team)
Branch creation CI run
Link:
CI run for the last commit
Link:
Merge / cherry-pick CI run
Links:
Type
🐛 Bug Fix
✅ Test
Changes
This fixes the native Google/Vertex
generate_contentpath dropping top-leveltoolConfig.Before this change, LiteLLM preserved
contentsbut rebuilt the downstream native request body withouttoolConfig. For affected Gemini tool-calling requests, that changed provider behavior from a normal follow-up tool call into a single emptySTOPevent.This PR:
toolConfig/tool_configthrough the nativegenerate_contentsetup pathtoolConfigthroughBaseLLMHTTPHandler.generate_content_handler()andasync_generate_content_handler()toolConfigin both Google-native and Vertex-nativetransform_generate_content_request()outputtoolConfigtoolConfig