package controllers import ( "time" "doormile/db" "doormile/internal/ai/telemetry" "doormile/models" "doormile/utils" "github.com/gofiber/fiber/v2" ) // GetAIInsights — GET /admin/ai/insights?days=7 // // What AI_engine's agents did over the window: runs and failures per agent // (aiagentruns, from telemetry.task), decisions by type and outcome // (agent_decisions), and each agent's latest heartbeat (Redis). `receiving` // says whether this backend is subscribed to the telemetry at all, so an // empty page can tell "not connected" from "nothing happened". func GetAIInsights(c *fiber.Ctx) error { days := telemetry.ClampDays(c.QueryInt("days", 7)) // A real instant: aiagentruns.receivedat and agent_decisions.created_at are // both timestamptz (see telemetry.NewRecorder on why not utils.DBNow). since := time.Now().AddDate(0, 0, -days) runs, err := telemetry.RunStats(db.DB, since) if err != nil { utils.Error("ai insights: runs", "error", err.Error()) return utils.Internal(c, "failed to read agent runs") } decisions, err := telemetry.DecisionCounts(db.DB, since) if err != nil { utils.Error("ai insights: decisions", "error", err.Error()) return utils.Internal(c, "failed to read agent decisions") } var engineAgents []string if err := db.DB.Model(&models.AIAgent{}).Where("runtime = ?", "engine").Order("sortorder").Pluck("agentid", &engineAgents).Error; err != nil { utils.Error("ai insights: agents", "error", err.Error()) } return utils.OK(c, telemetry.Insights{ Days: days, Since: since, Receiving: telemetry.Receiving.Load(), Runs: telemetry.SummariseRuns(runs), Decisions: telemetry.SummariseDecisions(decisions), Live: telemetry.LiveStates(db.Rdb, engineAgents), }) } // GetAIDecisions — GET /admin/ai/decisions?type=&before=&limit= // // Recent agent decisions, newest first, keyset-paged by id. Reasoning is // trimmed and the context column is left out (it can hold rider data). func GetAIDecisions(c *fiber.Ctx) error { rows, err := telemetry.RecentDecisions(db.DB, c.Query("type"), uint64(c.QueryInt("before", 0)), c.QueryInt("limit", 25)) if err != nil { utils.Error("ai insights: recent decisions", "error", err.Error()) return utils.Internal(c, "failed to read agent decisions") } return utils.List(c, rows, int64(len(rows))) }