diff --git a/litellm/llms/deepseek/chat/transformation.py b/litellm/llms/deepseek/chat/transformation.py index 5cd8d119542f..a40b3e111449 100644 --- a/litellm/llms/deepseek/chat/transformation.py +++ b/litellm/llms/deepseek/chat/transformation.py @@ -47,18 +47,20 @@ def map_openai_params( thinking_value = optional_params.pop("thinking", None) reasoning_effort = optional_params.pop("reasoning_effort", None) - # Handle thinking parameter - only accept {"type": "enabled"} + # Handle thinking parameter if thinking_value is not None: - if ( - isinstance(thinking_value, dict) - and thinking_value.get("type") == "enabled" + if isinstance(thinking_value, dict) and thinking_value.get("type") in ( + "enabled", + "disabled", ): - # DeepSeek only accepts {"type": "enabled"}, ignore budget_tokens - optional_params["thinking"] = {"type": "enabled"} + optional_params["thinking"] = {"type": thinking_value["type"]} - # Handle reasoning_effort - map to thinking enabled - elif reasoning_effort is not None and reasoning_effort != "none": - optional_params["thinking"] = {"type": "enabled"} + # Handle reasoning_effort - map to thinking enabled/disabled + elif reasoning_effort is not None: + if reasoning_effort == "none": + optional_params["thinking"] = {"type": "disabled"} + else: + optional_params["thinking"] = {"type": "enabled"} return optional_params diff --git a/tests/test_litellm/llms/deepseek/__init__.py b/tests/test_litellm/llms/deepseek/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/tests/test_litellm/llms/deepseek/test_deepseek_chat_transformation.py b/tests/test_litellm/llms/deepseek/test_deepseek_chat_transformation.py new file mode 100644 index 000000000000..266b0aafd5e8 --- /dev/null +++ b/tests/test_litellm/llms/deepseek/test_deepseek_chat_transformation.py @@ -0,0 +1,90 @@ +""" +Tests for DeepSeekChatConfig.map_openai_params thinking/reasoning_effort handling. + +Regression tests for https://github.com/BerriAI/litellm/issues/27453 +""" + +import os +import sys + +sys.path.insert(0, os.path.abspath("../../../..")) + +import pytest + +from litellm.llms.deepseek.chat.transformation import DeepSeekChatConfig + + +@pytest.fixture +def config(): + return DeepSeekChatConfig() + + +class TestReasoningEffortNone: + """reasoning_effort='none' should disable thinking.""" + + def test_reasoning_effort_none_disables_thinking(self, config): + result = config.map_openai_params( + non_default_params={"reasoning_effort": "none"}, + optional_params={}, + model="deepseek-v4-pro", + drop_params=False, + ) + assert result["thinking"] == {"type": "disabled"} + + def test_reasoning_effort_high_enables_thinking(self, config): + result = config.map_openai_params( + non_default_params={"reasoning_effort": "high"}, + optional_params={}, + model="deepseek-v4-pro", + drop_params=False, + ) + assert result["thinking"] == {"type": "enabled"} + + def test_reasoning_effort_low_enables_thinking(self, config): + result = config.map_openai_params( + non_default_params={"reasoning_effort": "low"}, + optional_params={}, + model="deepseek-v4-pro", + drop_params=False, + ) + assert result["thinking"] == {"type": "enabled"} + + +class TestThinkingParam: + """Direct thinking parameter should be passed through.""" + + def test_thinking_enabled(self, config): + result = config.map_openai_params( + non_default_params={"thinking": {"type": "enabled", "budget_tokens": 5000}}, + optional_params={}, + model="deepseek-v4-pro", + drop_params=False, + ) + assert result["thinking"] == {"type": "enabled"} + + def test_thinking_disabled(self, config): + result = config.map_openai_params( + non_default_params={"thinking": {"type": "disabled"}}, + optional_params={}, + model="deepseek-v4-pro", + drop_params=False, + ) + assert result["thinking"] == {"type": "disabled"} + + def test_thinking_strips_budget_tokens(self, config): + result = config.map_openai_params( + non_default_params={"thinking": {"type": "enabled", "budget_tokens": 5000}}, + optional_params={}, + model="deepseek-v4-pro", + drop_params=False, + ) + assert "budget_tokens" not in result["thinking"] + + def test_no_thinking_params_leaves_thinking_unset(self, config): + result = config.map_openai_params( + non_default_params={"temperature": 0.5}, + optional_params={}, + model="deepseek-v4-pro", + drop_params=False, + ) + assert "thinking" not in result