An errored GPU job's stored reason is a suspicion; the file probe is the
verdict. A 15-min beat sweep (triage_gpu_errors) runs verify_integrity's own
probe (sha256 + decode) on each errored image ONCE and writes both verdicts:
ImageRecord.integrity_status and the new GpuJob.triage_status ('defect' |
'file_ok', migration 0072). Every classification logs at WARNING so it
surfaces in Logs/System Activity.
- 'defect' rows are excluded from /retry_errors (re-running a known-bad file
burns agent time re-minting the tombstone); response now reports
defects_kept and the GpuAgentCard toast says so.
- GET /api/gpu/errors: triage view — reason buckets (classify_reason),
probe verdicts, per-job detail. POST /errors/triage runs the sweep now.
- POST /api/gpu/errors/<id>/recover: reuses the Layer-2 refetch pattern —
delete the defective copy + record (full cascade takes the tombstones too)
and re-poll its subscription Source so a fresh copy re-imports and re-enters
the pipeline; 'no_source' when nothing pollable resolves.
- New 'Failed processing' card (GpuTriageCard) in Maintenance: verdict counts,
reason summary, probe-now, defect list with thumbnails + per-image Recover.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CDgx8bQS5YrGRK76v8HUnM
68 lines
2.4 KiB
JavaScript
68 lines
2.4 KiB
JavaScript
import { defineStore } from 'pinia'
|
|
|
|
import { useApi } from '../composables/useApi.js'
|
|
|
|
// GPU agent control surface (#114): the FC-side admin for the desktop agent —
|
|
// the bearer token it authenticates with, the job-queue depth, and the backfill
|
|
// trigger. The agent itself talks to /api/gpu/jobs/* over HTTP; nothing here
|
|
// touches Redis/Postgres directly.
|
|
export const useGpuStore = defineStore('gpu', () => {
|
|
const api = useApi()
|
|
|
|
// { token: <string|null>, configured: bool }
|
|
async function token() {
|
|
return await api.get('/api/gpu/token')
|
|
}
|
|
|
|
// Generate a fresh token (invalidates the old one). Returns { token }.
|
|
async function rotateToken() {
|
|
return await api.post('/api/gpu/token/rotate')
|
|
}
|
|
|
|
// { pending, leased, done, error }
|
|
async function status() {
|
|
return await api.get('/api/gpu/status')
|
|
}
|
|
|
|
// Enqueue a job per image lacking one for `task` (the agent drains it).
|
|
async function backfill(task = 'ccip') {
|
|
return await api.post('/api/gpu/backfill', { body: { task } })
|
|
}
|
|
|
|
// Reset every done/error `task` job to pending → re-run the WHOLE library
|
|
// under the current pipeline (e.g. after adding crop detectors).
|
|
async function reprocess(task = 'ccip') {
|
|
return await api.post('/api/gpu/reprocess', { body: { task } })
|
|
}
|
|
|
|
// Requeue ONLY the errored jobs (all task types) — the scoped recovery after
|
|
// an agent fix, without re-running the done library. Triage-confirmed
|
|
// defects stay put (they recover via recoverImage instead).
|
|
// Returns { requeued, pruned, defects_kept }.
|
|
async function retryErrors() {
|
|
return await api.post('/api/gpu/retry_errors')
|
|
}
|
|
|
|
// Failure-triage view (#125): errored jobs joined with integrity verdicts.
|
|
// Returns { total, by_class, triage, items }.
|
|
async function errors() {
|
|
return await api.get('/api/gpu/errors')
|
|
}
|
|
|
|
// Run the file-probe sweep now instead of waiting out the 15-min beat.
|
|
async function triageErrors() {
|
|
return await api.post('/api/gpu/errors/triage')
|
|
}
|
|
|
|
// Recover a defective original: delete the bad copy + record, re-poll its
|
|
// Source. Returns { status: 'refetch_queued'|'no_source'|'not_found', ... }.
|
|
async function recoverImage(imageId) {
|
|
return await api.post(`/api/gpu/errors/${imageId}/recover`)
|
|
}
|
|
|
|
return {
|
|
token, rotateToken, status, backfill, reprocess, retryErrors,
|
|
errors, triageErrors, recoverImage,
|
|
}
|
|
})
|