fix(mimo): round-trip reasoning_content for thinking-mode providers

This commit is contained in:
zhayujie
2026-05-18 17:49:41 +08:00
parent 4634a7bc2f
commit 2720bba5b7

View File

@@ -249,24 +249,49 @@ class OpenAICompatibleBot:
def _convert_messages_to_openai_format(self, messages):
"""
Convert messages from Claude format to OpenAI format
Claude uses content blocks with types like 'tool_use', 'tool_result'
OpenAI uses 'tool_calls' in assistant messages and 'tool' role for results
Claude content blocks (tool_use / tool_result / thinking) → OpenAI
tool_calls / tool role / reasoning_content. Some thinking-mode
providers require reasoning_content on assistant messages after a
tool_call appears in history; back-fill with empty string when the
trace was not captured.
"""
if not messages:
return []
# Detect any prior tool-call turn — gates reasoning_content back-fill below.
has_tool_call_history = False
for msg in messages:
if msg.get("role") != "assistant":
continue
if msg.get("tool_calls"):
has_tool_call_history = True
break
inner = msg.get("content")
if isinstance(inner, list) and any(
isinstance(b, dict) and b.get("type") == "tool_use" for b in inner
):
has_tool_call_history = True
break
openai_messages = []
for msg in messages:
role = msg.get("role")
content = msg.get("content")
# Handle string content (already in correct format)
if isinstance(content, str):
openai_messages.append(msg)
if (role == "assistant" and has_tool_call_history
and isinstance(msg, dict)
and "reasoning_content" not in msg):
patched = dict(msg)
patched["reasoning_content"] = ""
openai_messages.append(patched)
else:
openai_messages.append(msg)
continue
# Handle list content (Claude format with content blocks)
if isinstance(content, list):
# Check if this is a tool result message (user role with tool_result blocks)
@@ -305,14 +330,17 @@ class OpenAICompatibleBot:
# Check if this is an assistant message with tool_use blocks
elif role == "assistant":
# Separate text content and tool_use blocks
text_parts = []
tool_calls = []
reasoning_parts = []
for block in content:
if block.get("type") == "text":
if not isinstance(block, dict):
continue
btype = block.get("type")
if btype == "text":
text_parts.append(block.get("text", ""))
elif block.get("type") == "tool_use":
elif btype == "tool_use":
tool_id = block.get("id") or ""
if not tool_id:
logger.warning(f"[OpenAICompatible] tool_use missing id for '{block.get('name')}'")
@@ -324,6 +352,8 @@ class OpenAICompatibleBot:
"arguments": json.dumps(block.get("input", {}))
}
})
elif btype == "thinking":
reasoning_parts.append(block.get("thinking", ""))
# Build OpenAI format assistant message
openai_msg = {
@@ -334,6 +364,13 @@ class OpenAICompatibleBot:
if tool_calls:
openai_msg["tool_calls"] = tool_calls
# Round-trip reasoning_content; empty string when missing
# after a tool-call turn keeps strict providers happy.
if reasoning_parts:
openai_msg["reasoning_content"] = "\n".join(reasoning_parts)
elif has_tool_call_history:
openai_msg["reasoning_content"] = ""
if msg.get("_gemini_raw_parts"):
openai_msg["_gemini_raw_parts"] = msg["_gemini_raw_parts"]