From 7c47609f7af6f32cc5777e04bfe19763fa8dd1f2 Mon Sep 17 00:00:00 2001 From: mubashir1osmani Date: Thu, 5 Mar 2026 00:33:17 -0500 Subject: [PATCH 1/3] fix(provider): register bedrock_mantle in model_list and models_by_provider Adds bedrock_mantle_models to the model_list union and models_by_provider dict so models are discoverable via litellm.model_list and litellm.models_by_provider["bedrock_mantle"]. Co-Authored-By: Claude Sonnet 4.6 --- litellm/__init__.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/litellm/__init__.py b/litellm/__init__.py index 57e9cb25f4..ff7ef55c50 100644 --- a/litellm/__init__.py +++ b/litellm/__init__.py @@ -962,6 +962,7 @@ model_list = list( | ovhcloud_models | lemonade_models | docker_model_runner_models + | bedrock_mantle_models | set(clarifai_models) ) @@ -1065,6 +1066,7 @@ models_by_provider: dict = { "aws_polly": aws_polly_models, "gigachat": gigachat_models, "llamagate": llamagate_models, + "bedrock_mantle": bedrock_mantle_models } # mapping for those models which have larger equivalents From f1b86366d38d0c090f483db6cd34d98f4452c013 Mon Sep 17 00:00:00 2001 From: mubashir1osmani Date: Thu, 5 Mar 2026 00:33:44 -0500 Subject: [PATCH 2/3] Revert "fix(provider): register bedrock_mantle in model_list and models_by_provider" This reverts commit 7c47609f7af6f32cc5777e04bfe19763fa8dd1f2. --- litellm/__init__.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/litellm/__init__.py b/litellm/__init__.py index ff7ef55c50..57e9cb25f4 100644 --- a/litellm/__init__.py +++ b/litellm/__init__.py @@ -962,7 +962,6 @@ model_list = list( | ovhcloud_models | lemonade_models | docker_model_runner_models - | bedrock_mantle_models | set(clarifai_models) ) @@ -1066,7 +1065,6 @@ models_by_provider: dict = { "aws_polly": aws_polly_models, "gigachat": gigachat_models, "llamagate": llamagate_models, - "bedrock_mantle": bedrock_mantle_models } # mapping for those models which have larger equivalents From 23cf360be768f77eb1b85a0ce113056cec0ee57b Mon Sep 17 00:00:00 2001 From: mubashir1osmani Date: Mon, 9 Mar 2026 23:53:55 -0400 Subject: [PATCH 3/3] fix: explicit type conversion for prompt caching --- .../anthropic_cache_control_hook.py | 4 +- .../test_anthropic_cache_control_hook.py | 72 +++++++++++++++++++ 2 files changed, 75 insertions(+), 1 deletion(-) diff --git a/litellm/integrations/anthropic_cache_control_hook.py b/litellm/integrations/anthropic_cache_control_hook.py index 5df79580d3..67b95c7694 100644 --- a/litellm/integrations/anthropic_cache_control_hook.py +++ b/litellm/integrations/anthropic_cache_control_hook.py @@ -82,8 +82,10 @@ class AnthropicCacheControlHook(CustomPromptManagement): _targetted_index: Optional[Union[int, str]] = point.get("index", None) targetted_index: Optional[int] = None if isinstance(_targetted_index, str): - if _targetted_index.isdigit(): + try: targetted_index = int(_targetted_index) + except ValueError: + pass else: targetted_index = _targetted_index diff --git a/tests/test_litellm/integrations/test_anthropic_cache_control_hook.py b/tests/test_litellm/integrations/test_anthropic_cache_control_hook.py index 38baaedef1..afeeb4a1ba 100644 --- a/tests/test_litellm/integrations/test_anthropic_cache_control_hook.py +++ b/tests/test_litellm/integrations/test_anthropic_cache_control_hook.py @@ -903,3 +903,75 @@ async def test_anthropic_cache_control_hook_document_analysis_multiple_pages(): if isinstance(item, dict) and "cachePoint" in item ) assert cache_control_count == 1, f"Expected exactly 1 cache control point (last item only), found {cache_control_count}. Before fix, this would be 6 (one for each content item)." + + +@pytest.mark.asyncio +async def test_anthropic_cache_control_hook_string_negative_index(): + """ + Test that string negative indices like "-1" are handled correctly. + + When cache_control_injection_points are stored in DB/config as JSON, indices + like -1 become the string "-1". Previously, str.isdigit() returned False for + "-1" so the cache control was silently skipped. This tests the fix. + """ + with patch.dict( + os.environ, + { + "AWS_ACCESS_KEY_ID": "fake_access_key_id", + "AWS_SECRET_ACCESS_KEY": "fake_secret_access_key", + "AWS_REGION_NAME": "us-west-2", + }, + ): + anthropic_cache_control_hook = AnthropicCacheControlHook() + litellm.callbacks = [anthropic_cache_control_hook] + + mock_response = MagicMock() + mock_response.json.return_value = { + "output": { + "message": { + "role": "assistant", + "content": "Response", + } + }, + "stopReason": "end_turn", + "usage": { + "inputTokens": 100, + "outputTokens": 50, + "totalTokens": 150, + }, + } + mock_response.status_code = 200 + + client = AsyncHTTPHandler() + with patch.object(client, "post", return_value=mock_response) as mock_post: + await litellm.acompletion( + model="bedrock/us.anthropic.claude-3-7-sonnet-20250219-v1:0", + messages=[ + {"role": "user", "content": "First message"}, + {"role": "assistant", "content": "First response"}, + {"role": "user", "content": "Second message"}, + ], + # index is a string "-1" (as stored in DB/config JSON) + cache_control_injection_points=[ + {"location": "message", "index": "-1"}, + ], + client=client, + ) + + mock_post.assert_called_once() + request_body = json.loads(mock_post.call_args.kwargs["data"]) + + # The last user message should have cache control applied + last_message = request_body["messages"][-1] + last_message_content = last_message["content"] + assert isinstance(last_message_content, list), ( + f"Expected list content, got {type(last_message_content)}" + ) + has_cache_point = any( + isinstance(item, dict) and "cachePoint" in item + for item in last_message_content + ) + assert has_cache_point, ( + f"Expected cachePoint in last message content, got: {last_message_content}. " + "String index '-1' was not parsed correctly (str.isdigit() returns False for negative strings)." + )