feat(metrics): add persistent LLM usage analytics

This commit is contained in:
pphua committed 2026-08-24 19:30:07 +08:00
1 parent 04fda4cd01
commit 8dca38c4b9
4 files changed
+539 -316

No files matched your search

+49
View File
@@ -4,6 +4,8 @@ import (
"net/http"
"strconv"
"strings"
"github.com/Autumn-27/artex/db"
)
// pgListCommands returns tool executions (any tool) from the activity table.
@@ -45,6 +47,53 @@ func (s *Server) pgListLLMRecords(w http.ResponseWriter, r *http.Request) {
writeJSON(w, 200, map[string]any{"records": records, "total": total})
}
// pgTokenByModel returns a task's LLM token usage grouped by model, from the
// always-on llm_usage metering ledger. Accurate even with per-agent model bindings,
// pool rotation/failover, and interrupted runs (every call is metered on success or
// error). No recording toggle required.
func (s *Server) pgTokenByModel(w http.ResponseWriter, r *http.Request) {
id := strings.TrimSpace(r.URL.Query().Get("task"))
if id == "" {
writeErr(w, 400, "missing task")
return
}
pg := s.m.PG()
if pg == nil {
writeJSON(w, 200, map[string]any{"models": []db.ModelTokenStat{}})
return
}
models, err := pg.TokenByModel(id)
if err != nil {
writeErr(w, 500, err.Error())
return
}
writeJSON(w, 200, map[string]any{"models": models})
}
// pgUsageStats returns global llm_usage aggregates for the dashboard's "new" token
// view: per-profile totals (all-time) + per-(profile, day) buckets for the daily
// chart. Sourced from the always-on metering ledger, so it is accurate across
// per-agent bindings, pool rotation, and interrupted runs.
func (s *Server) pgUsageStats(w http.ResponseWriter, r *http.Request) {
pg := s.m.PG()
if pg == nil {
writeJSON(w, 200, map[string]any{"by_profile": []db.ProfileUsage{}, "daily": []db.ProfileDayUsage{}})
return
}
days := atoiDefault(r.URL.Query().Get("days"), 365)
byProfile, err := pg.UsageByProfile()
if err != nil {
writeErr(w, 500, err.Error())
return
}
daily, err := pg.UsageDaily(days)
if err != nil {
writeErr(w, 500, err.Error())
return
}
writeJSON(w, 200, map[string]any{"by_profile": byProfile, "daily": daily})
}
// pgLLMTasks returns distinct recorded tasks with counts, for the page's task
// picker (pick a task → filter the list, then delete its conversations).
func (s *Server) pgLLMTasks(w http.ResponseWriter, r *http.Request) {