feat(metrics): add persistent LLM usage analytics
This commit is contained in:
1 parent
04fda4cd01
commit
8dca38c4b9
4 files changed
+539
-316
No files matched your search
@@ -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) {
|
||||
|
||||
Reference in new issue
Block a user