fix: add 570s total timeout to agent runner

Wraps the tool-calling loop in asyncio.wait_for so long-running tasks
(e.g. consultation summary generation) return a clean Hebrew message
instead of being abruptly cut when PHP closes the connection at 600s.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-06 17:56:50 +00:00
parent 098a86e3fd
commit c697743557
+42 -1
View File
@@ -2,6 +2,7 @@
from __future__ import annotations
import asyncio
import json
import logging
from typing import Any, Callable
@@ -84,6 +85,7 @@ class AgentRunner:
model: str = "sonnet",
max_tokens: int = 4096,
max_iterations: int = 10,
total_timeout: float = 570.0,
):
self.client = AsyncOpenAI(
base_url=f"{ai_gateway_url.rstrip('/')}/v1",
@@ -92,6 +94,7 @@ class AgentRunner:
self.model = model
self.max_tokens = max_tokens
self.max_iterations = max_iterations
self.total_timeout = total_timeout
async def run(
self,
@@ -108,7 +111,45 @@ class AgentRunner:
kb_topic_name: str | None = None,
on_event: Callable[[dict], None] | None = None,
) -> str:
"""Run a conversation with tool-calling loop. Returns the final text response.
"""Run a conversation with tool-calling loop, bounded by total_timeout seconds."""
try:
return await asyncio.wait_for(
self._run_impl(
system_prompt=system_prompt,
messages=messages,
context=context,
espocrm_url=espocrm_url,
espocrm_api_key=espocrm_api_key,
depth=depth,
blocked_tools=blocked_tools,
allowed_toolsets=allowed_toolsets,
kb_sources_used=kb_sources_used,
kb_topic_id=kb_topic_id,
kb_topic_name=kb_topic_name,
on_event=on_event,
),
timeout=self.total_timeout,
)
except asyncio.TimeoutError:
logger.warning("[agent] total timeout (%.0fs) reached", self.total_timeout)
return "⏱️ המשימה לקחה יותר זמן מהמותר. נסי לפצל אותה לחלקים קטנים — למשל: קודם בקשי לקרוא מסמך ספציפי, ואז לכתוב את הסיכום."
async def _run_impl(
self,
system_prompt: str,
messages: list[dict[str, str]],
context: dict,
espocrm_url: str,
espocrm_api_key: str,
depth: int = 0,
blocked_tools: set[str] | None = None,
allowed_toolsets: list[str] | None = None,
kb_sources_used: list[dict] | None = None,
kb_topic_id: int | None = None,
kb_topic_name: str | None = None,
on_event: Callable[[dict], None] | None = None,
) -> str:
"""Internal tool-calling loop. Returns the final text response.
Args:
depth: Current delegation depth. 0 = main conversation, 1 = child agent.