First Commit
ci / go (push) Waiting to run
ci / go-db (agent) (push) Waiting to run
ci / go-db (config) (push) Waiting to run
ci / go-db (db) (push) Waiting to run
ci / go-db (evidence) (push) Waiting to run
ci / go-db (llmrec) (push) Waiting to run
ci / go-db (server) (push) Waiting to run
web / web (push) Waiting to run
docs / links (push) Canceled after 0s
detections / detections (push) Canceled after 0s
ci / go (push) Waiting to run
ci / go-db (agent) (push) Waiting to run
ci / go-db (config) (push) Waiting to run
ci / go-db (db) (push) Waiting to run
ci / go-db (evidence) (push) Waiting to run
ci / go-db (llmrec) (push) Waiting to run
ci / go-db (server) (push) Waiting to run
web / web (push) Waiting to run
docs / links (push) Canceled after 0s
detections / detections (push) Canceled after 0s
This commit is contained in:
@@ -0,0 +1,161 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/Autumn-27/artex/db"
|
||||
)
|
||||
|
||||
// pgListCommands returns tool executions (any tool) from the activity table.
|
||||
func (s *Server) pgListCommands(w http.ResponseWriter, r *http.Request) {
|
||||
q := r.URL.Query()
|
||||
page := atoiDefault(q.Get("page"), 0)
|
||||
size := atoiDefault(q.Get("size"), 50)
|
||||
keyword := q.Get("q")
|
||||
|
||||
records, total, err := s.m.PG().ListCommands(commandTaskFilter(q.Get("task")), keyword, page, size)
|
||||
if err != nil {
|
||||
writeErr(w, 500, err.Error())
|
||||
return
|
||||
}
|
||||
writeJSON(w, 200, map[string]any{"commands": records, "total": total})
|
||||
}
|
||||
|
||||
// pgToolStats returns per-tool call counts for the tool-execution history, under
|
||||
// the same task/keyword filters the list takes — the summary describes the whole
|
||||
// filtered set, not the page on screen.
|
||||
func (s *Server) pgToolStats(w http.ResponseWriter, r *http.Request) {
|
||||
q := r.URL.Query()
|
||||
pg := s.m.PG()
|
||||
if pg == nil {
|
||||
writeJSON(w, 200, map[string]any{"stats": []db.ToolStat{}})
|
||||
return
|
||||
}
|
||||
stats, err := pg.ToolStats(commandTaskFilter(q.Get("task")), q.Get("q"))
|
||||
if err != nil {
|
||||
writeErr(w, 500, err.Error())
|
||||
return
|
||||
}
|
||||
writeJSON(w, 200, map[string]any{"stats": stats})
|
||||
}
|
||||
|
||||
// commandTaskFilter parses the ?task= exploration id. Absent or unparsable → nil
|
||||
// (no filter), matching the list endpoint's long-standing lenient behaviour.
|
||||
func commandTaskFilter(v string) *int64 {
|
||||
if v == "" {
|
||||
return nil
|
||||
}
|
||||
n, err := strconv.ParseInt(v, 10, 64)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return &n
|
||||
}
|
||||
|
||||
// pgListLLMRecords returns paginated LLM call records.
|
||||
func (s *Server) pgListLLMRecords(w http.ResponseWriter, r *http.Request) {
|
||||
q := r.URL.Query()
|
||||
page := atoiDefault(q.Get("page"), 0)
|
||||
size := atoiDefault(q.Get("size"), 50)
|
||||
model := q.Get("model")
|
||||
session := q.Get("session")
|
||||
task := q.Get("task")
|
||||
|
||||
records, total, err := s.m.PG().ListLLMRecords(model, session, task, page, size)
|
||||
if err != nil {
|
||||
writeErr(w, 500, err.Error())
|
||||
return
|
||||
}
|
||||
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) {
|
||||
tasks, err := s.m.PG().LLMTasks()
|
||||
if err != nil {
|
||||
writeErr(w, 500, err.Error())
|
||||
return
|
||||
}
|
||||
writeJSON(w, 200, map[string]any{"tasks": tasks})
|
||||
}
|
||||
|
||||
// pgDeleteLLMRecords removes all LLM records for one exact task_id (the same
|
||||
// match the task picker uses). Empty task → 400. Returns rows deleted.
|
||||
func (s *Server) pgDeleteLLMRecords(w http.ResponseWriter, r *http.Request) {
|
||||
task := strings.TrimSpace(r.URL.Query().Get("task"))
|
||||
if task == "" {
|
||||
writeErr(w, 400, "missing task")
|
||||
return
|
||||
}
|
||||
n, err := s.m.PG().DeleteLLMRecords(task)
|
||||
if err != nil {
|
||||
writeErr(w, 500, err.Error())
|
||||
return
|
||||
}
|
||||
writeJSON(w, 200, map[string]any{"deleted": n})
|
||||
}
|
||||
|
||||
// pgGetLLMRecord returns a single LLM record with full request/response bodies.
|
||||
func (s *Server) pgGetLLMRecord(w http.ResponseWriter, r *http.Request) {
|
||||
id, err := strconv.ParseInt(r.PathValue("id"), 10, 64)
|
||||
if err != nil {
|
||||
writeErr(w, 400, "invalid id")
|
||||
return
|
||||
}
|
||||
rec, err := s.m.PG().GetLLMRecord(id)
|
||||
if err != nil {
|
||||
writeErr(w, 404, "not found")
|
||||
return
|
||||
}
|
||||
writeJSON(w, 200, rec)
|
||||
}
|
||||
Reference in New Issue
Block a user