feat: delete traffic targets and LLM task conversations from UI
Add delete controls to the Traffic and LLM Records pages so users can purge recorded data without hand-editing the data volume. Traffic page: - Host target picker (Popover with checkboxes + select-all toggle) - Exact-match batch delete for multiple hosts (DELETE /api/traffic/hosts) - Substring delete for the host-filter input (DELETE /api/traffic?host=) - Orphaned content-addressed blob garbage collection after deletes - Distinct-hosts endpoint (GET /api/traffic/hosts) for the picker LLM Records page: - Task picker dropdown (distinct task_id + record counts) - Per-task delete (DELETE /api/llm/records?task=) Other: - Add /dist/ to .gitignore (build artifact) - Extend traffic_test.go with DeleteHost, DeleteHostsExact, Hosts tests - Fix Popover render bug (replace ScrollArea that broke height calc) - Remove fade/zoom animation on the host picker Popover
This commit is contained in:
1 parent
5962826f2d
commit
c76d0f4c93
12 files changed
+883
-20
No files matched your search
+42
-1
@@ -152,7 +152,7 @@ VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14)`,
|
||||
}
|
||||
|
||||
// ListLLMRecords returns paginated LLM records with optional filters.
|
||||
func (d *DB) ListLLMRecords(model, session string, page, size int) ([]LLMRecord, int, error) {
|
||||
func (d *DB) ListLLMRecords(model, session, task string, page, size int) ([]LLMRecord, int, error) {
|
||||
if size <= 0 {
|
||||
size = 50
|
||||
}
|
||||
@@ -174,6 +174,11 @@ func (d *DB) ListLLMRecords(model, session string, page, size int) ([]LLMRecord,
|
||||
args = append(args, "%"+session+"%")
|
||||
argN++
|
||||
}
|
||||
if task != "" {
|
||||
where += fmt.Sprintf(` AND COALESCE(task_id,'') = $%d`, argN)
|
||||
args = append(args, task)
|
||||
argN++
|
||||
}
|
||||
|
||||
var total int
|
||||
if err := d.QueryRow(`SELECT COUNT(*) FROM llm_records `+where, args...).Scan(&total); err != nil {
|
||||
@@ -204,6 +209,42 @@ func (d *DB) ListLLMRecords(model, session string, page, size int) ([]LLMRecord,
|
||||
return out, total, rows.Err()
|
||||
}
|
||||
|
||||
// LLMTask is one distinct task with its LLM-record count.
|
||||
type LLMTask struct {
|
||||
TaskID string `json:"task_id"`
|
||||
Count int `json:"count"`
|
||||
}
|
||||
|
||||
// LLMTasks returns distinct non-empty task_ids with record counts, most recent
|
||||
// first — powers the LLM-records page's task picker.
|
||||
func (d *DB) LLMTasks() ([]LLMTask, error) {
|
||||
rows, err := d.Query(`SELECT task_id, COUNT(*) AS n FROM llm_records
|
||||
WHERE COALESCE(task_id,'') <> '' GROUP BY task_id ORDER BY MAX(id) DESC`)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var out []LLMTask
|
||||
for rows.Next() {
|
||||
var t LLMTask
|
||||
if err := rows.Scan(&t.TaskID, &t.Count); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out = append(out, t)
|
||||
}
|
||||
return out, rows.Err()
|
||||
}
|
||||
|
||||
// DeleteLLMRecords removes every LLM record for one exact task_id — the same
|
||||
// match the page's task picker/filter uses. Returns rows deleted.
|
||||
func (d *DB) DeleteLLMRecords(task string) (int64, error) {
|
||||
res, err := d.Exec(`DELETE FROM llm_records WHERE COALESCE(task_id,'') = $1`, task)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return res.RowsAffected()
|
||||
}
|
||||
|
||||
// GetLLMRecord returns a single LLM record with full request/response bodies.
|
||||
func (d *DB) GetLLMRecord(id int64) (*LLMRecord, error) {
|
||||
var r LLMRecord
|
||||
|
||||
Reference in new issue
Block a user