refactor(agent): make runner consume required runtime
This commit is contained in:
@@ -13,6 +13,7 @@ from unittest.mock import AsyncMock, MagicMock
|
||||
|
||||
import pytest
|
||||
|
||||
from agent.runner_helpers import make_run_spec
|
||||
from nanobot.agent.hook import AgentHook, AgentHookContext
|
||||
from nanobot.config.schema import AgentDefaults
|
||||
from nanobot.providers.base import LLMResponse, ToolCallRequest
|
||||
@@ -38,7 +39,7 @@ class _RecordingHook(AgentHook):
|
||||
async def test_runner_preserves_reasoning_fields_in_assistant_history():
|
||||
"""Reasoning fields ride along on the persisted assistant message so
|
||||
follow-up provider calls retain the model's prior thinking context."""
|
||||
from nanobot.agent.runner import AgentRunner, AgentRunSpec
|
||||
from nanobot.agent.runner import AgentRunner
|
||||
|
||||
provider = MagicMock()
|
||||
captured_second_call: list[dict] = []
|
||||
@@ -62,8 +63,8 @@ async def test_runner_preserves_reasoning_fields_in_assistant_history():
|
||||
tools.get_definitions.return_value = []
|
||||
tools.execute = AsyncMock(return_value="tool result")
|
||||
|
||||
runner = AgentRunner(provider)
|
||||
result = await runner.run(AgentRunSpec(
|
||||
runner = AgentRunner()
|
||||
result = await runner.run(make_run_spec(provider,
|
||||
initial_messages=[
|
||||
{"role": "system", "content": "system"},
|
||||
{"role": "user", "content": "do task"},
|
||||
@@ -86,7 +87,7 @@ async def test_runner_preserves_reasoning_fields_in_assistant_history():
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_runner_emits_anthropic_thinking_blocks():
|
||||
from nanobot.agent.runner import AgentRunner, AgentRunSpec
|
||||
from nanobot.agent.runner import AgentRunner
|
||||
|
||||
provider = MagicMock()
|
||||
|
||||
@@ -106,8 +107,8 @@ async def test_runner_emits_anthropic_thinking_blocks():
|
||||
tools.get_definitions.return_value = []
|
||||
|
||||
hook = _RecordingHook()
|
||||
runner = AgentRunner(provider)
|
||||
result = await runner.run(AgentRunSpec(
|
||||
runner = AgentRunner()
|
||||
result = await runner.run(make_run_spec(provider,
|
||||
initial_messages=[{"role": "user", "content": "question"}],
|
||||
tools=tools,
|
||||
model="test-model",
|
||||
@@ -126,7 +127,7 @@ async def test_runner_emits_anthropic_thinking_blocks():
|
||||
async def test_runner_emits_inline_think_content_as_reasoning():
|
||||
"""Models embedding reasoning in <think>...</think> blocks should have
|
||||
that content extracted and emitted, and stripped from the answer."""
|
||||
from nanobot.agent.runner import AgentRunner, AgentRunSpec
|
||||
from nanobot.agent.runner import AgentRunner
|
||||
|
||||
provider = MagicMock()
|
||||
|
||||
@@ -142,8 +143,8 @@ async def test_runner_emits_inline_think_content_as_reasoning():
|
||||
tools.get_definitions.return_value = []
|
||||
|
||||
hook = _RecordingHook()
|
||||
runner = AgentRunner(provider)
|
||||
result = await runner.run(AgentRunSpec(
|
||||
runner = AgentRunner()
|
||||
result = await runner.run(make_run_spec(provider,
|
||||
initial_messages=[{"role": "user", "content": "what is the answer?"}],
|
||||
tools=tools,
|
||||
model="test-model",
|
||||
@@ -161,7 +162,7 @@ async def test_runner_emits_inline_think_content_as_reasoning():
|
||||
async def test_runner_prefers_reasoning_content_over_inline_think():
|
||||
"""Fallback priority: dedicated reasoning_content wins; inline <think>
|
||||
is still scrubbed from the answer content."""
|
||||
from nanobot.agent.runner import AgentRunner, AgentRunSpec
|
||||
from nanobot.agent.runner import AgentRunner
|
||||
|
||||
provider = MagicMock()
|
||||
|
||||
@@ -178,8 +179,8 @@ async def test_runner_prefers_reasoning_content_over_inline_think():
|
||||
tools.get_definitions.return_value = []
|
||||
|
||||
hook = _RecordingHook()
|
||||
runner = AgentRunner(provider)
|
||||
result = await runner.run(AgentRunSpec(
|
||||
runner = AgentRunner()
|
||||
result = await runner.run(make_run_spec(provider,
|
||||
initial_messages=[{"role": "user", "content": "question"}],
|
||||
tools=tools,
|
||||
model="test-model",
|
||||
@@ -197,7 +198,7 @@ async def test_runner_emits_reasoning_content_even_when_answer_was_streamed():
|
||||
"""`reasoning_content` arrives only on the final response; streaming the
|
||||
answer must not suppress it (the answer stream and the reasoning channel
|
||||
are independent — only the reasoning-already-emitted bit matters)."""
|
||||
from nanobot.agent.runner import AgentRunner, AgentRunSpec
|
||||
from nanobot.agent.runner import AgentRunner
|
||||
|
||||
provider = MagicMock()
|
||||
provider.supports_progress_deltas = True
|
||||
@@ -223,8 +224,8 @@ async def test_runner_emits_reasoning_content_even_when_answer_was_streamed():
|
||||
progress_calls.append(content)
|
||||
|
||||
hook = _RecordingHook()
|
||||
runner = AgentRunner(provider)
|
||||
result = await runner.run(AgentRunSpec(
|
||||
runner = AgentRunner()
|
||||
result = await runner.run(make_run_spec(provider,
|
||||
initial_messages=[{"role": "user", "content": "question"}],
|
||||
tools=tools,
|
||||
model="test-model",
|
||||
@@ -244,7 +245,7 @@ async def test_runner_emits_reasoning_content_even_when_answer_was_streamed():
|
||||
async def test_runner_does_not_double_emit_when_inline_think_already_streamed():
|
||||
"""Inline `<think>` blocks streamed incrementally during the answer
|
||||
stream must not be re-emitted from the final response."""
|
||||
from nanobot.agent.runner import AgentRunner, AgentRunSpec
|
||||
from nanobot.agent.runner import AgentRunner
|
||||
|
||||
provider = MagicMock()
|
||||
provider.supports_progress_deltas = True
|
||||
@@ -267,8 +268,8 @@ async def test_runner_does_not_double_emit_when_inline_think_already_streamed():
|
||||
pass
|
||||
|
||||
hook = _RecordingHook()
|
||||
runner = AgentRunner(provider)
|
||||
result = await runner.run(AgentRunSpec(
|
||||
runner = AgentRunner()
|
||||
result = await runner.run(make_run_spec(provider,
|
||||
initial_messages=[{"role": "user", "content": "question"}],
|
||||
tools=tools,
|
||||
model="test-model",
|
||||
@@ -289,7 +290,7 @@ async def test_runner_closes_reasoning_stream_after_one_shot_response():
|
||||
"""A non-streaming response carrying ``reasoning_content`` must emit
|
||||
both a reasoning delta and an end marker so channels can finalize the
|
||||
in-place bubble."""
|
||||
from nanobot.agent.runner import AgentRunner, AgentRunSpec
|
||||
from nanobot.agent.runner import AgentRunner
|
||||
|
||||
provider = MagicMock()
|
||||
|
||||
@@ -306,8 +307,8 @@ async def test_runner_closes_reasoning_stream_after_one_shot_response():
|
||||
tools.get_definitions.return_value = []
|
||||
|
||||
hook = _RecordingHook()
|
||||
runner = AgentRunner(provider)
|
||||
result = await runner.run(AgentRunSpec(
|
||||
runner = AgentRunner()
|
||||
result = await runner.run(make_run_spec(provider,
|
||||
initial_messages=[{"role": "user", "content": "q"}],
|
||||
tools=tools,
|
||||
model="test-model",
|
||||
@@ -333,7 +334,7 @@ class _StreamRecordingHook(_RecordingHook):
|
||||
async def test_runner_streams_native_thinking_deltas_without_post_hoc_dup():
|
||||
"""Anthropic-style ``on_thinking_delta`` should fan out to ``emit_reasoning``;
|
||||
final ``thinking_blocks`` must not emit again when already streamed."""
|
||||
from nanobot.agent.runner import AgentRunner, AgentRunSpec
|
||||
from nanobot.agent.runner import AgentRunner
|
||||
|
||||
provider = MagicMock()
|
||||
|
||||
@@ -357,8 +358,8 @@ async def test_runner_streams_native_thinking_deltas_without_post_hoc_dup():
|
||||
tools.get_definitions.return_value = []
|
||||
|
||||
hook = _StreamRecordingHook()
|
||||
runner = AgentRunner(provider)
|
||||
result = await runner.run(AgentRunSpec(
|
||||
runner = AgentRunner()
|
||||
result = await runner.run(make_run_spec(provider,
|
||||
initial_messages=[{"role": "user", "content": "q"}],
|
||||
tools=tools,
|
||||
model="test-model",
|
||||
@@ -373,7 +374,7 @@ async def test_runner_streams_native_thinking_deltas_without_post_hoc_dup():
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_runner_strips_thinking_tags_from_native_thinking_deltas():
|
||||
from nanobot.agent.runner import AgentRunner, AgentRunSpec
|
||||
from nanobot.agent.runner import AgentRunner
|
||||
|
||||
provider = MagicMock()
|
||||
|
||||
@@ -393,8 +394,8 @@ async def test_runner_strips_thinking_tags_from_native_thinking_deltas():
|
||||
tools.get_definitions.return_value = []
|
||||
|
||||
hook = _StreamRecordingHook()
|
||||
runner = AgentRunner(provider)
|
||||
result = await runner.run(AgentRunSpec(
|
||||
runner = AgentRunner()
|
||||
result = await runner.run(make_run_spec(provider,
|
||||
initial_messages=[{"role": "user", "content": "q"}],
|
||||
tools=tools,
|
||||
model="test-model",
|
||||
@@ -409,7 +410,7 @@ async def test_runner_strips_thinking_tags_from_native_thinking_deltas():
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_runner_ignores_empty_thinking_marker_before_final_reasoning():
|
||||
from nanobot.agent.runner import AgentRunner, AgentRunSpec
|
||||
from nanobot.agent.runner import AgentRunner
|
||||
|
||||
provider = MagicMock()
|
||||
|
||||
@@ -432,8 +433,8 @@ async def test_runner_ignores_empty_thinking_marker_before_final_reasoning():
|
||||
tools.get_definitions.return_value = []
|
||||
|
||||
hook = _StreamRecordingHook()
|
||||
runner = AgentRunner(provider)
|
||||
result = await runner.run(AgentRunSpec(
|
||||
runner = AgentRunner()
|
||||
result = await runner.run(make_run_spec(provider,
|
||||
initial_messages=[{"role": "user", "content": "q"}],
|
||||
tools=tools,
|
||||
model="test-model",
|
||||
|
||||
Reference in New Issue
Block a user