From 37087b7e1d98a92ffab8df81d5682ffbbf0545c3 Mon Sep 17 00:00:00 2001 From: theCyberTech <84775494+theCyberTech@users.noreply.github.com> Date: Sat, 11 Jul 2026 18:12:20 +0800 Subject: [PATCH] fix(agent): recognize OpenAI Responses API tool-call shape in native tool loop is_tool_call_list() and extract_tool_call_info() only recognized Chat-Completions-style ({"function": {...}}), Anthropic-style ({"name", "input"}), and Gemini-style tool-call shapes. The Responses API's function_call output items are flat dicts shaped {"id", "name", "arguments"} with no nested "function" key and no "input" key, so they matched none of the checks. This caused is_tool_call_list() to misclassify a genuine tool call as a plain text answer, so the native tool loop returned the raw tool-call list as the agent's final output instead of executing the tool. Even after recognizing the shape, extract_tool_call_info() would have passed an empty arguments dict, since it only read "input" for the dict fallback. Verified against LLM(api="responses") with tools attached: the agent now correctly executes the tool with the parsed arguments instead of returning the unexecuted tool-call JSON as its answer. Co-Authored-By: Claude Sonnet 5 --- lib/crewai/src/crewai/utilities/agent_utils.py | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/lib/crewai/src/crewai/utilities/agent_utils.py b/lib/crewai/src/crewai/utilities/agent_utils.py index 91cfbb6db..545305d23 100644 --- a/lib/crewai/src/crewai/utilities/agent_utils.py +++ b/lib/crewai/src/crewai/utilities/agent_utils.py @@ -1228,7 +1228,14 @@ def extract_tool_call_info( ) func_info = tool_call.get("function", {}) func_name = func_info.get("name", "") or tool_call.get("name", "") - func_args = func_info.get("arguments") or tool_call.get("input") or {} + # OpenAI Responses API function_call items are flat dicts using + # "arguments" (not "input") with no nested "function" key. + func_args = ( + func_info.get("arguments") + or tool_call.get("arguments") + or tool_call.get("input") + or {} + ) return call_id, sanitize_tool_name(func_name), func_args return None @@ -1260,6 +1267,13 @@ def is_tool_call_list(response: list[Any]) -> bool: # Bedrock-style if isinstance(first_item, dict) and "name" in first_item and "input" in first_item: return True + # OpenAI Responses API-style (flat dict, no nested "function" key) + if ( + isinstance(first_item, dict) + and "name" in first_item + and "arguments" in first_item + ): + return True # Gemini-style if hasattr(first_item, "function_call") and first_item.function_call: return True