mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-12 23:01:41 +00:00
refactor(deepseek): drop hardcoded V4 model list, guard on supports_reasoning
Replace the DEFAULT_THINKING_MODEL_PREFIXES name list with the existing
supports_reasoning registry flag (same approach as the Moonshot
reasoning_content fix), so new DeepSeek models only need a cost map entry,
not a code change.
Safety verified against the live DeepSeek API:
- reasoning_content is ignored in non-thinking requests (incl. explicit
thinking={"type": "disabled"}), so injecting for any reasoning-capable
model is harmless
- deepseek-v3.2 (the opt-in model the stricter guard protected) is no
longer served: the API only accepts deepseek-v4-pro / deepseek-v4-flash
thinking={"type": "disabled"} still skips injection entirely.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
2ef49eed60
commit
6aa6d40c5d
3 changed files with 42 additions and 36 deletions
|
|
@ -136,33 +136,23 @@ class DeepSeekChatConfig(OpenAIGPTConfig):
|
|||
messages=messages, model=model, is_async=False
|
||||
)
|
||||
|
||||
# Model families where DeepSeek enables thinking mode BY DEFAULT (no
|
||||
# `thinking` param required). Reference:
|
||||
# https://api-docs.deepseek.com/guides/thinking_mode
|
||||
DEFAULT_THINKING_MODEL_PREFIXES = ("deepseek-v4",)
|
||||
|
||||
def _is_default_thinking_model(self, model: str) -> bool:
|
||||
return any(
|
||||
prefix in model for prefix in self.DEFAULT_THINKING_MODEL_PREFIXES
|
||||
)
|
||||
|
||||
def _thinking_mode_active(self, model: str, optional_params: dict) -> bool:
|
||||
"""
|
||||
Returns True when thinking mode is active for this request:
|
||||
- user explicitly passed thinking={"type": "enabled"} on a model that
|
||||
supports reasoning, OR
|
||||
- the model runs in thinking mode by default (DeepSeek V4 family) and
|
||||
the user did not explicitly disable it.
|
||||
Returns True when thinking mode may be active for this request.
|
||||
|
||||
Models like deepseek-v3.2 (supports_reasoning but opt-in thinking)
|
||||
remain untouched unless thinking is explicitly enabled.
|
||||
DeepSeek V4 models enable thinking BY DEFAULT (no `thinking` param
|
||||
required - https://api-docs.deepseek.com/guides/thinking_mode), so any
|
||||
reasoning-capable model counts unless the user explicitly disabled
|
||||
thinking. Same approach as the Moonshot reasoning fix
|
||||
(litellm/llms/moonshot/chat/transformation.py).
|
||||
|
||||
Injecting `reasoning_content` when thinking is NOT active is harmless:
|
||||
the DeepSeek API ignores the field in non-thinking requests (verified
|
||||
against the live API, including thinking={"type": "disabled"}).
|
||||
"""
|
||||
thinking_type = (optional_params.get("thinking") or {}).get("type")
|
||||
if thinking_type == "disabled":
|
||||
if (optional_params.get("thinking") or {}).get("type") == "disabled":
|
||||
return False
|
||||
if thinking_type == "enabled":
|
||||
return supports_reasoning(model=model, custom_llm_provider="deepseek")
|
||||
return self._is_default_thinking_model(model)
|
||||
return supports_reasoning(model=model, custom_llm_provider="deepseek")
|
||||
|
||||
def transform_request(
|
||||
self,
|
||||
|
|
|
|||
|
|
@ -220,10 +220,12 @@ class TestDeepSeekV4DefaultThinkingMode:
|
|||
model=model, optional_params={"thinking": {"type": "enabled"}}
|
||||
)
|
||||
|
||||
def test_opt_in_models_unaffected_by_default(self):
|
||||
"""deepseek-v3.2 supports reasoning but thinking is opt-in: no thinking
|
||||
param -> guard must stay off (no spurious injection)."""
|
||||
assert not self.config._thinking_mode_active(
|
||||
def test_reasoning_capable_models_active_by_default(self):
|
||||
"""Any reasoning-capable DeepSeek model counts as potentially
|
||||
thinking-mode (V4 enables thinking by default). Injection is harmless
|
||||
when thinking is not actually active: the live API ignores
|
||||
reasoning_content in non-thinking requests."""
|
||||
assert self.config._thinking_mode_active(
|
||||
model="deepseek-v3.2", optional_params={}
|
||||
)
|
||||
assert self.config._thinking_mode_active(
|
||||
|
|
|
|||
|
|
@ -233,13 +233,14 @@ def test_deepseek_fill_reasoning_content_multiturn():
|
|||
|
||||
def test_deepseek_fill_reasoning_content_guard_in_transform_request():
|
||||
"""
|
||||
_fill_reasoning_content must only run when BOTH conditions are true:
|
||||
1. supports_reasoning() is True for the model
|
||||
2. thinking mode is explicitly enabled in optional_params ({"type": "enabled"})
|
||||
_fill_reasoning_content runs for any reasoning-capable DeepSeek model
|
||||
unless thinking is explicitly disabled.
|
||||
|
||||
This prevents spurious injection on models like deepseek-v3.2 that support
|
||||
thinking as opt-in but not always-on. Addresses oss-pr-review-agent feedback
|
||||
on PR #28057.
|
||||
DeepSeek V4 enables thinking mode BY DEFAULT (no `thinking` param
|
||||
required), so the guard cannot rely on an explicit opt-in (issue #26395).
|
||||
Injecting reasoning_content when thinking is not actually active is
|
||||
harmless: the DeepSeek API ignores the field in non-thinking requests
|
||||
(verified against the live API, including thinking={"type": "disabled"}).
|
||||
"""
|
||||
from litellm.llms.deepseek.chat.transformation import DeepSeekChatConfig
|
||||
|
||||
|
|
@ -263,7 +264,8 @@ def test_deepseek_fill_reasoning_content_guard_in_transform_request():
|
|||
"reasoning_content should be injected when thinking is enabled"
|
||||
)
|
||||
|
||||
# Case 2: reasoning model + thinking NOT in optional_params -> no injection
|
||||
# Case 2: reasoning model + thinking NOT in optional_params -> injection
|
||||
# (thinking is on by default for DeepSeek V4 / deepseek-reasoner)
|
||||
result = config.transform_request(
|
||||
model="deepseek-reasoner",
|
||||
messages=messages,
|
||||
|
|
@ -271,11 +273,23 @@ def test_deepseek_fill_reasoning_content_guard_in_transform_request():
|
|||
litellm_params={},
|
||||
headers={},
|
||||
)
|
||||
assert "reasoning_content" not in result["messages"][1], (
|
||||
"reasoning_content should not be injected when thinking is not enabled"
|
||||
assert result["messages"][1].get("reasoning_content") == " ", (
|
||||
"reasoning_content should be injected by default for reasoning models"
|
||||
)
|
||||
|
||||
# Case 3: non-reasoning model + thinking enabled -> no injection
|
||||
# Case 3: reasoning model + thinking explicitly disabled -> no injection
|
||||
result = config.transform_request(
|
||||
model="deepseek-reasoner",
|
||||
messages=messages,
|
||||
optional_params={"thinking": {"type": "disabled"}},
|
||||
litellm_params={},
|
||||
headers={},
|
||||
)
|
||||
assert "reasoning_content" not in result["messages"][1], (
|
||||
"reasoning_content should not be injected when thinking is disabled"
|
||||
)
|
||||
|
||||
# Case 4: non-reasoning model -> no injection
|
||||
result = config.transform_request(
|
||||
model="deepseek-chat",
|
||||
messages=messages,
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue