Merge pull request #1722 from NousResearch/fix/run-agent-role-violations

fix(core): message role alternation violations in JSON recovery and error handler
This commit is contained in:
Teknium 2026-03-17 04:13:51 -07:00 committed by GitHub
commit d604b9622c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -5971,19 +5971,32 @@ class AIAgent:
# Don't add anything to messages, just retry the API call # Don't add anything to messages, just retry the API call
continue continue
else: else:
# Instead of returning partial, inject a helpful message and let model recover # Instead of returning partial, inject tool error results so the model can recover.
self._vprint(f"{self.log_prefix}⚠️ Injecting recovery message for invalid JSON...") # Using tool results (not user messages) preserves role alternation.
self._vprint(f"{self.log_prefix}⚠️ Injecting recovery tool results for invalid JSON...")
self._invalid_json_retries = 0 # Reset for next attempt self._invalid_json_retries = 0 # Reset for next attempt
# Add a user message explaining the issue # Append the assistant message with its (broken) tool_calls
recovery_msg = ( recovery_assistant = self._build_assistant_message(assistant_message, finish_reason)
f"Your tool call to '{tool_name}' had invalid JSON arguments. " messages.append(recovery_assistant)
f"Error: {error_msg}. "
f"For tools with no required parameters, use an empty object: {{}}. " # Respond with tool error results for each tool call
f"Please either retry the tool call with valid JSON, or respond without using that tool." invalid_names = {name for name, _ in invalid_json_args}
) for tc in assistant_message.tool_calls:
recovery_dict = {"role": "user", "content": recovery_msg} if tc.function.name in invalid_names:
messages.append(recovery_dict) err = next(e for n, e in invalid_json_args if n == tc.function.name)
tool_result = (
f"Error: Invalid JSON arguments. {err}. "
f"For tools with no required parameters, use an empty object: {{}}. "
f"Please retry with valid JSON."
)
else:
tool_result = "Skipped: other tool call in this response had invalid JSON."
messages.append({
"role": "tool",
"tool_call_id": tc.id,
"content": tool_result,
})
continue continue
# Reset retry counter on successful JSON validation # Reset retry counter on successful JSON validation
@ -6222,10 +6235,11 @@ class AIAgent:
if not pending_handled: if not pending_handled:
# Error happened before tool processing (e.g. response parsing). # Error happened before tool processing (e.g. response parsing).
# Use a user-role message so the model can see what went wrong # Choose role to avoid consecutive same-role messages.
# without confusing the API with a fabricated assistant turn. last_role = messages[-1].get("role") if messages else None
err_role = "assistant" if last_role == "user" else "user"
sys_err_msg = { sys_err_msg = {
"role": "user", "role": err_role,
"content": f"[System error during processing: {error_msg}]", "content": f"[System error during processing: {error_msg}]",
} }
messages.append(sys_err_msg) messages.append(sys_err_msg)