generated from coulomb/repo-seed
feat(WARDEN-WP-0020): T2 — llm-connect brain (autonomous worker now thinks)
llm-connect is operational (operator set OPENROUTER_API_KEY). Contract discovered from
the running service: POST /execute {"prompt":...} -> {"content":...}.
LlmConnectBrain embeds the fixed charter + the inbox message as untrusted data, calls
/execute, and parses a JSON action plan (_extract_json tolerates fences/prose), escalating
defensively on malformed/empty/transport errors. The build_plans guardrail still enforces
the allowlist + no-secret invariant on whatever the model returns — the LLM cannot widen
ops-warden's authority. `warden worker run --brain rule|llm` selects the planner.
Live-verified on the real inbox: the LLM brain planned a sensible reply+mark_read for a
secrets-engine coordination message and correctly escalated a secret-custody request as
out-of-lane — better classification than the deterministic RuleBrain.
6 new tests, 236 pass, lint clean. T3 (guarded executor) and T4 (scheduling) remain.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -5,9 +5,11 @@ from typer.testing import CliRunner
|
||||
|
||||
from warden.cli import app
|
||||
from warden.worker import (
|
||||
LlmConnectBrain,
|
||||
PlannedAction,
|
||||
RuleBrain,
|
||||
WorkerPlan,
|
||||
_extract_json,
|
||||
build_plans,
|
||||
render_plans,
|
||||
validate_action,
|
||||
@@ -99,6 +101,56 @@ def test_build_plans_attaches_route_answer():
|
||||
assert plan.actions[0].payload.get("answer") # non-empty computed answer
|
||||
|
||||
|
||||
# --- LlmConnectBrain (T2) ---------------------------------------------------
|
||||
|
||||
def test_extract_json_tolerates_fences_and_prose():
|
||||
assert _extract_json('```json\n{"escalate": true}\n```') == {"escalate": True}
|
||||
assert _extract_json('here you go: {"a": 1} thanks') == {"a": 1}
|
||||
assert _extract_json("not json at all") is None
|
||||
|
||||
|
||||
def test_llm_brain_parses_actions(monkeypatch):
|
||||
brain = LlmConnectBrain(url="http://stub")
|
||||
monkeypatch.setattr(
|
||||
brain, "_call",
|
||||
lambda prompt: '{"actions":[{"kind":"route_answer","summary":"answer it"}],"escalate":false}',
|
||||
)
|
||||
plan = brain.plan(_msg())
|
||||
assert [a.kind for a in plan.actions] == ["route_answer"]
|
||||
assert plan.escalated is False
|
||||
|
||||
|
||||
def test_llm_brain_escalates_on_flag(monkeypatch):
|
||||
brain = LlmConnectBrain(url="http://stub")
|
||||
monkeypatch.setattr(brain, "_call", lambda prompt: '{"actions":[],"escalate":true,"reason":"secret"}')
|
||||
assert brain.plan(_msg()).escalated is True
|
||||
|
||||
|
||||
def test_llm_brain_escalates_on_malformed(monkeypatch):
|
||||
brain = LlmConnectBrain(url="http://stub")
|
||||
monkeypatch.setattr(brain, "_call", lambda prompt: "the model rambled with no json")
|
||||
assert brain.plan(_msg()).actions == []
|
||||
|
||||
|
||||
def test_llm_brain_escalates_on_transport_error(monkeypatch):
|
||||
brain = LlmConnectBrain(url="http://stub")
|
||||
def boom(prompt): raise RuntimeError("llm-connect down")
|
||||
monkeypatch.setattr(brain, "_call", boom)
|
||||
assert brain.plan(_msg()).escalated is True
|
||||
|
||||
|
||||
def test_llm_brain_unsafe_action_caught_by_guardrail(monkeypatch):
|
||||
# LLM proposes a reply on a secret-value task → guardrail downgrades to escalate.
|
||||
brain = LlmConnectBrain(url="http://stub")
|
||||
monkeypatch.setattr(
|
||||
brain, "_call",
|
||||
lambda prompt: '{"actions":[{"kind":"reply","summary":"here is the api_key value"}],"escalate":false}',
|
||||
)
|
||||
msg = _msg(subject="send the raw token", body="the api_key value please")
|
||||
[plan] = build_plans([msg], brain)
|
||||
assert plan.actions[0].risk == "escalate"
|
||||
|
||||
|
||||
def test_render_empty():
|
||||
assert "inbox empty" in render_plans([])
|
||||
|
||||
|
||||
Reference in New Issue
Block a user