From fe8a87dbb177831de8600d704723eb9e3021aaf9 Mon Sep 17 00:00:00 2001 From: Deepankar Mahapatro Date: Sat, 7 Jun 2025 09:56:10 +0530 Subject: [PATCH 1/2] feat(tools): run sync tools in a thread --- src/agents/tool.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/agents/tool.py b/src/agents/tool.py index 57272f9f4..2a36a78b8 100644 --- a/src/agents/tool.py +++ b/src/agents/tool.py @@ -1,5 +1,6 @@ from __future__ import annotations +import asyncio import inspect import json from collections.abc import Awaitable @@ -382,9 +383,9 @@ async def _on_invoke_tool_impl(ctx: RunContextWrapper[Any], input: str) -> Any: result = await the_func(*args, **kwargs_dict) else: if schema.takes_context: - result = the_func(ctx, *args, **kwargs_dict) + result = await asyncio.to_thread(the_func, ctx, *args, **kwargs_dict) else: - result = the_func(*args, **kwargs_dict) + result = await asyncio.to_thread(the_func, *args, **kwargs_dict) if _debug.DONT_LOG_TOOL_DATA: logger.debug(f"Tool {schema.name} completed.") From d6688b6a1367b7da641624a946f973ded03a772e Mon Sep 17 00:00:00 2001 From: Deepankar Mahapatro Date: Mon, 30 Jun 2025 12:17:15 +0530 Subject: [PATCH 2/2] feat: add run_in_thread to function tools --- src/agents/tool.py | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/src/agents/tool.py b/src/agents/tool.py index 35def9e0c..9b85cf6fb 100644 --- a/src/agents/tool.py +++ b/src/agents/tool.py @@ -31,7 +31,6 @@ from .util._types import MaybeAwaitable if TYPE_CHECKING: - from .agent import Agent ToolParams = ParamSpec("ToolParams") @@ -303,6 +302,7 @@ def function_tool( failure_error_function: ToolErrorFunction | None = None, strict_mode: bool = True, is_enabled: bool | Callable[[RunContextWrapper[Any], Agent[Any]], MaybeAwaitable[bool]] = True, + run_in_thread: bool = False, ) -> FunctionTool: """Overload for usage as @function_tool (no parentheses).""" ... @@ -318,6 +318,7 @@ def function_tool( failure_error_function: ToolErrorFunction | None = None, strict_mode: bool = True, is_enabled: bool | Callable[[RunContextWrapper[Any], Agent[Any]], MaybeAwaitable[bool]] = True, + run_in_thread: bool = False, ) -> Callable[[ToolFunction[...]], FunctionTool]: """Overload for usage as @function_tool(...).""" ... @@ -333,6 +334,7 @@ def function_tool( failure_error_function: ToolErrorFunction | None = default_tool_error_function, strict_mode: bool = True, is_enabled: bool | Callable[[RunContextWrapper[Any], Agent[Any]], MaybeAwaitable[bool]] = True, + run_in_thread: bool = False, ) -> FunctionTool | Callable[[ToolFunction[...]], FunctionTool]: """ Decorator to create a FunctionTool from a function. By default, we will: @@ -364,6 +366,9 @@ def function_tool( is_enabled: Whether the tool is enabled. Can be a bool or a callable that takes the run context and agent and returns whether the tool is enabled. Disabled tools are hidden from the LLM at runtime. + run_in_thread: Whether to run the tool in a thread. This only applies to non-async functions. + If True, the tool will be run in a thread, which can be useful to avoid blocking the + main thread. """ def _create_function_tool(the_func: ToolFunction[...]) -> FunctionTool: @@ -414,9 +419,15 @@ async def _on_invoke_tool_impl(ctx: ToolContext[Any], input: str) -> Any: result = await the_func(*args, **kwargs_dict) else: if schema.takes_context: - result = await asyncio.to_thread(the_func, ctx, *args, **kwargs_dict) + if run_in_thread: + result = await asyncio.to_thread(the_func, ctx, *args, **kwargs_dict) + else: + result = the_func(ctx, *args, **kwargs_dict) else: - result = await asyncio.to_thread(the_func, *args, **kwargs_dict) + if run_in_thread: + result = await asyncio.to_thread(the_func, *args, **kwargs_dict) + else: + result = the_func(*args, **kwargs_dict) if _debug.DONT_LOG_TOOL_DATA: logger.debug(f"Tool {schema.name} completed.")