diff --git a/litellm/llms/anthropic/chat/transformation.py b/litellm/llms/anthropic/chat/transformation.py index a971bf1e961..7f16c69481f 100644 --- a/litellm/llms/anthropic/chat/transformation.py +++ b/litellm/llms/anthropic/chat/transformation.py @@ -920,9 +920,7 @@ class AnthropicConfig(AnthropicModelInfo, BaseConfig): """ if reasoning_effort is None or reasoning_effort == "none": return None - if AnthropicConfig._is_claude_4_6_model( - model - ) or AnthropicConfig._is_claude_4_7_model(model): + if AnthropicConfig._is_adaptive_thinking_model(model): return AnthropicThinkingParam( type="adaptive", ) @@ -1262,11 +1260,12 @@ class AnthropicConfig(AnthropicModelInfo, BaseConfig): optional_params.pop("output_config", None) else: optional_params["thinking"] = mapped_thinking - # For Claude 4.6+ models, effort is controlled via output_config, - # not thinking budget_tokens. Map reasoning_effort to output_config. - if AnthropicConfig._is_claude_4_6_model( - model - ) or AnthropicConfig._is_claude_4_7_model(model): + # For Claude 4.6+ adaptive-thinking models, effort is + # controlled via ``output_config``, not + # ``thinking.budget_tokens``. Driven by + # ``supports_adaptive_thinking`` in the model map so + # adding a new adaptive Claude is a model-map-only change. + if AnthropicConfig._is_adaptive_thinking_model(model): # ``_map_reasoning_effort`` returns ``type=adaptive`` # for any string on adaptive models without checking # the value, so reject unmapped efforts here (matching diff --git a/litellm/llms/anthropic/common_utils.py b/litellm/llms/anthropic/common_utils.py index b095d40156d..d711d05c687 100644 --- a/litellm/llms/anthropic/common_utils.py +++ b/litellm/llms/anthropic/common_utils.py @@ -273,7 +273,26 @@ class AnthropicModelInfo(BaseLLMModelInfo): @staticmethod def _is_adaptive_thinking_model(model: str) -> bool: - """Claude 4.6+ models use adaptive thinking with output_config effort.""" + """Claude 4.6+ models use adaptive thinking with ``output_config.effort``. + + Driven by the ``supports_adaptive_thinking`` flag in + ``model_prices_and_context_window.json`` so that adding a new + adaptive-thinking model is a pure model-map change. Falls back to + the family-pattern check for OpenRouter / Vercel / Bedrock / + provider-prefixed variants whose model-map entries don't (yet) + carry the flag. + """ + from litellm.utils import _supports_factory + + try: + if _supports_factory( + model=model, + custom_llm_provider=None, + key="supports_adaptive_thinking", + ): + return True + except Exception: + pass return AnthropicModelInfo._is_claude_4_6_model( model ) or AnthropicModelInfo._is_claude_4_7_model(model) diff --git a/litellm/llms/bedrock/chat/converse_transformation.py b/litellm/llms/bedrock/chat/converse_transformation.py index d3dff398a6e..5a0087a270e 100644 --- a/litellm/llms/bedrock/chat/converse_transformation.py +++ b/litellm/llms/bedrock/chat/converse_transformation.py @@ -478,14 +478,15 @@ class AmazonConverseConfig(BaseConfig): optional_params.pop("output_config", None) else: optional_params["thinking"] = mapped_thinking - # Adaptive-thinking models (Claude 4.6 / 4.7) take the tier - # via output_config.effort. Mirror the mapping used by - # AnthropicConfig.map_openai_params and apply the same - # validation rules so unmapped/garbage efforts surface as a - # 400 instead of being silently flattened on the wire. - if AnthropicConfig._is_claude_4_6_model( - model - ) or AnthropicConfig._is_claude_4_7_model(model): + # Adaptive-thinking models (Claude 4.6 / 4.7+) take the + # tier via ``output_config.effort``. Mirror the mapping + # used by ``AnthropicConfig.map_openai_params`` and apply + # the same validation rules so unmapped/garbage efforts + # surface as a 400 instead of being silently flattened on + # the wire. Driven by ``supports_adaptive_thinking`` in + # ``model_prices_and_context_window.json`` so a future + # adaptive Claude release lands as a model-map change. + if AnthropicConfig._is_adaptive_thinking_model(model): # Use ``.get()`` without a fallback so unmapped efforts # (e.g. ``"disabled"``) surface as a clean 400 here # rather than leaking the raw garbage string through to diff --git a/litellm/llms/databricks/chat/transformation.py b/litellm/llms/databricks/chat/transformation.py index 34cf174c3fc..4cd3ad9e425 100644 --- a/litellm/llms/databricks/chat/transformation.py +++ b/litellm/llms/databricks/chat/transformation.py @@ -350,15 +350,17 @@ class DatabricksConfig(DatabricksBase, OpenAILikeChatConfig, AnthropicConfig): optional_params.pop("output_config", None) else: optional_params["thinking"] = mapped_thinking - # For Claude 4.6/4.7 adaptive models, ``_map_reasoning_effort`` + # For Claude 4.6+ adaptive models, ``_map_reasoning_effort`` # returns ``type=adaptive`` for ANY non-None / non-"none" # string without validating the value, so reject unmapped # efforts here and set ``output_config.effort`` (matching the # Anthropic native / Bedrock Converse / Bedrock Invoke / - # /v1/messages paths). - if AnthropicConfig._is_claude_4_6_model( - model - ) or AnthropicConfig._is_claude_4_7_model(model): + # /v1/messages paths). Driven by ``supports_adaptive_thinking`` + # in the model map so future adaptive Claudes land via a + # model-map update rather than a code release — the + # Anthropic-native and Bedrock routes already use the same + # helper, so all three paths stay in lock-step. + if AnthropicConfig._is_adaptive_thinking_model(model): # ``reasoning_effort_value`` comes from ``non_default_params`` # so its static type is ``Any | None``. Narrow to ``str`` for # the mapping lookup; non-strings fall through to the diff --git a/model_prices_and_context_window.json b/model_prices_and_context_window.json index 7946e2dceef..9c085c2c992 100644 --- a/model_prices_and_context_window.json +++ b/model_prices_and_context_window.json @@ -9228,6 +9228,7 @@ "search_context_size_low": 0.01, "search_context_size_medium": 0.01 }, + "supports_adaptive_thinking": true, "supports_assistant_prefill": true, "supports_computer_use": true, "supports_function_calling": true, @@ -9421,6 +9422,7 @@ "search_context_size_low": 0.01, "search_context_size_medium": 0.01 }, + "supports_adaptive_thinking": true, "supports_assistant_prefill": false, "supports_computer_use": true, "supports_function_calling": true, @@ -9454,6 +9456,7 @@ "search_context_size_low": 0.01, "search_context_size_medium": 0.01 }, + "supports_adaptive_thinking": true, "supports_assistant_prefill": false, "supports_computer_use": true, "supports_function_calling": true, @@ -9487,6 +9490,7 @@ "search_context_size_low": 0.01, "search_context_size_medium": 0.01 }, + "supports_adaptive_thinking": true, "supports_assistant_prefill": false, "supports_computer_use": true, "supports_function_calling": true, @@ -9521,6 +9525,7 @@ "search_context_size_low": 0.01, "search_context_size_medium": 0.01 }, + "supports_adaptive_thinking": true, "supports_assistant_prefill": false, "supports_computer_use": true, "supports_function_calling": true,