mirror of
https://github.com/crewAIInc/crewAI.git
synced 2026-07-27 09:45:16 +00:00
* fix(openai): make tool calling work on the Responses API path
An agent with tools on api="responses" never produced an answer. It returned the
raw tool-call list instead:
[{'id': 'call_...', 'name': 'multiply', 'arguments': '{"a":17,"b":23}'}]
Three defects in the chain, all on the Responses side only:
1. `is_tool_call_list()` knew the OpenAI-nested, Anthropic, Bedrock and Gemini
shapes but not the Responses one ({"id", "name", "arguments"} -- no nested
"function", no "input"). The list wasn't recognized as tool calls, so the
executor handed it back verbatim as the final answer.
2. `extract_tool_call_info()` read "arguments" only from a nested "function"
object, falling back to "input". For the Responses shape both missed and the
arguments silently became {}, so the tool would have run with no input.
3. With those fixed the tool ran, then the follow-up request 400'd:
Invalid type for 'input[1].content': expected one of an array of objects
or string, but got null instead.
Tool calling is expressed differently by the two APIs. Chat Completions uses an
assistant message carrying `tool_calls` with content: None, then role: "tool"
results. The Responses API uses flat function_call / function_call_output items
keyed by call_id. Those messages were passed through untranslated.
`_to_responses_input()` now converts them. Messages without tool calls pass
through unchanged, so nothing else moves.
Verified end to end against the live API:
api="responses" + tools -> 391 (was raw tool-call JSON)
chained multi-step tool calls -> 400 (17*23, then +9)
completions path (control) -> 391 (unchanged)
The generated `input` payload was also posted to /v1/responses directly and
accepted, and the pre-fix chat-shaped payload confirmed as a 400.
This is why api="responses" never worked for agents: the provider side has had a
full Responses implementation since #4258/c4c9208, but the executor never learned
the shape it emits. Fixing it also unblocks routing gpt-5.4+ tool calls to the
Responses API instead of dropping reasoning_effort.
Tests: 12 cases covering recognition, extraction (including that the Chat
Completions and Bedrock shapes are unaffected), translation of assistant/tool
messages, parallel calls, assistant text alongside tool calls, non-string tool
output, and the full prepared `input` list.
* fix(openai): prefer Responses "call_id" over the item's own "id"
Per CodeRabbit review. A raw Responses function_call item carries both keys with
different values, confirmed against the live API:
keys ['arguments', 'call_id', 'id', 'name', 'status', 'type']
id fc_0adeb715c5d740c7006a65ccb72b948199872ad8b5a5c53108
call_id call_dEoHFrYnOgWYvk17FymdcDZ5
function_call_output must reference call_id. Reading the item's own "id" would
produce a tool result the model can't correlate back to its invocation.
Our own _extract_function_calls_from_response already maps item.call_id into "id",
so the normal path was correct and the existing tests passed. But
extract_tool_call_info is a shared helper reached from every provider's tool loop,
and a raw Responses item is a plausible thing to hand it -- silently picking the
wrong identifier is a bad trap to leave in place for one line of guard.
Tests: raw item extraction asserting call_id is chosen over id, and a round-trip
check that the id extracted from a call is the one sent back with its result.
997 passed across tests/llms, test_agent_utils and tests/agents (1 pre-existing
unrelated failure from a local OLLAMA_API_KEY env leak). Real two-agent chained-tool
run still returns the correct answer.
---------
Co-authored-by: João Moura <joaomdmoura@gmail.com>