From bcb472eff98ffde4e1c12dc0618bb16118882796 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Tue, 22 Apr 2025 16:36:52 +0000 Subject: [PATCH] Fix issue #2664: Custom tools not being called by the agent Co-Authored-By: Joe Moura --- src/crewai/tools/tool_usage.py | 33 +++++++----- tests/tools/test_custom_tool_invocation.py | 60 ++++++++++++++++++++++ 2 files changed, 79 insertions(+), 14 deletions(-) create mode 100644 tests/tools/test_custom_tool_invocation.py diff --git a/src/crewai/tools/tool_usage.py b/src/crewai/tools/tool_usage.py index dc5f8f29a..917106abe 100644 --- a/src/crewai/tools/tool_usage.py +++ b/src/crewai/tools/tool_usage.py @@ -75,6 +75,7 @@ class ToolUsage: agent: Optional[Union["BaseAgent", "LiteAgent"]] = None, action: Any = None, fingerprint_context: Optional[Dict[str, str]] = None, + original_tools: List[Any] = [], ) -> None: self._i18n: I18N = agent.i18n if agent else I18N() self._printer: Printer = Printer() @@ -86,6 +87,7 @@ class ToolUsage: self.tools_description = render_text_description_and_args(tools) self.tools_names = get_tool_names(tools) self.tools_handler = tools_handler + self.original_tools = original_tools self.tools = tools self.task = task self.action = action @@ -191,13 +193,16 @@ class ToolUsage: ) # type: ignore from_cache = result is not None + original_tool = None + if hasattr(self, 'original_tools') and self.original_tools: + original_tool = next( + (ot for ot in self.original_tools if ot.name == tool.name), + None + ) + available_tool = next( - ( - available_tool - for available_tool in self.tools - if available_tool.name == tool.name - ), - None, + (at for at in self.tools if at.name == tool.name), + None ) if result is None: @@ -259,10 +264,11 @@ class ToolUsage: if self.tools_handler: should_cache = True - if ( - hasattr(available_tool, "cache_function") - and available_tool.cache_function # type: ignore # Item "None" of "Any | None" has no attribute "cache_function" - ): + if original_tool and hasattr(original_tool, "cache_function") and original_tool.cache_function: + should_cache = original_tool.cache_function( + calling.arguments, result + ) + elif available_tool and hasattr(available_tool, "cache_function") and available_tool.cache_function: should_cache = available_tool.cache_function( # type: ignore # Item "None" of "Any | None" has no attribute "cache_function" calling.arguments, result ) @@ -290,10 +296,9 @@ class ToolUsage: result=result, ) - if ( - hasattr(available_tool, "result_as_answer") - and available_tool.result_as_answer # type: ignore # Item "None" of "Any | None" has no attribute "cache_function" - ): + if original_tool and hasattr(original_tool, "result_as_answer") and original_tool.result_as_answer: + result_as_answer = original_tool.result_as_answer + elif available_tool and hasattr(available_tool, "result_as_answer") and available_tool.result_as_answer: result_as_answer = available_tool.result_as_answer # type: ignore # Item "None" of "Any | None" has no attribute "result_as_answer" data["result_as_answer"] = result_as_answer # type: ignore diff --git a/tests/tools/test_custom_tool_invocation.py b/tests/tools/test_custom_tool_invocation.py new file mode 100644 index 000000000..ca18ded7f --- /dev/null +++ b/tests/tools/test_custom_tool_invocation.py @@ -0,0 +1,60 @@ +import pytest +from unittest.mock import MagicMock + +from crewai import Agent, Task +from crewai.agents.crew_agent_executor import CrewAgentExecutor +from crewai.tools import BaseTool +from crewai.agents.tools_handler import ToolsHandler +from crewai.agents.parser import AgentAction +from pydantic import BaseModel, Field + + +class TestToolInput(BaseModel): + test_param: str = Field(..., description="A test parameter") + + +class TestCustomTool(BaseTool): + name: str = "Test Custom Tool" + description: str = "A test tool to verify custom tool invocation" + args_schema: type[BaseModel] = TestToolInput + + def _run(self, test_param: str) -> str: + return f"Tool executed with param: {test_param}" + + +def test_custom_tool_invocation(): + custom_tool = TestCustomTool() + + mock_agent = MagicMock() + mock_task = MagicMock() + mock_llm = MagicMock() + mock_crew = MagicMock() + tools_handler = ToolsHandler() + + executor = CrewAgentExecutor( + llm=mock_llm, + task=mock_task, + crew=mock_crew, + agent=mock_agent, + prompt={}, + max_iter=5, + tools=[custom_tool], + tools_names="Test Custom Tool", + stop_words=[], + tools_description="A test tool to verify custom tool invocation", + tools_handler=tools_handler, + original_tools=[custom_tool] + ) + + action = AgentAction( + tool="Test Custom Tool", + tool_input={"test_param": "test_value"}, + thought="I'll use the custom tool", + text="I'll use the Test Custom Tool to get a result", + message_log=[] + ) + + result = executor._execute_tool_and_check_finality(action) + + assert "Tool executed with param: test_value" in result.result + assert result.result_as_answer is False