From 5aff0e4da6175e1cbfba7a2a3f7b135c065c0c92 Mon Sep 17 00:00:00 2001 From: Chesars Date: Thu, 26 Feb 2026 17:25:23 -0300 Subject: [PATCH] refactor: move _is_claude_4_6_model to AnthropicModelInfo, use explicit effort_map - Move _is_claude_4_6_model from AnthropicConfig to AnthropicModelInfo to eliminate duplicated logic in is_effort_used - Use explicit effort_map dict instead of passing unknown values through to output_config --- litellm/llms/anthropic/chat/transformation.py | 25 +++---------------- litellm/llms/anthropic/common_utils.py | 25 +++++++++++-------- 2 files changed, 18 insertions(+), 32 deletions(-) diff --git a/litellm/llms/anthropic/chat/transformation.py b/litellm/llms/anthropic/chat/transformation.py index 2185890b338..17b5afb8437 100644 --- a/litellm/llms/anthropic/chat/transformation.py +++ b/litellm/llms/anthropic/chat/transformation.py @@ -168,24 +168,6 @@ class AnthropicConfig(AnthropicModelInfo, BaseConfig): tool_call["caller"] = cast(Dict[str, Any], anthropic_tool_content["caller"]) # type: ignore[typeddict-item] return tool_call - @staticmethod - def _is_claude_4_6_model(model: str) -> bool: - """Check if the model is a Claude 4.6 model that uses adaptive thinking.""" - model_lower = model.lower() - return any( - model_variant in model_lower - for model_variant in ( - "opus-4-6", - "opus_4_6", - "opus-4.6", - "opus_4.6", - "sonnet-4-6", - "sonnet_4_6", - "sonnet-4.6", - "sonnet_4.6", - ) - ) - @staticmethod def _is_opus_4_6_model(model: str) -> bool: """Check if the model is specifically Claude Opus 4.6.""" @@ -1017,9 +999,10 @@ class AnthropicConfig(AnthropicModelInfo, BaseConfig): ) if AnthropicConfig._is_claude_4_6_model(model): # Map reasoning_effort to Anthropic's output_config for 4.6 models - # "minimal" has no Anthropic equivalent → map to "low" - anthropic_effort = value if value != "minimal" else "low" - optional_params["output_config"] = {"effort": anthropic_effort} + effort_map = {"minimal": "low", "low": "low", "medium": "medium", "high": "high", "max": "max"} + anthropic_effort = effort_map.get(value) + if anthropic_effort is not None: + optional_params["output_config"] = {"effort": anthropic_effort} elif param == "web_search_options" and isinstance(value, dict): hosted_web_search_tool = self.map_web_search_tool( cast(OpenAIWebSearchOptions, value) diff --git a/litellm/llms/anthropic/common_utils.py b/litellm/llms/anthropic/common_utils.py index bf6a7bd63ca..a1a47daf6d4 100644 --- a/litellm/llms/anthropic/common_utils.py +++ b/litellm/llms/anthropic/common_utils.py @@ -224,6 +224,18 @@ class AnthropicModelInfo(BaseLLMModelInfo): return False + @staticmethod + def _is_claude_4_6_model(model: str) -> bool: + """Check if the model is a Claude 4.6 model (Opus 4.6 or Sonnet 4.6).""" + model_lower = model.lower() + return any( + v in model_lower + for v in ( + "opus-4-6", "opus_4_6", "opus-4.6", "opus_4.6", + "sonnet-4-6", "sonnet_4_6", "sonnet-4.6", "sonnet_4.6", + ) + ) + def is_effort_used( self, optional_params: Optional[dict], model: Optional[str] = None ) -> bool: @@ -238,17 +250,8 @@ class AnthropicModelInfo(BaseLLMModelInfo): return False # Claude 4.6 models use output_config as a stable API feature — no beta header needed - if model: - model_lower = model.lower() - is_4_6 = any( - v in model_lower - for v in ( - "opus-4-6", "opus_4_6", "opus-4.6", "opus_4.6", - "sonnet-4-6", "sonnet_4_6", "sonnet-4.6", "sonnet_4.6", - ) - ) - if is_4_6: - return False + if model and self._is_claude_4_6_model(model): + return False # Check if reasoning_effort is provided for Claude Opus 4.5 if model and ("opus-4-5" in model.lower() or "opus_4_5" in model.lower()):