feat(scouts): real triage LLM call via scout-triage-system prompt

This commit is contained in:
Roberto
2026-05-16 04:26:16 +02:00
parent d1016fd65a
commit 0c0299808c
2 changed files with 133 additions and 2 deletions

View File

@@ -172,6 +172,59 @@ async def test_idempotent_replay(monkeypatch):
assert len(rows) == 1, "Replay must not create duplicate queue rows"
@pytest.mark.asyncio
async def test_triage_llm_parses_json_response(monkeypatch):
"""Real _triage_llm path: mock the LLM ainvoke, verify TriageVerdict parsed correctly."""
from unittest.mock import MagicMock # noqa: PLC0415
from app.models import CloudScoutConfig # noqa: PLC0415
scout = CloudScoutConfig(
id=str(uuid.uuid4()),
user_id="00000000-0000-0000-0000-000000000003",
provider="gmail",
name="test-scout",
data_types=[],
prompt_template="watch invoices and project updates",
schedule_cron="0 * * * *",
enabled=True,
auto_trash_spam=False,
device_inactivity_pause_days=14,
)
content = ItemContent(
metadata=ItemMetadata(subject="Invoice 42", sender="billing@acme.com"),
body_text="Payment of €1 200 is due on 2026-06-01. Please confirm receipt.",
)
# Build a fake LangChain response whose .content is valid JSON.
fake_response = MagicMock()
fake_response.content = '{"verdict": "relevant", "reason": "invoice due", "confidence": 0.92}'
fake_response.usage_metadata = {"input_tokens": 10, "output_tokens": 5, "total_tokens": 15}
# Fake LLM: .bind() returns self (or another mock with ainvoke).
fake_llm = MagicMock()
fake_llm.bind.return_value = fake_llm
fake_llm.ainvoke = AsyncMock(return_value=fake_response)
# Patch get_llm inside app.scouts.engine so our fake is used.
monkeypatch.setattr("app.scouts.engine.get_llm", lambda **kwargs: fake_llm)
# Disable Langfuse for this test.
monkeypatch.setattr("app.scouts.engine.get_langfuse", lambda: None)
# Use fallback prompt (no Langfuse) — patch get_prompt_or_fallback to return fallback.
monkeypatch.setattr(
"app.scouts.engine.get_prompt_or_fallback",
lambda name, fallback: (fallback, None),
)
engine = ScoutEngine(session_factory=_TestSessionLocal)
verdict = await engine._triage_llm(scout, content)
assert verdict.verdict == "relevant"
assert verdict.reason == "invoice due"
assert abs(verdict.confidence - 0.92) < 1e-6
fake_llm.ainvoke.assert_awaited_once()
@pytest.mark.asyncio
async def test_deliver_pending_sends_one_frame_per_queued_row(monkeypatch):
user_id = "00000000-0000-0000-0000-000000000003"