feat(settings): tag-eval admin card — trigger + persisted report (survives nav)
CI / lint (push) Successful in 2s
CI / frontend-build (push) Successful in 17s
CI / backend-lint-and-test (push) Successful in 26s
CI / integration (push) Successful in 3m19s

Frontend for #1130. A maintenance tile in Settings → Tagging:
- Editable concept list + "Run eval" → POST /api/tag-eval (one running at a time).
- Rehydrates on mount via the persisted run (getRun by latest id) and polls while
  running — so the report SURVIVES navigation (operator-flagged); the task runs
  backend-side regardless and the card reconnects to its row.
- Renders the saved report: per-concept head-vs-centroid metrics table (AP/F1/
  precision/recall) with Δ AP, the learning curve (AP @ N positives), and
  thumbnail galleries (head-would-suggest / head-doubts-positive) for eyeballing.

Backend: _examples now stores thumbnail_urls (not just ids) so the report is a
self-contained artifact that renders without per-id lookups on reload.

No new top-level surface — slots into the existing maintenance area.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-27 22:56:41 -04:00
parent 6e3c5f697f
commit 6cd7281af5
4 changed files with 266 additions and 7 deletions
+27
View File
@@ -0,0 +1,27 @@
import { defineStore } from 'pinia'
import { useApi } from '../composables/useApi.js'
// Tag-eval (#1130): trigger + revisit the head-vs-centroid learning-curve eval.
// The run + full report live server-side (tag_eval_run), so the card rehydrates
// from getRun() on mount — the report survives navigation.
export const useTagEvalStore = defineStore('tagEval', () => {
const api = useApi()
async function start(params) {
return await api.post('/api/tag-eval', { body: { params } })
}
async function getRun(id) {
return await api.get(`/api/tag-eval/${id}`) // includes the full report
}
// The most recent run (light row, no report) — the card calls getRun() with
// its id to pull the persisted report on mount.
async function latest() {
const body = await api.get('/api/tag-eval', { params: { limit: 1 } })
return (body.runs && body.runs[0]) || null
}
return { start, getRun, latest }
})