@@ -602,19 +602,25 @@ def _agent_turn(self, user_input: str, silent: bool = False) -> str:
602602 final_response = response .content
603603 # Strip inline tool call blocks that local models (Qwen/Mistral)
604604 # sometimes emit as text instead of using the tool_use API.
605+ # Pattern 1: XML-wrapped tool calls <tool>{...}</tool>
605606 _TOOL_BLOCK = re .compile (r"<tools?>\s*\{.*?\}\s*</tools?>" , re .DOTALL )
606- if _TOOL_BLOCK .search (final_response ):
607- final_response = _TOOL_BLOCK .sub ("" , final_response ).strip ()
608- # If the response was ONLY a tool call block, suppress entirely
609- if not final_response :
610- response = CompletionResponse (
611- content = "" ,
612- model = response .model ,
613- input_tokens = response .input_tokens ,
614- output_tokens = response .output_tokens ,
615- estimated_cost_usd = response .estimated_cost_usd ,
616- tool_calls = response .tool_calls ,
617- )
607+ # Pattern 2: Bare JSON tool calls {"name":..., "arguments":...}
608+ _BARE_TOOL = re .compile (
609+ r'\{\s*"name"\s*:\s*"[^"]+"\s*,\s*"arguments"\s*:\s*\{[^}]*\}\s*\}' ,
610+ )
611+ for pat in (_TOOL_BLOCK , _BARE_TOOL ):
612+ if pat .search (final_response ):
613+ final_response = pat .sub ("" , final_response ).strip ()
614+ # If the response was ONLY tool call blocks, suppress entirely
615+ if not final_response :
616+ response = CompletionResponse (
617+ content = "" ,
618+ model = response .model ,
619+ input_tokens = response .input_tokens ,
620+ output_tokens = response .output_tokens ,
621+ estimated_cost_usd = response .estimated_cost_usd ,
622+ tool_calls = response .tool_calls ,
623+ )
618624
619625 if not response .has_tool_calls :
620626 # Non-English correction: if the final response is in a non-English
0 commit comments