mirror of
https://github.com/HKUDS/nanobot.git
synced 2026-09-04 02:01:48 +03:00
refactor(agent): reduce loop runner parameter plumbing
This commit is contained in:
@@ -6,6 +6,7 @@ from unittest.mock import AsyncMock, MagicMock
|
||||
import pytest
|
||||
|
||||
from nanobot.agent.loop import AgentLoop, TurnContext, TurnKind
|
||||
from nanobot.agent.tools.context import RequestContext
|
||||
from nanobot.agent.tools.filesystem import ReadFileTool
|
||||
from nanobot.bus.events import InboundMessage
|
||||
from nanobot.bus.queue import MessageBus
|
||||
@@ -145,11 +146,11 @@ async def test_pending_document_attachment_keeps_body_out_of_prompt(
|
||||
)
|
||||
)
|
||||
|
||||
runtime = loop.llm_runtime()
|
||||
result = await loop._run_agent_loop(
|
||||
[{"role": "user", "content": "hello"}],
|
||||
runtime=loop.llm_runtime(),
|
||||
channel="cli",
|
||||
chat_id="c",
|
||||
runtime=runtime,
|
||||
request_context=RequestContext(channel="cli", chat_id="c", runtime=runtime),
|
||||
pending_queue=pending_queue,
|
||||
)
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ from nanobot.agent.hook import (
|
||||
AgentTurnHookContext,
|
||||
CompositeHook,
|
||||
)
|
||||
from nanobot.agent.tools.context import RequestContext
|
||||
|
||||
|
||||
def _ctx() -> AgentHookContext:
|
||||
@@ -501,15 +502,19 @@ async def test_agent_loop_turn_hook_factories_receive_context(tmp_path):
|
||||
async def on_progress(*args, **kwargs):
|
||||
pass
|
||||
|
||||
runtime = loop.llm_runtime()
|
||||
await loop._run_agent_loop(
|
||||
[{"role": "user", "content": "hi"}],
|
||||
runtime=loop.llm_runtime(),
|
||||
runtime=runtime,
|
||||
on_progress=on_progress,
|
||||
channel="websocket",
|
||||
chat_id="chat-1",
|
||||
message_id="msg-1",
|
||||
metadata={"source": "test"},
|
||||
session_key="websocket:chat-1",
|
||||
request_context=RequestContext(
|
||||
channel="websocket",
|
||||
chat_id="chat-1",
|
||||
message_id="msg-1",
|
||||
session_key="websocket:chat-1",
|
||||
runtime=runtime,
|
||||
metadata={"source": "test"},
|
||||
),
|
||||
hook_factories=[factory("turn")],
|
||||
)
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ from unittest.mock import AsyncMock, MagicMock, patch
|
||||
import pytest
|
||||
|
||||
from nanobot.agent.goal_permission import goal_mutation_allowed, goal_mutation_permission
|
||||
from nanobot.agent.tools.context import RequestContext
|
||||
from nanobot.bus.outbound_events import StreamedResponseEvent
|
||||
from nanobot.config.schema import AgentDefaults
|
||||
from nanobot.providers.base import GenerationSettings, LLMProvider, LLMResponse, ToolCallRequest
|
||||
@@ -359,10 +360,16 @@ async def test_loop_goal_turn_uses_standard_iteration_budget(tmp_path):
|
||||
loop.tools.execute = AsyncMock(return_value="ok")
|
||||
loop.max_iterations = 2
|
||||
|
||||
runtime = loop.llm_runtime()
|
||||
result = await loop._run_agent_loop(
|
||||
[],
|
||||
runtime=loop.llm_runtime(),
|
||||
metadata={"original_command": "/goal"},
|
||||
runtime=runtime,
|
||||
request_context=RequestContext(
|
||||
channel="cli",
|
||||
chat_id="direct",
|
||||
runtime=runtime,
|
||||
metadata={"original_command": "/goal"},
|
||||
),
|
||||
)
|
||||
|
||||
assert result.stop_reason == "max_iterations"
|
||||
|
||||
@@ -1543,7 +1543,8 @@ async def test_process_message_keeps_delivery_chat_for_thread_session(tmp_path:
|
||||
|
||||
assert result is not None
|
||||
assert result.chat_id == "thread-777"
|
||||
assert loop._run_agent_loop.call_args.kwargs["chat_id"] == "thread-777"
|
||||
request = loop._run_agent_loop.call_args.kwargs["request_context"]
|
||||
assert request.chat_id == "thread-777"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@@ -1592,12 +1593,12 @@ async def test_process_message_uses_explicit_session_for_goal_context(
|
||||
assert result.content == "ok"
|
||||
kwargs = loop._run_agent_loop.call_args.kwargs
|
||||
assert kwargs["session"] is system_session
|
||||
assert kwargs["session_key"] == "system"
|
||||
assert kwargs["request_context"].session_key == "system"
|
||||
assert GOAL_STATE_KEY not in kwargs["session"].metadata
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_run_agent_loop_goal_continue_message_reads_latest_metadata(
|
||||
async def test_run_agent_loop_continuation_reads_latest_goal_metadata(
|
||||
tmp_path: Path,
|
||||
) -> None:
|
||||
from nanobot.agent.runner import AgentRunResult
|
||||
@@ -1607,12 +1608,12 @@ async def test_run_agent_loop_goal_continue_message_reads_latest_metadata(
|
||||
seen: dict[str, str | None] = {}
|
||||
|
||||
async def fake_run(spec):
|
||||
assert callable(spec.goal_continue_message)
|
||||
assert callable(spec.continuation_callback)
|
||||
session.metadata[GOAL_STATE_KEY] = {
|
||||
"status": "active",
|
||||
"objective": "Goal created during this runner call.",
|
||||
}
|
||||
seen["goal_continue"] = spec.goal_continue_message()
|
||||
seen["goal_continue"] = spec.continuation_callback()
|
||||
return AgentRunResult(
|
||||
final_content="ok",
|
||||
messages=[{"role": "assistant", "content": "ok"}],
|
||||
@@ -1620,13 +1621,17 @@ async def test_run_agent_loop_goal_continue_message_reads_latest_metadata(
|
||||
|
||||
loop.runner.run = fake_run # type: ignore[method-assign]
|
||||
|
||||
runtime = loop.llm_runtime()
|
||||
await loop._run_agent_loop(
|
||||
[],
|
||||
runtime=loop.llm_runtime(),
|
||||
runtime=runtime,
|
||||
session=session,
|
||||
channel="websocket",
|
||||
chat_id="late-goal",
|
||||
session_key=session.key,
|
||||
request_context=RequestContext(
|
||||
channel="websocket",
|
||||
chat_id="late-goal",
|
||||
session_key=session.key,
|
||||
runtime=runtime,
|
||||
),
|
||||
)
|
||||
|
||||
assert "Goal created during this runner call." in (seen["goal_continue"] or "")
|
||||
|
||||
@@ -135,10 +135,13 @@ async def test_loop_binds_request_context_for_tool_execution(tmp_path: Path) ->
|
||||
await loop._run_agent_loop(
|
||||
[],
|
||||
runtime=runtime,
|
||||
channel="slack",
|
||||
chat_id="C123",
|
||||
metadata=metadata,
|
||||
session_key="slack:C123:111.222",
|
||||
request_context=RequestContext(
|
||||
channel="slack",
|
||||
chat_id="C123",
|
||||
session_key="slack:C123:111.222",
|
||||
runtime=runtime,
|
||||
metadata=metadata,
|
||||
),
|
||||
)
|
||||
|
||||
assert cron.contexts[-1] == {
|
||||
@@ -233,10 +236,13 @@ async def test_agent_loop_restores_outer_request_context_after_runner_exception(
|
||||
await loop._run_agent_loop(
|
||||
[],
|
||||
runtime=runtime,
|
||||
channel="slack",
|
||||
chat_id="C123",
|
||||
session_key="slack:C123:111.222",
|
||||
original_user_text=" unchanged user text ",
|
||||
request_context=RequestContext(
|
||||
channel="slack",
|
||||
chat_id="C123",
|
||||
session_key="slack:C123:111.222",
|
||||
original_user_text=" unchanged user text ",
|
||||
runtime=runtime,
|
||||
),
|
||||
)
|
||||
assert current_request_context() is outer
|
||||
finally:
|
||||
|
||||
@@ -843,7 +843,6 @@ async def test_runner_closes_progress_reasoning_on_streaming_wall_timeout():
|
||||
max_tool_result_chars=_MAX_TOOL_RESULT_CHARS,
|
||||
hook=ProgressReasoningHook(),
|
||||
progress_callback=AsyncMock(),
|
||||
stream_progress_deltas=True,
|
||||
llm_timeout_s=1,
|
||||
))
|
||||
|
||||
@@ -994,7 +993,7 @@ async def test_runner_does_not_auto_continue_goal_after_policy_terminal(
|
||||
model="test-model",
|
||||
max_iterations=3,
|
||||
max_tool_result_chars=_MAX_TOOL_RESULT_CHARS,
|
||||
goal_active_predicate=lambda: True,
|
||||
continuation_callback=lambda: "Continue working.",
|
||||
terminal_injection_callback=terminal_injection_callback,
|
||||
))
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
"""Tests for sustained-goal continuation in AgentRunner.
|
||||
"""Tests for caller-controlled continuation in AgentRunner.
|
||||
|
||||
When a goal_active_predicate returns True, the runner must not exit with
|
||||
stop_reason="completed" after a plain-text final response. Instead it should
|
||||
inject a continuation message and keep looping (similar to mid-turn injection).
|
||||
When the continuation callback returns a message, the runner must not exit with
|
||||
stop_reason="completed" after a plain-text final response. Instead it injects
|
||||
that message and keeps looping, similar to a mid-turn injection.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
@@ -18,9 +18,13 @@ from nanobot.providers.base import LLMProvider, LLMResponse
|
||||
_MAX_TOOL_RESULT_CHARS = AgentDefaults().max_tool_result_chars
|
||||
|
||||
|
||||
def _continue_goal() -> str:
|
||||
return "Continue working toward the active sustained goal."
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_runner_exits_normally_without_predicate():
|
||||
"""Baseline: no predicate, runner exits with completed on final text."""
|
||||
async def test_runner_exits_normally_without_continuation_callback():
|
||||
"""Without a continuation request, final text completes the run."""
|
||||
from nanobot.agent.runner import AgentRunner
|
||||
|
||||
provider = MagicMock(spec=LLMProvider)
|
||||
@@ -44,8 +48,8 @@ async def test_runner_exits_normally_without_predicate():
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_runner_exits_normally_with_inactive_goal():
|
||||
"""Predicate returns False, runner should exit normally."""
|
||||
async def test_runner_exits_normally_when_continuation_callback_returns_none():
|
||||
"""A callback returning None leaves the final response terminal."""
|
||||
from nanobot.agent.runner import AgentRunner
|
||||
|
||||
provider = MagicMock(spec=LLMProvider)
|
||||
@@ -62,7 +66,7 @@ async def test_runner_exits_normally_with_inactive_goal():
|
||||
model="test-model",
|
||||
max_iterations=2,
|
||||
max_tool_result_chars=_MAX_TOOL_RESULT_CHARS,
|
||||
goal_active_predicate=lambda: False,
|
||||
continuation_callback=lambda: None,
|
||||
))
|
||||
|
||||
assert result.stop_reason == "completed"
|
||||
@@ -70,8 +74,8 @@ async def test_runner_exits_normally_with_inactive_goal():
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_runner_forces_continue_when_goal_active():
|
||||
"""Predicate returns True on final text → runner injects continuation and loops.
|
||||
async def test_runner_continues_when_callback_returns_message():
|
||||
"""A callback result after final text is injected for the next iteration.
|
||||
|
||||
We set max_iterations=3 and let the provider return final text every time.
|
||||
Without the fix this would exit on the first iteration with stop_reason
|
||||
@@ -94,10 +98,10 @@ async def test_runner_forces_continue_when_goal_active():
|
||||
model="test-model",
|
||||
max_iterations=3,
|
||||
max_tool_result_chars=_MAX_TOOL_RESULT_CHARS,
|
||||
goal_active_predicate=lambda: True,
|
||||
continuation_callback=_continue_goal,
|
||||
))
|
||||
|
||||
# Because the predicate keeps returning True, the runner should never
|
||||
# Because the callback keeps returning a message, the runner should never
|
||||
# naturally complete. It loops until max_iterations is exhausted.
|
||||
assert result.stop_reason == "max_iterations"
|
||||
# The injected continuation message should be present in the message list.
|
||||
@@ -106,8 +110,8 @@ async def test_runner_forces_continue_when_goal_active():
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_runner_respects_max_iterations_even_with_active_goal():
|
||||
"""A single iteration with active goal still hits max_iterations."""
|
||||
async def test_runner_respects_max_iterations_with_continuation():
|
||||
"""A continuation request after one iteration still hits max_iterations."""
|
||||
from nanobot.agent.runner import AgentRunner
|
||||
|
||||
provider = MagicMock(spec=LLMProvider)
|
||||
@@ -124,15 +128,15 @@ async def test_runner_respects_max_iterations_even_with_active_goal():
|
||||
model="test-model",
|
||||
max_iterations=1,
|
||||
max_tool_result_chars=_MAX_TOOL_RESULT_CHARS,
|
||||
goal_active_predicate=lambda: True,
|
||||
continuation_callback=_continue_goal,
|
||||
))
|
||||
|
||||
assert result.stop_reason == "max_iterations"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_runner_goal_continue_not_limited_by_injection_cycle_cap():
|
||||
"""Synthetic goal continuation should be governed by max_iterations."""
|
||||
async def test_runner_continuation_not_limited_by_injection_cycle_cap():
|
||||
"""Caller-requested continuation is governed by max_iterations."""
|
||||
from nanobot.agent.runner import _MAX_INJECTION_CYCLES, AgentRunner
|
||||
|
||||
provider = MagicMock(spec=LLMProvider)
|
||||
@@ -150,7 +154,7 @@ async def test_runner_goal_continue_not_limited_by_injection_cycle_cap():
|
||||
model="test-model",
|
||||
max_iterations=max_iterations,
|
||||
max_tool_result_chars=_MAX_TOOL_RESULT_CHARS,
|
||||
goal_active_predicate=lambda: True,
|
||||
continuation_callback=_continue_goal,
|
||||
finalize_on_max_iterations=False,
|
||||
))
|
||||
|
||||
@@ -159,8 +163,8 @@ async def test_runner_goal_continue_not_limited_by_injection_cycle_cap():
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_runner_does_not_force_continue_on_error():
|
||||
"""Even with active goal, an LLM error should exit with stop_reason="error"."""
|
||||
async def test_runner_does_not_continue_on_error():
|
||||
"""An LLM error remains terminal even when continuation is available."""
|
||||
from nanobot.agent.runner import AgentRunner
|
||||
|
||||
provider = MagicMock(spec=LLMProvider)
|
||||
@@ -178,15 +182,15 @@ async def test_runner_does_not_force_continue_on_error():
|
||||
model="test-model",
|
||||
max_iterations=2,
|
||||
max_tool_result_chars=_MAX_TOOL_RESULT_CHARS,
|
||||
goal_active_predicate=lambda: True,
|
||||
continuation_callback=_continue_goal,
|
||||
))
|
||||
|
||||
assert result.stop_reason == "error"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_runner_uses_custom_goal_continue_message():
|
||||
"""Custom goal_continue_message should be injected instead of the default."""
|
||||
async def test_runner_injects_continuation_callback_message():
|
||||
"""The callback result becomes the injected user message."""
|
||||
from nanobot.agent.runner import AgentRunner
|
||||
|
||||
provider = MagicMock(spec=LLMProvider)
|
||||
@@ -205,8 +209,7 @@ async def test_runner_uses_custom_goal_continue_message():
|
||||
model="test-model",
|
||||
max_iterations=2,
|
||||
max_tool_result_chars=_MAX_TOOL_RESULT_CHARS,
|
||||
goal_active_predicate=lambda: True,
|
||||
goal_continue_message=custom_msg,
|
||||
continuation_callback=lambda: custom_msg,
|
||||
))
|
||||
|
||||
user_msgs = [m for m in result.messages if m.get("role") == "user"]
|
||||
@@ -214,7 +217,7 @@ async def test_runner_uses_custom_goal_continue_message():
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_runner_resolves_goal_continue_message_lazily():
|
||||
async def test_runner_resolves_continuation_callback_lazily():
|
||||
"""The continuation text can depend on goal metadata created during the run."""
|
||||
from nanobot.agent.runner import AgentRunner
|
||||
|
||||
@@ -237,8 +240,7 @@ async def test_runner_resolves_goal_continue_message_lazily():
|
||||
model="test-model",
|
||||
max_iterations=1,
|
||||
max_tool_result_chars=_MAX_TOOL_RESULT_CHARS,
|
||||
goal_active_predicate=lambda: True,
|
||||
goal_continue_message=dynamic_msg,
|
||||
continuation_callback=dynamic_msg,
|
||||
finalize_on_max_iterations=False,
|
||||
))
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ import pytest
|
||||
|
||||
from agent.runner_helpers import make_run_spec
|
||||
from nanobot.agent.automation_turns import publish_next_deferred_turn
|
||||
from nanobot.agent.tools.context import RequestContext
|
||||
from nanobot.config.schema import AgentDefaults
|
||||
from nanobot.providers.base import LLMResponse, ToolCallRequest
|
||||
|
||||
@@ -390,13 +391,13 @@ async def test_goal_continuation_precedes_terminal_wait():
|
||||
])
|
||||
tools = MagicMock()
|
||||
tools.get_definitions.return_value = []
|
||||
goal_checks = 0
|
||||
continuation_checks = 0
|
||||
terminal_waits = 0
|
||||
|
||||
def goal_active() -> bool:
|
||||
nonlocal goal_checks
|
||||
goal_checks += 1
|
||||
return goal_checks == 1
|
||||
def continue_goal() -> str | None:
|
||||
nonlocal continuation_checks
|
||||
continuation_checks += 1
|
||||
return "Continue the active goal." if continuation_checks == 1 else None
|
||||
|
||||
async def drain_available():
|
||||
return []
|
||||
@@ -415,7 +416,7 @@ async def test_goal_continuation_precedes_terminal_wait():
|
||||
max_tool_result_chars=_MAX_TOOL_RESULT_CHARS,
|
||||
injection_callback=drain_available,
|
||||
terminal_injection_callback=wait_at_terminal,
|
||||
goal_active_predicate=goal_active,
|
||||
continuation_callback=continue_goal,
|
||||
))
|
||||
|
||||
assert provider.chat_with_retry.await_count == 2
|
||||
@@ -614,11 +615,11 @@ async def test_loop_injected_followup_preserves_image_media(tmp_path):
|
||||
media=[str(image_path)],
|
||||
))
|
||||
|
||||
runtime = loop.llm_runtime()
|
||||
result = await loop._run_agent_loop(
|
||||
[{"role": "user", "content": "hello"}],
|
||||
runtime=loop.llm_runtime(),
|
||||
channel="cli",
|
||||
chat_id="c",
|
||||
runtime=runtime,
|
||||
request_context=RequestContext(channel="cli", chat_id="c", runtime=runtime),
|
||||
pending_queue=pending_queue,
|
||||
)
|
||||
|
||||
@@ -708,13 +709,17 @@ async def test_pending_injection_resolves_its_own_runtime_context(tmp_path):
|
||||
},
|
||||
))
|
||||
|
||||
runtime = loop.llm_runtime()
|
||||
result = await loop._run_agent_loop(
|
||||
[{"role": "user", "content": "initial message from user A"}],
|
||||
runtime=loop.llm_runtime(),
|
||||
runtime=runtime,
|
||||
session=session,
|
||||
channel="telegram",
|
||||
chat_id="group-1",
|
||||
session_key=session.key,
|
||||
request_context=RequestContext(
|
||||
channel="telegram",
|
||||
chat_id="group-1",
|
||||
session_key=session.key,
|
||||
runtime=runtime,
|
||||
),
|
||||
pending_queue=pending_queue,
|
||||
)
|
||||
|
||||
@@ -805,11 +810,11 @@ async def test_subagent_pending_injection_is_hidden_history_and_not_merged(tmp_p
|
||||
metadata={"injected_event": "subagent_result", "subagent_task_id": "sub-1"},
|
||||
))
|
||||
|
||||
runtime = loop.llm_runtime()
|
||||
result = await loop._run_agent_loop(
|
||||
[{"role": "user", "content": "hello"}],
|
||||
runtime=loop.llm_runtime(),
|
||||
channel="cli",
|
||||
chat_id="c",
|
||||
runtime=runtime,
|
||||
request_context=RequestContext(channel="cli", chat_id="c", runtime=runtime),
|
||||
pending_queue=pending_queue,
|
||||
)
|
||||
|
||||
@@ -1469,11 +1474,11 @@ async def test_pending_queue_preserves_overflow_for_next_injection_cycle(tmp_pat
|
||||
content=f"follow-up-{idx}",
|
||||
))
|
||||
|
||||
runtime = loop.llm_runtime()
|
||||
result = await loop._run_agent_loop(
|
||||
[{"role": "user", "content": "hello"}],
|
||||
runtime=loop.llm_runtime(),
|
||||
channel="cli",
|
||||
chat_id="c",
|
||||
runtime=runtime,
|
||||
request_context=RequestContext(channel="cli", chat_id="c", runtime=runtime),
|
||||
pending_queue=pending_queue,
|
||||
)
|
||||
|
||||
|
||||
@@ -17,39 +17,6 @@ from nanobot.providers.base import LLMResponse, ToolCallRequest
|
||||
_MAX_TOOL_RESULT_CHARS = AgentDefaults().max_tool_result_chars
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_runner_can_disable_provider_progress_delta_streaming():
|
||||
"""AgentLoop disables token progress streaming for non-streaming channels."""
|
||||
provider = MagicMock()
|
||||
provider.supports_progress_deltas = True
|
||||
provider.chat_with_retry = AsyncMock(
|
||||
return_value=LLMResponse(content="done", tool_calls=[], usage=None)
|
||||
)
|
||||
provider.chat_stream_with_retry = AsyncMock()
|
||||
tools = MagicMock()
|
||||
tools.get_definitions.return_value = []
|
||||
progress_cb = AsyncMock()
|
||||
|
||||
runner = AgentRunner()
|
||||
result = await runner.run(make_run_spec(provider,
|
||||
initial_messages=[
|
||||
{"role": "system", "content": "system"},
|
||||
{"role": "user", "content": "hi"},
|
||||
],
|
||||
tools=tools,
|
||||
model="test-model",
|
||||
max_iterations=1,
|
||||
max_tool_result_chars=_MAX_TOOL_RESULT_CHARS,
|
||||
progress_callback=progress_cb,
|
||||
stream_progress_deltas=False,
|
||||
))
|
||||
|
||||
assert result.final_content == "done"
|
||||
provider.chat_with_retry.assert_awaited_once()
|
||||
provider.chat_stream_with_retry.assert_not_awaited()
|
||||
progress_cb.assert_not_awaited()
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_runner_streams_provider_progress_deltas_by_default():
|
||||
"""Direct runner users keep the existing opt-in provider progress behavior."""
|
||||
|
||||
@@ -232,7 +232,6 @@ async def test_runner_emits_reasoning_content_even_when_answer_was_streamed():
|
||||
max_iterations=3,
|
||||
max_tool_result_chars=_MAX_TOOL_RESULT_CHARS,
|
||||
hook=hook,
|
||||
stream_progress_deltas=True,
|
||||
progress_callback=_progress,
|
||||
))
|
||||
|
||||
@@ -276,7 +275,6 @@ async def test_runner_does_not_double_emit_when_inline_think_already_streamed():
|
||||
max_iterations=3,
|
||||
max_tool_result_chars=_MAX_TOOL_RESULT_CHARS,
|
||||
hook=hook,
|
||||
stream_progress_deltas=True,
|
||||
progress_callback=_progress,
|
||||
))
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ from unittest.mock import AsyncMock, MagicMock, patch
|
||||
|
||||
import pytest
|
||||
|
||||
from nanobot.agent.tools.context import RequestContext
|
||||
from nanobot.config.schema import AgentDefaults
|
||||
from nanobot.providers.base import GenerationSettings
|
||||
from nanobot.utils.llm_runtime import LLMRuntime
|
||||
@@ -503,12 +504,12 @@ async def test_drain_pending_no_block_when_no_subagents(tmp_path):
|
||||
|
||||
loop.runner.run = AsyncMock(side_effect=fake_runner_run)
|
||||
|
||||
runtime = loop.llm_runtime()
|
||||
await loop._run_agent_loop(
|
||||
[{"role": "user", "content": "test"}],
|
||||
runtime=loop.llm_runtime(),
|
||||
runtime=runtime,
|
||||
session=None,
|
||||
channel="test",
|
||||
chat_id="c1",
|
||||
request_context=RequestContext(channel="test", chat_id="c1", runtime=runtime),
|
||||
pending_queue=pending_queue,
|
||||
)
|
||||
|
||||
@@ -562,12 +563,17 @@ async def test_terminal_drain_timeout(tmp_path):
|
||||
loop.subagents._session_tasks.setdefault(session.key, set()).add("sub-timeout-1")
|
||||
loop.subagents._running_tasks["sub-timeout-1"] = hang_task
|
||||
|
||||
runtime = loop.llm_runtime()
|
||||
await loop._run_agent_loop(
|
||||
[{"role": "user", "content": "test"}],
|
||||
runtime=loop.llm_runtime(),
|
||||
runtime=runtime,
|
||||
session=session,
|
||||
channel="test",
|
||||
chat_id="c1",
|
||||
request_context=RequestContext(
|
||||
channel="test",
|
||||
chat_id="c1",
|
||||
session_key=session.key,
|
||||
runtime=runtime,
|
||||
),
|
||||
pending_queue=pending_queue,
|
||||
)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user