refactor(agent): make runner consume required runtime
This commit is contained in:
@@ -7,6 +7,7 @@ from unittest.mock import AsyncMock, MagicMock
|
||||
|
||||
import pytest
|
||||
|
||||
from agent.runner_helpers import make_run_spec
|
||||
from nanobot.config.schema import AgentDefaults
|
||||
from nanobot.providers.base import LLMProvider, LLMResponse, ToolCallRequest
|
||||
|
||||
@@ -16,7 +17,7 @@ _MAX_TOOL_RESULT_CHARS = AgentDefaults().max_tool_result_chars
|
||||
@pytest.mark.asyncio
|
||||
async def test_runner_calls_hooks_in_order():
|
||||
from nanobot.agent.hook import AgentHook, AgentHookContext
|
||||
from nanobot.agent.runner import AgentRunner, AgentRunSpec
|
||||
from nanobot.agent.runner import AgentRunner
|
||||
|
||||
provider = MagicMock(spec=LLMProvider)
|
||||
call_count = {"n": 0}
|
||||
@@ -67,8 +68,8 @@ async def test_runner_calls_hooks_in_order():
|
||||
events.append(("finalize_content", context.iteration, content))
|
||||
return content.upper() if content else content
|
||||
|
||||
runner = AgentRunner(provider)
|
||||
result = await runner.run(AgentRunSpec(
|
||||
runner = AgentRunner()
|
||||
result = await runner.run(make_run_spec(provider,
|
||||
initial_messages=[],
|
||||
tools=tools,
|
||||
model="test-model",
|
||||
@@ -100,7 +101,7 @@ async def test_runner_calls_hooks_in_order():
|
||||
@pytest.mark.asyncio
|
||||
async def test_runner_streaming_hook_receives_deltas_and_end_signal():
|
||||
from nanobot.agent.hook import AgentHook, AgentHookContext
|
||||
from nanobot.agent.runner import AgentRunner, AgentRunSpec
|
||||
from nanobot.agent.runner import AgentRunner
|
||||
|
||||
provider = MagicMock(spec=LLMProvider)
|
||||
streamed: list[str] = []
|
||||
@@ -126,8 +127,8 @@ async def test_runner_streaming_hook_receives_deltas_and_end_signal():
|
||||
async def on_stream_end(self, context: AgentHookContext, *, resuming: bool) -> None:
|
||||
endings.append(resuming)
|
||||
|
||||
runner = AgentRunner(provider)
|
||||
result = await runner.run(AgentRunSpec(
|
||||
runner = AgentRunner()
|
||||
result = await runner.run(make_run_spec(provider,
|
||||
initial_messages=[],
|
||||
tools=tools,
|
||||
model="test-model",
|
||||
@@ -146,7 +147,7 @@ async def test_runner_streaming_hook_receives_deltas_and_end_signal():
|
||||
async def test_runner_passes_cached_tokens_to_hook_context():
|
||||
"""Hook context.usage should contain cached_tokens."""
|
||||
from nanobot.agent.hook import AgentHook, AgentHookContext
|
||||
from nanobot.agent.runner import AgentRunner, AgentRunSpec
|
||||
from nanobot.agent.runner import AgentRunner
|
||||
|
||||
provider = MagicMock(spec=LLMProvider)
|
||||
captured_usage: list[dict] = []
|
||||
@@ -166,8 +167,8 @@ async def test_runner_passes_cached_tokens_to_hook_context():
|
||||
tools = MagicMock()
|
||||
tools.get_definitions.return_value = []
|
||||
|
||||
runner = AgentRunner(provider)
|
||||
await runner.run(AgentRunSpec(
|
||||
runner = AgentRunner()
|
||||
await runner.run(make_run_spec(provider,
|
||||
initial_messages=[],
|
||||
tools=tools,
|
||||
model="test-model",
|
||||
@@ -184,7 +185,7 @@ async def test_runner_passes_cached_tokens_to_hook_context():
|
||||
@pytest.mark.asyncio
|
||||
async def test_runner_estimates_usage_when_provider_omits_usage(monkeypatch):
|
||||
from nanobot.agent.hook import AgentHook, AgentHookContext
|
||||
from nanobot.agent.runner import AgentRunner, AgentRunSpec
|
||||
from nanobot.agent.runner import AgentRunner
|
||||
|
||||
provider = MagicMock(spec=LLMProvider)
|
||||
captured_usage: list[dict] = []
|
||||
@@ -205,8 +206,8 @@ async def test_runner_estimates_usage_when_provider_omits_usage(monkeypatch):
|
||||
)
|
||||
monkeypatch.setattr("nanobot.agent.runner.estimate_message_tokens", lambda message: 7)
|
||||
|
||||
runner = AgentRunner(provider)
|
||||
result = await runner.run(AgentRunSpec(
|
||||
runner = AgentRunner()
|
||||
result = await runner.run(make_run_spec(provider,
|
||||
initial_messages=[{"role": "user", "content": "hi"}],
|
||||
tools=tools,
|
||||
model="test-model",
|
||||
@@ -225,7 +226,7 @@ async def test_runner_estimates_usage_when_provider_omits_usage(monkeypatch):
|
||||
@pytest.mark.asyncio
|
||||
async def test_runner_calls_run_level_hooks_on_success():
|
||||
from nanobot.agent.hook import AgentHook, AgentRunHookContext
|
||||
from nanobot.agent.runner import AgentRunner, AgentRunSpec
|
||||
from nanobot.agent.runner import AgentRunner
|
||||
|
||||
provider = MagicMock(spec=LLMProvider)
|
||||
events: list[tuple] = []
|
||||
@@ -263,8 +264,8 @@ async def test_runner_calls_run_level_hooks_on_success():
|
||||
async def on_finally(self, context: AgentRunHookContext) -> None:
|
||||
events.append(("on_finally", context.stop_reason, context.exception))
|
||||
|
||||
runner = AgentRunner(provider)
|
||||
result = await runner.run(AgentRunSpec(
|
||||
runner = AgentRunner()
|
||||
result = await runner.run(make_run_spec(provider,
|
||||
initial_messages=[{"role": "user", "content": "hi"}],
|
||||
tools=tools,
|
||||
model="test-model",
|
||||
@@ -297,7 +298,7 @@ async def test_runner_calls_run_level_hooks_on_success():
|
||||
@pytest.mark.asyncio
|
||||
async def test_runner_run_level_context_is_detached_snapshot():
|
||||
from nanobot.agent.hook import AgentHook, AgentRunHookContext
|
||||
from nanobot.agent.runner import AgentRunner, AgentRunSpec
|
||||
from nanobot.agent.runner import AgentRunner
|
||||
|
||||
provider = MagicMock(spec=LLMProvider)
|
||||
call_count = {"n": 0}
|
||||
@@ -330,8 +331,8 @@ async def test_runner_run_level_context_is_detached_snapshot():
|
||||
async def on_finally(self, context: AgentRunHookContext) -> None:
|
||||
context.messages[0]["content"] = "mutated-finally"
|
||||
|
||||
runner = AgentRunner(provider)
|
||||
result = await runner.run(AgentRunSpec(
|
||||
runner = AgentRunner()
|
||||
result = await runner.run(make_run_spec(provider,
|
||||
initial_messages=[{"role": "user", "content": "hi"}],
|
||||
tools=tools,
|
||||
model="test-model",
|
||||
@@ -351,7 +352,7 @@ async def test_runner_run_level_context_is_detached_snapshot():
|
||||
@pytest.mark.asyncio
|
||||
async def test_runner_calls_on_error_for_model_error_result():
|
||||
from nanobot.agent.hook import AgentHook, AgentRunHookContext
|
||||
from nanobot.agent.runner import AgentRunner, AgentRunSpec
|
||||
from nanobot.agent.runner import AgentRunner
|
||||
|
||||
provider = MagicMock(spec=LLMProvider)
|
||||
events: list[tuple] = []
|
||||
@@ -376,8 +377,8 @@ async def test_runner_calls_on_error_for_model_error_result():
|
||||
async def on_finally(self, context: AgentRunHookContext) -> None:
|
||||
events.append(("on_finally", context.stop_reason, context.error))
|
||||
|
||||
runner = AgentRunner(provider)
|
||||
result = await runner.run(AgentRunSpec(
|
||||
runner = AgentRunner()
|
||||
result = await runner.run(make_run_spec(provider,
|
||||
initial_messages=[],
|
||||
tools=tools,
|
||||
model="test-model",
|
||||
@@ -399,7 +400,7 @@ async def test_runner_calls_on_error_for_model_error_result():
|
||||
@pytest.mark.asyncio
|
||||
async def test_runner_calls_on_error_and_finally_for_unhandled_exception():
|
||||
from nanobot.agent.hook import AgentHook, AgentRunHookContext
|
||||
from nanobot.agent.runner import AgentRunner, AgentRunSpec
|
||||
from nanobot.agent.runner import AgentRunner
|
||||
|
||||
provider = MagicMock(spec=LLMProvider)
|
||||
events: list[tuple] = []
|
||||
@@ -429,9 +430,9 @@ async def test_runner_calls_on_error_and_finally_for_unhandled_exception():
|
||||
async def on_finally(self, context: AgentRunHookContext) -> None:
|
||||
events.append(("on_finally", context.stop_reason))
|
||||
|
||||
runner = AgentRunner(provider)
|
||||
runner = AgentRunner()
|
||||
with pytest.raises(RuntimeError, match="provider exploded"):
|
||||
await runner.run(AgentRunSpec(
|
||||
await runner.run(make_run_spec(provider,
|
||||
initial_messages=[{"role": "user", "content": "hi"}],
|
||||
tools=tools,
|
||||
model="test-model",
|
||||
@@ -450,7 +451,7 @@ async def test_runner_calls_on_error_and_finally_for_unhandled_exception():
|
||||
@pytest.mark.asyncio
|
||||
async def test_runner_preserves_original_exception_when_finally_hook_fails():
|
||||
from nanobot.agent.hook import AgentHook, AgentRunHookContext
|
||||
from nanobot.agent.runner import AgentRunner, AgentRunSpec
|
||||
from nanobot.agent.runner import AgentRunner
|
||||
|
||||
provider = MagicMock(spec=LLMProvider)
|
||||
|
||||
@@ -465,9 +466,9 @@ async def test_runner_preserves_original_exception_when_finally_hook_fails():
|
||||
async def on_finally(self, context: AgentRunHookContext) -> None:
|
||||
raise RuntimeError("finally exploded")
|
||||
|
||||
runner = AgentRunner(provider)
|
||||
runner = AgentRunner()
|
||||
with pytest.raises(RuntimeError, match="provider exploded"):
|
||||
await runner.run(AgentRunSpec(
|
||||
await runner.run(make_run_spec(provider,
|
||||
initial_messages=[{"role": "user", "content": "hi"}],
|
||||
tools=tools,
|
||||
model="test-model",
|
||||
@@ -482,7 +483,7 @@ async def test_runner_does_not_report_cancellation_as_error():
|
||||
import asyncio
|
||||
|
||||
from nanobot.agent.hook import AgentHook, AgentRunHookContext
|
||||
from nanobot.agent.runner import AgentRunner, AgentRunSpec
|
||||
from nanobot.agent.runner import AgentRunner
|
||||
|
||||
provider = MagicMock(spec=LLMProvider)
|
||||
events: list[tuple] = []
|
||||
@@ -512,9 +513,9 @@ async def test_runner_does_not_report_cancellation_as_error():
|
||||
type(context.exception).__name__ if context.exception else None,
|
||||
))
|
||||
|
||||
runner = AgentRunner(provider)
|
||||
runner = AgentRunner()
|
||||
with pytest.raises(asyncio.CancelledError):
|
||||
await runner.run(AgentRunSpec(
|
||||
await runner.run(make_run_spec(provider,
|
||||
initial_messages=[{"role": "user", "content": "hi"}],
|
||||
tools=tools,
|
||||
model="test-model",
|
||||
@@ -534,7 +535,7 @@ async def test_runner_preserves_cancellation_when_finally_hook_fails():
|
||||
import asyncio
|
||||
|
||||
from nanobot.agent.hook import AgentHook, AgentRunHookContext
|
||||
from nanobot.agent.runner import AgentRunner, AgentRunSpec
|
||||
from nanobot.agent.runner import AgentRunner
|
||||
|
||||
provider = MagicMock(spec=LLMProvider)
|
||||
|
||||
@@ -549,9 +550,9 @@ async def test_runner_preserves_cancellation_when_finally_hook_fails():
|
||||
async def on_finally(self, context: AgentRunHookContext) -> None:
|
||||
raise RuntimeError("finally exploded")
|
||||
|
||||
runner = AgentRunner(provider)
|
||||
runner = AgentRunner()
|
||||
with pytest.raises(asyncio.CancelledError):
|
||||
await runner.run(AgentRunSpec(
|
||||
await runner.run(make_run_spec(provider,
|
||||
initial_messages=[{"role": "user", "content": "hi"}],
|
||||
tools=tools,
|
||||
model="test-model",
|
||||
|
||||
Reference in New Issue
Block a user