diff --git a/litellm/llms/deepseek/chat/transformation.py b/litellm/llms/deepseek/chat/transformation.py index 566c960333a..d3e0c7b0abd 100644 --- a/litellm/llms/deepseek/chat/transformation.py +++ b/litellm/llms/deepseek/chat/transformation.py @@ -11,7 +11,7 @@ from litellm.litellm_core_utils.prompt_templates.common_utils import ( ) from litellm.secret_managers.main import get_secret_str from litellm.types.llms.openai import AllMessageValues -from litellm.utils import supports_reasoning +from litellm.utils import is_thinking_always_on, supports_reasoning from ...openai.chat.gpt_transformation import OpenAIGPTConfig @@ -127,16 +127,20 @@ class DeepSeekChatConfig(OpenAIGPTConfig): def _thinking_mode_active(self, model: str, optional_params: dict) -> bool: """ - Returns True only when thinking mode is actually active for this request: + Returns True when thinking mode is active for this request: - model supports reasoning (capability check) - - user explicitly passed thinking={"type": "enabled"} (opt-in check) + - AND either the user explicitly opted in with thinking={"type": "enabled"}, + or thinking is on by default for this model and was not explicitly disabled. """ + if not supports_reasoning(model=model, custom_llm_provider="deepseek"): + return False thinking: Final = optional_params.get("thinking") - return ( - supports_reasoning(model=model, custom_llm_provider="deepseek") - and isinstance(thinking, dict) - and thinking.get("type") == "enabled" - ) + thinking_type = thinking.get("type") if isinstance(thinking, dict) else None + if thinking_type == "enabled": + return True + if thinking_type == "disabled": + return False + return is_thinking_always_on(model=model, custom_llm_provider="deepseek") @staticmethod def _drop_unsupported_tools(optional_params: dict) -> dict: diff --git a/litellm/model_prices_and_context_window_backup.json b/litellm/model_prices_and_context_window_backup.json index 9ca8d9e1bac..a744e6aa763 100644 --- a/litellm/model_prices_and_context_window_backup.json +++ b/litellm/model_prices_and_context_window_backup.json @@ -50402,7 +50402,8 @@ "supports_response_schema": true, "supports_system_messages": true, "supports_tool_choice": true, - "supports_vision": false + "supports_vision": false, + "thinking_always_on": true }, "deepseek-v4-pro": { "cache_creation_input_token_cost": 0.0, @@ -50428,7 +50429,8 @@ "supports_response_schema": true, "supports_system_messages": true, "supports_tool_choice": true, - "supports_vision": false + "supports_vision": false, + "thinking_always_on": true }, "deepseek/deepseek-v4-flash": { "cache_creation_input_token_cost": 0.0, @@ -50454,7 +50456,8 @@ "supports_response_schema": true, "supports_system_messages": true, "supports_tool_choice": true, - "supports_vision": false + "supports_vision": false, + "thinking_always_on": true }, "deepseek/deepseek-v4-pro": { "cache_creation_input_token_cost": 0.0, @@ -50480,7 +50483,8 @@ "supports_response_schema": true, "supports_system_messages": true, "supports_tool_choice": true, - "supports_vision": false + "supports_vision": false, + "thinking_always_on": true }, "tencent/deepseek-v4-pro": { "cache_creation_input_token_cost": 0.0, diff --git a/litellm/utils.py b/litellm/utils.py index 802dc151428..31a9a136c42 100644 --- a/litellm/utils.py +++ b/litellm/utils.py @@ -2722,6 +2722,13 @@ 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 is_thinking_always_on(model: str, custom_llm_provider: str | None = None) -> bool: + """ + Check if the given model always runs in thinking mode by default and return a boolean value. + """ + return _supports_factory(model=model, custom_llm_provider=custom_llm_provider, key="thinking_always_on") + + def supports_native_structured_output(model: str, custom_llm_provider: str | None = None) -> bool: """ Check if the given model supports native structured outputs and return a boolean value. diff --git a/model_prices_and_context_window.json b/model_prices_and_context_window.json index 9ca8d9e1bac..a744e6aa763 100644 --- a/model_prices_and_context_window.json +++ b/model_prices_and_context_window.json @@ -50402,7 +50402,8 @@ "supports_response_schema": true, "supports_system_messages": true, "supports_tool_choice": true, - "supports_vision": false + "supports_vision": false, + "thinking_always_on": true }, "deepseek-v4-pro": { "cache_creation_input_token_cost": 0.0, @@ -50428,7 +50429,8 @@ "supports_response_schema": true, "supports_system_messages": true, "supports_tool_choice": true, - "supports_vision": false + "supports_vision": false, + "thinking_always_on": true }, "deepseek/deepseek-v4-flash": { "cache_creation_input_token_cost": 0.0, @@ -50454,7 +50456,8 @@ "supports_response_schema": true, "supports_system_messages": true, "supports_tool_choice": true, - "supports_vision": false + "supports_vision": false, + "thinking_always_on": true }, "deepseek/deepseek-v4-pro": { "cache_creation_input_token_cost": 0.0, @@ -50480,7 +50483,8 @@ "supports_response_schema": true, "supports_system_messages": true, "supports_tool_choice": true, - "supports_vision": false + "supports_vision": false, + "thinking_always_on": true }, "tencent/deepseek-v4-pro": { "cache_creation_input_token_cost": 0.0, diff --git a/tests/test_litellm/llms/deepseek/chat/test_deepseek_chat_transformation.py b/tests/test_litellm/llms/deepseek/chat/test_deepseek_chat_transformation.py index fa6f23dc7ff..c05ee57b725 100644 --- a/tests/test_litellm/llms/deepseek/chat/test_deepseek_chat_transformation.py +++ b/tests/test_litellm/llms/deepseek/chat/test_deepseek_chat_transformation.py @@ -108,6 +108,24 @@ def test_thinking_mode_active_bool_thinking_returns_false_without_crashing(): assert config._thinking_mode_active(model="deepseek-reasoner", optional_params={"thinking": True}) is False +def test_thinking_mode_active_true_by_default_for_always_on_model(): + config = DeepSeekChatConfig() + assert config._thinking_mode_active(model="deepseek-v4-pro", optional_params={}) is True + + +def test_thinking_mode_active_false_when_explicitly_disabled_for_always_on_model(): + config = DeepSeekChatConfig() + assert ( + config._thinking_mode_active(model="deepseek-v4-pro", optional_params={"thinking": {"type": "disabled"}}) + is False + ) + + +def test_thinking_mode_active_false_by_default_for_opt_in_model(): + config = DeepSeekChatConfig() + assert config._thinking_mode_active(model="deepseek-reasoner", optional_params={}) is False + + class TestDeepSeekThinkingParams: """Test thinking and reasoning_effort parameter handling for DeepSeek."""