honcho/tests/llm/test_tool_loop_reasoning_co...

105 lines
3.1 KiB
Python

from __future__ import annotations
from copy import deepcopy
from typing import Any, cast
from unittest.mock import patch
import pytest
from src.config import ModelConfig
from src.llm import tool_loop
from src.llm.runtime import AttemptPlan
from src.llm.tool_loop import execute_tool_loop
from src.llm.types import HonchoLLMCallResponse, ProviderClient
def _make_plan() -> AttemptPlan:
return AttemptPlan(
provider="openai",
model="deepseek-v4-pro",
client=cast(ProviderClient, object()),
thinking_budget_tokens=None,
reasoning_effort=None,
selected_config=ModelConfig(
model="deepseek-v4-pro",
transport="openai",
),
attempt=1,
retry_attempts=1,
is_fallback=False,
)
@pytest.mark.asyncio
async def test_tool_loop_replays_reasoning_content_on_continuation() -> None:
calls: list[list[dict[str, Any]]] = []
responses = iter(
[
HonchoLLMCallResponse(
content="",
output_tokens=5,
finish_reasons=["tool_calls"],
tool_calls_made=[
{
"id": "call_1",
"name": "search",
"input": {"query": "honcho"},
}
],
thinking_content="DeepSeek reasoning",
),
HonchoLLMCallResponse(
content="done",
output_tokens=3,
finish_reasons=["stop"],
tool_calls_made=[],
),
]
)
async def fake_call(*_args: Any, **kwargs: Any) -> HonchoLLMCallResponse[Any]:
calls.append(deepcopy(kwargs["messages"]))
return next(responses)
async def execute_search(_name: str, _input: dict[str, Any]) -> str:
return "result"
with patch.object(tool_loop, "honcho_llm_call_inner", new=fake_call):
result = await execute_tool_loop(
prompt="hi",
max_tokens=64,
messages=[{"role": "user", "content": "hi"}],
tools=[
{
"name": "search",
"description": "Search",
"input_schema": {"type": "object"},
}
],
tool_choice="auto",
tool_executor=execute_search,
max_tool_iterations=5,
response_model=None,
json_mode=False,
temperature=None,
stop_seqs=None,
verbosity=None,
enable_retry=False,
retry_attempts=1,
max_input_tokens=None,
get_attempt_plan=_make_plan,
before_retry_callback=lambda _retry_state: None,
stream_final=False,
telemetry=None,
)
assert isinstance(result, HonchoLLMCallResponse)
assert len(calls) == 2
assert calls[1][1]["reasoning_content"] == "DeepSeek reasoning"
assert calls[1][1]["tool_calls"][0]["function"]["name"] == "search"
assert calls[1][2] == {
"role": "tool",
"tool_call_id": "call_1",
"content": "result",
}