Skip to content

Commit 6c130fa

Browse files
allow tool async to sync conversion (#8332)
1 parent ddc0581 commit 6c130fa

File tree

3 files changed

+30
-1
lines changed

3 files changed

+30
-1
lines changed

dspy/adapters/types/tool.py

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from pydantic import BaseModel, TypeAdapter, create_model
77

88
from dspy.adapters.types.base_type import BaseType
9+
from dspy.dsp.utils.settings import settings
910
from dspy.utils.callback import with_callbacks
1011

1112
if TYPE_CHECKING:
@@ -160,12 +161,26 @@ def format_as_litellm_function_call(self):
160161
},
161162
}
162163

164+
def _run_async_in_sync(self, coroutine):
165+
try:
166+
loop = asyncio.get_running_loop()
167+
except RuntimeError:
168+
return asyncio.run(coroutine)
169+
170+
return loop.run_until_complete(coroutine)
171+
163172
@with_callbacks
164173
def __call__(self, **kwargs):
165174
parsed_kwargs = self._validate_and_parse_args(**kwargs)
166175
result = self.func(**parsed_kwargs)
167176
if asyncio.iscoroutine(result):
168-
raise ValueError("You are calling `__call__` on an async tool, please use `acall` instead.")
177+
if settings.allow_tool_async_sync_conversion:
178+
return self._run_async_in_sync(result)
179+
else:
180+
raise ValueError(
181+
"You are calling `__call__` on an async tool, please use `acall` instead or set "
182+
"`allow_async=True` to run the async tool in sync mode."
183+
)
169184
return result
170185

171186
@with_callbacks

dspy/dsp/utils/settings.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@
2727
provide_traceback=False, # Whether to include traceback information in error logs.
2828
num_threads=8, # Number of threads to use for parallel processing.
2929
max_errors=10, # Maximum errors before halting operations.
30+
# If true, async tools can be called in sync mode by getting converted to sync.
31+
allow_tool_async_sync_conversion=False,
3032
)
3133

3234
# Global base configuration and owner tracking

tests/adapters/test_tool.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
from pydantic import BaseModel
66

77
from dspy.adapters.types.tool import Tool
8+
import dspy
89

910

1011
# Test fixtures
@@ -363,3 +364,14 @@ async def test_async_concurrent_calls():
363364
# Check that it ran concurrently (should take ~0.1s, not ~0.5s)
364365
# We use 0.3s as threshold to account for some overhead
365366
assert end_time - start_time < 0.3
367+
368+
369+
def test_async_tool_call_in_sync_mode():
370+
tool = Tool(async_dummy_function)
371+
with dspy.context(allow_tool_async_sync_conversion=False):
372+
with pytest.raises(ValueError):
373+
result = tool(x=1, y="hello")
374+
375+
with dspy.context(allow_tool_async_sync_conversion=True):
376+
result = tool(x=1, y="hello")
377+
assert result == "hello 1"

0 commit comments

Comments
 (0)