ligbox-ops-platform/api/app/agents/runner.py
Ligbox Spec Hub 3a2c64834b Initial import: ligbox-ops-platform + specs + LAPTOP + obsidian merge (CT130)
Source: VM122 /opt + obsidian-infra + LAPTOP
Hub: CT130 spec-hub 10.10.10.130
2026-06-19 17:26:41 +00:00

62 lines
2.9 KiB
Python

"""Agent orchestration — Spec 029."""
from __future__ import annotations
import os
from pathlib import Path
from app.agents import checks, llm_client, notify, registry, store
SPECS = Path(os.getenv("AGENTIC_SPECS_ROOT", "/opt/ligbox-ops-platform/specs"))
OPS_API = os.getenv("OPS_API_URL", "http://api:8080")
TOKEN = os.getenv("OPS_INTERNAL_TOKEN", "")
def sync_registry(conn):
for sc in registry.load_registry():
store.upsert_scenario(conn, sc)
return len(registry.load_registry())
def index_specs_kb(conn):
if not SPECS.exists(): return 0
n = 0
for p in sorted(SPECS.glob("**/*.md")):
try:
txt = p.read_text(encoding="utf-8", errors="ignore")
if len(txt) > 100:
store.index_kb_file(conn, str(p.relative_to(SPECS)), txt)
n += 1
except OSError:
pass
return n
def run_scenario(conn, scenario_id, *, trigger="cron"):
sc = store.get_scenario(conn, scenario_id)
if not sc: return {"ok": False, "error": "not found"}
fn = checks.SCENARIO_RUNNERS.get(scenario_id)
if not fn: return {"ok": False, "error": "no runner"}
run_id = store.create_run(conn, scenario_id, trigger)
store.log_event(conn, event_type="run.start", message=scenario_id, run_id=run_id)
raw = fn(conn, ops_api_url=OPS_API, internal_token=TOKEN)
fids = []
for f in raw:
kb = store.search_kb(conn, f.get("title", ""))
human = f.get("human_action") or ""
if f.get("severity") in ("high", "critical", "warn"):
advice, model = llm_client.advise_human_action(
finding_title=f.get("title",""), finding_detail=f.get("detail_md",""),
kb_snippets=[k["snippet"] for k in kb],
)
if advice and not human: human = advice
fid = store.add_finding(conn, run_id, severity=f.get("severity", sc.get("severity_default","warn")),
category=f.get("category","api"), title=f.get("title","Finding"), detail_md=f.get("detail_md",""),
evidence=f.get("evidence"), human_action=human, kb_refs=[k["source"] for k in kb])
fids.append(fid)
if f.get("severity") in ("high", "critical"):
notify.notify_finding({**f, "suggested_human_action": human})
store.log_event(conn, event_type="finding.created", message=f.get("title",""), run_id=run_id, payload={"id": fid})
status = "ok" if not raw else "degraded"
store.finish_run(conn, run_id, status=status, summary=f"{len(raw)} finding(s)" if raw else "healthy")
store.log_event(conn, event_type="run.finish", message=status, run_id=run_id)
return {"ok": True, "run_id": run_id, "scenario_id": scenario_id, "status": status, "findings_count": len(raw), "finding_ids": fids}
def run_all_enabled(conn, trigger="cron"):
sync_registry(conn)
return {"runs": [run_scenario(conn, s["id"], trigger=trigger) for s in store.list_scenarios(conn)],
"total": len(store.list_scenarios(conn))}