From 3a0709b64dd17223cab7af149e16f35550de06b4 Mon Sep 17 00:00:00 2001 From: Aditya Singh Date: Thu, 21 May 2026 16:47:02 -0700 Subject: [PATCH] Add regression tests for Bedrock and OpenAI _parse_native_tool_call The runtime fix from commit 25fcf39 handled the Bedrock Converse dict shape that previously dropped tool arguments, but no test locked the behaviour in. Add parametrized cases asserting both the Bedrock {'name', 'input', 'toolUseId'} shape and the OpenAI {'function': {'name', 'arguments'}} shape round-trip through _parse_native_tool_call without losing args. Fixes #4972 --- .../tests/agents/test_native_tool_calling.py | 95 +++++++++++++++++++ 1 file changed, 95 insertions(+) diff --git a/lib/crewai/tests/agents/test_native_tool_calling.py b/lib/crewai/tests/agents/test_native_tool_calling.py index 5cc218fa2b..6b9db5a323 100644 --- a/lib/crewai/tests/agents/test_native_tool_calling.py +++ b/lib/crewai/tests/agents/test_native_tool_calling.py @@ -1272,3 +1272,98 @@ def _run(self, code: str, language: str) -> str: assert "Error" in result["result"] assert "validation failed" in result["result"].lower() or "missing" in result["result"].lower() + + +# ============================================================================= +# Regression tests for _parse_native_tool_call (issue #4972) +# ============================================================================= + + +class TestParseNativeToolCall: + """Regression tests for `_parse_native_tool_call`. + + Issue #4972: the Bedrock Converse API returns tool calls as dicts shaped + like ``{"name": ..., "input": {...}, "toolUseId": ...}`` (no ``function`` + key). A previous truthy default in the dict branch caused + ``func_info.get("arguments", "{}")`` to short-circuit before falling back + to ``tool_call["input"]``, so every Bedrock tool call lost its arguments. + + These tests lock in both the Bedrock shape and the OpenAI + ``{"function": {"name": ..., "arguments": ...}}`` shape so the bug + cannot silently regress. + """ + + def _make_executor(self) -> "CrewAgentExecutor": + """Build a minimal CrewAgentExecutor for invoking the parser.""" + from crewai.agents.crew_agent_executor import CrewAgentExecutor + + return CrewAgentExecutor(tools=[], original_tools=[]) + + @pytest.mark.parametrize( + ("tool_call", "expected_id", "expected_name", "expected_args"), + [ + pytest.param( + { + "name": "search_in_user_knowledgebase", + "input": {"search_query": "test"}, + "toolUseId": "tooluse_abc123", + }, + "tooluse_abc123", + "search_in_user_knowledgebase", + {"search_query": "test"}, + id="bedrock_converse_dict_shape", + ), + pytest.param( + { + "id": "call_xyz789", + "function": { + "name": "calculator", + "arguments": '{"expression": "15 * 8"}', + }, + }, + "call_xyz789", + "calculator", + '{"expression": "15 * 8"}', + id="openai_dict_shape", + ), + ], + ) + def test_parse_native_tool_call_preserves_args( + self, + tool_call: dict, + expected_id: str, + expected_name: str, + expected_args, + ) -> None: + """Both Bedrock and OpenAI dict shapes must round-trip through + ``_parse_native_tool_call`` without losing the tool arguments.""" + executor = self._make_executor() + + parsed = executor._parse_native_tool_call(tool_call) + + assert parsed is not None + call_id, func_name, func_args = parsed + assert call_id == expected_id + assert func_name == expected_name + assert func_args == expected_args + + def test_parse_native_tool_call_bedrock_args_match_input(self) -> None: + """The parsed args must equal the original Bedrock ``input`` dict + (not the empty dict that triggered the bug).""" + original_input = { + "search_query": "vector embeddings", + "limit": 5, + } + tool_call = { + "name": "search_in_user_knowledgebase", + "input": original_input, + "toolUseId": "tooluse_regression_4972", + } + executor = self._make_executor() + + parsed = executor._parse_native_tool_call(tool_call) + + assert parsed is not None + _, _, func_args = parsed + assert func_args == original_input + assert func_args != {}