diff --git a/litellm/llms/deepseek/chat/transformation.py b/litellm/llms/deepseek/chat/transformation.py index 80775889118..4c3eb55cb54 100644 --- a/litellm/llms/deepseek/chat/transformation.py +++ b/litellm/llms/deepseek/chat/transformation.py @@ -70,8 +70,10 @@ class DeepSeekChatConfig(OpenAIGPTConfig): # can run away, exhausting max_tokens during reasoning and returning empty content # (finish_reason="length", content:null). Preserve the value into the request body # when thinking is on so callers can bound effort (e.g. reasoning_effort="low"). - # See https://api-docs.deepseek.com/guides/thinking_mode - if thinking_enabled and reasoning_effort is not None: + # Exclude the "none" sentinel: it is an OpenAI-style thinking-OFF switch, not a real + # effort value, so passing it alongside explicit thinking:enabled would be + # contradictory. See https://api-docs.deepseek.com/guides/thinking_mode + if thinking_enabled and reasoning_effort not in (None, "none"): optional_params["reasoning_effort"] = reasoning_effort return optional_params diff --git a/tests/llm_translation/test_deepseek_reasoning_effort.py b/tests/llm_translation/test_deepseek_reasoning_effort.py index f9d3f29d2ab..6e1bb8f8281 100644 --- a/tests/llm_translation/test_deepseek_reasoning_effort.py +++ b/tests/llm_translation/test_deepseek_reasoning_effort.py @@ -39,3 +39,11 @@ def test_explicit_thinking_disabled_wins_and_drops_effort(): def test_no_reasoning_effort_leaves_no_key(): out = _map({}) assert out.get("reasoning_effort") is None + + +def test_explicit_enabled_thinking_with_none_effort_not_contradictory(): + # "none" is a thinking-OFF sentinel, not a real effort value: it must not be + # passed alongside explicit thinking:enabled (would contradict the provider). + out = _map({"thinking": {"type": "enabled"}, "reasoning_effort": "none"}) + assert out["thinking"] == {"type": "enabled"} + assert "reasoning_effort" not in out