Fix 3 arc reactor bugs: metrics_json→metric_data, system nesting, message→content in conversations

- guardian_loop + sitrep: SELECT metric_data (not metrics_json which does not exist)
- guardian_loop: metrics are flat (cpu_percent at top level), not nested under system key
- guardian_loop + sitrep: filter metric_type=system to avoid parsing proxmox blobs
- guardian_loop: disk.mount key (not mountpoint) + fallback for both key names
- sitrep: same metric_data + root-level key fixes
- _guardian_inject_chat (x2): INSERT into conversations.content (not message)
- /guardian/chat endpoint (x2): SELECT content (not message) from conversations

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-12 01:59:58 +00:00
parent 7dc457562b
commit 04bd412b65
+13 -13
View File
@@ -945,10 +945,11 @@ async def guardian_loop() -> None:
# ── 2. Metrics analysis ─────────────────────────────────────────── # ── 2. Metrics analysis ───────────────────────────────────────────
# Get latest metric per online agent # Get latest metric per online agent
metrics_rows = await db_fetchall( metrics_rows = await db_fetchall(
"""SELECT m.agent_id, r.hostname, m.metrics_json """SELECT m.agent_id, r.hostname, m.metric_data
FROM agent_metrics m FROM agent_metrics m
JOIN registered_agents r ON r.agent_id = m.agent_id JOIN registered_agents r ON r.agent_id = m.agent_id
WHERE r.status = 'online' WHERE r.status = 'online'
AND m.metric_type = 'system'
AND m.recorded_at > DATE_SUB(NOW(), INTERVAL 5 MINUTE) AND m.recorded_at > DATE_SUB(NOW(), INTERVAL 5 MINUTE)
ORDER BY m.recorded_at DESC""" ORDER BY m.recorded_at DESC"""
) )
@@ -961,8 +962,7 @@ async def guardian_loop() -> None:
hostname = row["hostname"] hostname = row["hostname"]
try: try:
m = json.loads(row["metrics_json"]) if isinstance(row["metrics_json"], str) else row["metrics_json"] sys_data = json.loads(row["metric_data"]) if isinstance(row["metric_data"], str) else row["metric_data"]
sys_data = m.get("system", {})
cpu = sys_data.get("cpu_percent") cpu = sys_data.get("cpu_percent")
if cpu is not None and float(cpu) >= cpu_thresh: if cpu is not None and float(cpu) >= cpu_thresh:
@@ -988,7 +988,7 @@ async def guardian_loop() -> None:
for disk in disks: for disk in disks:
dpct = disk.get("percent", 0) dpct = disk.get("percent", 0)
if dpct and float(str(dpct).rstrip("%")) >= disk_thresh: if dpct and float(str(dpct).rstrip("%")) >= disk_thresh:
mount = disk.get("mountpoint", "/") mount = disk.get("mount", disk.get("mountpoint", "/"))
key = f"disk_{mount}" key = f"disk_{mount}"
if await _guardian_cooldown_ok(aid, key): if await _guardian_cooldown_ok(aid, key):
msg = f"{hostname}: Disk {mount} at {dpct}% (threshold: {disk_thresh}%)" msg = f"{hostname}: Disk {mount} at {dpct}% (threshold: {disk_thresh}%)"
@@ -1051,7 +1051,7 @@ async def _guardian_inject_chat(message: str) -> None:
"""Write a proactive JARVIS message into the conversations table so the HUD picks it up.""" """Write a proactive JARVIS message into the conversations table so the HUD picks it up."""
try: try:
await db_execute( await db_execute(
"""INSERT INTO conversations (session_id, role, message, created_at) """INSERT INTO conversations (session_id, role, content, created_at)
VALUES ('guardian', 'assistant', %s, NOW())""", VALUES ('guardian', 'assistant', %s, NOW())""",
(message,) (message,)
) )
@@ -1079,16 +1079,16 @@ async def handle_sitrep(payload: dict) -> dict:
# 2. Get latest metrics per agent # 2. Get latest metrics per agent
metrics_map = {} metrics_map = {}
metrics_rows = await db_fetchall( metrics_rows = await db_fetchall(
"""SELECT m.agent_id, m.metrics_json, m.recorded_at """SELECT m.agent_id, m.metric_data, m.recorded_at
FROM agent_metrics m FROM agent_metrics m
WHERE m.recorded_at > DATE_SUB(NOW(), INTERVAL 10 MINUTE) WHERE m.metric_type = 'system'
AND m.recorded_at > DATE_SUB(NOW(), INTERVAL 10 MINUTE)
ORDER BY m.recorded_at DESC""" ORDER BY m.recorded_at DESC"""
) )
for row in metrics_rows: for row in metrics_rows:
if row["agent_id"] not in metrics_map: if row["agent_id"] not in metrics_map:
try: try:
m = json.loads(row["metrics_json"]) if isinstance(row["metrics_json"], str) else row["metrics_json"] metrics_map[row["agent_id"]] = json.loads(row["metric_data"]) if isinstance(row["metric_data"], str) else row["metric_data"]
metrics_map[row["agent_id"]] = m
except Exception: except Exception:
pass pass
@@ -1117,7 +1117,7 @@ async def handle_sitrep(payload: dict) -> dict:
sys = m.get("system", {}) if m else {} sys = m.get("system", {}) if m else {}
cpu = sys.get("cpu_percent", "?") cpu = sys.get("cpu_percent", "?")
mem = sys.get("memory", {}).get("percent", "?") if isinstance(sys.get("memory"), dict) else "?" mem = sys.get("memory", {}).get("percent", "?") if isinstance(sys.get("memory"), dict) else "?"
disk = next((d.get("percent","?") for d in (sys.get("disk") or []) if d.get("mountpoint") == "/"), "?") disk = next((d.get("percent","?") for d in (sys.get("disk") or []) if d.get("mount","").rstrip("/") == "" or d.get("mountpoint","") == "/"), "?")
line = (f" {ag['hostname']} ({ag['status'].upper()}) — " line = (f" {ag['hostname']} ({ag['status'].upper()}) — "
f"CPU:{cpu}% MEM:{mem}% DISK:{disk}%") f"CPU:{cpu}% MEM:{mem}% DISK:{disk}%")
agent_lines.append(line) agent_lines.append(line)
@@ -1806,7 +1806,7 @@ Keep the tone confident and action-oriented. Format with clear sections. Under 4
# Store in conversations so HUD can surface it # Store in conversations so HUD can surface it
await db_execute( await db_execute(
"INSERT INTO conversations (session_id, role, message, created_at) VALUES ('guardian','assistant',%s,NOW())", "INSERT INTO conversations (session_id, role, content, created_at) VALUES ('guardian','assistant',%s,NOW())",
(review_text,) (review_text,)
) )
@@ -2728,13 +2728,13 @@ async def guardian_chat_events(since: str = ""):
"""Return proactive guardian messages injected into conversations.""" """Return proactive guardian messages injected into conversations."""
if since: if since:
rows = await db_fetchall( rows = await db_fetchall(
"SELECT id, message, created_at FROM conversations " "SELECT id, content, created_at FROM conversations"
"WHERE session_id='guardian' AND created_at > %s ORDER BY created_at ASC", "WHERE session_id='guardian' AND created_at > %s ORDER BY created_at ASC",
(since,) (since,)
) )
else: else:
rows = await db_fetchall( rows = await db_fetchall(
"SELECT id, message, created_at FROM conversations " "SELECT id, content, created_at FROM conversations"
"WHERE session_id='guardian' ORDER BY created_at DESC LIMIT 10" "WHERE session_id='guardian' ORDER BY created_at DESC LIMIT 10"
) )
return rows or [] return rows or []