Specs stay at repo root (cross-VM). Move deploy and code into logical projects with README per domain, updated manifest.yaml, and symlinks at legacy paths for VM122 backward compatibility.
62 lines
2.9 KiB
Python
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))}
|