diff --git a/litellm/llms/vertex_ai/context_caching/transformation.py b/litellm/llms/vertex_ai/context_caching/transformation.py index e23374d57a1..00dce1d5ccd 100644 --- a/litellm/llms/vertex_ai/context_caching/transformation.py +++ b/litellm/llms/vertex_ai/context_caching/transformation.py @@ -158,7 +158,7 @@ def cached_messages_end_on_supported_turn(cached_messages: Sequence[AllMessageVa The cachedContents API rejects contents ending on a model turn, which is how it classifies both assistant messages and tool results, with HTTP 400 "Requests ending with a model turn are not supported". System messages are - extracted into system_instruction before contents are built, so the terminal + extracted into systemInstruction before contents are built, so the terminal turn is the last non-system message. """ non_system_messages: Final = tuple(message for message in cached_messages if message.get("role") != "system") @@ -206,6 +206,6 @@ def transform_openai_messages_to_gemini_context_caching( data["ttl"] = ttl if transformed_system_messages is not None: - data["system_instruction"] = transformed_system_messages + data["systemInstruction"] = transformed_system_messages return data diff --git a/litellm/types/llms/vertex_ai.py b/litellm/types/llms/vertex_ai.py index 64b4d4ce5c5..5a8eacfaa61 100644 --- a/litellm/types/llms/vertex_ai.py +++ b/litellm/types/llms/vertex_ai.py @@ -350,7 +350,7 @@ class RequestBody(TypedDict, total=False): class CachedContentRequestBody(TypedDict, total=False): contents: Required[list[ContentType]] - system_instruction: SystemInstructions + systemInstruction: SystemInstructions tools: Tools toolConfig: ToolConfig model: Required[str] # Format: models/{model} diff --git a/tests/test_litellm/llms/vertex_ai/context_caching/test_context_caching_ttl.py b/tests/test_litellm/llms/vertex_ai/context_caching/test_context_caching_ttl.py index 8a13baa0006..a5e736a30f7 100644 --- a/tests/test_litellm/llms/vertex_ai/context_caching/test_context_caching_ttl.py +++ b/tests/test_litellm/llms/vertex_ai/context_caching/test_context_caching_ttl.py @@ -334,7 +334,7 @@ class TestTransformationWithTTL: assert "ttl" in result assert result["ttl"] == "7200s" - assert "system_instruction" in result + assert "systemInstruction" in result if custom_llm_provider == "gemini": assert result["model"] == "models/gemini-2.5-pro" diff --git a/tests/test_litellm/llms/vertex_ai/context_caching/test_vertex_ai_context_caching.py b/tests/test_litellm/llms/vertex_ai/context_caching/test_vertex_ai_context_caching.py index f666829d2e8..68e6e07703c 100644 --- a/tests/test_litellm/llms/vertex_ai/context_caching/test_vertex_ai_context_caching.py +++ b/tests/test_litellm/llms/vertex_ai/context_caching/test_vertex_ai_context_caching.py @@ -1599,6 +1599,29 @@ def test_cached_messages_end_on_supported_turn(): assert cached_messages_end_on_supported_turn([]) is False +@pytest.mark.parametrize("custom_llm_provider", ["gemini", "vertex_ai", "vertex_ai_beta"]) +def test_cached_content_system_instruction_uses_canonical_rest_key(custom_llm_provider): + """cachedContents.create takes systemInstruction; this was the last snake_case sender in the provider.""" + from litellm.llms.vertex_ai.context_caching.transformation import ( + transform_openai_messages_to_gemini_context_caching, + ) + + result = transform_openai_messages_to_gemini_context_caching( + model="gemini-2.5-pro", + messages=[ + {"role": "system", "content": "You are a concise assistant."}, + {"role": "user", "content": "Reply with exactly: ok"}, + ], + custom_llm_provider=custom_llm_provider, + cache_key="test-cache-key", + vertex_project="test_project", + vertex_location="test_location", + ) + + assert "system_instruction" not in result + assert result["systemInstruction"]["parts"][0]["text"] == "You are a concise assistant." + + class TestCheckCachePagination: """Test pagination logic in check_cache and async_check_cache methods."""