mirror of
https://github.com/HKUDS/nanobot.git
synced 2026-05-19 16:12:30 +00:00
fix(agent): prevent outer wall-clock timeout for streaming requests
This commit is contained in:
parent
06a1bef9fe
commit
e87c07c368
@ -669,14 +669,25 @@ class AgentRunner:
|
|||||||
else:
|
else:
|
||||||
coro = self.provider.chat_with_retry(**kwargs)
|
coro = self.provider.chat_with_retry(**kwargs)
|
||||||
|
|
||||||
|
# Streaming requests already have provider-level idle timeouts
|
||||||
|
# (NANOBOT_STREAM_IDLE_TIMEOUT_S). Do not also apply the outer wall-clock
|
||||||
|
# LLM timeout here, or healthy long reasoning streams can be killed just
|
||||||
|
# because total elapsed time exceeded NANOBOT_LLM_TIMEOUT_S.
|
||||||
|
outer_timeout_s = None if (wants_streaming or wants_progress_streaming) else timeout_s
|
||||||
try:
|
try:
|
||||||
response = (
|
response = (
|
||||||
await coro if timeout_s is None
|
await coro if outer_timeout_s is None
|
||||||
else await asyncio.wait_for(coro, timeout=timeout_s)
|
else await asyncio.wait_for(coro, timeout=outer_timeout_s)
|
||||||
)
|
)
|
||||||
except asyncio.TimeoutError:
|
except asyncio.TimeoutError:
|
||||||
|
if outer_timeout_s is None:
|
||||||
|
return LLMResponse(
|
||||||
|
content="Error calling LLM: stream stalled",
|
||||||
|
finish_reason="error",
|
||||||
|
error_kind="timeout",
|
||||||
|
)
|
||||||
return LLMResponse(
|
return LLMResponse(
|
||||||
content=f"Error calling LLM: timed out after {timeout_s:g}s",
|
content=f"Error calling LLM: timed out after {outer_timeout_s:g}s",
|
||||||
finish_reason="error",
|
finish_reason="error",
|
||||||
error_kind="timeout",
|
error_kind="timeout",
|
||||||
)
|
)
|
||||||
|
|||||||
@ -133,6 +133,50 @@ async def test_runner_times_out_hung_llm_request():
|
|||||||
assert "timed out" in (result.final_content or "").lower()
|
assert "timed out" in (result.final_content or "").lower()
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_runner_does_not_apply_outer_wall_timeout_to_streaming_requests():
|
||||||
|
from nanobot.agent.hook import AgentHook, AgentHookContext
|
||||||
|
from nanobot.agent.runner import AgentRunSpec, AgentRunner
|
||||||
|
|
||||||
|
provider = MagicMock(spec=LLMProvider)
|
||||||
|
streamed: list[str] = []
|
||||||
|
|
||||||
|
async def chat_stream_with_retry(*, on_content_delta, **kwargs):
|
||||||
|
await asyncio.sleep(0.08)
|
||||||
|
await on_content_delta("still ")
|
||||||
|
await asyncio.sleep(0.08)
|
||||||
|
await on_content_delta("alive")
|
||||||
|
return LLMResponse(content="still alive", tool_calls=[])
|
||||||
|
|
||||||
|
provider.chat_stream_with_retry = chat_stream_with_retry
|
||||||
|
provider.chat_with_retry = AsyncMock()
|
||||||
|
tools = MagicMock()
|
||||||
|
tools.get_definitions.return_value = []
|
||||||
|
|
||||||
|
class StreamingHook(AgentHook):
|
||||||
|
def wants_streaming(self) -> bool:
|
||||||
|
return True
|
||||||
|
|
||||||
|
async def on_stream(self, context: AgentHookContext, delta: str) -> None:
|
||||||
|
streamed.append(delta)
|
||||||
|
|
||||||
|
runner = AgentRunner(provider)
|
||||||
|
result = await runner.run(AgentRunSpec(
|
||||||
|
initial_messages=[{"role": "user", "content": "think for a while"}],
|
||||||
|
tools=tools,
|
||||||
|
model="test-model",
|
||||||
|
max_iterations=1,
|
||||||
|
max_tool_result_chars=_MAX_TOOL_RESULT_CHARS,
|
||||||
|
hook=StreamingHook(),
|
||||||
|
llm_timeout_s=0.01,
|
||||||
|
))
|
||||||
|
|
||||||
|
assert result.stop_reason == "completed"
|
||||||
|
assert result.final_content == "still alive"
|
||||||
|
assert streamed == ["still ", "alive"]
|
||||||
|
provider.chat_with_retry.assert_not_awaited()
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_runner_replaces_empty_tool_result_with_marker():
|
async def test_runner_replaces_empty_tool_result_with_marker():
|
||||||
from nanobot.agent.runner import AgentRunSpec, AgentRunner
|
from nanobot.agent.runner import AgentRunSpec, AgentRunner
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user