diff --git a/ui/src/hooks/useApi.ts b/ui/src/hooks/useApi.ts index a991d7c..72cefe8 100644 --- a/ui/src/hooks/useApi.ts +++ b/ui/src/hooks/useApi.ts @@ -422,6 +422,17 @@ export async function getRunDetail(runId: string) { return resp.json() } +export async function deleteRun(runId: string): Promise<{ ok: boolean; run_id?: string; detail?: string }> { + const resp = await fetch(`/api/benchmark/runs/${encodeURIComponent(runId)}`, { + method: 'DELETE', + }) + const data = await resp.json().catch(() => ({})) + if (!resp.ok) { + throw new Error(data.detail || data.error || `delete failed: ${resp.status}`) + } + return data +} + export async function getHFModelDetails(repo: string): Promise { const resp = await fetch(`/api/models/hf-details?repo=${encodeURIComponent(repo)}`) return resp.json() diff --git a/ui/src/pages/RunDetailPage.tsx b/ui/src/pages/RunDetailPage.tsx index 109310c..12962e3 100644 --- a/ui/src/pages/RunDetailPage.tsx +++ b/ui/src/pages/RunDetailPage.tsx @@ -1,6 +1,7 @@ import { useEffect, useState } from 'react' import { useParams, useNavigate, Link } from 'react-router-dom' import ScoreBadge from '../components/ScoreBadge' +import { deleteRun } from '../hooks/useApi' interface RunDetail { status?: string @@ -27,6 +28,8 @@ export default function RunDetailPage() { const navigate = useNavigate() const [data, setData] = useState(null) const [loading, setLoading] = useState(true) + const [deleting, setDeleting] = useState(false) + const [deleteError, setDeleteError] = useState(null) useEffect(() => { if (!runId) return @@ -107,6 +110,23 @@ export default function RunDetailPage() { const sm = run.speed_metrics || {} const machine = run.machine || {} + const handleDelete = async () => { + if (!runId || isRunning) return + const ok = window.confirm(`Delete local run ${runId}? This cannot be undone.`) + if (!ok) return + + setDeleting(true) + setDeleteError(null) + try { + await deleteRun(runId) + navigate('/leaderboard') + } catch (err) { + setDeleteError(err instanceof Error ? err.message : 'Failed to delete run') + } finally { + setDeleting(false) + } + } + return (
{isFailed && ( @@ -132,6 +152,12 @@ export default function RunDetailPage() {
)} + {deleteError && ( +
+ {deleteError} +
+ )} +
- - Compare with… - +
+ + + Compare with… + +
{/* Top-line scores */} diff --git a/ui/src/tabs/LeaderboardTab.tsx b/ui/src/tabs/LeaderboardTab.tsx index 5806cf9..c2c1948 100644 --- a/ui/src/tabs/LeaderboardTab.tsx +++ b/ui/src/tabs/LeaderboardTab.tsx @@ -1,6 +1,6 @@ import { useMemo, useState } from 'react' import { Link, useNavigate } from 'react-router-dom' -import { useRuns } from '../hooks/useApi' +import { deleteRun, useRuns } from '../hooks/useApi' import ScoreBadge from '../components/ScoreBadge' // "Full" = at least these quality suites + speed. Coding is optional bonus. @@ -71,6 +71,8 @@ export default function LeaderboardTab() { const [providerFilter, setProviderFilter] = useState('all') const [harnessFilter, setHarnessFilter] = useState('all') const [rankMode, setRankMode] = useState('overall') + const [deletingRunId, setDeletingRunId] = useState(null) + const [deleteError, setDeleteError] = useState(null) const scoreOf = (r: RunRow): number => { switch (rankMode) { @@ -110,6 +112,22 @@ export default function LeaderboardTab() { const totalRuns = ranked.length const filteredPartialCount = ranked.length - ranked.filter(isFullRun).length + const handleDelete = async (run: RunRow) => { + const ok = window.confirm(`Delete local run ${run.id}? This cannot be undone.`) + if (!ok) return + + setDeletingRunId(run.id) + setDeleteError(null) + try { + await deleteRun(run.id) + refresh() + } catch (err) { + setDeleteError(err instanceof Error ? err.message : 'Failed to delete run') + } finally { + setDeletingRunId(null) + } + } + return (
@@ -273,6 +291,21 @@ export default function LeaderboardTab() { ⚠️ {speedAtCapCount} runs have speed score at the 100 ceiling. Switch to "Raw tok/s" to see actual speed differences.
)} + {deleteError && ( +
+ {deleteError} +
+ )} {!loading && filtered.length === 0 && (
@@ -362,12 +395,31 @@ export default function LeaderboardTab() { {run.total_runtime_sec ? `${run.total_runtime_sec.toFixed(1)}s` : '—'} e.stopPropagation()}> - - Compare - +
+ + Compare + + +
)