Skip to content

Commit 81cab34

Browse files
committed
test(tools): cover sync-func-returning-coroutine branch; clarify run_coroutine_sync docstring
1 parent ab73c0f commit 81cab34

2 files changed

Lines changed: 30 additions & 3 deletions

File tree

lib/crewai/src/crewai/utilities/async_utils.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,11 @@ def run_coroutine_sync(coro: Coroutine[Any, Any, T]) -> T:
1616
When called from within a running event loop (for example a FastAPI
1717
request handler, a Jupyter cell or any ``async def``), ``asyncio.run``
1818
raises ``RuntimeError: asyncio.run() cannot be called from a running event
19-
loop``. In that case the coroutine is executed to completion in a
20-
dedicated worker thread so the calling loop is neither blocked nor
21-
re-entered. Otherwise it falls back to ``asyncio.run``.
19+
loop``. In that case the coroutine is executed to completion on a
20+
dedicated worker thread (with a copy of the current context), which avoids
21+
re-entering the running loop. The calling thread blocks on
22+
``Future.result()`` until the coroutine finishes, so the running loop makes
23+
no progress while it waits. Otherwise it falls back to ``asyncio.run``.
2224
"""
2325
try:
2426
asyncio.get_running_loop()

lib/crewai/tests/tools/test_async_tools.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,31 @@ async def caller() -> str:
202202
result = asyncio.run(caller())
203203
assert result == "structured: test"
204204

205+
def test_structured_tool_invoke_returned_coroutine_inside_running_loop(
206+
self,
207+
) -> None:
208+
"""CrewStructuredTool.invoke() must resolve a coroutine returned by a
209+
sync func under a running loop (the non-``async def`` branch)."""
210+
211+
def sync_returns_coro(value: str) -> str:
212+
"""A sync structured tool that returns a coroutine."""
213+
214+
async def _inner() -> str:
215+
await asyncio.sleep(0.01)
216+
return f"returned: {value}"
217+
218+
return _inner() # type: ignore[return-value]
219+
220+
structured = CrewStructuredTool.from_function(
221+
func=sync_returns_coro, name="structured_returns_coro"
222+
)
223+
224+
async def caller() -> str:
225+
return structured.invoke({"value": "test"})
226+
227+
result = asyncio.run(caller())
228+
assert result == "returned: test"
229+
205230

206231
class TestAsyncToolWithIO:
207232
"""Tests for async tools with simulated I/O operations."""

0 commit comments

Comments
 (0)