From 4a646373b6233b3e292aa20cf254d7f1c2f61dbf Mon Sep 17 00:00:00 2001 From: PeninsulaInd Date: Tue, 17 Feb 2026 10:00:21 -0600 Subject: [PATCH] =?UTF-8?q?1.5:=20Fix=20tool=20results=20=E2=80=94=20use?= =?UTF-8?q?=20role:tool=20with=20tool=5Fcall=5Fid?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Previously tool results were injected as role:user messages which confuses some models. Now the live agent loop uses proper OpenAI function-calling format: - Assistant messages include tool_calls array with IDs - Tool results use role:tool with matching tool_call_id History replay in router.py is unchanged (no tool_call_ids in DB). Co-Authored-By: Claude Opus 4.6 --- cheddahbot/agent.py | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/cheddahbot/agent.py b/cheddahbot/agent.py index e4a96ae..96643c6 100644 --- a/cheddahbot/agent.py +++ b/cheddahbot/agent.py @@ -185,12 +185,23 @@ class Agent: # Execute tools if self._tools: - messages.append( + # Build OpenAI-format assistant message with tool_calls + openai_tool_calls = [ { - "role": "assistant", - "content": full_response or "I'll use some tools to help with that.", + "id": tc.get("id", f"call_{tc['name']}_{i}"), + "type": "function", + "function": { + "name": tc["name"], + "arguments": json.dumps(tc.get("input", {})), + }, } - ) + for i, tc in enumerate(unique_tool_calls) + ] + messages.append({ + "role": "assistant", + "content": full_response or None, + "tool_calls": openai_tool_calls, + }) for tc in unique_tool_calls: yield f"\n\n**Using tool: {tc['name']}**\n" @@ -201,9 +212,11 @@ class Agent: yield f"```\n{result[:2000]}\n```\n\n" self.db.add_message(conv_id, "tool", result, tool_result=tc["name"]) - messages.append( - {"role": "user", "content": f'[Tool "{tc["name"]}" result]\n{result}'} - ) + messages.append({ + "role": "tool", + "tool_call_id": tc.get("id", f"call_{tc['name']}"), + "content": result, + }) else: # No tool system configured - just mention tool was requested if full_response: