Make function_call_timeout_secs optional and skip timeout task when unset

Change the default from 10s to None so deferred function calls can run
indefinitely when no timeout is configured. Only create the timeout
task when a timeout is actually provided (per-call or service-level).
This commit is contained in:
Aleix Conchillo Flaqué
2026-04-01 13:58:09 -07:00
parent a3c7f6c2af
commit 53f809b7d5

View File

@@ -185,7 +185,7 @@ class LLMService(UserTurnCompletionLLMServiceMixin, AIService):
def __init__( def __init__(
self, self,
run_in_parallel: bool = True, run_in_parallel: bool = True,
function_call_timeout_secs: float = 10.0, function_call_timeout_secs: Optional[float] = None,
settings: Optional[LLMSettings] = None, settings: Optional[LLMSettings] = None,
**kwargs, **kwargs,
): ):
@@ -194,8 +194,8 @@ class LLMService(UserTurnCompletionLLMServiceMixin, AIService):
Args: Args:
run_in_parallel: Whether to run function calls in parallel or sequentially. run_in_parallel: Whether to run function calls in parallel or sequentially.
Defaults to True. Defaults to True.
function_call_timeout_secs: Timeout in seconds for deferred function calls. function_call_timeout_secs: Optional timeout in seconds for deferred function
Defaults to 10.0 seconds. calls.
settings: The runtime-updatable settings for the LLM service. settings: The runtime-updatable settings for the LLM service.
**kwargs: Additional arguments passed to the parent AIService. **kwargs: Additional arguments passed to the parent AIService.
@@ -753,11 +753,7 @@ class LLMService(UserTurnCompletionLLMServiceMixin, AIService):
# Start a timeout task for deferred function calls # Start a timeout task for deferred function calls
async def timeout_handler(): async def timeout_handler():
try: try:
effective_timeout = ( effective_timeout = item.timeout_secs or self._function_call_timeout_secs
item.timeout_secs
if item.timeout_secs is not None
else self._function_call_timeout_secs
)
await asyncio.sleep(effective_timeout) await asyncio.sleep(effective_timeout)
logger.warning( logger.warning(
f"{self} Function call [{runner_item.function_name}:{runner_item.tool_call_id}] timed out after {effective_timeout} seconds." f"{self} Function call [{runner_item.function_name}:{runner_item.tool_call_id}] timed out after {effective_timeout} seconds."
@@ -768,13 +764,15 @@ class LLMService(UserTurnCompletionLLMServiceMixin, AIService):
except asyncio.CancelledError: except asyncio.CancelledError:
raise raise
timeout_task = self.create_task(timeout_handler()) if item.timeout_secs or self._function_call_timeout_secs:
timeout_task = self.create_task(timeout_handler())
# Yield to the event loop so the timeout task coroutine gets entered
# before it could be cancelled. Without this, cancelling the task before
# it starts would leave the coroutine in a "never awaited" state.
await asyncio.sleep(0)
try: try:
# Yield to the event loop so the timeout task coroutine gets entered
# before it could be cancelled. Without this, cancelling the task before
# it starts would leave the coroutine in a "never awaited" state.
await asyncio.sleep(0)
if isinstance(item.handler, DirectFunctionWrapper): if isinstance(item.handler, DirectFunctionWrapper):
# Handler is a DirectFunctionWrapper # Handler is a DirectFunctionWrapper
await item.handler.invoke( await item.handler.invoke(