diff --git a/litellm/llms/databricks/chat/transformation.py b/litellm/llms/databricks/chat/transformation.py index 09aaf970dc5d..82c3b5d91d3b 100644 --- a/litellm/llms/databricks/chat/transformation.py +++ b/litellm/llms/databricks/chat/transformation.py @@ -272,6 +272,15 @@ def get_supported_openai_params(self, model: str | None = None) -> list: "thinking", ] + @staticmethod + def _uses_anthropic_thinking_param(model: str) -> bool: + from litellm.utils import supports_anthropic_thinking_payload + + normalized: Final = model.lower().replace(".", "-") + return "claude" in normalized or supports_anthropic_thinking_payload( + model=normalized, custom_llm_provider="databricks" + ) + def convert_anthropic_tool_to_databricks_tool(self, tool: AllAnthropicToolsValues | None) -> DatabricksTool | None: if tool is None: return None @@ -377,7 +386,7 @@ def map_openai_params( "response_format", None ) # unsupported for claude models - if json_schema -> convert to tool call - if "reasoning_effort" in non_default_params and "claude" in model: + if "reasoning_effort" in non_default_params and self._uses_anthropic_thinking_param(model): reasoning_effort_value: Final = non_default_params.get("reasoning_effort") mapped_thinking: Final = AnthropicConfig._map_reasoning_effort( reasoning_effort=reasoning_effort_value, diff --git a/litellm/model_prices_and_context_window_backup.json b/litellm/model_prices_and_context_window_backup.json index 7fa09951eaed..39856b0d933a 100644 --- a/litellm/model_prices_and_context_window_backup.json +++ b/litellm/model_prices_and_context_window_backup.json @@ -17597,6 +17597,7 @@ "supports_function_calling": true, "supports_prompt_caching": true, "supports_reasoning": true, + "supports_anthropic_thinking_payload": true, "supports_tool_choice": true }, "databricks/databricks-claude-fable-5": { @@ -17622,6 +17623,7 @@ "supports_mid_conversation_system": true, "supports_prompt_caching": true, "supports_reasoning": true, + "supports_anthropic_thinking_payload": true, "supports_sampling_params": false, "supports_tool_choice": true, "supports_vision": false, @@ -17651,6 +17653,7 @@ "supports_mid_conversation_system": true, "supports_prompt_caching": true, "supports_reasoning": true, + "supports_anthropic_thinking_payload": true, "supports_sampling_params": false, "supports_tool_choice": true, "supports_vision": true, @@ -17676,6 +17679,7 @@ "supports_function_calling": true, "supports_prompt_caching": true, "supports_reasoning": true, + "supports_anthropic_thinking_payload": true, "supports_tool_choice": true, "prompt_cache_min_tokens": 4096 }, @@ -17699,6 +17703,7 @@ "supports_function_calling": true, "supports_prompt_caching": true, "supports_reasoning": true, + "supports_anthropic_thinking_payload": true, "supports_tool_choice": true, "prompt_cache_min_tokens": 1024 }, @@ -17722,6 +17727,7 @@ "supports_function_calling": true, "supports_prompt_caching": true, "supports_reasoning": true, + "supports_anthropic_thinking_payload": true, "supports_tool_choice": true, "prompt_cache_min_tokens": 1024 }, @@ -17745,6 +17751,7 @@ "supports_function_calling": true, "supports_prompt_caching": true, "supports_reasoning": true, + "supports_anthropic_thinking_payload": true, "supports_tool_choice": true, "supports_output_config": true, "prompt_cache_min_tokens": 4096 @@ -17770,6 +17777,7 @@ "supports_legacy_thinking": true, "supports_prompt_caching": true, "supports_reasoning": true, + "supports_anthropic_thinking_payload": true, "supports_tool_choice": true, "prompt_cache_min_tokens": 4096 }, @@ -17795,6 +17803,7 @@ "supports_function_calling": true, "supports_prompt_caching": true, "supports_reasoning": true, + "supports_anthropic_thinking_payload": true, "supports_sampling_params": false, "supports_tool_choice": true, "supports_vision": true @@ -17822,6 +17831,7 @@ "supports_mid_conversation_system": true, "supports_prompt_caching": true, "supports_reasoning": true, + "supports_anthropic_thinking_payload": true, "supports_sampling_params": false, "supports_tool_choice": true, "supports_vision": true @@ -17849,6 +17859,7 @@ "supports_mid_conversation_system": true, "supports_prompt_caching": true, "supports_reasoning": true, + "supports_anthropic_thinking_payload": true, "supports_sampling_params": false, "supports_tool_choice": true, "supports_vision": true @@ -17875,6 +17886,7 @@ "supports_function_calling": true, "supports_prompt_caching": true, "supports_reasoning": true, + "supports_anthropic_thinking_payload": true, "supports_tool_choice": true }, "databricks/databricks-claude-sonnet-4-1": { @@ -17897,6 +17909,7 @@ "supports_function_calling": true, "supports_prompt_caching": true, "supports_reasoning": true, + "supports_anthropic_thinking_payload": true, "supports_tool_choice": true }, "databricks/databricks-claude-sonnet-4-5": { @@ -17919,6 +17932,7 @@ "supports_function_calling": true, "supports_prompt_caching": true, "supports_reasoning": true, + "supports_anthropic_thinking_payload": true, "supports_tool_choice": true, "prompt_cache_min_tokens": 1024 }, @@ -17943,6 +17957,7 @@ "supports_legacy_thinking": true, "supports_prompt_caching": true, "supports_reasoning": true, + "supports_anthropic_thinking_payload": true, "supports_tool_choice": true, "prompt_cache_min_tokens": 1024 }, @@ -17969,6 +17984,7 @@ "supports_mid_conversation_system": true, "supports_prompt_caching": true, "supports_reasoning": true, + "supports_anthropic_thinking_payload": true, "supports_sampling_params": false, "supports_tool_choice": true, "supports_vision": true @@ -18047,6 +18063,7 @@ "output_dbu_cost_per_token": 3.5714e-05, "source": "https://www.databricks.com/product/pricing/proprietary-foundation-model-serving", "supports_function_calling": true, + "supports_anthropic_thinking_payload": true, "supports_prompt_caching": true, "supports_tool_choice": true }, @@ -18067,6 +18084,7 @@ "output_dbu_cost_per_token": 0.000142857, "source": "https://www.databricks.com/product/pricing/proprietary-foundation-model-serving", "supports_function_calling": true, + "supports_anthropic_thinking_payload": true, "supports_prompt_caching": true, "supports_tool_choice": true }, diff --git a/litellm/types/utils.py b/litellm/types/utils.py index e3ea37dc0c8c..8391fbf61c82 100644 --- a/litellm/types/utils.py +++ b/litellm/types/utils.py @@ -169,6 +169,7 @@ class ProviderSpecificModelInfo(TypedDict, total=False): default_reasoning_effort: ReadOnly[Literal["none", "minimal", "low", "medium", "high", "xhigh"] | None] supports_output_config: bool | None supports_image_size: bool | None + supports_anthropic_thinking_payload: ReadOnly[bool | None] supported_audio_formats: ReadOnly[Sequence[Literal["mp3", "wav"]] | None] vertex_ai_audio_api: ReadOnly[Literal["lyria_predict", "lyria_interactions"] | None] bedrock_output_config_effort_ceiling: Literal["low", "medium", "high", "max", "xhigh"] | None diff --git a/litellm/utils.py b/litellm/utils.py index 1a77655a5a45..c52a8fbc42b9 100644 --- a/litellm/utils.py +++ b/litellm/utils.py @@ -2850,6 +2850,12 @@ def supports_reasoning(model: str, custom_llm_provider: str | None = None) -> bo return _supports_factory(model=model, custom_llm_provider=custom_llm_provider, key="supports_reasoning") +def supports_anthropic_thinking_payload(model: str, custom_llm_provider: str | None = None) -> bool: + return _supports_factory( + model=model, custom_llm_provider=custom_llm_provider, key="supports_anthropic_thinking_payload" + ) + + def supports_none_reasoning_effort(model: str, custom_llm_provider: str | None = None) -> bool: """ Check if the given model accepts reasoning effort "none" and return a boolean value. @@ -5996,6 +6002,7 @@ def _get_model_info_helper( thinking_always_on=_model_info.get("thinking_always_on", None), supports_tool_search=_model_info.get("supports_tool_search", None), supports_mid_conversation_system=_model_info.get("supports_mid_conversation_system", None), + supports_anthropic_thinking_payload=_model_info.get("supports_anthropic_thinking_payload", None), supports_none_reasoning_effort=_model_info.get("supports_none_reasoning_effort", None), supports_minimal_reasoning_effort=_model_info.get("supports_minimal_reasoning_effort", None), supports_low_reasoning_effort=_model_info.get("supports_low_reasoning_effort", None), diff --git a/model_prices_and_context_window.json b/model_prices_and_context_window.json index 7fa09951eaed..39856b0d933a 100644 --- a/model_prices_and_context_window.json +++ b/model_prices_and_context_window.json @@ -17597,6 +17597,7 @@ "supports_function_calling": true, "supports_prompt_caching": true, "supports_reasoning": true, + "supports_anthropic_thinking_payload": true, "supports_tool_choice": true }, "databricks/databricks-claude-fable-5": { @@ -17622,6 +17623,7 @@ "supports_mid_conversation_system": true, "supports_prompt_caching": true, "supports_reasoning": true, + "supports_anthropic_thinking_payload": true, "supports_sampling_params": false, "supports_tool_choice": true, "supports_vision": false, @@ -17651,6 +17653,7 @@ "supports_mid_conversation_system": true, "supports_prompt_caching": true, "supports_reasoning": true, + "supports_anthropic_thinking_payload": true, "supports_sampling_params": false, "supports_tool_choice": true, "supports_vision": true, @@ -17676,6 +17679,7 @@ "supports_function_calling": true, "supports_prompt_caching": true, "supports_reasoning": true, + "supports_anthropic_thinking_payload": true, "supports_tool_choice": true, "prompt_cache_min_tokens": 4096 }, @@ -17699,6 +17703,7 @@ "supports_function_calling": true, "supports_prompt_caching": true, "supports_reasoning": true, + "supports_anthropic_thinking_payload": true, "supports_tool_choice": true, "prompt_cache_min_tokens": 1024 }, @@ -17722,6 +17727,7 @@ "supports_function_calling": true, "supports_prompt_caching": true, "supports_reasoning": true, + "supports_anthropic_thinking_payload": true, "supports_tool_choice": true, "prompt_cache_min_tokens": 1024 }, @@ -17745,6 +17751,7 @@ "supports_function_calling": true, "supports_prompt_caching": true, "supports_reasoning": true, + "supports_anthropic_thinking_payload": true, "supports_tool_choice": true, "supports_output_config": true, "prompt_cache_min_tokens": 4096 @@ -17770,6 +17777,7 @@ "supports_legacy_thinking": true, "supports_prompt_caching": true, "supports_reasoning": true, + "supports_anthropic_thinking_payload": true, "supports_tool_choice": true, "prompt_cache_min_tokens": 4096 }, @@ -17795,6 +17803,7 @@ "supports_function_calling": true, "supports_prompt_caching": true, "supports_reasoning": true, + "supports_anthropic_thinking_payload": true, "supports_sampling_params": false, "supports_tool_choice": true, "supports_vision": true @@ -17822,6 +17831,7 @@ "supports_mid_conversation_system": true, "supports_prompt_caching": true, "supports_reasoning": true, + "supports_anthropic_thinking_payload": true, "supports_sampling_params": false, "supports_tool_choice": true, "supports_vision": true @@ -17849,6 +17859,7 @@ "supports_mid_conversation_system": true, "supports_prompt_caching": true, "supports_reasoning": true, + "supports_anthropic_thinking_payload": true, "supports_sampling_params": false, "supports_tool_choice": true, "supports_vision": true @@ -17875,6 +17886,7 @@ "supports_function_calling": true, "supports_prompt_caching": true, "supports_reasoning": true, + "supports_anthropic_thinking_payload": true, "supports_tool_choice": true }, "databricks/databricks-claude-sonnet-4-1": { @@ -17897,6 +17909,7 @@ "supports_function_calling": true, "supports_prompt_caching": true, "supports_reasoning": true, + "supports_anthropic_thinking_payload": true, "supports_tool_choice": true }, "databricks/databricks-claude-sonnet-4-5": { @@ -17919,6 +17932,7 @@ "supports_function_calling": true, "supports_prompt_caching": true, "supports_reasoning": true, + "supports_anthropic_thinking_payload": true, "supports_tool_choice": true, "prompt_cache_min_tokens": 1024 }, @@ -17943,6 +17957,7 @@ "supports_legacy_thinking": true, "supports_prompt_caching": true, "supports_reasoning": true, + "supports_anthropic_thinking_payload": true, "supports_tool_choice": true, "prompt_cache_min_tokens": 1024 }, @@ -17969,6 +17984,7 @@ "supports_mid_conversation_system": true, "supports_prompt_caching": true, "supports_reasoning": true, + "supports_anthropic_thinking_payload": true, "supports_sampling_params": false, "supports_tool_choice": true, "supports_vision": true @@ -18047,6 +18063,7 @@ "output_dbu_cost_per_token": 3.5714e-05, "source": "https://www.databricks.com/product/pricing/proprietary-foundation-model-serving", "supports_function_calling": true, + "supports_anthropic_thinking_payload": true, "supports_prompt_caching": true, "supports_tool_choice": true }, @@ -18067,6 +18084,7 @@ "output_dbu_cost_per_token": 0.000142857, "source": "https://www.databricks.com/product/pricing/proprietary-foundation-model-serving", "supports_function_calling": true, + "supports_anthropic_thinking_payload": true, "supports_prompt_caching": true, "supports_tool_choice": true }, diff --git a/model_prices_and_context_window.schema.json b/model_prices_and_context_window.schema.json index 7ed1e7e568bc..d1ac3e67b2bc 100644 --- a/model_prices_and_context_window.schema.json +++ b/model_prices_and_context_window.schema.json @@ -682,6 +682,9 @@ "supports_adaptive_thinking": { "type": "boolean" }, + "supports_anthropic_thinking_payload": { + "type": "boolean" + }, "supports_assistant_prefill": { "type": "boolean" }, diff --git a/tests/test_litellm/llms/databricks/chat/test_databricks_chat_transformation.py b/tests/test_litellm/llms/databricks/chat/test_databricks_chat_transformation.py index caf7bed73850..52bb89fed5a9 100644 --- a/tests/test_litellm/llms/databricks/chat/test_databricks_chat_transformation.py +++ b/tests/test_litellm/llms/databricks/chat/test_databricks_chat_transformation.py @@ -5,6 +5,12 @@ from unittest.mock import MagicMock, patch +import litellm +from litellm.constants import ( + DEFAULT_REASONING_EFFORT_HIGH_THINKING_BUDGET, + DEFAULT_REASONING_EFFORT_LOW_THINKING_BUDGET, + DEFAULT_REASONING_EFFORT_MEDIUM_THINKING_BUDGET, +) from litellm.llms.databricks.chat.transformation import ( DatabricksChatResponseIterator, DatabricksConfig, @@ -12,6 +18,12 @@ ) +@pytest.fixture() +def _use_local_model_cost_map(monkeypatch): + monkeypatch.setenv("LITELLM_LOCAL_MODEL_COST_MAP", "True") + monkeypatch.setattr(litellm, "model_cost", litellm.get_model_cost_map(url="")) + + def test_transform_choices(): config = DatabricksConfig() databricks_choices = [ @@ -532,6 +544,115 @@ def test_map_openai_params_upgrades_legacy_thinking_on_adaptive_only_claude( assert mapped.get("output_config") == expected_output_config +def _map_reasoning_effort(model: str, reasoning_effort: str): + return DatabricksConfig().map_openai_params( + non_default_params={"reasoning_effort": reasoning_effort}, + optional_params={}, + model=model, + drop_params=False, + ) + + +def test_claude_translates_reasoning_effort_to_thinking(_use_local_model_cost_map): + params = _map_reasoning_effort("databricks-claude-3-7-sonnet", "low") + assert params.get("thinking") == { + "type": "enabled", + "budget_tokens": DEFAULT_REASONING_EFFORT_LOW_THINKING_BUDGET, + } + assert "reasoning_effort" not in params + + +def test_adaptive_claude_translates_reasoning_effort_to_output_config(_use_local_model_cost_map): + params = _map_reasoning_effort("databricks-claude-opus-4-7", "high") + assert params.get("thinking") == {"type": "adaptive", "display": "summarized"} + assert params.get("output_config") == {"effort": "high"} + assert "reasoning_effort" not in params + + +def test_unmapped_claude_endpoint_still_translates(_use_local_model_cost_map): + params = _map_reasoning_effort("my-claude-serving-endpoint", "low") + assert params.get("thinking") == { + "type": "enabled", + "budget_tokens": DEFAULT_REASONING_EFFORT_LOW_THINKING_BUDGET, + } + assert "reasoning_effort" not in params + + +def test_gemini_2_5_low_translates_to_thinking_budget(_use_local_model_cost_map): + params = _map_reasoning_effort("databricks-gemini-2-5-flash", "low") + assert params.get("thinking") == { + "type": "enabled", + "budget_tokens": DEFAULT_REASONING_EFFORT_LOW_THINKING_BUDGET, + } + assert "reasoning_effort" not in params + + +def test_gemini_2_5_medium_translates_to_thinking_budget(_use_local_model_cost_map): + params = _map_reasoning_effort("databricks-gemini-2-5-flash", "medium") + assert params.get("thinking") == { + "type": "enabled", + "budget_tokens": DEFAULT_REASONING_EFFORT_MEDIUM_THINKING_BUDGET, + } + assert "reasoning_effort" not in params + + +def test_gemini_2_5_high_translates_to_thinking_budget(_use_local_model_cost_map): + params = _map_reasoning_effort("databricks-gemini-2-5-flash", "high") + assert params.get("thinking") == { + "type": "enabled", + "budget_tokens": DEFAULT_REASONING_EFFORT_HIGH_THINKING_BUDGET, + } + assert "reasoning_effort" not in params + + +def test_gemini_2_5_pro_translates_to_thinking_budget(_use_local_model_cost_map): + params = _map_reasoning_effort("databricks-gemini-2-5-pro", "high") + assert params.get("thinking") == { + "type": "enabled", + "budget_tokens": DEFAULT_REASONING_EFFORT_HIGH_THINKING_BUDGET, + } + assert "reasoning_effort" not in params + + +def test_gemini_2_5_with_dot_notation_translates(_use_local_model_cost_map): + params = _map_reasoning_effort("databricks-gemini-2.5-flash", "low") + assert params.get("thinking") == { + "type": "enabled", + "budget_tokens": DEFAULT_REASONING_EFFORT_LOW_THINKING_BUDGET, + } + assert "reasoning_effort" not in params + + +def test_gemini_2_0_does_not_match(_use_local_model_cost_map): + params = _map_reasoning_effort("databricks-gemini-2-0-flash", "low") + assert "thinking" not in params + assert params.get("reasoning_effort") == "low" + + +def test_gemini_2_5_none_drops_thinking_and_reasoning_effort(_use_local_model_cost_map): + params = _map_reasoning_effort("databricks-gemini-2-5-flash", "none") + assert "thinking" not in params + assert "reasoning_effort" not in params + + +def test_gemini_3_passes_reasoning_effort_through(_use_local_model_cost_map): + params = _map_reasoning_effort("databricks-gemini-3-1-pro", "low") + assert params.get("reasoning_effort") == "low" + assert "thinking" not in params + + +def test_gpt_5_passes_reasoning_effort_through(_use_local_model_cost_map): + params = _map_reasoning_effort("databricks-gpt-5-1", "low") + assert params.get("reasoning_effort") == "low" + assert "thinking" not in params + + +def test_gpt_oss_passes_reasoning_effort_through(_use_local_model_cost_map): + params = _map_reasoning_effort("databricks-gpt-oss-120b", "high") + assert params.get("reasoning_effort") == "high" + assert "thinking" not in params + + def _streaming_chunk(usage=None, choices=None): base = { "id": "chatcmpl-test", diff --git a/tests/test_litellm/test_utils.py b/tests/test_litellm/test_utils.py index 19ed31c7b222..0e35ecdc7329 100644 --- a/tests/test_litellm/test_utils.py +++ b/tests/test_litellm/test_utils.py @@ -1150,6 +1150,7 @@ def test_aaamodel_prices_and_context_window_json_is_valid(): "enum": ["none", "minimal", "low", "medium", "high", "xhigh"], }, "supports_adaptive_thinking": {"type": "boolean"}, + "supports_anthropic_thinking_payload": {"type": "boolean"}, "supports_legacy_thinking": {"type": "boolean"}, "thinking_always_on": {"type": "boolean"}, "supports_mid_conversation_system": {"type": "boolean"},