diff --git a/src/crewai/tools/structured_tool.py b/src/crewai/tools/structured_tool.py index a514f4229..d4b1b5cfe 100644 --- a/src/crewai/tools/structured_tool.py +++ b/src/crewai/tools/structured_tool.py @@ -282,8 +282,6 @@ class CrewStructuredTool: except Exception: raise - result = self.func(**parsed_args, **kwargs) - if asyncio.iscoroutine(result): return asyncio.run(result) diff --git a/tests/tools/test_structured_tool.py b/tests/tools/test_structured_tool.py index f347b1db1..7eb00540d 100644 --- a/tests/tools/test_structured_tool.py +++ b/tests/tools/test_structured_tool.py @@ -144,6 +144,32 @@ def test_default_values_in_schema(): ) assert result == "test custom 42" + +def test_tool_not_executed_twice(): + """Test that tool function is only executed once per invoke call (bug #3489)""" + call_count = 0 + + def counting_func(param: str) -> str: + """Function that counts how many times it's called.""" + nonlocal call_count + call_count += 1 + return f"Called {call_count} times with {param}" + + tool = CrewStructuredTool.from_function( + func=counting_func, name="counting_tool", description="Counts calls" + ) + + call_count = 0 + + result = tool.invoke({"param": "test"}) + + assert call_count == 1, f"Expected function to be called once, but was called {call_count} times" + assert result == "Called 1 times with test" + + result = tool.invoke({"param": "test2"}) + assert call_count == 2, f"Expected function to be called twice total, but was called {call_count} times" + assert result == "Called 2 times with test2" + @pytest.fixture def custom_tool_decorator(): from crewai.tools import tool @@ -227,4 +253,4 @@ def test_async_tool_using_decorator_within_flow(custom_tool_decorator): flow = StructuredExampleFlow() result = flow.kickoff() - assert result.raw == "Hello World from Custom Tool" \ No newline at end of file + assert result.raw == "Hello World from Custom Tool"