diff --git a/src/crewai/tools/tool_usage.py b/src/crewai/tools/tool_usage.py index d4d128dbde..0a60ff8dee 100644 --- a/src/crewai/tools/tool_usage.py +++ b/src/crewai/tools/tool_usage.py @@ -2,6 +2,7 @@ from difflib import SequenceMatcher from textwrap import dedent from typing import Any, List, Union +import asyncio from langchain_core.tools import BaseTool from langchain_openai import ChatOpenAI @@ -123,6 +124,17 @@ def _use( tool=calling.tool_name, input=calling.arguments ) + tool_method = tool.func or tool.coroutine + is_async_tool = asyncio.iscoroutinefunction(tool_method) + + if is_async_tool: + if tool.func: + # async tool defined using BaseTool class from crewai_tools + async_tool_run = tool._run + elif tool.coroutine: + # async tool defined using @tool decorator from langchain + async_tool_run = tool._arun + if not result: try: if calling.tool_name in [ @@ -139,16 +151,28 @@ def _use( for k, v in calling.arguments.items() if k in acceptable_args } - result = tool._run(**arguments) + if is_async_tool: + result = asyncio.run(async_tool_run(**arguments)) + else: + result = tool._run(**arguments) except Exception: if tool.args_schema: arguments = calling.arguments - result = tool._run(**arguments) + if is_async_tool: + result = asyncio.run(async_tool_run(**arguments)) + else: + result = tool._run(**arguments) else: arguments = calling.arguments.values() # type: ignore # Incompatible types in assignment (expression has type "dict_values[str, Any]", variable has type "dict[str, Any]") - result = tool._run(*arguments) + if is_async_tool: + result = asyncio.run(async_tool_run(*arguments)) + else: + result = tool._run(*arguments) else: - result = tool._run() + if is_async_tool: + result = asyncio.run(async_tool_run()) + else: + result = tool._run() except Exception as e: self._run_attempts += 1 if self._run_attempts > self._max_parsing_attempts: diff --git a/tests/crew_test.py b/tests/crew_test.py index 75868ac9e3..f81afdc7fe 100644 --- a/tests/crew_test.py +++ b/tests/crew_test.py @@ -941,6 +941,81 @@ def test_manager_agent(): execute.assert_called() +def test_crew_async_tool_execution_langchain(): + from langchain.tools import tool + from langchain_openai import ChatOpenAI + import asyncio + + @tool + async def async_search(query: str) -> str: + """Useful to get the answer to a user query.""" + await asyncio.sleep(1) + return "The capital of France is Paris." + + agent = Agent( + role="Research", + goal="Research the user query and provide a brief and concise response. If you need more information, ask the user for it.", + backstory=( + "You are a virtual concierge specialized in research. Respond to our first-class users with the latest information." + ), + tools=[async_search], + llm=ChatOpenAI(temperature=0, model="gpt-4"), + ) + task_description = "Find the capital of France" + expected_output = "A response to the user query." + + async_task = Task( + description=task_description, expected_output=expected_output, agent=agent + ) + + crew = Crew(agents=[agent], tasks=[async_task]) + + result = crew.kickoff() + assert result == "The capital of France is Paris." + + +def test_crew_async_tool_execution(): + from langchain.tools import tool + from langchain_openai import ChatOpenAI + import asyncio + import time + from crewai_tools import BaseTool + + class AsyncSearch(BaseTool): + def __init__(self): + super().__init__( + name="AsyncSearch", + description="Performs searches based on query", + ) + + async def _run(self, query: str): + await asyncio.sleep(1) + return "The capital of France is Paris." + + async_search = AsyncSearch() + + agent = Agent( + role="Research", + goal="Research the user query and provide a brief and concise response. If you need more information, ask the user for it.", + backstory=( + "You are a virtual concierge specialized in research. Respond to our first-class users with the latest information." + ), + tools=[async_search], + llm=ChatOpenAI(temperature=0, model="gpt-4"), + ) + task_description = "Find the capital of France" + expected_output = "A response to the user query." + + async_task = Task( + description=task_description, expected_output=expected_output, agent=agent + ) + + crew = Crew(agents=[agent], tasks=[async_task]) + + result = crew.kickoff() + assert result == "The capital of France is Paris." + + def test_manager_agent_in_agents_raises_exception(): task = Task( description="Come up with a list of 5 interesting ideas to explore for an article, then write one amazing paragraph highlight for each idea that showcases how good an article about this topic could be. Return the list of ideas with their paragraph and your notes.",