Fix Qwen 400 template errors and add retry logic
- Add _assistant_msg_dict() to serialize assistant messages as plain dicts with content normalized from None to "", preventing Qwen's jinja template from rejecting tool-call messages with content=null - Apply fix in all three agent loops (_run_agent_inner, run_reflection_agent, run_life_event_reflection) - Replace error-to-chat fallback with silent logging + up to 3 retries (2s apart) before giving up Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
1 parent
d53835bb5f
commit
65982bb636
1 file changed
+28
-4
@@ -678,6 +678,24 @@ async def send_image_to_channel(url: str, channel: discord.abc.Messageable, capt
|
|||||||
return f"Failed to fetch or send image: {exc}"
|
return f"Failed to fetch or send image: {exc}"
|
||||||
|
|
||||||
|
|
||||||
|
def _assistant_msg_dict(msg) -> dict:
|
||||||
|
"""Serialize an assistant ChatCompletionMessage to a plain dict.
|
||||||
|
|
||||||
|
Qwen's jinja template rejects content=null, so we normalize it to "".
|
||||||
|
"""
|
||||||
|
d: dict = {"role": "assistant", "content": msg.content or ""}
|
||||||
|
if msg.tool_calls:
|
||||||
|
d["tool_calls"] = [
|
||||||
|
{
|
||||||
|
"id": tc.id,
|
||||||
|
"type": "function",
|
||||||
|
"function": {"name": tc.function.name, "arguments": tc.function.arguments},
|
||||||
|
}
|
||||||
|
for tc in msg.tool_calls
|
||||||
|
]
|
||||||
|
return d
|
||||||
|
|
||||||
|
|
||||||
def _ensure_user_tail(messages: list) -> None:
|
def _ensure_user_tail(messages: list) -> None:
|
||||||
"""Qwen's chat template requires a non-empty user message as the last message."""
|
"""Qwen's chat template requires a non-empty user message as the last message."""
|
||||||
if not messages:
|
if not messages:
|
||||||
@@ -750,7 +768,7 @@ async def _run_agent_inner(messages: list, channel: discord.abc.Messageable | No
|
|||||||
if not msg.tool_calls:
|
if not msg.tool_calls:
|
||||||
return msg.content
|
return msg.content
|
||||||
|
|
||||||
kwargs["messages"].append(msg)
|
kwargs["messages"].append(_assistant_msg_dict(msg))
|
||||||
|
|
||||||
for tc in msg.tool_calls:
|
for tc in msg.tool_calls:
|
||||||
args = json.loads(tc.function.arguments)
|
args = json.loads(tc.function.arguments)
|
||||||
@@ -822,7 +840,7 @@ async def run_reflection_agent(messages: list, max_turns: int) -> tuple[str, str
|
|||||||
if not msg.tool_calls:
|
if not msg.tool_calls:
|
||||||
break
|
break
|
||||||
|
|
||||||
kwargs["messages"].append(msg)
|
kwargs["messages"].append(_assistant_msg_dict(msg))
|
||||||
|
|
||||||
for tc in msg.tool_calls:
|
for tc in msg.tool_calls:
|
||||||
args = json.loads(tc.function.arguments)
|
args = json.loads(tc.function.arguments)
|
||||||
@@ -875,7 +893,7 @@ async def run_life_event_reflection(messages: list):
|
|||||||
if not msg.tool_calls:
|
if not msg.tool_calls:
|
||||||
break
|
break
|
||||||
|
|
||||||
kwargs["messages"].append(msg)
|
kwargs["messages"].append(_assistant_msg_dict(msg))
|
||||||
|
|
||||||
for tc in msg.tool_calls:
|
for tc in msg.tool_calls:
|
||||||
args = json.loads(tc.function.arguments)
|
args = json.loads(tc.function.arguments)
|
||||||
@@ -1244,11 +1262,17 @@ async def on_message(message: discord.Message):
|
|||||||
messages = [{"role": "system", "content": system_content}] + list(history)[:-1] + [current_msg]
|
messages = [{"role": "system", "content": system_content}] + list(history)[:-1] + [current_msg]
|
||||||
|
|
||||||
async with message.channel.typing():
|
async with message.channel.typing():
|
||||||
|
reply = None
|
||||||
|
for attempt in range(3):
|
||||||
try:
|
try:
|
||||||
reply = await run_agent(messages, channel=message.channel)
|
reply = await run_agent(messages, channel=message.channel)
|
||||||
|
break
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
reply = f"Error: {exc}"
|
log.error(f"[agent] Error (attempt {attempt + 1}/3): {exc}")
|
||||||
|
if attempt == 2:
|
||||||
history.pop()
|
history.pop()
|
||||||
|
return
|
||||||
|
await asyncio.sleep(2)
|
||||||
|
|
||||||
if reply:
|
if reply:
|
||||||
history.append({"role": "assistant", "content": reply})
|
history.append({"role": "assistant", "content": reply})
|
||||||
|
|||||||
Reference in new issue
Block a user