diff --git a/litellm/constants.py b/litellm/constants.py index 871b7e5a80b..9a10a8b6f3a 100644 --- a/litellm/constants.py +++ b/litellm/constants.py @@ -177,6 +177,9 @@ DEFAULT_REASONING_EFFORT_MEDIUM_THINKING_BUDGET = int( DEFAULT_REASONING_EFFORT_HIGH_THINKING_BUDGET = int( os.getenv("DEFAULT_REASONING_EFFORT_HIGH_THINKING_BUDGET", 4096) ) +DEFAULT_REASONING_EFFORT_XHIGH_THINKING_BUDGET = int( + os.getenv("DEFAULT_REASONING_EFFORT_XHIGH_THINKING_BUDGET", 10000) +) MAX_TOKEN_TRIMMING_ATTEMPTS = int( os.getenv("MAX_TOKEN_TRIMMING_ATTEMPTS", 10) ) # Maximum number of attempts to trim the message diff --git a/litellm/llms/anthropic/chat/transformation.py b/litellm/llms/anthropic/chat/transformation.py index c5041e21c4a..4e5a7d69043 100644 --- a/litellm/llms/anthropic/chat/transformation.py +++ b/litellm/llms/anthropic/chat/transformation.py @@ -13,6 +13,7 @@ from litellm.constants import ( DEFAULT_REASONING_EFFORT_LOW_THINKING_BUDGET, DEFAULT_REASONING_EFFORT_MEDIUM_THINKING_BUDGET, DEFAULT_REASONING_EFFORT_MINIMAL_THINKING_BUDGET, + DEFAULT_REASONING_EFFORT_XHIGH_THINKING_BUDGET, RESPONSE_FORMAT_TOOL_NAME, ) from litellm.litellm_core_utils.core_helpers import map_finish_reason @@ -55,7 +56,10 @@ from litellm.types.utils import ( CompletionTokensDetailsWrapper, ) from litellm.types.utils import Message as LitellmMessage -from litellm.types.utils import PromptTokensDetailsWrapper, ServerToolUse +from litellm.types.utils import ( + PromptTokensDetailsWrapper, + ServerToolUse, +) from litellm.utils import ( ModelResponse, Usage, @@ -729,32 +733,30 @@ class AnthropicConfig(AnthropicModelInfo, BaseConfig): ) -> Optional[AnthropicThinkingParam]: if reasoning_effort is None or reasoning_effort == "none": return None + effort_to_budget = { + "minimal": DEFAULT_REASONING_EFFORT_MINIMAL_THINKING_BUDGET, + "low": DEFAULT_REASONING_EFFORT_LOW_THINKING_BUDGET, + "medium": DEFAULT_REASONING_EFFORT_MEDIUM_THINKING_BUDGET, + "high": DEFAULT_REASONING_EFFORT_HIGH_THINKING_BUDGET, + "xhigh": DEFAULT_REASONING_EFFORT_XHIGH_THINKING_BUDGET, + } if AnthropicConfig._is_claude_4_6_model(model): + budget_tokens = effort_to_budget.get(reasoning_effort) + if budget_tokens is not None: + return AnthropicThinkingParam( + type="adaptive", + budget_tokens=budget_tokens, + ) return AnthropicThinkingParam( type="adaptive", ) - elif reasoning_effort == "low": + budget_tokens = effort_to_budget.get(reasoning_effort) + if budget_tokens is not None: return AnthropicThinkingParam( type="enabled", - budget_tokens=DEFAULT_REASONING_EFFORT_LOW_THINKING_BUDGET, + budget_tokens=budget_tokens, ) - elif reasoning_effort == "medium": - return AnthropicThinkingParam( - type="enabled", - budget_tokens=DEFAULT_REASONING_EFFORT_MEDIUM_THINKING_BUDGET, - ) - elif reasoning_effort == "high": - return AnthropicThinkingParam( - type="enabled", - budget_tokens=DEFAULT_REASONING_EFFORT_HIGH_THINKING_BUDGET, - ) - elif reasoning_effort == "minimal": - return AnthropicThinkingParam( - type="enabled", - budget_tokens=DEFAULT_REASONING_EFFORT_MINIMAL_THINKING_BUDGET, - ) - else: - raise ValueError(f"Unmapped reasoning effort: {reasoning_effort}") + raise ValueError(f"Unmapped reasoning effort: {reasoning_effort}") def _extract_json_schema_from_response_format( self, value: Optional[dict] @@ -1014,6 +1016,7 @@ class AnthropicConfig(AnthropicModelInfo, BaseConfig): "minimal": "low", "medium": "medium", "high": "high", + "xhigh": "max", "max": "max", } mapped_effort = effort_map.get(value, value) diff --git a/litellm/model_prices_and_context_window_backup.json b/litellm/model_prices_and_context_window_backup.json index f52288ea72a..cbd64a178b8 100644 --- a/litellm/model_prices_and_context_window_backup.json +++ b/litellm/model_prices_and_context_window_backup.json @@ -16289,7 +16289,7 @@ "cache_read_input_token_cost": 3e-08, "input_cost_per_audio_token": 1e-06, "input_cost_per_token": 3e-07, - "litellm_provider": "vertex_ai-language-models", + "litellm_provider": "gemini", "max_audio_length_hours": 8.4, "max_audio_per_prompt": 1, "supports_reasoning": false, diff --git a/tests/litellm/llms/anthropic/test_anthropic_reasoning_effort.py b/tests/litellm/llms/anthropic/test_anthropic_reasoning_effort.py index 98ae7148c77..305555de0d5 100644 --- a/tests/litellm/llms/anthropic/test_anthropic_reasoning_effort.py +++ b/tests/litellm/llms/anthropic/test_anthropic_reasoning_effort.py @@ -28,12 +28,28 @@ class TestMapReasoningEffort: reasoning_effort="low", model="claude-opus-4-6" ) assert result["type"] == "adaptive" + assert result["budget_tokens"] == 1024 def test_opus_4_6_returns_adaptive_for_high(self): result = AnthropicConfig._map_reasoning_effort( reasoning_effort="high", model="claude-opus-4-6" ) assert result["type"] == "adaptive" + assert result["budget_tokens"] == 4096 + + def test_opus_4_6_returns_adaptive_for_medium(self): + result = AnthropicConfig._map_reasoning_effort( + reasoning_effort="medium", model="claude-opus-4-6" + ) + assert result["type"] == "adaptive" + assert result["budget_tokens"] == 2048 + + def test_opus_4_6_returns_adaptive_for_minimal(self): + result = AnthropicConfig._map_reasoning_effort( + reasoning_effort="minimal", model="claude-opus-4-6" + ) + assert result["type"] == "adaptive" + assert result["budget_tokens"] == 128 def test_other_model_low_returns_enabled_with_budget(self): result = AnthropicConfig._map_reasoning_effort( @@ -62,3 +78,17 @@ class TestMapReasoningEffort: reasoning_effort="none", model="claude-4-sonnet-20250514" ) assert result is None + + def test_opus_4_6_returns_adaptive_with_budget_for_xhigh(self): + result = AnthropicConfig._map_reasoning_effort( + reasoning_effort="xhigh", model="claude-opus-4-6" + ) + assert result["type"] == "adaptive" + assert result["budget_tokens"] == 10000 + + def test_other_model_xhigh_returns_enabled_with_budget(self): + result = AnthropicConfig._map_reasoning_effort( + reasoning_effort="xhigh", model="claude-4-sonnet-20250514" + ) + assert result["type"] == "enabled" + assert result["budget_tokens"] == 10000