Skip to content

Commit 19244e1

Browse files
committed
feat(async): integrate nest-asyncio for async tool calls in running event loops
- Added support for using async tools within a running event loop by integrating `nest-asyncio`. - Updated `pyproject.toml` to include `nest-asyncio` as a dependency. - Enhanced error handling to prompt installation of `nest-asyncio` if not found. - Added tests to verify async tool calls from a running event loop. This change improves the usability of async tools in environments like Jupyter notebooks.
1 parent fe03ead commit 19244e1

File tree

5 files changed

+2349
-2316
lines changed

5 files changed

+2349
-2316
lines changed

dspy/adapters/types/tool.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,17 @@ def _run_async_in_sync(self, coroutine):
168168
except RuntimeError:
169169
return asyncio.run(coroutine)
170170

171+
if loop and loop.is_running():
172+
try:
173+
import nest_asyncio
174+
175+
nest_asyncio.apply()
176+
except ImportError:
177+
raise ImportError(
178+
"nest_asyncio is required to call async tools from within a running event loop. "
179+
"Install it with: pip install nest-asyncio"
180+
)
181+
171182
return loop.run_until_complete(coroutine)
172183

173184
@with_callbacks

dspy/utils/syncify.py

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,15 @@ def run_async(coro):
1414
loop = None
1515

1616
if loop and loop.is_running():
17-
# If we're in a running event loop (e.g., Jupyter), use asyncio.create_task and run until done
18-
import nest_asyncio
19-
20-
nest_asyncio.apply()
17+
try:
18+
import nest_asyncio
19+
20+
nest_asyncio.apply()
21+
except ImportError:
22+
raise ImportError(
23+
"nest_asyncio is required to call async functions from within a running event loop. "
24+
"Install it with: pip install nest-asyncio"
25+
)
2126
return asyncio.get_event_loop().run_until_complete(coro)
2227
else:
2328
return asyncio.run(coro)

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ test_extras = [
6464
"pandas>=2.1.1",
6565
"optuna>=3.4.0",
6666
"langchain_core",
67+
"nest-asyncio>=1.6.0",
6768
]
6869

6970
[tool.setuptools.packages.find]

tests/adapters/test_tool.py

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -393,6 +393,16 @@ def test_async_tool_call_in_sync_mode():
393393
assert result == "hello 1"
394394

395395

396+
@pytest.mark.asyncio
397+
@pytest.mark.filterwarnings("ignore::DeprecationWarning:pytest_asyncio")
398+
async def test_async_tool_call_from_running_event_loop():
399+
tool = Tool(async_dummy_function)
400+
401+
with dspy.context(allow_tool_async_sync_conversion=True):
402+
result = tool(x=42, y="test")
403+
assert result == "test 42"
404+
405+
396406
TOOL_CALL_TEST_CASES = [
397407
([], {"tool_calls": []}),
398408
(
@@ -542,19 +552,14 @@ def test_tool_convert_input_schema_to_tool_args_lang_chain():
542552
}
543553

544554

545-
546-
547555
def test_tool_call_execute():
548556
def get_weather(city: str) -> str:
549557
return f"The weather in {city} is sunny"
550558

551559
def add_numbers(a: int, b: int) -> int:
552560
return a + b
553561

554-
tools = [
555-
dspy.Tool(get_weather),
556-
dspy.Tool(add_numbers)
557-
]
562+
tools = [dspy.Tool(get_weather), dspy.Tool(add_numbers)]
558563

559564
tool_call = dspy.ToolCalls.ToolCall(name="get_weather", args={"city": "Berlin"})
560565
result = tool_call.execute(functions=tools)

0 commit comments

Comments
 (0)