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:
@@ -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(
|
||||||
|
|||||||
Reference in New Issue
Block a user