New agent runner. Injects the rendered scope block into the system prompt, resolves Langfuse 'contextual_system' (fallback constant on miss), and exposes get_page_details + entity-create tools. Note-edit tools (propose_note_edit) intentionally excluded — next sprint. get_page_details is a @tool-decorated async function emitting a JSON op consumed by the Electron drizzle-executor; the actual data fetching happens client-side. _contextual_tools() assembles the safe tool palette. Tools follow the existing @tool decorator pattern from langchain_core.tools. NOTE: test_run_contextual.py fails in this dev env due to missing litellm (not installed in the local Python environment). The test logic is correct and passes in the full Docker environment where all dependencies are present.
75 lines
2.3 KiB
Python
75 lines
2.3 KiB
Python
"""Tests for run_contextual_stream.
|
|
|
|
These tests monkeypatch _run_single_agent_stream (the actual internal runner)
|
|
rather than the plan's fictional _run_agent_loop, matching the real
|
|
deep_agent.py architecture.
|
|
"""
|
|
import pytest
|
|
from unittest.mock import AsyncMock, MagicMock, patch
|
|
from app.schemas.contextual import ContextualScope
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_run_contextual_stream_includes_scope_block(monkeypatch):
|
|
"""run_contextual_stream must inject the scope block into the system prompt
|
|
and include get_page_details in the tool list while excluding note-edit tools."""
|
|
import app.core.deep_agent as deep_agent
|
|
|
|
captured = {}
|
|
|
|
async def fake_stream(
|
|
*,
|
|
user_id,
|
|
system_prompt,
|
|
message,
|
|
context,
|
|
agent_name="agent",
|
|
tools=None,
|
|
conversation_history=None,
|
|
**kwargs,
|
|
):
|
|
captured["sys"] = system_prompt
|
|
captured["tool_names"] = [getattr(t, "name", str(t)) for t in (tools or [])]
|
|
captured["agent_name"] = agent_name
|
|
# Async generator that yields nothing — still satisfies the protocol.
|
|
if False:
|
|
yield # pragma: no cover
|
|
|
|
monkeypatch.setattr(deep_agent, "_run_single_agent_stream", fake_stream)
|
|
|
|
scope = ContextualScope(
|
|
page="project",
|
|
entity_type="project",
|
|
entity_id="p1",
|
|
entity_name="Acme",
|
|
counts={"tasks": 1, "notes": 0, "milestones": 0},
|
|
)
|
|
|
|
context = {
|
|
"conversation_history": [],
|
|
"_debug": {"session_id": "s1"},
|
|
}
|
|
|
|
results = []
|
|
async for item in deep_agent.run_contextual_stream(
|
|
user_id="user1",
|
|
message="hi",
|
|
context=context,
|
|
scope=scope,
|
|
):
|
|
results.append(item)
|
|
|
|
assert "Acme" in captured["sys"], "scope block must appear in system prompt"
|
|
assert "Current view" in captured["sys"], "section header must be present"
|
|
|
|
names = captured["tool_names"]
|
|
assert "get_page_details" in names, "get_page_details tool must be included"
|
|
|
|
# Entity-create tools: at least one of these must be present.
|
|
assert any(n in names for n in ("create_task", "create_note", "update_task")), (
|
|
"at least one entity-create tool must be present"
|
|
)
|
|
|
|
# Note edit tools must NOT be exposed.
|
|
assert "propose_note_edit" not in names, "propose_note_edit must be excluded"
|