From 3eb29372a610ad1e775a88db997adca82ddced5c Mon Sep 17 00:00:00 2001 From: Bartok Moltbot Date: Sun, 1 Mar 2026 03:50:48 -0500 Subject: [PATCH] fix(agent): strip finish_reason and internal fields before API calls Fixes #134 Mistral API (and potentially others) returns 422 error when extra fields like finish_reason are sent in the request. The error message is: 'Extra inputs are not permitted', 'loc': ['body', 'messages', 2, 'assistant', 'finish_reason'] This fix ensures internal-only fields (finish_reason, reasoning) are stripped from all message copies before sending to the API: - flush_memories API call - summary generation API call - main conversation API call The fields are preserved in the local messages array for trajectory storage and debugging, but excluded from API requests. --- run_agent.py | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/run_agent.py b/run_agent.py index 4c60b4bd81ea7..7a4ddba8e9281 100644 --- a/run_agent.py +++ b/run_agent.py @@ -2271,9 +2271,9 @@ def flush_memories(self, messages: list = None, min_turns: int = None): reasoning = msg.get("reasoning") if reasoning: api_msg["reasoning_content"] = reasoning - api_msg.pop("reasoning", None) - api_msg.pop("finish_reason", None) - api_msg.pop("_flush_sentinel", None) + # Remove internal-only fields (not part of API schema) + for internal_field in ("reasoning", "finish_reason", "_flush_sentinel"): + api_msg.pop(internal_field, None) api_messages.append(api_msg) if self._cached_system_prompt: @@ -2640,7 +2640,14 @@ def _handle_max_iterations(self, messages: list, api_call_count: int) -> str: messages.append({"role": "user", "content": summary_request}) try: - api_messages = messages.copy() + # Build API messages, stripping internal-only fields + api_messages = [] + for msg in messages: + api_msg = msg.copy() + for internal_field in ("reasoning", "finish_reason"): + api_msg.pop(internal_field, None) + api_messages.append(api_msg) + effective_system = self._cached_system_prompt or "" if self.ephemeral_system_prompt: effective_system = (effective_system + "\n\n" + self.ephemeral_system_prompt).strip() @@ -2911,13 +2918,12 @@ def run_conversation( # Add reasoning_content for API compatibility (Moonshot AI, Novita, OpenRouter) api_msg["reasoning_content"] = reasoning_text - # Remove 'reasoning' field - it's for trajectory storage only - # We've copied it to 'reasoning_content' for the API above - if "reasoning" in api_msg: - api_msg.pop("reasoning") - # Remove finish_reason - not accepted by strict APIs (e.g. Mistral) - if "finish_reason" in api_msg: - api_msg.pop("finish_reason") + # Remove internal-only fields that aren't part of the API schema. + # - 'reasoning': trajectory storage only (copied to reasoning_content above) + # - 'finish_reason': response metadata, not a valid request field + # Some providers (e.g., Mistral) reject extra fields with 422 errors. + for internal_field in ("reasoning", "finish_reason"): + api_msg.pop(internal_field, None) # Keep 'reasoning_details' - OpenRouter uses this for multi-turn reasoning context # The signature field helps maintain reasoning continuity api_messages.append(api_msg)