fix(deepseek): aggregate missing reasoning warnings

This commit is contained in:
IvanShang 2026-08-21 01:05:25 +08:00
parent ff02d5cfc0
commit 6abaa6c083
2 changed files with 48 additions and 10 deletions

View file

@ -72,6 +72,15 @@ class DeepSeekChatConfig(OpenAIGPTConfig):
(LiteLLM stores provider-specific response fields there).
2. Otherwise inject a single space — the minimum value the API accepts.
"""
missing_reasoning_content: Final = any(
msg.get("role") == "assistant"
and not msg.get("reasoning_content")
and not (
isinstance(provider_fields := msg.get("provider_specific_fields"), dict)
and provider_fields.get("reasoning_content")
)
for msg in messages
)
result: Final[list[AllMessageValues]] = []
for msg in messages:
if msg.get("role") == "assistant" and not msg.get("reasoning_content"):
@ -84,20 +93,21 @@ class DeepSeekChatConfig(OpenAIGPTConfig):
cleaned.pop("reasoning_content", None)
patched["provider_specific_fields"] = cleaned
else:
litellm.verbose_logger.warning(
"DeepSeek thinking mode: assistant message is missing "
"`reasoning_content` and none was saved in "
"`provider_specific_fields`. A single-space placeholder "
"is being injected to satisfy API validation, but the "
"model will receive a blank reasoning chain for this turn, "
"which may silently degrade multi-turn response quality. "
"Preserve `reasoning_content` from the original assistant "
"response when building multi-turn conversation history."
)
patched["reasoning_content"] = " "
result.append(cast(AllMessageValues, patched))
else:
result.append(msg)
if missing_reasoning_content:
litellm.verbose_logger.warning(
"DeepSeek thinking mode: assistant message is missing "
"`reasoning_content` and none was saved in "
"`provider_specific_fields`. A single-space placeholder "
"is being injected to satisfy API validation, but the "
"model will receive a blank reasoning chain for this turn, "
"which may silently degrade multi-turn response quality. "
"Preserve `reasoning_content` from the original assistant "
"response when building multi-turn conversation history."
)
return result
@overload

View file

@ -103,6 +103,34 @@ async def test_async_transform_request_strips_unsupported_tools_from_body():
assert body["tools"][0]["function"]["name"] == "shell"
def test_fill_reasoning_content_warns_once_per_request_and_preserves_history(caplog):
messages = [
{"role": "user", "content": "Use both tools."},
{"role": "assistant", "content": None, "tool_calls": [{"id": "first"}], "reasoning_content": ""},
{"role": "tool", "tool_call_id": "first", "content": "first result"},
{"role": "assistant", "content": None, "tool_calls": [{"id": "second"}], "reasoning_content": ""},
{"role": "tool", "tool_call_id": "second", "content": "second result"},
{"role": "user", "content": "Continue."},
]
with caplog.at_level("WARNING"):
first_result = DeepSeekChatConfig()._fill_reasoning_content(messages)
second_result = DeepSeekChatConfig()._fill_reasoning_content(messages)
warnings = [
record
for record in caplog.records
if "DeepSeek thinking mode: assistant message is missing `reasoning_content`" in record.message
]
assert len(warnings) == 2
assert first_result[1]["reasoning_content"] == " "
assert first_result[3]["reasoning_content"] == " "
assert second_result[1]["reasoning_content"] == " "
assert second_result[3]["reasoning_content"] == " "
assert messages[1]["reasoning_content"] == ""
assert messages[3]["reasoning_content"] == ""
def test_thinking_mode_active_bool_thinking_returns_false_without_crashing():
config = DeepSeekChatConfig()
assert config._thinking_mode_active(model="deepseek-reasoner", optional_params={"thinking": True}) is False