fix(gemini): pop tool_choice from optional_params when creating cached content - #25659
fix(gemini): pop tool_choice from optional_params when creating cached content#25659enzoomoreira wants to merge 2 commits into
Conversation
…d content When using context caching with Gemini, `check_and_create_cache()` correctly pops `tools` from `optional_params` and includes them in the cached content. However, `tool_choice` was not being popped, causing it to remain as `toolConfig` in the final `GenerateContent` request alongside `cachedContent`. The Gemini API rejects requests that include both `cachedContent` and `toolConfig` with error 400: "CachedContent can not be used with GenerateContent request setting system_instruction, tools or tool_config". This fix pops `tool_choice` from `optional_params` (both sync and async paths) and includes it as `toolConfig` in the cached content request body, matching the existing pattern for `tools`. Verified against the Gemini API: the CachedContent creation endpoint accepts `toolConfig` alongside `tools` and `system_instruction`.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
Greptile SummaryFixes a Confidence Score: 5/5Safe to merge — the fix is correct, targeted, and both sync and async paths are handled symmetrically. All remaining findings are P2 (test coverage gaps and test realism). The core fix is straightforward and low-risk, the cache-key concern from the previous thread is addressed, and no existing tests were weakened. No files require special attention.
|
| Filename | Overview |
|---|---|
| litellm/llms/vertex_ai/context_caching/vertex_ai_context_caching.py | Pops tool_choice from optional_params alongside tools in both sync and async paths, adds it to the cache key, and includes it as toolConfig in the cached content request body. Fix is correct and complete. |
| tests/test_litellm/llms/vertex_ai/context_caching/test_vertex_ai_context_caching.py | Adds 4 new tool_choice tests and reformats existing tests. New tests verify the pop behavior but don't exercise the HTTP path where toolConfig is placed in the request body, and use raw string "auto" rather than the ToolConfig object that production code produces. |
Sequence Diagram
sequenceDiagram
participant C as Caller
participant T as transform_request_body
participant CC as check_and_create_cache
participant G as Gemini API
C->>T: optional_params with tools and tool_choice
T->>CC: optional_params
CC->>CC: pop tools from optional_params
CC->>CC: pop tool_choice from optional_params
CC->>CC: get_cache_key with tools and tool_choice
alt cache miss - create new cache
CC->>G: POST cachedContents with tools and toolConfig
G-->>CC: cache name
end
CC-->>T: messages, optional_params without tools or tool_choice, cache name
T->>T: build GenerateContent request with cachedContent
Note over T: No toolConfig conflict because tool_choice was already removed
Reviews (2): Last reviewed commit: "fix(gemini): include tool_choice in cach..." | Re-trigger Greptile
| generated_cache_key = local_cache_obj.get_cache_key( | ||
| messages=cached_messages, tools=tools, model=model | ||
| ) | ||
| generated_cache_key = local_cache_obj.get_cache_key(messages=cached_messages, tools=tools, model=model) |
There was a problem hiding this comment.
tool_choice omitted from cache key
tool_choice is popped and embedded in the cached content, but it is not passed to get_cache_key. Two calls with the same messages, tools, and model but different tool_choice values hash to the same key. The second call hits the first call's cached content (containing the wrong toolConfig), tool_choice is then discarded from optional_params, and the GenerateContent request proceeds with the wrong calling mode silently.
The key should include tool_choice — get_cache_key accepts **kwargs so the argument can be added directly. The same fix is needed in the equivalent get_cache_key call inside async_check_and_create_cache.
The cache key for context caching was computed from messages, tools, and model, but did not include tool_choice. This meant two requests with the same messages/tools/model but different tool_choice values (e.g. 'auto' vs 'required') would share the same cache key, silently reusing cached content created with a different toolConfig. Add tool_choice to the get_cache_key() call in both sync and async paths so that different tool_choice values produce distinct cache entries. Add tests verifying: - tool_choice is passed to cache key computation - toolConfig appears in the HTTP request body when creating new cached content (sync and async)
Summary
When using context caching with Gemini + tool calling,
check_and_create_cache()correctly popstoolsfromoptional_paramsand includes them in the cached content (added in #11989). However,tool_choiceis not being popped, causing it to appear astoolConfigin the finalGenerateContentrequest alongsidecachedContent.The Gemini API rejects this with error 400:
Root cause
In both
check_and_create_cache()andasync_check_and_create_cache():Later in
_transform_request_body(),tool_choiceis read fromoptional_paramsand added astoolConfigto the request, conflicting withcachedContent.Fix
Pop
tool_choicealongsidetoolsand include it astoolConfigin the cached content request body. The Gemini CachedContent API acceptstoolConfigas a valid field.Applied to both sync and async code paths.
Reproduction
Before fix:
BadRequestError 400: CachedContent can not be used with GenerateContent request setting ... tool_configAfter fix: Cache hit works correctly --
cache_read_input_tokens > 0in usage stats.Tests
Added 4 new test cases (sync/async x cached/non-cached) mirroring the existing
toolstests:test_check_and_create_cache_tool_choice_popped_from_optional_paramstest_check_and_create_cache_tool_choice_not_popped_when_no_cached_messagestest_async_check_and_create_cache_tool_choice_popped_from_optional_paramstest_async_check_and_create_cache_tool_choice_not_popped_when_no_cached_messagesAll 86 tests pass (74 existing + 12 new across 3 providers).