From 7fb899954540dd3df1fae532b86da351d8611378 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Wed, 14 Jan 2026 22:54:18 +0000 Subject: [PATCH] fix: only pass tools parameter when tools are available MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This fixes the CI test failures by only passing the tools parameter to llm.call() and llm.acall() when tools are actually available. This maintains backward compatibility with existing code that checks 'tools' in kwargs to determine if tools were provided. The previous commit always passed tools=None, which caused tests that check 'tools' in kwargs to fail because the key was always present. Co-Authored-By: João --- .../src/crewai/utilities/agent_utils.py | 54 +++++++++++++------ .../tests/utilities/test_agent_utils.py | 27 +++++----- 2 files changed, 53 insertions(+), 28 deletions(-) diff --git a/lib/crewai/src/crewai/utilities/agent_utils.py b/lib/crewai/src/crewai/utilities/agent_utils.py index 58794d4b2..e281cf16e 100644 --- a/lib/crewai/src/crewai/utilities/agent_utils.py +++ b/lib/crewai/src/crewai/utilities/agent_utils.py @@ -310,14 +310,25 @@ def get_llm_response( tools = _extract_tools_from_context(executor_context) try: - answer = llm.call( - messages, - tools=tools, - callbacks=callbacks, - from_task=from_task, - from_agent=from_agent, # type: ignore[arg-type] - response_model=response_model, - ) + # Only pass tools parameter if tools are available to maintain backward compatibility + # with code that checks "tools" in kwargs + if tools is not None: + answer = llm.call( + messages, + tools=tools, + callbacks=callbacks, + from_task=from_task, + from_agent=from_agent, # type: ignore[arg-type] + response_model=response_model, + ) + else: + answer = llm.call( + messages, + callbacks=callbacks, + from_task=from_task, + from_agent=from_agent, # type: ignore[arg-type] + response_model=response_model, + ) except Exception as e: raise e if not answer: @@ -368,14 +379,25 @@ async def aget_llm_response( tools = _extract_tools_from_context(executor_context) try: - answer = await llm.acall( - messages, - tools=tools, - callbacks=callbacks, - from_task=from_task, - from_agent=from_agent, # type: ignore[arg-type] - response_model=response_model, - ) + # Only pass tools parameter if tools are available to maintain backward compatibility + # with code that checks "tools" in kwargs + if tools is not None: + answer = await llm.acall( + messages, + tools=tools, + callbacks=callbacks, + from_task=from_task, + from_agent=from_agent, # type: ignore[arg-type] + response_model=response_model, + ) + else: + answer = await llm.acall( + messages, + callbacks=callbacks, + from_task=from_task, + from_agent=from_agent, # type: ignore[arg-type] + response_model=response_model, + ) except Exception as e: raise e if not answer: diff --git a/lib/crewai/tests/utilities/test_agent_utils.py b/lib/crewai/tests/utilities/test_agent_utils.py index b9d4cc02a..5122b9386 100644 --- a/lib/crewai/tests/utilities/test_agent_utils.py +++ b/lib/crewai/tests/utilities/test_agent_utils.py @@ -190,8 +190,8 @@ class TestGetLlmResponse: assert len(call_kwargs["tools"]) == 1 assert call_kwargs["tools"][0]["name"] == "test_tool" - def test_passes_none_tools_when_no_context(self, mock_llm, mock_printer): - """Test that tools=None is passed when no executor_context.""" + def test_does_not_pass_tools_when_no_context(self, mock_llm, mock_printer): + """Test that tools parameter is not passed when no executor_context.""" result = get_llm_response( llm=mock_llm, messages=[{"role": "user", "content": "test"}], @@ -202,13 +202,14 @@ class TestGetLlmResponse: mock_llm.call.assert_called_once() call_kwargs = mock_llm.call.call_args[1] - assert "tools" in call_kwargs - assert call_kwargs["tools"] is None + # tools should NOT be in kwargs when there are no tools + # This maintains backward compatibility with code that checks "tools" in kwargs + assert "tools" not in call_kwargs - def test_passes_none_tools_when_context_has_no_tools( + def test_does_not_pass_tools_when_context_has_no_tools( self, mock_llm, mock_printer ): - """Test that tools=None is passed when context has no tools.""" + """Test that tools parameter is not passed when context has no tools.""" mock_context = Mock() mock_context.tools = [] mock_context.messages = [{"role": "user", "content": "test"}] @@ -233,8 +234,9 @@ class TestGetLlmResponse: mock_llm.call.assert_called_once() call_kwargs = mock_llm.call.call_args[1] - assert "tools" in call_kwargs - assert call_kwargs["tools"] is None + # tools should NOT be in kwargs when there are no tools + # This maintains backward compatibility with code that checks "tools" in kwargs + assert "tools" not in call_kwargs class TestAgetLlmResponse: @@ -293,8 +295,8 @@ class TestAgetLlmResponse: assert call_kwargs["tools"][0]["name"] == "async_test_tool" @pytest.mark.asyncio - async def test_passes_none_tools_when_no_context(self, mock_llm, mock_printer): - """Test that tools=None is passed when no executor_context.""" + async def test_does_not_pass_tools_when_no_context(self, mock_llm, mock_printer): + """Test that tools parameter is not passed when no executor_context.""" result = await aget_llm_response( llm=mock_llm, messages=[{"role": "user", "content": "test"}], @@ -305,8 +307,9 @@ class TestAgetLlmResponse: mock_llm.acall.assert_called_once() call_kwargs = mock_llm.acall.call_args[1] - assert "tools" in call_kwargs - assert call_kwargs["tools"] is None + # tools should NOT be in kwargs when there are no tools + # This maintains backward compatibility with code that checks "tools" in kwargs + assert "tools" not in call_kwargs class TestToolsPassedToGeminiModels: