refactor: switch to single-agent deep runner and add mock memory/tool tests
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -5,7 +5,6 @@ langchain>=0.3.0
|
|||||||
langchain-openai>=0.3.0
|
langchain-openai>=0.3.0
|
||||||
langchain-litellm>=0.1.0
|
langchain-litellm>=0.1.0
|
||||||
litellm>=1.50.0
|
litellm>=1.50.0
|
||||||
langgraph>=0.4.0
|
|
||||||
pydantic>=2.10.0
|
pydantic>=2.10.0
|
||||||
pydantic-settings>=2.7.0
|
pydantic-settings>=2.7.0
|
||||||
python-jose[cryptography]>=3.3.0
|
python-jose[cryptography]>=3.3.0
|
||||||
|
|||||||
81
tests/test_deep_agent.py
Normal file
81
tests/test_deep_agent.py
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
"""Unit tests for single-agent deep_agent flows with mocked tool results."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from types import SimpleNamespace
|
||||||
|
from unittest.mock import patch
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
from langchain_core.messages import AIMessage, ToolMessage
|
||||||
|
|
||||||
|
from app.core.deep_agent import run_floating_stream, run_home
|
||||||
|
|
||||||
|
|
||||||
|
class _FakeTool:
|
||||||
|
name = "list_tasks"
|
||||||
|
|
||||||
|
async def ainvoke(self, args):
|
||||||
|
return {"rows": [{"id": "task-1", "title": "Mock Task"}], "echo": args}
|
||||||
|
|
||||||
|
|
||||||
|
class _FakeLLM:
|
||||||
|
def __init__(self) -> None:
|
||||||
|
self.calls = 0
|
||||||
|
|
||||||
|
def bind_tools(self, _tools):
|
||||||
|
return self
|
||||||
|
|
||||||
|
async def ainvoke(self, messages):
|
||||||
|
self.calls += 1
|
||||||
|
if self.calls == 1:
|
||||||
|
return AIMessage(
|
||||||
|
content="",
|
||||||
|
tool_calls=[
|
||||||
|
{
|
||||||
|
"id": "call-1",
|
||||||
|
"name": "list_tasks",
|
||||||
|
"args": {"project_id": "proj-1"},
|
||||||
|
}
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
tool_messages = [m for m in messages if isinstance(m, ToolMessage)]
|
||||||
|
assert tool_messages, "Expected at least one tool message"
|
||||||
|
return AIMessage(content=f"Final answer from mocked tool: {tool_messages[-1].content}")
|
||||||
|
|
||||||
|
async def astream(self, _messages):
|
||||||
|
yield SimpleNamespace(content="stream-")
|
||||||
|
yield SimpleNamespace(content="ok")
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_run_home_uses_mocked_tool_result():
|
||||||
|
fake_llm = _FakeLLM()
|
||||||
|
|
||||||
|
with patch("app.core.deep_agent.get_llm", return_value=fake_llm), patch(
|
||||||
|
"app.core.deep_agent._all_tools", return_value=[_FakeTool()]
|
||||||
|
):
|
||||||
|
out = await run_home("user-1", "list my tasks", {})
|
||||||
|
|
||||||
|
assert "Final answer from mocked tool" in out
|
||||||
|
assert "Mock Task" in out
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_run_floating_stream_emits_domain_then_tokens_with_mocked_tool_result():
|
||||||
|
fake_llm = _FakeLLM()
|
||||||
|
|
||||||
|
with patch("app.core.deep_agent.get_llm", return_value=fake_llm), patch(
|
||||||
|
"app.core.deep_agent._all_tools", return_value=[_FakeTool()]
|
||||||
|
):
|
||||||
|
events = []
|
||||||
|
async for event in run_floating_stream(
|
||||||
|
"user-1",
|
||||||
|
"show me timeline updates",
|
||||||
|
{"scope": {"type": "timeline", "id": "tl-1"}},
|
||||||
|
):
|
||||||
|
events.append(event)
|
||||||
|
|
||||||
|
assert events[0] == ("floating_domain", "timelines")
|
||||||
|
assert ("token", "stream-") in events
|
||||||
|
assert ("token", "ok") in events
|
||||||
Reference in New Issue
Block a user