Files
foodlinkk-command-center/cockpit/app/routes/agents_api.py
T
Aissa 5d60d33db1 Platform bundle: marketing publish, IT ops, packaging, agents mesh.
Volledige Foodlinkk Command Center uitbreiding met social automatisering,
reclamefolder filters, Proxmox monitoring en documentatie.
2026-06-09 00:41:27 +00:00

96 lines
3.1 KiB
Python

"""Agents API — souls & activity."""
from __future__ import annotations
from typing import Any, Optional
from fastapi import APIRouter, HTTPException
from pydantic import BaseModel, Field
from app.db import fetch_all
from app.services import agent_souls
router = APIRouter(prefix="/api/agents", tags=["agents-api"])
class SoulUpdate(BaseModel):
display_name: Optional[str] = None
role_title: Optional[str] = None
soul_md: Optional[str] = None
responsibilities: Optional[str] = None
is_active: Optional[bool] = None
@router.get("/souls")
def api_list_souls() -> dict[str, Any]:
return {"items": agent_souls.list_souls(), "count": len(agent_souls.list_souls())}
@router.get("/souls/{agent_key}")
def api_get_soul(agent_key: str) -> dict[str, Any]:
soul = agent_souls.get_soul(agent_key)
if not soul:
raise HTTPException(404, "Agent not found")
return {"soul": soul}
@router.put("/souls/{agent_key}")
def api_update_soul(agent_key: str, body: SoulUpdate) -> dict[str, Any]:
try:
soul = agent_souls.update_soul(agent_key, **body.model_dump(exclude_none=True))
except ValueError as exc:
raise HTTPException(404, str(exc)) from exc
return {"soul": soul}
@router.get("/mesh")
def api_agents_mesh() -> dict[str, Any]:
souls = agent_souls.list_souls()
stats_rows = fetch_all(
"""
SELECT LOWER(agent_name) AS agent_key,
MAX(created_at) AS last_event_at,
COUNT(*) FILTER (WHERE created_at >= NOW() - INTERVAL '6 hours') AS events_6h,
COUNT(*) FILTER (
WHERE created_at >= NOW() - INTERVAL '24 hours'
AND status IN ('error', 'rejected')
) AS errors_24h
FROM agent_events
GROUP BY LOWER(agent_name)
"""
)
by_key = {str(r["agent_key"]): dict(r) for r in stats_rows}
nodes: list[dict[str, Any]] = []
for soul in souls:
key = str(soul.get("agent_key") or "").lower()
row = by_key.get(key, {})
events_6h = int(row.get("events_6h") or 0)
errors_24h = int(row.get("errors_24h") or 0)
health = "offline"
if events_6h > 0 and errors_24h == 0:
health = "healthy"
elif events_6h > 0:
health = "warn"
elif int(soul.get("event_count") or 0) > 0:
health = "idle"
node = dict(soul)
node["health"] = health
node["events_6h"] = events_6h
node["errors_24h"] = errors_24h
if row.get("last_event_at") is not None and hasattr(row["last_event_at"], "isoformat"):
node["last_event_at"] = row["last_event_at"].isoformat()
nodes.append(node)
edge_rows = fetch_all(
"""
SELECT LOWER(agent_name) AS source_key, COUNT(*) AS weight
FROM agent_events
WHERE created_at >= NOW() - INTERVAL '24 hours'
AND LOWER(agent_name) <> 'herman'
GROUP BY LOWER(agent_name)
ORDER BY weight DESC
"""
)
edges = [{"source": str(r["source_key"]), "target": "herman", "weight": int(r["weight"])} for r in edge_rows]
return {"nodes": nodes, "edges": edges}