fix(agent): add wall-clock timeout for streaming LLM requests

This commit is contained in:
WK Wong
2026-07-14 15:51:22 +08:00
committed by chengyongru
parent 6c9e3a2cc3
commit 11eb9d8cc8
2 changed files with 60 additions and 9 deletions
+49 -4
View File
@@ -189,7 +189,7 @@ async def test_runner_times_out_hung_llm_request():
@pytest.mark.asyncio
async def test_runner_does_not_apply_outer_wall_timeout_to_streaming_requests():
async def test_runner_applies_outer_wall_timeout_to_streaming_requests():
from nanobot.agent.hook import AgentHook, AgentHookContext
from nanobot.agent.runner import AgentRunner
@@ -216,8 +216,13 @@ async def test_runner_does_not_apply_outer_wall_timeout_to_streaming_requests():
streamed.append(delta)
runner = AgentRunner()
wait_for = AsyncMock(side_effect=AssertionError("streaming path must not use wait_for"))
with patch("nanobot.agent.runner.asyncio.wait_for", wait_for):
wait_for_calls: list[float] = []
async def fake_wait_for(coro, *, timeout):
wait_for_calls.append(timeout)
return await coro
with patch("nanobot.agent.runner.asyncio.wait_for", fake_wait_for):
result = await runner.run(make_run_spec(provider,
initial_messages=[{"role": "user", "content": "think for a while"}],
tools=tools,
@@ -232,7 +237,47 @@ async def test_runner_does_not_apply_outer_wall_timeout_to_streaming_requests():
assert result.final_content == "still alive"
assert streamed == ["still ", "alive"]
provider.chat_with_retry.assert_not_awaited()
wait_for.assert_not_awaited()
assert wait_for_calls == [300.0]
@pytest.mark.asyncio
async def test_runner_times_out_never_ending_streaming_request():
from nanobot.agent.hook import AgentHook
from nanobot.agent.runner import AgentRunner
provider = MagicMock(spec=LLMProvider)
async def chat_stream_with_retry(*, on_content_delta, **kwargs):
await asyncio.sleep(3600)
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 fake_wait_for(coro, *, timeout):
coro.close()
raise asyncio.TimeoutError
runner = AgentRunner()
with patch("nanobot.agent.runner.asyncio.wait_for", fake_wait_for):
result = await runner.run(make_run_spec(provider,
initial_messages=[{"role": "user", "content": "think forever"}],
tools=tools,
model="test-model",
max_iterations=1,
max_tool_result_chars=_MAX_TOOL_RESULT_CHARS,
hook=StreamingHook(),
llm_timeout_s=200,
))
assert result.stop_reason == "error"
assert result.final_content == "Error calling LLM: timed out after 400s"
provider.chat_with_retry.assert_not_awaited()
@pytest.mark.asyncio