From 6ccfb8b45e2c4ca2253b94f0fb9c6d045ed42d34 Mon Sep 17 00:00:00 2001 From: Sameer Kankute Date: Tue, 16 Dec 2025 16:20:19 +0530 Subject: [PATCH] Remove ttl fiedl when routing to bedrock --- .../anthropic_claude3_transformation.py | 26 ++++- .../test_anthropic_claude3_transformation.py | 99 +++++++++++++++++++ 2 files changed, 124 insertions(+), 1 deletion(-) diff --git a/litellm/llms/bedrock/messages/invoke_transformations/anthropic_claude3_transformation.py b/litellm/llms/bedrock/messages/invoke_transformations/anthropic_claude3_transformation.py index 32be1a780a3..ca646254d5d 100644 --- a/litellm/llms/bedrock/messages/invoke_transformations/anthropic_claude3_transformation.py +++ b/litellm/llms/bedrock/messages/invoke_transformations/anthropic_claude3_transformation.py @@ -108,6 +108,27 @@ class AmazonAnthropicClaudeMessagesConfig( stream=stream, ) + def _remove_ttl_from_cache_control( + self, anthropic_messages_request: Dict + ) -> None: + """ + Remove `ttl` field from cache_control in messages. + Bedrock doesn't support the ttl field in cache_control. + + Args: + anthropic_messages_request: The request dictionary to modify in-place + """ + if "messages" in anthropic_messages_request: + for message in anthropic_messages_request["messages"]: + if isinstance(message, dict) and "content" in message: + content = message["content"] + if isinstance(content, list): + for item in content: + if isinstance(item, dict) and "cache_control" in item: + cache_control = item["cache_control"] + if isinstance(cache_control, dict) and "ttl" in cache_control: + cache_control.pop("ttl", None) + def transform_anthropic_messages_request( self, model: str, @@ -141,8 +162,11 @@ class AmazonAnthropicClaudeMessagesConfig( # 3. `model` is not allowed in request body for bedrock invoke if "model" in anthropic_messages_request: anthropic_messages_request.pop("model", None) + + # 4. Remove `ttl` field from cache_control in messages (Bedrock doesn't support it) + self._remove_ttl_from_cache_control(anthropic_messages_request) - # 4. AUTO-INJECT beta headers based on features used + # 5. AUTO-INJECT beta headers based on features used anthropic_model_info = AnthropicModelInfo() tools = anthropic_messages_optional_request_params.get("tools") messages_typed = cast(List[AllMessageValues], messages) diff --git a/tests/test_litellm/llms/bedrock/messages/invoke_transformations/test_anthropic_claude3_transformation.py b/tests/test_litellm/llms/bedrock/messages/invoke_transformations/test_anthropic_claude3_transformation.py index 0d21c163761..a4da4ebb683 100644 --- a/tests/test_litellm/llms/bedrock/messages/invoke_transformations/test_anthropic_claude3_transformation.py +++ b/tests/test_litellm/llms/bedrock/messages/invoke_transformations/test_anthropic_claude3_transformation.py @@ -79,3 +79,102 @@ def test_chunk_parser_usage_transformation(): assert "usage" in parsed assert parsed["usage"]["input_tokens"] == 10 assert parsed["usage"]["output_tokens"] == 5 + + +def test_remove_ttl_from_cache_control(): + """Ensure ttl field is removed from cache_control in messages.""" + + cfg = AmazonAnthropicClaudeMessagesConfig() + + # Test case 1: Message with cache_control containing ttl + request = { + "messages": [ + { + "role": "user", + "content": [ + { + "type": "text", + "text": "Hello", + "cache_control": { + "type": "ephemeral", + "ttl": "1h" + } + } + ] + } + ] + } + + cfg._remove_ttl_from_cache_control(request) + + # Verify ttl is removed but cache_control remains + assert "cache_control" in request["messages"][0]["content"][0] + assert "ttl" not in request["messages"][0]["content"][0]["cache_control"] + assert request["messages"][0]["content"][0]["cache_control"]["type"] == "ephemeral" + + # Test case 2: Message with multiple content items + request2 = { + "messages": [ + { + "role": "user", + "content": [ + { + "type": "text", + "text": "Hello", + "cache_control": { + "type": "ephemeral", + "ttl": "1h" + } + }, + { + "type": "text", + "text": "World", + "cache_control": { + "type": "ephemeral", + "ttl": "2h" + } + } + ] + } + ] + } + + cfg._remove_ttl_from_cache_control(request2) + + # Verify ttl is removed from all items + for item in request2["messages"][0]["content"]: + if "cache_control" in item: + assert "ttl" not in item["cache_control"] + + # Test case 3: Message without ttl (should remain unchanged) + request3 = { + "messages": [ + { + "role": "user", + "content": [ + { + "type": "text", + "text": "Hello", + "cache_control": { + "type": "ephemeral" + } + } + ] + } + ] + } + + cfg._remove_ttl_from_cache_control(request3) + + # Verify cache_control is unchanged + assert request3["messages"][0]["content"][0]["cache_control"]["type"] == "ephemeral" + + # Test case 4: Empty messages (should not raise error) + request4 = {"messages": []} + cfg._remove_ttl_from_cache_control(request4) + assert request4 == {"messages": []} + + # Test case 5: Request without messages key (should not raise error) + request5 = {} + cfg._remove_ttl_from_cache_control(request5) + assert request5 == {}