From 249add6f0d25e89be79e0b45f374dc4728a723cb Mon Sep 17 00:00:00 2001 From: "devin-ai-integration[bot]" <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Mon, 12 Oct 2026 18:34:33 +0000 Subject: [PATCH] refactor(bedrock): rename is_claude_4_5_on_bedrock to supports_bedrock_prompt_cache_ttl (#46058) Co-authored-by: kerry Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- .../prompt_templates/factory.py | 4 ++-- .../bedrock/chat/converse_transformation.py | 4 ++-- litellm/llms/bedrock/common_utils.py | 22 ++++++++++------- .../anthropic_claude3_transformation.py | 24 ++++++------------- .../chat/test_converse_transformation.py | 6 ++--- .../llms/bedrock/test_bedrock_common_utils.py | 24 +++++++++++++++++-- 6 files changed, 50 insertions(+), 34 deletions(-) diff --git a/litellm/litellm_core_utils/prompt_templates/factory.py b/litellm/litellm_core_utils/prompt_templates/factory.py index 5d6c1ac0e4b..7d635712e42 100644 --- a/litellm/litellm_core_utils/prompt_templates/factory.py +++ b/litellm/litellm_core_utils/prompt_templates/factory.py @@ -5093,7 +5093,7 @@ def make_valid_bedrock_tool_name(input_tool_name: str) -> str: def add_cache_point_tool_block(tool: dict, model: str | None = None) -> BedrockToolBlock | None: from litellm.llms.bedrock.common_utils import ( bedrock_model_accepts_cache_points, - is_claude_4_5_on_bedrock, + supports_bedrock_prompt_cache_ttl, ) cache_control: Final = tool.get("cache_control", None) @@ -5103,7 +5103,7 @@ def add_cache_point_tool_block(tool: dict, model: str | None = None) -> BedrockT cache_point_block: Final[CachePointBlock] = {"type": "default"} if isinstance(cache_control, dict) and "ttl" in cache_control: ttl: Final = cache_control["ttl"] - if ttl in ["5m", "1h"] and model is not None and is_claude_4_5_on_bedrock(model): + if ttl in ["5m", "1h"] and model is not None and supports_bedrock_prompt_cache_ttl(model): cache_point_block["ttl"] = ttl return {"cachePoint": cache_point_block} return None diff --git a/litellm/llms/bedrock/chat/converse_transformation.py b/litellm/llms/bedrock/chat/converse_transformation.py index d5a54bb1e41..1cdcf0d6e4c 100644 --- a/litellm/llms/bedrock/chat/converse_transformation.py +++ b/litellm/llms/bedrock/chat/converse_transformation.py @@ -110,8 +110,8 @@ from ..common_utils import ( get_anthropic_beta_from_headers, get_bedrock_tool_name, is_bedrock_application_inference_profile_arn, - is_claude_4_5_on_bedrock, normalize_bedrock_opus_output_config_effort, + supports_bedrock_prompt_cache_ttl, ) if TYPE_CHECKING: @@ -1385,7 +1385,7 @@ class AmazonConverseConfig(BaseConfig): cache_point: Final = CachePointBlock(type="default") if isinstance(control, dict) and "ttl" in control: ttl: Final = control["ttl"] - if ttl in ["5m", "1h"] and model is not None and is_claude_4_5_on_bedrock(model): + if ttl in ["5m", "1h"] and model is not None and supports_bedrock_prompt_cache_ttl(model): cache_point["ttl"] = ttl return cache_point diff --git a/litellm/llms/bedrock/common_utils.py b/litellm/llms/bedrock/common_utils.py index 151f4858fdb..9722f9dccb2 100644 --- a/litellm/llms/bedrock/common_utils.py +++ b/litellm/llms/bedrock/common_utils.py @@ -9,6 +9,7 @@ import functools import json import os import re +import warnings from collections.abc import Iterator, Mapping, Sequence from types import MappingProxyType from typing import TYPE_CHECKING, Any, Final, Literal, TypeAlias @@ -1230,15 +1231,11 @@ def bedrock_supports_tool_search(model: str) -> bool: return AnthropicModelInfo.supports_model_capability(model, "supports_tool_search", "bedrock") -def is_claude_4_5_on_bedrock(model: str) -> bool: +def supports_bedrock_prompt_cache_ttl(model: str) -> bool: """ - Check if the model supports Bedrock prompt caching with an extended '1h' TTL - (in addition to the default 5m TTL). - - Backed by the ``cache_creation_input_token_cost_above_1hr`` field in - ``model_prices_and_context_window.json`` instead of a hardcoded list of - model-name patterns, so newly released models pick up support as soon as - their pricing entry ships, with no code change required here. + Whether Bedrock accepts a ``ttl`` ("5m"/"1h") on prompt ``cache_control`` + for this model, decided by ``cache_creation_input_token_cost_above_1hr`` on + the model's or its base model's cost-map entry. """ return any( (litellm.model_cost.get(candidate) or {}).get("cache_creation_input_token_cost_above_1hr") is not None @@ -1246,6 +1243,15 @@ def is_claude_4_5_on_bedrock(model: str) -> bool: ) +def is_claude_4_5_on_bedrock(model: str) -> bool: + warnings.warn( + "is_claude_4_5_on_bedrock is deprecated, use supports_bedrock_prompt_cache_ttl", + UserWarning, + stacklevel=2, + ) + return supports_bedrock_prompt_cache_ttl(model) + + _BEDROCK_MODEL_VERSION_SUFFIX_RE: Final = re.compile(r"-v\d+(?::\d+)?$") _DEPLOYMENT_MODEL_INFO: Final = TypeAdapter(dict[str, object]) 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 26a0302ef61..a89a6c97249 100644 --- a/litellm/llms/bedrock/messages/invoke_transformations/anthropic_claude3_transformation.py +++ b/litellm/llms/bedrock/messages/invoke_transformations/anthropic_claude3_transformation.py @@ -34,11 +34,11 @@ from litellm.llms.bedrock.common_utils import ( bedrock_supports_tool_search, ensure_bedrock_anthropic_messages_tool_names, get_anthropic_beta_from_headers, - is_claude_4_5_on_bedrock, normalize_bedrock_opus_output_config_effort, normalize_custom_field_on_tools, normalize_tool_input_schema_types_for_bedrock_invoke, strip_unsupported_bedrock_invoke_output_config_keys, + supports_bedrock_prompt_cache_ttl, tools_without_eager_input_streaming, ) from litellm.llms.bedrock.request_metadata import ( @@ -169,9 +169,9 @@ class AmazonAnthropicClaudeMessagesConfig( anthropic_messages_request: The request dictionary to modify in-place model: The model name to check if it supports ttl """ - is_claude_4_5 = False + supports_ttl = False if model: - is_claude_4_5 = self._is_claude_4_5_on_bedrock(model) + supports_ttl = self._supports_prompt_cache_ttl(model) def _sanitize_cache_control(cache_control: dict) -> None: if not isinstance(cache_control, dict): @@ -181,7 +181,7 @@ class AmazonAnthropicClaudeMessagesConfig( # Remove ttl for models that don't support it if "ttl" in cache_control: ttl: Final = cache_control["ttl"] - if is_claude_4_5 and ttl in ["5m", "1h"]: + if supports_ttl and ttl in ["5m", "1h"]: return cache_control.pop("ttl", None) @@ -386,19 +386,9 @@ class AmazonAnthropicClaudeMessagesConfig( ] return any(pattern in model_lower for pattern in opus_4_5_patterns) - def _is_claude_4_5_on_bedrock(self, model: str) -> bool: - """ - Check if the model is Claude 4.5 on Bedrock. - - Claude Sonnet 4.5, Haiku 4.5, and Opus 4.5 support 1-hour prompt caching. - - Args: - model: The model name - - Returns: - True if the model is Claude 4.5 - """ - return is_claude_4_5_on_bedrock(model) + def _supports_prompt_cache_ttl(self, model: str) -> bool: + """Whether Bedrock accepts a ``ttl`` on prompt ``cache_control`` for ``model``.""" + return supports_bedrock_prompt_cache_ttl(model) def _supports_tool_search_on_bedrock(self, model: str) -> bool: """ diff --git a/tests/unit/llms/bedrock/chat/test_converse_transformation.py b/tests/unit/llms/bedrock/chat/test_converse_transformation.py index c68f8a59333..469d06f7205 100644 --- a/tests/unit/llms/bedrock/chat/test_converse_transformation.py +++ b/tests/unit/llms/bedrock/chat/test_converse_transformation.py @@ -5347,12 +5347,12 @@ def test_parallel_tool_calls_flag_decoupled_from_ttl_pricing(monkeypatch): The disable_parallel_tool_use gate must read supports_parallel_tool_use_config, not the 1h-TTL pricing field: a model carrying only the former still gets the flag. """ - from litellm.llms.bedrock.common_utils import is_claude_4_5_on_bedrock + from litellm.llms.bedrock.common_utils import supports_bedrock_prompt_cache_ttl config = AmazonConverseConfig() model = "anthropic.claude-parallel-tool-use-only" monkeypatch.setitem(litellm.model_cost, model, {"supports_parallel_tool_use_config": True}) - assert is_claude_4_5_on_bedrock(model) is False + assert supports_bedrock_prompt_cache_ttl(model) is False messages = [{"role": "user", "content": "What's the weather in SF and NYC?"}] optional_params = config.map_openai_params( @@ -5924,7 +5924,7 @@ def test_cache_control_injection_tool_config_honors_ttl_for_supported_model(monk cache_control behavior, instead of always emitting a bare {"type": "default"} cachePoint with no ttl. - Forces the bundled local cost map so `is_claude_4_5_on_bedrock` (which + Forces the bundled local cost map so `supports_bedrock_prompt_cache_ttl` (which reads `cache_creation_input_token_cost_above_1hr` from litellm.model_cost) sees this branch's pricing data rather than the network-fetched `main` copy, which lacks it until merge. diff --git a/tests/unit/llms/bedrock/test_bedrock_common_utils.py b/tests/unit/llms/bedrock/test_bedrock_common_utils.py index 3a2c851807c..d10ce9d1fae 100644 --- a/tests/unit/llms/bedrock/test_bedrock_common_utils.py +++ b/tests/unit/llms/bedrock/test_bedrock_common_utils.py @@ -473,7 +473,7 @@ def test_capability_lookups_fall_back_to_base_model_when_regional_entry_lacks_fi import litellm from litellm.llms.bedrock.common_utils import ( bedrock_converse_supports_parallel_tool_use_config, - is_claude_4_5_on_bedrock, + supports_bedrock_prompt_cache_ttl, ) base = "anthropic.claude-fallback-test" @@ -488,10 +488,30 @@ def test_capability_lookups_fall_back_to_base_model_when_regional_entry_lacks_fi }, ) - assert is_claude_4_5_on_bedrock(regional) is True + assert supports_bedrock_prompt_cache_ttl(regional) is True assert bedrock_converse_supports_parallel_tool_use_config(regional) is True +def test_deprecated_is_claude_4_5_on_bedrock_warns_and_matches_new_name(monkeypatch): + import litellm + from litellm.llms.bedrock.common_utils import ( + is_claude_4_5_on_bedrock, + supports_bedrock_prompt_cache_ttl, + ) + + with_ttl = "anthropic.claude-ttl-supported" + without_ttl = "anthropic.claude-ttl-unsupported" + monkeypatch.setitem(litellm.model_cost, with_ttl, {"cache_creation_input_token_cost_above_1hr": 1e-05}) + monkeypatch.setitem(litellm.model_cost, without_ttl, {"input_cost_per_token": 1e-06}) + + with pytest.warns(UserWarning, match="supports_bedrock_prompt_cache_ttl"): + assert is_claude_4_5_on_bedrock(with_ttl) is True + with pytest.warns(UserWarning, match="supports_bedrock_prompt_cache_ttl"): + assert is_claude_4_5_on_bedrock(without_ttl) is False + assert supports_bedrock_prompt_cache_ttl(with_ttl) is True + assert supports_bedrock_prompt_cache_ttl(without_ttl) is False + + @pytest.mark.parametrize( ("entry", "expected"), [