mirror of
https://github.com/crewAIInc/crewAI.git
synced 2026-07-27 09:45:16 +00:00
test(agent_utils): cover OpenAI Responses API tool-call shape
Regression tests for is_tool_call_list() and extract_tool_call_info()
against the Responses API's flat {"id", "name", "arguments"} dict
shape, alongside existing Chat-Completions and Bedrock/Anthropic
shapes to confirm no regression there.
Confirmed these tests fail against the pre-fix version of
agent_utils.py (3 failures matching exactly the Responses API cases)
and pass against the fix in 37087b7e1.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -25,6 +25,8 @@ from crewai.utilities.agent_utils import (
|
||||
_split_messages_into_chunks,
|
||||
convert_tools_to_openai_schema,
|
||||
execute_single_native_tool_call,
|
||||
extract_tool_call_info,
|
||||
is_tool_call_list,
|
||||
NativeToolCallResult,
|
||||
parse_tool_call_args,
|
||||
summarize_messages,
|
||||
@@ -981,6 +983,88 @@ class TestParallelSummarizationVCR:
|
||||
assert "report.pdf" in summary_msg["files"]
|
||||
|
||||
|
||||
class TestIsToolCallListResponsesApiShape:
|
||||
"""Regression tests: OpenAI Responses API tool-call dicts must be recognized.
|
||||
|
||||
Responses API function_call output items are flat dicts shaped
|
||||
{"id", "name", "arguments"} - no nested "function" key, and "arguments"
|
||||
instead of Anthropic/Bedrock-style "input".
|
||||
"""
|
||||
|
||||
def test_responses_api_dict_is_recognized_as_tool_call(self) -> None:
|
||||
response = [
|
||||
{
|
||||
"id": "call_abc123",
|
||||
"name": "fetch_page",
|
||||
"arguments": '{"url": "https://example.com"}',
|
||||
}
|
||||
]
|
||||
assert is_tool_call_list(response) is True
|
||||
|
||||
def test_plain_text_answer_not_misclassified(self) -> None:
|
||||
assert is_tool_call_list(["just a string, not a tool call"]) is False
|
||||
|
||||
def test_empty_list_returns_false(self) -> None:
|
||||
assert is_tool_call_list([]) is False
|
||||
|
||||
def test_chat_completions_style_still_recognized(self) -> None:
|
||||
response = [{"function": {"name": "fetch_page", "arguments": "{}"}}]
|
||||
assert is_tool_call_list(response) is True
|
||||
|
||||
def test_bedrock_anthropic_style_still_recognized(self) -> None:
|
||||
response = [{"name": "fetch_page", "input": {"url": "https://example.com"}}]
|
||||
assert is_tool_call_list(response) is True
|
||||
|
||||
|
||||
class TestExtractToolCallInfoResponsesApiShape:
|
||||
"""Regression tests: extract_tool_call_info must parse Responses API dicts."""
|
||||
|
||||
def test_responses_api_dict_extracts_real_arguments(self) -> None:
|
||||
tool_call = {
|
||||
"id": "call_abc123",
|
||||
"name": "fetch_page",
|
||||
"arguments": '{"url": "https://example.com"}',
|
||||
}
|
||||
result = extract_tool_call_info(tool_call)
|
||||
assert result is not None
|
||||
call_id, func_name, func_args = result
|
||||
assert call_id == "call_abc123"
|
||||
assert func_name == "fetch_page"
|
||||
assert func_args == '{"url": "https://example.com"}'
|
||||
|
||||
def test_responses_api_dict_does_not_return_empty_args(self) -> None:
|
||||
tool_call = {
|
||||
"id": "call_xyz",
|
||||
"name": "fetch_page",
|
||||
"arguments": '{"url": "https://example.com"}',
|
||||
}
|
||||
_, _, func_args = extract_tool_call_info(tool_call)
|
||||
assert func_args != {}
|
||||
|
||||
def test_bedrock_anthropic_style_still_uses_input(self) -> None:
|
||||
tool_call = {"name": "fetch_page", "input": {"url": "https://example.com"}}
|
||||
_, func_name, func_args = extract_tool_call_info(tool_call)
|
||||
assert func_name == "fetch_page"
|
||||
assert func_args == {"url": "https://example.com"}
|
||||
|
||||
def test_chat_completions_style_still_uses_nested_function(self) -> None:
|
||||
tool_call = {
|
||||
"id": "call_1",
|
||||
"function": {"name": "fetch_page", "arguments": "{}"},
|
||||
}
|
||||
_, func_name, func_args = extract_tool_call_info(tool_call)
|
||||
assert func_name == "fetch_page"
|
||||
assert func_args == "{}"
|
||||
|
||||
def test_non_dict_unrecognized_shape_returns_none(self) -> None:
|
||||
assert extract_tool_call_info("just a string") is None
|
||||
|
||||
def test_unrecognized_dict_shape_returns_empty_name_and_args(self) -> None:
|
||||
call_id, func_name, func_args = extract_tool_call_info({"unrelated": "data"})
|
||||
assert func_name == ""
|
||||
assert func_args == {}
|
||||
|
||||
|
||||
class TestParseToolCallArgs:
|
||||
"""Unit tests for parse_tool_call_args."""
|
||||
|
||||
|
||||
Reference in New Issue
Block a user