fix(deepseek): forward graded reasoning_effort instead of discarding it

map_openai_params collapsed every non-"none" reasoning_effort into a bare
thinking: {"type": "enabled"} and dropped the level, so low, high and max
were all indistinguishable from the default and callers could not ask for
reduced reasoning at all

DeepSeek accepts reasoning_effort directly in thinking mode and grades it
server-side, so the value is forwarded rather than remapped here

Fixes #27439
This commit is contained in:
NasonZ 2026-09-11 12:56:55 +01:00
parent 9a715df212
commit ee779d4259
2 changed files with 45 additions and 2 deletions

View file

@ -39,7 +39,8 @@ class DeepSeekChatConfig(OpenAIGPTConfig):
Handles `thinking` and `reasoning_effort` parameters for DeepSeek reasoner models.
DeepSeek supports `{"type": "enabled"}` and `{"type": "disabled"}` - no budget_tokens
like Anthropic. `reasoning_effort="none"` is the OpenAI-style way to ask for thinking
off, so it maps to `{"type": "disabled"}`; any other effort keeps thinking on.
off, so it maps to `{"type": "disabled"}`; any other effort keeps thinking on and
is forwarded, since DeepSeek grades effort itself server-side.
Reference: https://api-docs.deepseek.com/guides/thinking_mode
"""
@ -57,7 +58,10 @@ class DeepSeekChatConfig(OpenAIGPTConfig):
# Otherwise fall back to reasoning_effort: "none" disables, anything else enables
elif reasoning_effort is not None:
optional_params["thinking"] = {"type": "disabled" if reasoning_effort == "none" else "enabled"}
thinking_enabled: Final = reasoning_effort != "none"
optional_params["thinking"] = {"type": "enabled" if thinking_enabled else "disabled"}
if thinking_enabled:
optional_params["reasoning_effort"] = reasoning_effort
return optional_params

View file

@ -443,6 +443,7 @@ class TestDeepSeekThinkingParams:
)
assert result["thinking"] == {"type": "enabled"}
assert result["reasoning_effort"] == "medium"
def test_map_reasoning_effort_low(self):
"""Test that reasoning_effort='low' maps to thinking enabled."""
@ -457,6 +458,7 @@ class TestDeepSeekThinkingParams:
)
assert result["thinking"] == {"type": "enabled"}
assert result["reasoning_effort"] == "low"
def test_map_reasoning_effort_high(self):
"""Test that reasoning_effort='high' maps to thinking enabled."""
@ -471,6 +473,43 @@ class TestDeepSeekThinkingParams:
)
assert result["thinking"] == {"type": "enabled"}
assert result["reasoning_effort"] == "high"
def test_map_graded_effort_is_forwarded_not_collapsed(self):
"""Every effort value reaches DeepSeek, which grades it server-side."""
for effort in ("minimal", "low", "medium", "default", "high", "xhigh", "max", "ultra"):
result = self.config.map_openai_params(
non_default_params={"reasoning_effort": effort},
optional_params={},
model=self.model,
drop_params=False,
)
assert result["thinking"] == {"type": "enabled"}
assert result["reasoning_effort"] == effort
def test_explicit_thinking_is_honoured_exactly_as_given(self):
"""An explicit toggle decides thinking, and no effort is merged into it."""
result = self.config.map_openai_params(
non_default_params={"thinking": {"type": "enabled"}, "reasoning_effort": "low"},
optional_params={},
model=self.model,
drop_params=False,
)
assert result["thinking"] == {"type": "enabled"}
assert "reasoning_effort" not in result
def test_disabled_thinking_carries_no_effort(self):
"""Nothing is thinking, so an effort value would be meaningless."""
for params in ({"reasoning_effort": "none"},
{"thinking": {"type": "disabled"}}):
result = self.config.map_openai_params(
non_default_params=params,
optional_params={},
model=self.model,
drop_params=False,
)
assert result["thinking"] == {"type": "disabled"}
assert "reasoning_effort" not in result
def test_map_reasoning_effort_none_does_not_enable_thinking(self):
"""Test that reasoning_effort='none' does not enable thinking."""