From 93333387d60f4a53bc850ae2ea59baa76b587708 Mon Sep 17 00:00:00 2001 From: dmahan93 Date: Mon, 9 Mar 2026 21:21:49 -0500 Subject: [PATCH] fix: handle dict and object tool_calls in agent loop vLLM's ToolCallTranslator returns tool_calls as dicts, while OpenAI API returns them as objects with .id, .function.name etc. Normalize both formats in the agent loop. --- environments/agent_loop.py | 45 ++++++++++++++++++++++++++------------ 1 file changed, 31 insertions(+), 14 deletions(-) diff --git a/environments/agent_loop.py b/environments/agent_loop.py index b2d29c6f9..ab8c0236e 100644 --- a/environments/agent_loop.py +++ b/environments/agent_loop.py @@ -279,21 +279,32 @@ class HermesAgentLoop: pass # Fall through to no tool calls if assistant_msg.tool_calls: + # Normalize tool calls to dicts — they may come as objects + # (OpenAI API) or dicts (vLLM ToolCallTranslator). + def _tc_to_dict(tc): + if isinstance(tc, dict): + return { + "id": tc.get("id", f"call_{uuid.uuid4().hex[:8]}"), + "type": "function", + "function": { + "name": tc.get("function", {}).get("name", tc.get("name", "")), + "arguments": tc.get("function", {}).get("arguments", tc.get("arguments", "{}")), + }, + } + return { + "id": tc.id, + "type": "function", + "function": { + "name": tc.function.name, + "arguments": tc.function.arguments, + }, + } + # Build the assistant message dict for conversation history msg_dict: Dict[str, Any] = { "role": "assistant", "content": assistant_msg.content or "", - "tool_calls": [ - { - "id": tc.id, - "type": "function", - "function": { - "name": tc.function.name, - "arguments": tc.function.arguments, - }, - } - for tc in assistant_msg.tool_calls - ], + "tool_calls": [_tc_to_dict(tc) for tc in assistant_msg.tool_calls], } # Preserve reasoning_content for multi-turn chat template handling @@ -306,8 +317,13 @@ class HermesAgentLoop: # Execute each tool call via hermes-agent's dispatch for tc in assistant_msg.tool_calls: - tool_name = tc.function.name - tool_args_raw = tc.function.arguments + # Handle both object (OpenAI) and dict (vLLM) formats + if isinstance(tc, dict): + tool_name = tc.get("function", {}).get("name", tc.get("name", "")) + tool_args_raw = tc.get("function", {}).get("arguments", tc.get("arguments", "{}")) + else: + tool_name = tc.function.name + tool_args_raw = tc.function.arguments # Validate tool name if tool_name not in self.valid_tool_names: @@ -418,10 +434,11 @@ class HermesAgentLoop: pass # Add tool response to conversation + tc_id = tc.get("id", "") if isinstance(tc, dict) else tc.id messages.append( { "role": "tool", - "tool_call_id": tc.id, + "tool_call_id": tc_id, "content": tool_result, } )