fix: prevent API 400 errors by ensuring assistant messages with reasoning_content but no content or tool_calls are preserved with a placeholder content value (#8483)

* fix: preserve assistant messages with reasoning_content in sanitize pass

When _sanitize_assistant_messages encounters an assistant message with empty content and no tool_calls but with reasoning_content, keep it with content set to empty string instead of dropping it. Reasoning models (DeepSeek V4, MiMo, etc.) require this history for subsequent turn validation.

* Update astrbot/core/provider/sources/openai_source.py

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>

* fix: default to empty TokenUsage when completion.usage is None

When completion.usage is None (e.g. certain proxy/streaming edge cases), llm_response.usage stayed unset (None). Plugins accessing .input_tokens on it would crash with AttributeError.

Always assign llm_response.usage — extract from completion.usage if present, otherwise fall back to a zeroed TokenUsage().

Closes #8605

---------

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Co-authored-by: renchonghan <renchonghan@users.noreply.github.com>
This commit is contained in:
renchonghan
2026-06-27 15:26:09 +08:00
committed by GitHub
parent 8213c14cc6
commit 3db778ff09

View File

@@ -458,12 +458,20 @@ class ProviderOpenAIOfficial(Provider):
tool_calls = msg.get("tool_calls")
reasoning_content = msg.get("reasoning_content")
if _is_empty(content) and not tool_calls and not reasoning_content:
logger.warning(f"过滤第 {idx} 条空 assistant 消息 (无工具调用)")
continue
if _is_empty(content) and not tool_calls:
if not reasoning_content:
# 三者全空,真正的垃圾消息,丢弃
logger.debug(f"过滤第 {idx} 条空 assistant 消息 (无 content | tool_calls | reasoning_content)")
continue
else:
# ⭐ 有 reasoning_content 但没有 content 和 tool_calls
# 不能丢(推理模型需要 reasoning 历史)
# 但 API 要求 content 或 tool_calls 至少有一个
# → 设空字符串占位,满足校验
msg["content"] = ""
if _is_empty(content) and tool_calls:
msg["content"] = None
elif _is_empty(content) and tool_calls:
msg["content"] = None # 有 tool_calls按 OpenAI 规范
cleaned.append(msg)
@@ -872,8 +880,11 @@ class ProviderOpenAIOfficial(Provider):
llm_response.raw_completion = completion
llm_response.id = completion.id
if completion.usage:
llm_response.usage = self._extract_usage(completion.usage)
llm_response.usage = (
self._extract_usage(completion.usage)
if completion.usage
else TokenUsage()
)
return llm_response