From 6da29ac249cd144a3b46f0c62c087f678cf37933 Mon Sep 17 00:00:00 2001 From: Dera Okeke Date: Mon, 24 Aug 2026 14:00:18 +0100 Subject: [PATCH 1/3] new dashboard link --- app/components/Nav.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/components/Nav.tsx b/app/components/Nav.tsx index 341e71d..5f913b8 100644 --- a/app/components/Nav.tsx +++ b/app/components/Nav.tsx @@ -9,7 +9,7 @@ const linkCls = const navLinks = [ { to: '/', label: 'Home' }, - { to: '/dashboard', label: 'Dashboard' }, + { href: 'https://dash.cacheon.ai/', label: 'Dashboard' }, { to: '/docs', label: 'Docs' }, { href: 'https://discord.com/invite/cacheon', label: 'Discord', icon: }, { href: 'https://x.com/cacheon_ai', label: 'X', icon: }, From 94e6632808a62d6f70a06ac89cd09e39e19224d4 Mon Sep 17 00:00:00 2001 From: Dera Okeke Date: Mon, 24 Aug 2026 14:39:04 +0100 Subject: [PATCH 2/3] removed old dashboard logic --- app/components/HowItWorks.tsx | 2 +- app/components/Team.tsx | 2 +- .../dashboard/evaluations-section.tsx | 731 ----------- app/components/dashboard/leader-section.tsx | 77 -- app/components/dashboard/log-viewer.tsx | 220 ---- app/components/dashboard/logs-section.tsx | 206 --- app/components/dashboard/pulse-section.tsx | 1120 ----------------- app/components/dashboard/rounds-section.tsx | 275 ---- app/components/dashboard/shared.tsx | 284 ----- .../dashboard/validator-logs-section.tsx | 193 --- app/components/ui/glass-card.tsx | 27 + app/lib/api.client.ts | 245 ---- app/lib/docs-redirects.js | 2 - app/lib/eval-gates.ts | 219 ---- app/lib/use-poll.ts | 55 - app/routes.ts | 9 - app/routes/dashboard._index.tsx | 5 - app/routes/dashboard.evaluations.tsx | 10 - app/routes/dashboard.leader.tsx | 10 - app/routes/dashboard.logs.tsx | 10 - app/routes/dashboard.pulse.tsx | 10 - app/routes/dashboard.rounds.tsx | 10 - app/routes/dashboard.tsx | 53 - app/routes/dashboard.validator-logs.tsx | 10 - vite.config.ts | 11 - workers/app.ts | 24 - 26 files changed, 29 insertions(+), 3791 deletions(-) delete mode 100644 app/components/dashboard/evaluations-section.tsx delete mode 100644 app/components/dashboard/leader-section.tsx delete mode 100644 app/components/dashboard/log-viewer.tsx delete mode 100644 app/components/dashboard/logs-section.tsx delete mode 100644 app/components/dashboard/pulse-section.tsx delete mode 100644 app/components/dashboard/rounds-section.tsx delete mode 100644 app/components/dashboard/shared.tsx delete mode 100644 app/components/dashboard/validator-logs-section.tsx create mode 100644 app/components/ui/glass-card.tsx delete mode 100644 app/lib/api.client.ts delete mode 100644 app/lib/eval-gates.ts delete mode 100644 app/lib/use-poll.ts delete mode 100644 app/routes/dashboard._index.tsx delete mode 100644 app/routes/dashboard.evaluations.tsx delete mode 100644 app/routes/dashboard.leader.tsx delete mode 100644 app/routes/dashboard.logs.tsx delete mode 100644 app/routes/dashboard.pulse.tsx delete mode 100644 app/routes/dashboard.rounds.tsx delete mode 100644 app/routes/dashboard.tsx delete mode 100644 app/routes/dashboard.validator-logs.tsx diff --git a/app/components/HowItWorks.tsx b/app/components/HowItWorks.tsx index 0756538..4dbc0e8 100644 --- a/app/components/HowItWorks.tsx +++ b/app/components/HowItWorks.tsx @@ -1,6 +1,6 @@ import { cn } from '~/lib/cn' import SectionHeader from './SectionHeader' -import { GlassCard } from './dashboard/shared' +import { GlassCard } from './ui/glass-card' const STEPS = [ { diff --git a/app/components/Team.tsx b/app/components/Team.tsx index cdb922c..ed3302b 100644 --- a/app/components/Team.tsx +++ b/app/components/Team.tsx @@ -1,6 +1,6 @@ import { TEAM, type TeamMember } from '../constants/team' import SectionHeader from './SectionHeader' -import { GlassCard } from './dashboard/shared' +import { GlassCard } from './ui/glass-card' function IconGithub({ size = 16 }: { size?: number }) { return ( diff --git a/app/components/dashboard/evaluations-section.tsx b/app/components/dashboard/evaluations-section.tsx deleted file mode 100644 index c8a68ec..0000000 --- a/app/components/dashboard/evaluations-section.tsx +++ /dev/null @@ -1,731 +0,0 @@ -import { useEffect, useState } from 'react' -import { ArrowDown, ArrowUp } from 'lucide-react' - -import { cn } from '~/lib/cn' -import { - PASS1_TEXT_SIM_DQ_THRESHOLD, - buildContainerLogLabel, - containerLogHref, - findContainerLogLabel, - findEvalRunLogLabels, - parsePass1TextSimilarity, - summarizeEvalGates, - validatorLogHref, - type PassStatus, -} from '~/lib/eval-gates' -import { usePoll } from '~/lib/use-poll' -import { - fetchContainerLogs, - fetchEvaluations, - fetchEvaluationsByHotkey, - fetchValidatorLogs, - type EvaluationRecord, -} from '~/lib/api.client' -import { - fmtScore, - fmtPct, - fmtImprovement, - truncHotkey, - relativeTimeAgo, - GlassCard, - Skeleton, - StatusPill, - MiniStat, - ImageTag, -} from './shared' -import { CopyButton } from '~/components/ui/copy-button' -import { CloseButton } from '~/components/ui/close-button' - -type EvalFilter = 'all' | 'active' | 'dq' -type SortDir = 'desc' | 'asc' -type SortKey = 'score' | 'speed_improvement' - -function isEffectiveDq(e: EvaluationRecord): boolean { - return e.disqualified -} - -function formatPass1GateDetail(ev: EvaluationRecord, pass1: PassStatus): string | undefined { - if (pass1 === 'na') return undefined - const gate = fmtPct(PASS1_TEXT_SIM_DQ_THRESHOLD) - if (pass1 === 'fail') { - const sim = parsePass1TextSimilarity(ev.disqualify_reason) - if (sim != null) return `similarity ${fmtPct(sim)} (gate ${gate})` - return `below gate ${gate}` - } - return undefined -} - -export function EvaluationsSection() { - const [filter, setFilter] = useState('all') - const [sortKey, setSortKey] = useState('score') - const [sortDir, setSortDir] = useState('desc') - const [selectedHotkey, setSelectedHotkey] = useState(null) - - const evals = usePoll(fetchEvaluations, 30_000) - const containerLogs = usePoll(fetchContainerLogs, 60_000) - const validatorLogs = usePoll(fetchValidatorLogs, 60_000) - const containerLabels = containerLogs.data?.logs.map((l) => l.label) ?? [] - const validatorLabels = validatorLogs.data?.logs.map((l) => l.label) ?? [] - const allEvals = evals.data?.evaluations ?? [] - const filtered = - filter === 'active' - ? allEvals.filter((e) => !isEffectiveDq(e)) - : filter === 'dq' - ? allEvals.filter((e) => isEffectiveDq(e)) - : allEvals - const list = [...filtered].sort((a, b) => - sortDir === 'desc' ? b[sortKey] - a[sortKey] : a[sortKey] - b[sortKey], - ) - - const handleSort = (key: SortKey) => { - if (key === sortKey) { - setSortDir((d) => (d === 'desc' ? 'asc' : 'desc')) - } else { - setSortKey(key) - setSortDir('desc') - } - } - - return ( -
-
- {(['all', 'active', 'dq'] as EvalFilter[]).map((f) => { - const count = evals.data - ? f === 'all' - ? allEvals.length - : f === 'active' - ? allEvals.filter((e) => !isEffectiveDq(e)).length - : allEvals.filter((e) => isEffectiveDq(e)).length - : null - return ( - - ) - })} -
- - - {evals.loading ? ( -
- {[...Array(5)].map((_, i) => ( -
- -
- ))} -
- ) : evals.error ? ( -
- Could not load data -
- ) : list.length === 0 ? ( -
- No evaluations found -
- ) : ( - <> -
- {list.map((ev) => ( - - ))} -
- - - - - - - - - - - - - {list.map((ev) => ( - - ))} - -
- Hotkey - - Image - - Status -
- - )} -
- - {selectedHotkey !== null && ( - setSelectedHotkey(null)} - containerLabels={containerLabels} - validatorLabels={validatorLabels} - /> - )} -
- ) -} - -function SortableHeader({ - label, - title, - sortKey, - activeKey, - dir, - onSort, - className, -}: { - label: string - title?: string - sortKey: SortKey - activeKey: SortKey - dir: SortDir - onSort: (key: SortKey) => void - className?: string -}) { - const isActive = activeKey === sortKey - const Arrow = dir === 'desc' ? ArrowDown : ArrowUp - return ( - - - - ) -} - -function DqHint({ ev }: { ev: EvaluationRecord }) { - const gates = summarizeEvalGates(ev) - if (!isEffectiveDq(ev) || !gates.shortLabel) return null - return ( - - {gates.shortLabel} - - ) -} - -function EvalRow({ ev, onSelect }: { ev: EvaluationRecord; onSelect: (hotkey: string) => void }) { - const dq = isEffectiveDq(ev) - return ( - onSelect(ev.hotkey)} - onKeyDown={(e) => e.key === 'Enter' && onSelect(ev.hotkey)} - role="button" - tabIndex={0} - > - -
- - {truncHotkey(ev.hotkey)} - - -
- - - - - - {fmtScore(ev.score)} - - - {fmtImprovement(ev.speed_improvement)} - - - - - - - ) -} - -function EvalCard({ ev, onSelect }: { ev: EvaluationRecord; onSelect: (hotkey: string) => void }) { - const dq = isEffectiveDq(ev) - return ( - - ) -} - -function PassStatusLine({ - label, - status, - detail, -}: { - label: string - status: PassStatus - detail?: string -}) { - const statusLabel = - status === 'pass' - ? 'Pass' - : status === 'fail' - ? 'Fail' - : status === 'skipped' - ? 'Not reached' - : 'N/A' - const statusCls = - status === 'pass' ? 'text-accent' : status === 'fail' ? 'text-error' : 'text-secondary/50' - - return ( -
- {label} -
- {statusLabel} - {detail && {detail}} -
-
- ) -} - -function EvalOutcomeCard({ - ev, - containerLabels, - validatorLabels, -}: { - ev: EvaluationRecord - containerLabels: string[] - validatorLabels: string[] -}) { - const gates = summarizeEvalGates(ev) - const logLabel = buildContainerLogLabel(ev.uid, ev.hotkey, ev.evaluation_block) - const minerLogLabel = findContainerLogLabel(containerLabels, ev.uid, ev.evaluation_block) - const logsHref = minerLogLabel - ? containerLogHref(minerLogLabel) - : `/dashboard/logs?uid=${ev.uid}&block=${ev.evaluation_block}` - const runLogs = findEvalRunLogLabels(containerLabels, validatorLabels, ev.evaluation_block) - - return ( -
-
- Evaluation gates -
- -
- - -
- -

- Speed and correctness use the same 10 scored prompts (~6k in, 512 out). The speed gate - compares decoded output text (40% similarity floor). Token match in the table is telemetry - only. Correctness teacher-forcing runs on those same outputs after all miners finish. -

- - {ev.disqualify_reason && ( -
-
- Disqualify reason -
-

{ev.disqualify_reason}

-
- )} - - {!ev.disqualified && ev.score === 0 && gates.pass1 === 'pass' && gates.pass2 === 'pass' && ( -

- Gates passed but score is 0: no end-to-end speed improvement vs baseline. -

- )} - - {gates.pass2 === 'fail' && ( -
-

- Correctness checks whether output text is plausible under Qwen (teacher-forcing - logprobs). Per-prompt correctness metrics are not stored in the API yet. -

- {gates.isInfraFail && ( -

- This looks like a validator scoring issue. Open the paired GPU eval log and scoring - baseline container log for block {ev.evaluation_block}. -

- )} -
- )} - - -

Miner log label: {logLabel}

- {gates.isInfraFail && (runLogs.gpuEval || runLogs.baselineScoring) && ( -

- {runLogs.gpuEval && <>GPU: {runLogs.gpuEval}} - {runLogs.gpuEval && runLogs.baselineScoring && ' · '} - {runLogs.baselineScoring && <>Scoring: {runLogs.baselineScoring}} -

- )} -
- ) -} - -function EvalDetailDrawer({ - hotkey, - onClose, - containerLabels, - validatorLabels, -}: { - hotkey: string - onClose: () => void - containerLabels: string[] - validatorLabels: string[] -}) { - const [data, setData] = useState(null) - const [error, setError] = useState(null) - - useEffect(() => { - let cancelled = false - setData(null) - setError(null) - fetchEvaluationsByHotkey(hotkey) - .then((r) => { - if (!cancelled) setData(r.evaluations) - }) - .catch((e) => { - if (!cancelled) setError(e instanceof Error ? e.message : String(e)) - }) - return () => { - cancelled = true - } - }, [hotkey]) - - const ev = data?.[0] - - return ( -
-
-
-
-
-

- {truncHotkey(hotkey)} -

- -
- -
- -
- {error && ( -
- {error} -
- )} - - {!data && !error && ( -
- - - -
- )} - - {ev && ( - <> - - -
- Hotkey -
- - {ev.hotkey} - - -
- - Image - - - Digest - - {ev.digest} - - - Evaluated -
- {relativeTimeAgo(ev.evaluated_at)} - - Block #{ev.evaluation_block} · Commit #{ev.commit_block} - -
-
- -
- - - -
- - {ev.per_prompt && ev.per_prompt.length > 0 && ( -
-
- Speed ({ev.per_prompt.length} prompts) -
-

- Token match {fmtPct(ev.token_match_rate)} (telemetry) · Speed{' '} - {fmtImprovement(ev.speed_improvement)} -

-
- - - - - - - - - - - - - {ev.per_prompt.map((pp, i) => { - const zeroE2e = pp.e2e_s === 0 - return ( - - - - - - - - - ) - })} - -
# - TTFT - - E2E - - Baseline - - Tokens - - Match -
{i + 1} - {pp.ttft_s != null ? `${pp.ttft_s.toFixed(3)}s` : '-'} - - {pp.e2e_s > 0 ? `${pp.e2e_s.toFixed(3)}s` : '-'} - - {pp.baseline_e2e_s != null && pp.baseline_e2e_s > 0 - ? `${pp.baseline_e2e_s.toFixed(3)}s` - : '-'} - - {pp.output_tokens} - - {fmtPct(pp.token_match_rate)} -
-
-

- Highlighted rows: zero E2E (stream ended before tolerance band). Per-prompt - token match is telemetry; the speed gate uses decoded text similarity. -

-
- )} - - {data && data.length > 1 && ( -
-
- Evaluation History ({data.length} total) -
-
- {data.slice(1).map((e) => ( -
-
-
- {truncHotkey(e.hotkey)} - -
- - {relativeTimeAgo(e.evaluated_at)} · Block #{e.evaluation_block} - -
- - {fmtScore(e.score)} - - - - -
- ))} -
-
- )} - - )} -
-
-
- ) -} diff --git a/app/components/dashboard/leader-section.tsx b/app/components/dashboard/leader-section.tsx deleted file mode 100644 index 0ceaeb1..0000000 --- a/app/components/dashboard/leader-section.tsx +++ /dev/null @@ -1,77 +0,0 @@ -import { cn } from '~/lib/cn' -import { usePoll } from '~/lib/use-poll' -import { fetchLeaderHistory, type LeaderHistoryEntry } from '~/lib/api.client' -import { fmtScore, truncHotkey, relativeTimeAgo, GlassCard, Skeleton } from './shared' -import { CopyButton } from '~/components/ui/copy-button' -import { LinkButton } from '~/components/ui/link-button' - -export function LeaderSection() { - const history = usePoll(fetchLeaderHistory, 30_000) - - return ( -
- -
- Leader History -
- {history.loading ? ( -
- {[...Array(3)].map((_, i) => ( - - ))} -
- ) : !history.data?.history.length ? ( -

- {history.error ? 'Could not load data' : 'No history yet'} -

- ) : ( -
-
- {[...history.data.history].reverse().map((entry, i) => ( - - ))} -
- )} - -
- ) -} - -function LeaderHistoryNode({ entry, isLatest }: { entry: LeaderHistoryEntry; isLatest: boolean }) { - return ( -
- -
-
- - {truncHotkey(entry.new_leader_hotkey)} - - -
-
UID {entry.new_leader_uid}
-
- {relativeTimeAgo(entry.ts)} - · - Block #{entry.block} - -
-
- Score {fmtScore(entry.new_leader_score)} - {entry.prev_leader_uid != null && ( - - {' '} - (overtook UID {entry.prev_leader_uid}, was {fmtScore(entry.prev_leader_score)}) - - )} -
-
-
- ) -} diff --git a/app/components/dashboard/log-viewer.tsx b/app/components/dashboard/log-viewer.tsx deleted file mode 100644 index f69585d..0000000 --- a/app/components/dashboard/log-viewer.tsx +++ /dev/null @@ -1,220 +0,0 @@ -import { useEffect, useRef, useState, type ReactNode } from 'react' -import { ChevronLeft } from 'lucide-react' -import { cn } from '~/lib/cn' -import { fmtBytes, Skeleton } from './shared' -import { CopyButton } from '~/components/ui/copy-button' -import { CloseButton } from '~/components/ui/close-button' - -export interface LogEntry { - label: string - size_bytes: number -} - -interface LogViewerProps { - entries: LogEntry[] - loading: boolean - error: boolean - fetchLog: (label: string) => Promise - renderLabel?: (entry: LogEntry) => ReactNode - title: string - emptyMessage?: string - /** Optional controls to render above the list in the sidebar */ - sidebarControls?: ReactNode - /** Optional footer line (e.g. "Showing X of Y") */ - sidebarFooter?: ReactNode - /** Optional label to auto-select when it appears in `entries` */ - initialSelectedLabel?: string | null -} - -export function LogViewer({ - entries, - loading, - error, - fetchLog, - renderLabel, - title, - emptyMessage = 'No logs available', - sidebarControls, - sidebarFooter, - initialSelectedLabel, -}: LogViewerProps) { - const [selectedLabel, setSelectedLabel] = useState(null) - const [logText, setLogText] = useState(null) - const [logLoading, setLogLoading] = useState(false) - const logViewerRef = useRef(null) - const appliedInitialRef = useRef(null) - - useEffect(() => { - if (!selectedLabel) return - if (!entries.some((l) => l.label === selectedLabel)) setSelectedLabel(null) - }, [entries, selectedLabel]) - - useEffect(() => { - if (!initialSelectedLabel) return - if (appliedInitialRef.current === initialSelectedLabel) return - if (entries.some((l) => l.label === initialSelectedLabel)) { - setSelectedLabel(initialSelectedLabel) - appliedInitialRef.current = initialSelectedLabel - } - }, [initialSelectedLabel, entries]) - - useEffect(() => { - if (!selectedLabel) { - setLogText(null) - return - } - let cancelled = false - setLogLoading(true) - setLogText(null) - fetchLog(selectedLabel) - .then((text) => { - if (cancelled) return - setLogText(text) - requestAnimationFrame(() => { - logViewerRef.current?.scrollTo(0, logViewerRef.current.scrollHeight) - }) - }) - .catch(() => { - if (!cancelled) setLogText('Failed to load log.') - }) - .finally(() => { - if (!cancelled) setLogLoading(false) - }) - return () => { - cancelled = true - } - }, [selectedLabel, fetchLog]) - - const empty = !loading && entries.length === 0 - - return ( -
- {/* ── Sidebar ── */} -
- {/* Controls slot */} - {sidebarControls && ( -
{sidebarControls}
- )} - - {/* Section label */} -
- - {title} - -
- - {/* List */} -
- {loading ? ( -
- {[...Array(6)].map((_, i) => ( - - ))} -
- ) : error ? ( -
Could not load data
- ) : empty ? ( -
- {emptyMessage} -
- ) : ( - entries.map((log) => ( - - )) - )} -
- - {/* Footer */} - {sidebarFooter && ( -
{sidebarFooter}
- )} -
- - {/* ── Viewer pane ── */} -
- {!selectedLabel ? ( -
-
Select a log file to view
-
- {entries.length > 0 - ? `${entries.length} file${entries.length !== 1 ? 's' : ''} available` - : ''} -
-
- ) : logLoading ? ( -
- -
- ) : ( - <> -
-
- - - {selectedLabel} - -
-
- {logText && } - setSelectedLabel(null)} - size={13} - className="hidden md:inline-flex" - /> -
-
-
-              {logText}
-            
- - )} -
-
- ) -} diff --git a/app/components/dashboard/logs-section.tsx b/app/components/dashboard/logs-section.tsx deleted file mode 100644 index b13d38d..0000000 --- a/app/components/dashboard/logs-section.tsx +++ /dev/null @@ -1,206 +0,0 @@ -import { useCallback, useEffect, useMemo, useState } from 'react' -import { useSearchParams } from 'react-router' - -import { cn } from '~/lib/cn' -import { findBaselineScoringLogLabel, findContainerLogLabel } from '~/lib/eval-gates' -import { usePoll } from '~/lib/use-poll' -import { fetchContainerLogs, fetchContainerLog, type ContainerLogEntry } from '~/lib/api.client' -import { LogViewer, type LogEntry } from './log-viewer' - -function logLabelUidDigits(label: string): string | null { - const m = /^uid(\d+)_/i.exec(label) - return m ? m[1] : null -} - -function logLabelBlockNum(label: string): number | null { - const parts = label.split('_') - if (parts.length < 2) return null - const last = parts[parts.length - 1] - if (!/^\d+$/.test(last)) return null - return parseInt(last, 10) -} - -function uidFilterMatches(label: string, query: string): boolean { - const q = query.trim() - if (!q) return true - const id = logLabelUidDigits(label) - if (id === null) return false - if (/^\d+$/.test(q)) return id === q || id.startsWith(q) - return id.includes(q) -} - -function hotkeyFilterMatches(label: string, query: string): boolean { - const q = query.trim().toLowerCase() - if (!q) return true - return label.toLowerCase().includes(q) -} - -type LogsSortOption = 'size_desc' | 'size_asc' | 'uid_asc' | 'uid_desc' | 'time_desc' | 'time_asc' - -const SORT_OPTIONS: { value: LogsSortOption; label: string }[] = [ - { value: 'size_desc', label: 'Size ↓' }, - { value: 'size_asc', label: 'Size ↑' }, - { value: 'uid_asc', label: 'UID ↑' }, - { value: 'uid_desc', label: 'UID ↓' }, - { value: 'time_desc', label: 'Time ↓' }, - { value: 'time_asc', label: 'Time ↑' }, -] - -function sortLogs(entries: ContainerLogEntry[], sortBy: LogsSortOption): ContainerLogEntry[] { - const out = [...entries] - out.sort((a, b) => { - if (sortBy === 'size_desc') return b.size_bytes - a.size_bytes - if (sortBy === 'size_asc') return a.size_bytes - b.size_bytes - if (sortBy === 'time_desc' || sortBy === 'time_asc') { - const ba = logLabelBlockNum(a.label) ?? -1 - const bb = logLabelBlockNum(b.label) ?? -1 - if (ba !== bb) return sortBy === 'time_desc' ? bb - ba : ba - bb - return a.label.localeCompare(b.label) - } - const na = logLabelUidDigits(a.label) - const nb = logLabelUidDigits(b.label) - const va = na != null ? parseInt(na, 10) : Number.POSITIVE_INFINITY - const vb = nb != null ? parseInt(nb, 10) : Number.POSITIVE_INFINITY - if (sortBy === 'uid_asc') { - if (va !== vb) return va - vb - return a.label.localeCompare(b.label) - } - if (va !== vb) return vb - va - return a.label.localeCompare(b.label) - }) - return out -} - -const compactInputCls = - 'w-full min-w-0 rounded border border-border/50 bg-surface/40 px-2 py-1 font-mono text-xs text-primary outline-none placeholder:text-secondary/30 focus:border-accent/40' - -export function LogsSection() { - const [searchParams] = useSearchParams() - const logs = usePoll(fetchContainerLogs, 60_000) - const [uidFilter, setUidFilter] = useState('') - const [hotkeyFilter, setHotkeyFilter] = useState('') - const [sortBy, setSortBy] = useState('size_desc') - const [excludeBaseline, setExcludeBaseline] = useState(false) - - useEffect(() => { - const uid = searchParams.get('uid') - if (uid) setUidFilter(uid) - }, [searchParams]) - - const list = logs.data?.logs ?? [] - - const initialSelectedLabel = useMemo(() => { - const labels = list.map((l) => l.label) - const labelParam = searchParams.get('label') - if (labelParam && labels.includes(labelParam)) { - return labelParam - } - const uidParam = searchParams.get('uid') - const blockParam = searchParams.get('block') - if (uidParam && blockParam) { - const uid = parseInt(uidParam, 10) - const block = parseInt(blockParam, 10) - if (Number.isFinite(uid) && Number.isFinite(block)) { - const miner = findContainerLogLabel(labels, uid, block) - if (miner) return miner - } - } - if (blockParam && !uidParam) { - const block = parseInt(blockParam, 10) - if (Number.isFinite(block)) { - const scoring = findBaselineScoringLogLabel(labels, block) - if (scoring) return scoring - } - } - return null - }, [searchParams, list]) - - const processedLogs: LogEntry[] = useMemo(() => { - const filtered = list.filter( - (log) => - uidFilterMatches(log.label, uidFilter) && - hotkeyFilterMatches(log.label, hotkeyFilter) && - (!excludeBaseline || !log.label.toLowerCase().startsWith('baseline')), - ) - return sortLogs(filtered, sortBy) - }, [list, uidFilter, hotkeyFilter, sortBy, excludeBaseline]) - - const fetchLog = useCallback((label: string) => fetchContainerLog(label), []) - - const emptyMessage = - list.length > 0 && processedLogs.length === 0 - ? 'No logs match your filters' - : 'No logs available' - - const controls = ( -
-
- Filter & sort -
- setUidFilter(e.target.value)} - className={compactInputCls} - aria-label="Filter logs by miner UID" - /> - setHotkeyFilter(e.target.value)} - className={compactInputCls} - aria-label="Filter logs by hotkey substring" - /> - - -
- ) - - const footer = - !logs.loading && list.length > 0 ? ( -

- {processedLogs.length} / {list.length} log{list.length === 1 ? '' : 's'} -

- ) : undefined - - return ( -
- -
- ) -} diff --git a/app/components/dashboard/pulse-section.tsx b/app/components/dashboard/pulse-section.tsx deleted file mode 100644 index 996f47e..0000000 --- a/app/components/dashboard/pulse-section.tsx +++ /dev/null @@ -1,1120 +0,0 @@ -import { useEffect, useMemo, useState, type ReactNode } from 'react' -import { - Users, - Search, - Cpu, - CreditCard, - Server, - Wrench, - CheckCircle, - Play, - FileText, - Activity, - Check, - Zap, - Award, - Clock, - ChevronDown, - ChevronRight, - Crown, - Medal, - Trophy, -} from 'lucide-react' -import { cn } from '~/lib/cn' -import { usePoll } from '~/lib/use-poll' -import { - fetchStatus, - fetchEvalProgress, - fetchLeader, - fetchRounds, - type EvalProgressResponse, - type EvalProgressChallenger, - type EvalProgressIncumbent, - type EvalProgressStep, - type LeaderRecord, - type Round, - type StatusResponse, -} from '~/lib/api.client' -import { - fmtScore, - relativeTimeAgo, - truncHotkey, - truncImage, - RankCard, - Skeleton, - StatusDot, -} from './shared' -import { CopyButton } from '~/components/ui/copy-button' -import { LinkButton } from '~/components/ui/link-button' -import { Badge } from '~/components/ui/badge' -/** Splits "38 min ago" → { n: "38", unit: "min ago" } for two-line metric display. */ -function splitRelativeTime(s: string): { n: string; unit: string } | null { - if (s === '-') return null - // "Less than 1 min ago" - if (s.startsWith('Less')) return { n: '<1', unit: 'min ago' } - const m = /^(\d+)\s+(.+)$/.exec(s) - if (!m) return null - return { n: m[1], unit: m[2] } -} - -function CompactLastEval({ ts }: { ts: number | null | undefined }) { - const split = splitRelativeTime(relativeTimeAgo(ts)) - if (!split) return — - return ( - - {split.n} - - {split.unit} - - - ) -} - -function SecondaryStat({ - label, - value, - loading, -}: { - label: string - value: ReactNode - loading: boolean -}) { - return ( -
-
- {label} -
- {loading ? ( - - ) : ( -
- {value} -
- )} -
- ) -} - -/** - * Operational counts, intentionally quieter than the standings above - * (Law of Common Region: one grouped, lower-emphasis strip rather than a - * row of equal-weight hero cards). - */ -function NetworkStatsStrip({ - status, - loading, -}: { - status: StatusResponse | null | undefined - loading: boolean -}) { - const hasBlocks = - status != null && (status.last_scan_block != null || status.last_weights_set_block != null) - - return ( -
-
- - - - } - loading={loading} - /> -
- - {hasBlocks && ( -
- {status!.last_scan_block != null && ( - Last scan: block #{status!.last_scan_block} - )} - {status!.last_weights_set_block != null && ( - Weights set: block #{status!.last_weights_set_block} - )} -
- )} -
- ) -} - -type RoundLeader = { - uid: number - score: number -} - -type RoundChartPoint = { - roundIndex: number - block: number - evaluatedAt: number | null - leader: RoundLeader | null - nChallengers: number - nScored: number - nDisqualified: number -} - -type ChartDot = { - point: RoundChartPoint - uid: number - score: number - isRoundLeader: boolean -} - -function roundLeader(challengers: Round['challengers']): RoundLeader | null { - const scored = challengers.filter((c) => !c.disqualified && c.score != null) - if (scored.length === 0) return null - - const roundMax = Math.max(...scored.map((c) => c.score!)) - if (roundMax <= 0) return null - - const winner = scored.reduce((a, b) => (a.score! >= b.score! ? a : b)) - return { uid: winner.uid, score: roundMax } -} - -function buildRoundChart(rounds: Round[] | undefined): RoundChartPoint[] { - const sorted = [...(rounds ?? [])].sort((a, b) => a.evaluation_block - b.evaluation_block) - return sorted.map((round, i) => { - const challengers = round.challengers - return { - roundIndex: i + 1, - block: round.evaluation_block, - evaluatedAt: round.evaluated_at, - leader: roundLeader(challengers), - nChallengers: round.n_challengers, - nScored: challengers.filter((c) => !c.disqualified && c.score != null).length, - nDisqualified: challengers.filter((c) => c.disqualified).length, - } - }) -} - -function buildChartDots(points: RoundChartPoint[], rounds: Round[] | undefined): ChartDot[] { - const sorted = [...(rounds ?? [])].sort((a, b) => a.evaluation_block - b.evaluation_block) - const dots: ChartDot[] = [] - - for (let i = 0; i < sorted.length; i++) { - const round = sorted[i] - const point = points[i] - if (!point) continue - - const scored = round.challengers.filter((c) => !c.disqualified && c.score != null) - if (scored.length === 0) continue - - for (const c of scored) { - dots.push({ - point, - uid: c.uid, - score: c.score!, - isRoundLeader: point.leader != null && c.uid === point.leader.uid, - }) - } - } - - return dots -} - -export function PulseSection() { - const status = usePoll(fetchStatus, 30_000) - const progress = usePoll(fetchEvalProgress, 10_000) - const leader = usePoll(fetchLeader, 30_000) - const rounds = usePoll(fetchRounds, 30_000) - const [, setTick] = useState(0) - - // Derive API liveness from the two polls we already run. - // - null = still loading (no answer yet from either) - // - true = at least one returned data, or both are only rate-limited (API is up) - // - false = both failed with a hard error (network down or server truly unreachable) - const alive: boolean | null = (() => { - const bothPending = status.loading && progress.loading - if (bothPending) return null - if (status.data != null || progress.data != null) return true - // If every failure is just a rate-limit the API is reachable. - const allRateLimited = - (status.error == null || status.rateLimited) && - (progress.error == null || progress.rateLimited) - if (allRateLimited) return true - return false - })() - - const s = status.data - - useEffect(() => { - if (s?.last_eval_ts == null || s.last_eval_ts <= 0) return - const id = setInterval(() => setTick((n) => n + 1), 1000) - return () => clearInterval(id) - }, [s?.last_eval_ts]) - - return ( -
-
- -

- {alive === null - ? 'Connecting...' - : alive - ? 'API Online' - : status.rateLimited || progress.rateLimited - ? 'API Busy' - : 'API Unreachable'} -

-
- -
- } - iconGlow - record={leader.data?.leader} - loading={leader.loading} - error={!!leader.error} - emptyText="No leader yet" - accent - /> - } - record={leader.data?.runner_up} - loading={leader.loading} - error={!!leader.error} - emptyText="No runner-up yet" - /> -
- - - - - - - - {progress.data && - (progress.data.status === 'running' || progress.data.status === 'complete') && ( - - )} -
- ) -} - -// ── Score history chart ────────────────────────────────── - -const CHART_LEADER_COLOR = '#9EFFE3' -const CHART_DOT_COLOR = 'rgba(255, 255, 255, 0.28)' -const CHART_WIDTH = 1000 -const CHART_HEIGHT = 300 -const CHART_MARGIN = { top: 16, right: 12, bottom: 32, left: 48 } -/** Extra inset so the tooltip clears y-axis tick labels (anchored end at margin.left - 8). */ -const TOOLTIP_LEFT_INSET = CHART_MARGIN.left + 40 -/** Min horizontal space per round when the chart scrolls on narrow screens. */ -const MOBILE_PX_PER_ROUND = 26 -const MOBILE_CHART_HEIGHT = 220 - -function useMaxSm(): boolean { - const [maxSm, setMaxSm] = useState(false) - useEffect(() => { - const mq = window.matchMedia('(max-width: 639px)') - const onChange = () => setMaxSm(mq.matches) - onChange() - mq.addEventListener('change', onChange) - return () => mq.removeEventListener('change', onChange) - }, []) - return maxSm -} - -const Y_TICK_STEP = 0.05 - -function yTicksFixedStep(yMin: number, yMax: number, step = Y_TICK_STEP): number[] { - const max = Math.max(step, Math.ceil(yMax / step) * step) - const ticks: number[] = [] - for (let v = yMin; v <= max + 1e-9; v += step) { - ticks.push(Number(v.toFixed(2))) - } - return ticks -} - -function roundXTicks(n: number, maxLabels = 6): number[] { - if (n <= 0) return [] - if (n <= maxLabels) return Array.from({ length: n }, (_, i) => i + 1) - - const count = maxLabels - const seen = new Set() - const ticks: number[] = [] - for (let i = 0; i < count; i++) { - const round = i === 0 ? 1 : i === count - 1 ? n : Math.round(1 + (i * (n - 1)) / (count - 1)) - if (!seen.has(round)) { - seen.add(round) - ticks.push(round) - } - } - return ticks -} - -function ScoreHistoryChart({ - loading, - error, - leader, - rounds, -}: { - loading: boolean - error: boolean - leader: LeaderRecord | null | undefined - rounds: Round[] | undefined -}) { - const points = useMemo(() => buildRoundChart(rounds), [rounds]) - const chartDots = useMemo(() => buildChartDots(points, rounds), [points, rounds]) - const hasEnough = points.length >= 1 - const isNarrow = useMaxSm() - const [hovered, setHovered] = useState(null) - - const layout = useMemo(() => { - if (!hasEnough || chartDots.length === 0) return null - - const leaderScore = leader?.score - const scores = [ - ...chartDots.map((d) => d.score), - ...(leaderScore != null && leaderScore > 0 ? [leaderScore] : []), - ] - - const yMin = 0 - const yMax = Math.max( - Y_TICK_STEP, - Math.ceil((Math.max(...scores) * 1.1) / Y_TICK_STEP) * Y_TICK_STEP, - ) - const nRounds = points.length - const scrollMinWidth = isNarrow && nRounds > 12 ? nRounds * MOBILE_PX_PER_ROUND : undefined - const svgWidth = - scrollMinWidth != null ? scrollMinWidth * (CHART_HEIGHT / MOBILE_CHART_HEIGHT) : CHART_WIDTH - const plotW = svgWidth - CHART_MARGIN.left - CHART_MARGIN.right - const plotH = CHART_HEIGHT - CHART_MARGIN.top - CHART_MARGIN.bottom - - const xAt = (roundIndex: number) => - CHART_MARGIN.left + (plotW * (roundIndex - 1)) / Math.max(nRounds - 1, 1) - const yAt = (score: number) => - CHART_MARGIN.top + plotH - (plotH * (score - yMin)) / Math.max(yMax - yMin, 1e-6) - - const xLabelMax = nRounds > 18 ? 4 : nRounds > 10 ? 5 : 6 - - return { - nRounds, - plotW, - plotH, - xAt, - yAt, - yTicks: yTicksFixedStep(yMin, yMax), - xTicks: roundXTicks(nRounds, xLabelMax), - scrollMinWidth, - svgWidth, - } - }, [points, chartDots, hasEnough, leader?.score, isNarrow]) - - const leaderLinePath = useMemo(() => { - if (!layout) return null - const leaders = points.filter((p) => p.leader != null) - if (leaders.length < 2) return null - - const parts: string[] = [] - let prev: RoundChartPoint | null = null - - for (const p of leaders) { - const x = layout.xAt(p.roundIndex) - const y = layout.yAt(p.leader!.score) - const adjacent = prev != null && p.roundIndex - prev.roundIndex === 1 - parts.push(adjacent ? `L ${x} ${y}` : `M ${x} ${y}`) - prev = p - } - - return parts.some((s) => s.startsWith('L')) ? parts.join(' ') : null - }, [layout, points]) - - function viewBoxXFromClient(svg: SVGSVGElement, clientX: number, clientY: number): number | null { - const ctm = svg.getScreenCTM() - if (!ctm) return null - const pt = svg.createSVGPoint() - pt.x = clientX - pt.y = clientY - return pt.matrixTransform(ctm.inverse()).x - } - - function selectRoundAt(svg: SVGSVGElement, clientX: number, clientY: number) { - if (!layout || points.length === 0) return - const x = viewBoxXFromClient(svg, clientX, clientY) - if (x == null) return - - let nearest = points[0] - let nearestDist = Infinity - for (const pt of points) { - const dist = Math.abs(layout.xAt(pt.roundIndex) - x) - if (dist < nearestDist) { - nearestDist = dist - nearest = pt - } - } - setHovered(nearest) - } - - const mobileScroll = - isNarrow && layout?.scrollMinWidth != null ? layout.scrollMinWidth : undefined - - return ( -
-
- Score Progress -
- - {loading ? ( - - ) : error ? ( -

Could not load score history

- ) : !hasEnough || chartDots.length === 0 ? ( -

Not enough history yet

- ) : ( - <> -
-
- {layout && ( - selectRoundAt(e.currentTarget, e.clientX, e.clientY)} - onMouseLeave={() => setHovered(null)} - onTouchStart={(e) => { - const touch = e.changedTouches[0] - if (touch) selectRoundAt(e.currentTarget, touch.clientX, touch.clientY) - }} - > - {layout.yTicks.map((tick) => { - const y = layout.yAt(tick) - return ( - - - - {tick.toFixed(2)} - - - ) - })} - - - - {layout.xTicks.map((tick) => ( - - R{tick} - - ))} - - {leaderLinePath && ( - - )} - - {chartDots.map(({ point: pt, uid, score, isRoundLeader }) => { - const active = hovered?.roundIndex === pt.roundIndex - return ( - - ) - })} - - {points.map((pt) => { - const slotW = layout.plotW / Math.max(layout.nRounds, 1) - const cx = layout.xAt(pt.roundIndex) - return ( - { - e.stopPropagation() - setHovered(pt) - }} - onClick={() => setHovered(pt)} - /> - ) - })} - - )} - - {hovered && } -
-
- - {hovered && } - - )} -
- ) -} - -function roundDetailMeta(point: RoundChartPoint): string { - return [ - `${point.nChallengers} challenger${point.nChallengers !== 1 ? 's' : ''}`, - point.nScored > 0 ? `${point.nScored} scored` : null, - point.nDisqualified > 0 ? `${point.nDisqualified} DQ` : null, - ] - .filter(Boolean) - .join(' · ') -} - -function RoundDetailBar({ point, className }: { point: RoundChartPoint; className?: string }) { - const meta = roundDetailMeta(point) - const when = - point.evaluatedAt != null && point.evaluatedAt > 0 ? relativeTimeAgo(point.evaluatedAt) : null - - return ( -
-
- Round {point.roundIndex} · Block #{point.block} - {when != null && · {when}} -
-
- {point.leader != null && ( - - Leader {fmtScore(point.leader.score)} - - )} - {meta && {meta}} -
-
- ) -} - -function RoundTooltip({ point }: { point: RoundChartPoint }) { - const plotTopPct = (CHART_MARGIN.top / CHART_HEIGHT) * 100 - const tooltipLeftPct = (TOOLTIP_LEFT_INSET / CHART_WIDTH) * 100 - const plotWidthPct = ((CHART_WIDTH - TOOLTIP_LEFT_INSET - CHART_MARGIN.right) / CHART_WIDTH) * 100 - const meta = roundDetailMeta(point) - - return ( -
-
- Round {point.roundIndex} · Block #{point.block} -
- {point.evaluatedAt != null && point.evaluatedAt > 0 && ( -
{relativeTimeAgo(point.evaluatedAt)}
- )} - {point.leader != null && ( -
- Leader {fmtScore(point.leader.score)} -
- )} - {meta &&
{meta}
} -
- ) -} - -// ── Phase config ───────────────────────────────────────── - -const PHASE_META: Record = { - challengers_found: { - label: 'Challengers identified', - icon: , - }, - gpu_searching: { label: 'Searching for GPU', icon: }, - gpu_match_found: { label: 'GPU matched', icon: }, - gpu_renting: { label: 'Renting pod', icon: }, - gpu_ready: { label: 'Pod ready', icon: }, - gpu_setup: { label: 'Setting up pod', icon: }, - gpu_setup_complete: { - label: 'Pod setup complete', - icon: , - }, - gpu_eval_started: { label: 'GPU eval running', icon: }, - prompts_generated: { label: 'Prompts generated', icon: }, - baseline_running: { label: 'Running baseline', icon: }, - baseline_complete: { label: 'Baseline complete', icon: }, - challenger_eval: { label: 'Evaluating challengers', icon: }, - eval_complete: { label: 'Eval complete', icon: }, - leader_running: { label: 'Leader running', icon: }, - runner_up_running: { label: 'Runner-up running', icon: }, - teacher_forcing_running: { - label: 'Correctness scoring', - icon: , - }, - teacher_forcing_complete: { - label: 'Correctness complete', - icon: , - }, -} - -function phaseLabel(phase: string | undefined, detail?: string | null): string { - if (!phase) return 'Starting...' - const meta = PHASE_META[phase] - const base = meta?.label ?? phase.replace(/_/g, ' ') - if (phase === 'gpu_setup' && detail) return `Setup: ${detail}` - return base -} - -function phaseIcon(phase: string | undefined): ReactNode { - if (!phase) return · - return PHASE_META[phase]?.icon ?? · -} - -const CHALLENGER_STYLES: Record = - { - pending: { dot: 'bg-secondary/30', bg: '', text: 'text-secondary/50', label: 'Queued' }, - pulling: { - dot: 'bg-warning', - bg: 'bg-warning/[0.04]', - text: 'text-warning', - label: 'Pulling', - }, - started: { - dot: 'bg-warning', - bg: 'bg-warning/[0.04]', - text: 'text-warning', - label: 'Starting', - }, - evaluating: { - dot: 'bg-accent', - bg: 'bg-accent/[0.04]', - text: 'text-accent', - label: 'Evaluating', - }, - awaiting_correctness: { - dot: 'bg-secondary/50', - bg: 'bg-white/[0.02]', - text: 'text-secondary/70', - label: 'Awaiting correctness', - }, - scored: { - dot: 'bg-success', - bg: 'bg-success/[0.04]', - text: 'text-success', - label: 'Scored', - }, - dq: { dot: 'bg-error', bg: 'bg-error/[0.04]', text: 'text-error', label: 'DQ' }, - skipped: { dot: 'bg-secondary/40', bg: '', text: 'text-secondary/40', label: 'Skipped' }, - } - -// ── Elapsed time (ticks every second) ──────────────────── - -function ElapsedTime({ startedAt }: { startedAt: number }) { - const [, setTick] = useState(0) - useEffect(() => { - const id = setInterval(() => setTick((n) => n + 1), 1000) - return () => clearInterval(id) - }, []) - - const sec = Math.max(0, Math.floor(Date.now() / 1000 - startedAt)) - const m = Math.floor(sec / 60) - const s = sec % 60 - return ( - - {m}:{s.toString().padStart(2, '0')} - - ) -} - -// ── Eval schedule notice ───────────────────────────────── - -function EvalScheduleBanner() { - return ( -
- -

- Evaluations run daily between 09:00 and 21:00 UTC. -

-
- ) -} - -// ── Main banner ────────────────────────────────────────── - -function EvalProgressBanner({ progress }: { progress: EvalProgressResponse }) { - const challengers = progress.challengers ?? [] - const gpu = progress.gpu - const stale = progress.possibly_stale - const steps = progress.steps ?? [] - const isComplete = progress.status === 'complete' - - const borderColor = stale - ? 'border-warning/25' - : isComplete - ? 'border-success/25' - : 'border-accent/20' - const accentColor = stale ? 'text-warning' : isComplete ? 'text-success' : 'text-accent' - const pingColor = stale ? 'bg-warning/50' : 'bg-accent/50' - const dotColor = stale ? 'bg-warning' : isComplete ? 'bg-success' : 'bg-accent' - - function incumbentStatus(idx: number): EvalProgressChallenger['status'] { - const cur = progress.current_idx - if (cur == null) return 'pending' - if (cur > idx) return 'scored' - if (cur === idx) return 'evaluating' - return 'pending' - } - - function incumbentRow(incumbent: EvalProgressIncumbent, idx: number): EvalProgressChallenger { - return { - idx, - uid: incumbent.uid, - hotkey: incumbent.hotkey, - image: incumbent.image, - status: incumbent.status ?? incumbentStatus(idx), - score: incumbent.score, - dq_reason: incumbent.dq_reason, - } - } - - // Build ordered list matching GPU eval order: leader → runner_up → challengers - const incumbents: EvalProgressChallenger[] = [ - ...(progress.leader ? [incumbentRow(progress.leader, -2)] : []), - ...(progress.runner_up ? [incumbentRow(progress.runner_up, -1)] : []), - ] - const allRows = [...incumbents, ...challengers] - - return ( -
- {/* Header */} -
- - {!isComplete && ( - - )} - - -
- - {stale - ? 'Signal stale' - : isComplete - ? 'Round complete' - : phaseLabel(progress.phase, progress.detail)} - - {progress.started_at != null && ( - - - - - )} -
- {progress.round_block != null && ( - - #{progress.round_block} - - - )} -
- - {/* GPU + meta pills */} - {(gpu || challengers.length > 0) && ( -
- {gpu?.provider && ( - - {gpu.provider} - {gpu.gpu_type ? ` ${gpu.num_gpus ?? ''}x ${gpu.gpu_type}` : ''} - - )} - {gpu?.pod_id && ( - - Pod ID:{' '} - {gpu.pod_id.length > 8 - ? `${gpu.pod_id.slice(0, 6)}...${gpu.pod_id.slice(-4)}` - : gpu.pod_id} - - )} - {challengers.length > 0 && ( - - {challengers.length} challenger{challengers.length !== 1 ? 's' : ''} - - )} -
- )} - - {/* Eval list: leader → runner_up → challengers (GPU eval order) */} - {allRows.length > 0 && ( -
- {allRows.map((c, i) => ( - - ))} -
- )} - - {/* Step timeline */} - {steps.length > 0 && } - - {isComplete && ( - - )} -
- ) -} - -// ── Challenger row ─────────────────────────────────────── - -function ChallengerRow({ - challenger: c, - active, - last, -}: { - challenger: EvalProgressChallenger - active: boolean - last: boolean -}) { - const style = CHALLENGER_STYLES[c.status] ?? CHALLENGER_STYLES.pending - const isLive = c.status === 'pulling' || c.status === 'started' || c.status === 'evaluating' - const isLeader = c.idx === -2 - const isRunnerUp = c.idx === -1 - const hasScore = c.score != null && c.status === 'scored' - const hasDq = Boolean(c.dq_reason) - const hasOutcome = hasScore || hasDq - - return ( -
- {/* Status dot */} - - {isLive && active && ( - - )} - - - - {/* UID + role badge (fixed width so hotkey column lines up) */} -
- - UID {c.uid} - - - {isLeader && ( - - )} - {isRunnerUp && ( - - )} - -
- - {/* Status */} - - - {c.status === 'awaiting_correctness' ? 'Awaiting' : style.label} - - {style.label} - - - {/* Score / DQ reason */} - {hasOutcome && ( -
- {hasScore && ( - - {fmtScore(c.score)} - - )} - {hasDq && ( - - {c.dq_reason} - - )} -
- )} - - {/* Hotkey + image stack */} -
-
-
- - {truncHotkey(c.hotkey)} - - -
- {c.image && ( -
- - {truncImage(c.image)} - - - -
- )} -
-
-
- ) -} - -// ── Step timeline ──────────────────────────────────────── - -function StepTimeline({ steps }: { steps: EvalProgressStep[] }) { - const [expanded, setExpanded] = useState(false) - const visible = expanded ? steps : steps.slice(-5) - const hidden = steps.length - visible.length - - return ( -
- -
- {visible.map((step) => ( - - ))} -
-
- ) -} - -function TimelineEntry({ step }: { step: EvalProgressStep }) { - const d = new Date(step.ts * 1000) - const time = d.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit', second: '2-digit' }) - - const extra: string[] = [] - if (step.step && typeof step.step === 'string') extra.push(step.step) - if (typeof step.n === 'number') extra.push(`n=${step.n}`) - if (typeof step.elapsed_s === 'number') extra.push(`${step.elapsed_s}s`) - if (typeof step.timeout_min === 'number') extra.push(`timeout ${step.timeout_min}m`) - if (typeof step.uid === 'number') extra.push(`UID ${step.uid}`) - if (step.status && typeof step.status === 'string' && step.phase === 'challenger_eval') - extra.push(step.status as string) - - return ( -
- -
- - {time} - - - {phaseIcon(step.phase)} - {phaseLabel(step.phase, typeof step.step === 'string' ? step.step : undefined)} - - {extra.length > 0 && ( - - {extra.join(' · ')} - - )} -
-
- ) -} diff --git a/app/components/dashboard/rounds-section.tsx b/app/components/dashboard/rounds-section.tsx deleted file mode 100644 index f6fb6af..0000000 --- a/app/components/dashboard/rounds-section.tsx +++ /dev/null @@ -1,275 +0,0 @@ -import { useMemo, useState } from 'react' -import { ChevronDown } from 'lucide-react' - -import { cn } from '~/lib/cn' -import { - findEvalRunLogLabels, - findContainerLogLabel, - containerLogHref, - validatorLogHref, -} from '~/lib/eval-gates' -import { usePoll } from '~/lib/use-poll' -import { - fetchRounds, - fetchContainerLogs, - fetchValidatorLogs, - type Round, - type RoundChallenger, -} from '~/lib/api.client' -import { - fmtScore, - fmtImprovement, - fmtPct, - truncHotkey, - relativeTimeAgo, - GlassCard, - Skeleton, - StatusPill, - ImageTag, -} from './shared' - -export function RoundsSection() { - const rounds = usePoll(fetchRounds, 30_000) - const containerLogs = usePoll(fetchContainerLogs, 60_000) - const validatorLogs = usePoll(fetchValidatorLogs, 60_000) - const [expandedBlock, setExpandedBlock] = useState(null) - const [visibleCount, setVisibleCount] = useState(10) - - const containerLabels = containerLogs.data?.logs.map((l) => l.label) ?? [] - const validatorLabels = validatorLogs.data?.logs.map((l) => l.label) ?? [] - - const list = rounds.data?.rounds ?? [] - const visible = list.slice(0, visibleCount) - - return ( -
- {rounds.loading ? ( -
- {[...Array(4)].map((_, i) => ( - - ))} -
- ) : rounds.error ? ( - - Could not load data - - ) : list.length === 0 ? ( - - No evaluation rounds yet - - ) : ( - <> -
- {visible.map((round) => ( - - setExpandedBlock( - expandedBlock === round.evaluation_block ? null : round.evaluation_block, - ) - } - /> - ))} -
- {visibleCount < list.length && ( - - )} - - )} -
- ) -} - -function RoundCard({ - round, - containerLabels, - validatorLabels, - expanded, - onToggle, -}: { - round: Round - containerLabels: string[] - validatorLabels: string[] - expanded: boolean - onToggle: () => void -}) { - const logLinks = useMemo( - () => findEvalRunLogLabels(containerLabels, validatorLabels, round.evaluation_block), - [containerLabels, validatorLabels, round.evaluation_block], - ) - - return ( - -
e.key === 'Enter' && onToggle()} - role="button" - tabIndex={0} - > -
- - {relativeTimeAgo(round.evaluated_at)} - - - Block #{round.evaluation_block} - -
-
- - {round.n_challengers} challenger{round.n_challengers !== 1 ? 's' : ''} - - -
-
- - {expanded && ( -
-
- {round.challengers.map((c) => ( - - ))} -
- - {round.challengers - .filter((c) => c.disqualified && c.disqualify_reason) - .map((c) => ( -
- UID {c.uid}: {c.disqualify_reason} -
- ))} - - {(logLinks.gpuEval || - logLinks.cpuLog || - logLinks.baselineScoring || - logLinks.baselineGeneration) && ( -
- {logLinks.gpuEval && ( - - GPU log - - )} - {logLinks.cpuLog && ( - - CPU log - - )} - {logLinks.baselineGeneration && ( - - Baseline log - - )} - {logLinks.baselineScoring && ( - - Scoring log - - )} -
- )} -
- )} -
- ) -} - -function ChallengerRow({ - challenger: c, - block, - containerLabels, -}: { - challenger: RoundChallenger - block: number - containerLabels: string[] -}) { - const logLabel = findContainerLogLabel(containerLabels, c.uid, block) - - return ( -
-
- - {c.uid} - - -
- - {truncHotkey(c.hotkey)} - - - - {fmtScore(c.score)} - - - {fmtImprovement(c.speed_improvement)} - - - {fmtPct(c.token_match_rate)} - - - {logLabel ? ( - e.stopPropagation()} - className="text-secondary/50 hover:text-accent font-mono text-xs whitespace-nowrap no-underline transition-colors" - > - log ↗ - - ) : null} - -
- ) -} diff --git a/app/components/dashboard/shared.tsx b/app/components/dashboard/shared.tsx deleted file mode 100644 index 1ecc751..0000000 --- a/app/components/dashboard/shared.tsx +++ /dev/null @@ -1,284 +0,0 @@ -import type { ReactNode } from 'react' -import { cn } from '~/lib/cn' -import { LinkButton } from '~/components/ui/link-button' -import { CopyButton } from '~/components/ui/copy-button' -import type { LeaderRecord } from '~/lib/api.client' - -export const DASHBOARD_TABS = [ - { slug: 'pulse', label: 'Pulse' }, - { slug: 'leader', label: 'Leader' }, - { slug: 'evaluations', label: 'Evaluations' }, - { slug: 'rounds', label: 'Rounds' }, - { slug: 'logs', label: 'Miner Logs' }, - { slug: 'validator-logs', label: 'Validator Logs' }, -] as const - -// ── Formatters ────────────────────────────────────────── - -export function relativeTimeAgo(ts: number | null | undefined): string { - if (ts == null || ts <= 0) return '-' - let sec = Math.floor(Date.now() / 1000 - ts) - if (sec < 0) sec = 0 - if (sec < 60) return 'Less than 1 min ago' - const min = Math.floor(sec / 60) - if (min < 60) return `${min} min ago` - const hr = Math.floor(min / 60) - if (hr < 24) return `${hr} hr ago` - const days = Math.floor(hr / 24) - return `${days} ${days === 1 ? 'day' : 'days'} ago` -} - -export function truncImage(img: string | null | undefined): string { - if (!img) return '' - return img.replace(/^docker\.io\//, '') -} - -export function truncHotkey(hk: string | undefined | null): string { - if (!hk) return '-' - if (hk.length <= 12) return hk - return `${hk.slice(0, 6)}...${hk.slice(-4)}` -} - -export function fmtScore(n: number | null | undefined): string { - if (n == null) return '-' - return n.toFixed(4) -} - -export function fmtPct(n: number | null | undefined): string { - if (n == null) return '-' - return `${(n * 100).toFixed(1)}%` -} - -export function fmtImprovement(n: number | null | undefined): string { - if (n == null) return '-' - const sign = n >= 0 ? '+' : '' - return `${sign}${(n * 100).toFixed(1)}%` -} - -export function fmtBytes(bytes: number): string { - if (bytes < 1024) return `${bytes} B` - if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB` - return `${(bytes / (1024 * 1024)).toFixed(1)} MB` -} - -// ── UI primitives ─────────────────────────────────────── - -export function Skeleton({ className }: { className?: string }) { - return
-} - -export function GlassCard({ - children, - className, - onClick, -}: { - children: React.ReactNode - className?: string - onClick?: () => void -}) { - return ( -
e.key === 'Enter' && onClick() : undefined} - role={onClick ? 'button' : undefined} - tabIndex={onClick ? 0 : undefined} - > - {children} -
- ) -} - -export function StatusDot({ alive }: { alive: boolean | null }) { - if (alive === null) return - return ( - - ) -} - -export function StatusPill({ active, label }: { active: boolean; label: string }) { - return ( - - - {label} - - ) -} - -export function MetricCard({ - label, - value, - accent = false, - loading = false, - valueClassName, -}: { - label: string - value: React.ReactNode - accent?: boolean - loading?: boolean - valueClassName?: string -}) { - return ( - -
- {label} -
- {loading ? ( - - ) : ( -
- {value} -
- )} -
- ) -} - -export function ImageTag({ - image, - className, -}: { - image: string | null | undefined - className?: string -}) { - const clean = truncImage(image) - if (!clean) return - - return ( -
- {clean} - -
- ) -} - -export const inputCls = - 'mt-1 w-full min-w-0 rounded-md border border-border/60 bg-surface/40 px-2.5 py-2 font-mono text-sm text-primary outline-none placeholder:text-secondary/35 focus:border-accent/40' - -export const labelCls = - 'flex min-w-0 flex-[1_1_7rem] flex-col font-mono text-xs font-semibold tracking-[0.12em] uppercase text-secondary/60' - -export function MiniStat({ - label, - value, - accent = false, -}: { - label: string - value: string - accent?: boolean -}) { - return ( -
-
{label}
-
- {value} -
-
- ) -} - -export function RankCard({ - title, - icon, - iconGlow, - record, - loading, - error, - emptyText, - accent, -}: { - title: string - icon: ReactNode - iconGlow?: boolean - record: LeaderRecord | null | undefined - loading: boolean - error: boolean - emptyText: string - accent?: boolean -}) { - const image = truncImage(record?.image) - - return ( - -
- - {icon} - -
- {title} -
-
- - {loading && !record ? ( -
- - -
- ) : !record ? ( -

- {error ? 'Could not load data' : emptyText} -

- ) : ( - <> -
- - {image || '-'} - - {image && ( - - )} -
- -
-
- UID {record.uid} - · - - {truncHotkey(record.hotkey)} - - -
-
- {relativeTimeAgo(record.evaluated_at)} - · - Block #{record.won_at_block} -
-
- - )} -
- ) -} diff --git a/app/components/dashboard/validator-logs-section.tsx b/app/components/dashboard/validator-logs-section.tsx deleted file mode 100644 index 4fc6702..0000000 --- a/app/components/dashboard/validator-logs-section.tsx +++ /dev/null @@ -1,193 +0,0 @@ -import { useCallback, useMemo, useState, type ReactNode } from 'react' -import { useSearchParams } from 'react-router' - -import { cn } from '~/lib/cn' -import { - blockFromValidatorLogLabel, - findValidatorLogForBlock, - isIdleCpuValidatorLog, -} from '~/lib/eval-gates' -import { usePoll } from '~/lib/use-poll' -import { fetchValidatorLogs, fetchValidatorLog, type ValidatorLogEntry } from '~/lib/api.client' -import { LogViewer, type LogEntry } from './log-viewer' - -function logLabelTimestamp(label: string): number | null { - const parts = label.split('_') - if (parts.length < 2) return null - const date = parts[parts.length - 2] - const time = parts[parts.length - 1] - if (!/^\d{8}$/.test(date) || !/^\d{6}$/.test(time)) return null - return parseInt(date + time, 10) -} - -type LogType = 'all' | 'cpu' | 'gpu' -type ValidatorSortOption = 'time_desc' | 'time_asc' - -const SORT_OPTIONS: { value: ValidatorSortOption; label: string }[] = [ - { value: 'time_desc', label: 'Time ↓' }, - { value: 'time_asc', label: 'Time ↑' }, -] - -function typeMatches(label: string, logType: LogType): boolean { - if (logType === 'all') return true - if (logType === 'cpu') return label.startsWith('cpu_') - return label.startsWith('gpu_') -} - -function sortLogs(entries: ValidatorLogEntry[], sortBy: ValidatorSortOption): ValidatorLogEntry[] { - const out = [...entries] - out.sort((a, b) => { - const ta = logLabelTimestamp(a.label) ?? -1 - const tb = logLabelTimestamp(b.label) ?? -1 - if (ta !== tb) return sortBy === 'time_desc' ? tb - ta : ta - tb - return a.label.localeCompare(b.label) - }) - return out -} - -function fmtLogDate(label: string): string { - const parts = label.split('_') - const date = parts[parts.length - 2] - const time = parts[parts.length - 1] - if (!date || !time || date.length !== 8 || time.length !== 6) return '' - return `${date.slice(0, 4)}-${date.slice(4, 6)}-${date.slice(6, 8)} ${time.slice(0, 2)}:${time.slice(2, 4)}:${time.slice(4, 6)}` -} - -function ValidatorLogLabel({ - entry, - block, -}: { - entry: LogEntry - block?: number | null -}): ReactNode { - const isGpu = entry.label.startsWith('gpu_') - const isIdle = isIdleCpuValidatorLog(entry.label) - const dateStr = fmtLogDate(entry.label) - return ( - - - {isGpu ? 'GPU' : 'CPU'} - - {block != null ? ( - - #{block} - - ) : isIdle ? ( - Idle - ) : ( - #— - )} - - {dateStr || entry.label} - - - ) -} - -const compactInputCls = - 'w-full min-w-0 rounded border border-border/50 bg-surface/40 px-2 py-1 font-mono text-xs text-primary outline-none placeholder:text-secondary/30 focus:border-accent/40' - -export function ValidatorLogsSection() { - const [searchParams] = useSearchParams() - const logs = usePoll(fetchValidatorLogs, 60_000) - const [logType, setLogType] = useState('all') - const [sortBy, setSortBy] = useState('time_desc') - - const list = logs.data?.logs ?? [] - - const initialSelectedLabel = useMemo(() => { - const validatorLabels = list.map((l) => l.label) - const labelParam = searchParams.get('label') - if (labelParam && validatorLabels.includes(labelParam)) { - return labelParam - } - const blockParam = searchParams.get('block') - if (blockParam) { - const block = parseInt(blockParam, 10) - if (Number.isFinite(block)) { - return findValidatorLogForBlock(validatorLabels, block, 'gpu') - } - } - return null - }, [searchParams, list]) - - const processedLogs: LogEntry[] = useMemo(() => { - const filtered = list.filter((log) => typeMatches(log.label, logType)) - return sortLogs(filtered, sortBy) - }, [list, logType, sortBy]) - - const fetchLog = useCallback((label: string) => fetchValidatorLog(label), []) - - const emptyMessage = - list.length > 0 && processedLogs.length === 0 - ? 'No logs match your filters' - : 'No logs available' - - const controls = ( -
-
- Filter & sort -
-
- {(['all', 'cpu', 'gpu'] as LogType[]).map((t) => ( - - ))} -
- -
- ) - - const footer = - !logs.loading && list.length > 0 ? ( -

- {processedLogs.length} / {list.length} log{list.length === 1 ? '' : 's'} -

- ) : undefined - - return ( -
- ( - - )} - title="Validator Logs" - emptyMessage={emptyMessage} - sidebarControls={controls} - sidebarFooter={footer} - initialSelectedLabel={initialSelectedLabel} - /> -
- ) -} diff --git a/app/components/ui/glass-card.tsx b/app/components/ui/glass-card.tsx new file mode 100644 index 0000000..2d1e085 --- /dev/null +++ b/app/components/ui/glass-card.tsx @@ -0,0 +1,27 @@ +import { cn } from '~/lib/cn' + +export function GlassCard({ + children, + className, + onClick, +}: { + children: React.ReactNode + className?: string + onClick?: () => void +}) { + return ( +
e.key === 'Enter' && onClick() : undefined} + role={onClick ? 'button' : undefined} + tabIndex={onClick ? 0 : undefined} + > + {children} +
+ ) +} diff --git a/app/lib/api.client.ts b/app/lib/api.client.ts deleted file mode 100644 index d3ec264..0000000 --- a/app/lib/api.client.ts +++ /dev/null @@ -1,245 +0,0 @@ -const API_BASE = '/proxy-api' - -export class ApiError extends Error { - readonly status: number - constructor(status: number, message: string) { - super(message) - this.name = 'ApiError' - this.status = status - } - get isRateLimit() { - return this.status === 429 - } -} - -async function get(path: string): Promise { - const res = await fetch(`${API_BASE}${path}`) - if (!res.ok) throw new ApiError(res.status, `${res.status} ${res.statusText}`) - return res.json() as Promise -} - -async function getText(path: string): Promise { - const res = await fetch(`${API_BASE}${path}`) - if (!res.ok) throw new ApiError(res.status, `${res.status} ${res.statusText}`) - return res.text() -} - -// ── Response types ────────────────────────────────────── - -export interface HealthResponse { - status: string -} - -export interface StatusResponse { - leader_uid: number | null - leader_score: number | null - leader_image: string | null - n_evaluated: number - n_active: number - n_disqualified: number - last_eval_ts: number | null - last_eval_age_min: number | null - last_scan_block: number | null - last_weights_set_block: number | null -} - -export interface LeaderRecord { - uid: number - hotkey: string - commit_block: number - image: string - digest: string - score: number - speed_improvement: number - token_match_rate: number - evaluated_at: number - evaluation_block: number - won_at_block: number -} - -export interface LeaderResponse { - leader: LeaderRecord | null - runner_up: LeaderRecord | null - message?: string -} - -export interface LeaderHistoryEntry { - ts: number - block: number - new_leader_uid: number - new_leader_hotkey: string - new_leader_score: number - new_leader_image: string - new_leader_digest: string - overtake_threshold: number - prev_leader_uid?: number - prev_leader_hotkey?: string - prev_leader_score?: number -} - -export interface LeaderHistoryResponse { - history: LeaderHistoryEntry[] - total: number -} - -export interface EvaluationRecord { - uid: number - hotkey: string - commit_block: number - image: string - digest: string - score: number - speed_improvement: number - token_match_rate: number - disqualified: boolean - disqualify_reason: string | null - evaluated_at: number - evaluation_block: number - per_prompt?: Array<{ - ttft_s: number - e2e_s: number - output_tokens: number - token_match_rate: number - baseline_e2e_s?: number - }> -} - -export interface EvaluationsResponse { - evaluations: EvaluationRecord[] - total: number -} - -export interface EvaluationsByUidResponse { - uid: number - evaluations: EvaluationRecord[] - total: number -} - -export interface EvaluationsByHotkeyResponse { - hotkey: string - evaluations: EvaluationRecord[] - total: number -} - -export interface RoundChallenger { - uid: number - hotkey: string - image: string - commit_block: number | null - score: number | null - speed_improvement: number | null - token_match_rate: number | null - disqualified: boolean - disqualify_reason: string | null -} - -export interface Round { - evaluation_block: number - evaluated_at: number | null - n_challengers: number - challengers: RoundChallenger[] -} - -export interface RoundsResponse { - rounds: Round[] - total: number -} - -export interface EvalProgressChallenger { - idx: number - uid: number - hotkey: string - image: string - status: - | 'pending' - | 'pulling' - | 'started' - | 'evaluating' - | 'awaiting_correctness' - | 'scored' - | 'dq' - | 'skipped' - score?: number - dq_reason?: string -} - -export interface EvalProgressStep { - ts: number - phase: string - [key: string]: unknown -} - -export interface EvalProgressIncumbent { - uid: number - hotkey: string - image: string - status?: EvalProgressChallenger['status'] - score?: number - dq_reason?: string -} - -export interface EvalProgressResponse { - status: 'idle' | 'running' | 'complete' - phase?: string - detail?: string - round_block?: number - current_idx?: number | null - challengers?: EvalProgressChallenger[] - leader?: EvalProgressIncumbent | null - runner_up?: EvalProgressIncumbent | null - gpu?: { - provider?: string - pod_id?: string - gpu_type?: string - num_gpus?: number - cost_per_hr?: number - } | null - steps?: EvalProgressStep[] - started_at?: number - updated_at?: number - completed_at?: number - possibly_stale?: boolean -} - -export interface ContainerLogEntry { - label: string - filename: string - size_bytes: number -} - -export interface ContainerLogsResponse { - logs: ContainerLogEntry[] - total: number -} - -export interface ValidatorLogEntry { - label: string - filename: string - size_bytes: number -} - -export interface ValidatorLogsResponse { - logs: ValidatorLogEntry[] - total: number -} - -// ── Fetchers ──────────────────────────────────────────── - -export const fetchHealth = () => get('/api/health') -export const fetchStatus = () => get('/api/status') -export const fetchLeader = () => get('/api/leader') -export const fetchLeaderHistory = () => get('/api/leader/history') -export const fetchEvaluations = (status?: 'active' | 'dq') => - get(`/api/evaluations${status ? `?status=${status}` : ''}`) -export const fetchEvaluationsByUid = (uid: number) => - get(`/api/evaluations/${uid}`) -export const fetchEvaluationsByHotkey = (hotkey: string) => - get(`/api/evaluations/hotkey/${encodeURIComponent(hotkey)}`) -export const fetchRounds = () => get('/api/rounds') -export const fetchEvalProgress = () => get('/api/eval-progress') -export const fetchContainerLogs = () => get('/api/container-logs') -export const fetchContainerLog = (label: string) => - getText(`/api/container-log/${encodeURIComponent(label)}`) -export const fetchValidatorLogs = () => get('/api/validator-logs') -export const fetchValidatorLog = (label: string) => - getText(`/api/validator-log/${encodeURIComponent(label)}`) diff --git a/app/lib/docs-redirects.js b/app/lib/docs-redirects.js index afb531f..d1cada6 100644 --- a/app/lib/docs-redirects.js +++ b/app/lib/docs-redirects.js @@ -4,9 +4,7 @@ const EXACT_REDIRECTS = new Map([ ['FIDELITY', 'validator-guide/fidelity'], ['GPU_SETUP', 'dev/gpu-setup'], ['HOW_CACHEON_WORKS', 'architecture/overview'], - ['HOW_OPTIMA_WORKS', 'architecture/overview'], ['INCENTIVES', 'miner-guide/incentives'], - ['INCENTIVE_LOAD_VALIDATION', 'results/incentive-load-validation'], ['MINER_GUIDE', 'miner-guide/overview'], ['PRODUCT_CONTRACT', 'architecture/product-model'], ['REFEREE_HARDENING_AUDIT', 'history/changelog'], diff --git a/app/lib/eval-gates.ts b/app/lib/eval-gates.ts deleted file mode 100644 index 555bfcc..0000000 --- a/app/lib/eval-gates.ts +++ /dev/null @@ -1,219 +0,0 @@ -/** Pass 1 decoded text-similarity DQ threshold (matches validator default). */ -export const PASS1_TEXT_SIM_DQ_THRESHOLD = 0.4 - -export type PassStatus = 'pass' | 'fail' | 'skipped' | 'na' - -export interface EvalGateSummary { - prefix: string | null - /** Compact label for list rows, e.g. "Correctness" */ - shortLabel: string | null - pass1: PassStatus - pass2: PassStatus - isInfraFail: boolean -} - -export function disqualifyPrefix(reason: string | null | undefined): string | null { - if (!reason) return null - const idx = reason.indexOf(':') - if (idx === -1) return reason.trim().toLowerCase() - return reason.slice(0, idx).trim().toLowerCase() -} - -/** Parse aggregate text similarity from a pass1_match_fail disqualify reason. */ -export function parsePass1TextSimilarity(reason: string | null | undefined): number | null { - if (!reason) return null - const m = /text similarity ([\d.]+)/i.exec(reason) - return m ? parseFloat(m[1]) : null -} - -export function summarizeEvalGates(ev: { - disqualified: boolean - disqualify_reason: string | null -}): EvalGateSummary { - const prefix = disqualifyPrefix(ev.disqualify_reason) - - if (prefix === 'duplicate_submission' || prefix === 'duplicate_of_leader') { - return { - prefix, - shortLabel: 'Skipped', - pass1: 'na', - pass2: 'na', - isInfraFail: false, - } - } - - if (!ev.disqualified) { - return { - prefix: null, - shortLabel: null, - pass1: 'pass', - pass2: 'pass', - isInfraFail: false, - } - } - - if (prefix === 'pass1_match_fail') { - return { - prefix, - shortLabel: 'Text similarity', - pass1: 'fail', - pass2: 'skipped', - isInfraFail: false, - } - } - - if (prefix === 'correctness_fail') { - return { - prefix, - shortLabel: 'Correctness', - pass1: 'pass', - pass2: 'fail', - isInfraFail: false, - } - } - - if (prefix === 'scoring_infra_fail' || prefix === 'baseline_scoring_unavailable') { - return { - prefix, - shortLabel: 'Scoring infra', - pass1: 'pass', - pass2: 'fail', - isInfraFail: true, - } - } - - if (prefix === 'prompt_errors') { - return { - prefix, - shortLabel: 'Prompt error', - pass1: 'pass', - pass2: 'fail', - isInfraFail: false, - } - } - - return { - prefix, - shortLabel: prefix ? prefix.replace(/_/g, ' ').slice(0, 20) : 'DQ', - pass1: 'pass', - pass2: 'fail', - isInfraFail: false, - } -} - -export function buildContainerLogLabel( - uid: number, - hotkey: string, - evaluationBlock: number, -): string { - return `uid${uid}_${hotkey.slice(0, 8)}_${evaluationBlock}` -} - -export function findContainerLogLabel( - labels: string[], - uid: number, - evaluationBlock: number, -): string | null { - const uidStr = String(uid) - const blockStr = String(evaluationBlock) - return ( - labels.find((label) => { - const m = /^uid(\d+)_/i.exec(label) - if (!m || m[1] !== uidStr) return false - const parts = label.split('_') - return parts[parts.length - 1] === blockStr - }) ?? null - ) -} - -/** Parse `YYYYMMDD_HHMMSS` suffix from a log label into a sortable integer. */ -function logTimestampSortKey(label: string, pattern: RegExp): number { - const m = pattern.exec(label) - if (!m) return -1 - return parseInt(m[1].replace('_', ''), 10) -} - -function pickNewestLabel(labels: string[], pattern: RegExp): string | null { - if (labels.length === 0) return null - const sorted = [...labels].sort( - (a, b) => logTimestampSortKey(b, pattern) - logTimestampSortKey(a, pattern), - ) - return sorted[0] ?? null -} - -/** True for pre-eval routine CPU logs (`cpu_idle_{ts}`). */ -export function isIdleCpuValidatorLog(label: string): boolean { - return /^cpu_idle_\d{8}_\d{6}$/.test(label) -} - -/** Block from validator log label: cpu_{block}_{ts} or gpu_{block}_{ts}. */ -export function blockFromValidatorLogLabel(label: string): number | null { - if (isIdleCpuValidatorLog(label)) return null - const m = /^(cpu|gpu)_(\d+)_\d{8}_\d{6}$/.exec(label) - return m ? parseInt(m[2], 10) : null -} - -export function findValidatorLogForBlock( - validatorLabels: string[], - block: number, - kind: 'cpu' | 'gpu', -): string | null { - const prefix = `${kind}_${block}_` - const matches = validatorLabels.filter((l) => l.startsWith(prefix)) - if (matches.length === 0) return null - return pickNewestLabel(matches, new RegExp(`^${kind}_\\d+_(\\d{8}_\\d{6})$`)) -} - -/** Container log for generation baseline vLLM (`baseline_{block}_{cid}`). */ -export function findBaselineGenerationLogLabel( - containerLabels: string[], - evaluationBlock: number, -): string | null { - const prefix = `baseline_${evaluationBlock}_` - const matches = containerLabels.filter( - (l) => l.startsWith(prefix) && !l.startsWith('baseline_scoring_'), - ) - if (matches.length === 0) return null - return matches.sort((a, b) => b.localeCompare(a))[0] ?? null -} - -/** Container log for teacher-forcing scoring vLLM (`baseline_scoring_{block}_{ts}`). */ -export function findBaselineScoringLogLabel( - containerLabels: string[], - evaluationBlock: number, -): string | null { - const blockPrefix = `baseline_scoring_${evaluationBlock}_` - const blockSpecific = containerLabels.filter((l) => l.startsWith(blockPrefix)) - if (blockSpecific.length === 0) return null - return pickNewestLabel(blockSpecific, /^baseline_scoring_\d+_(\d{8}_\d{6})$/) -} - -export interface EvalRunLogLabels { - baselineScoring: string | null - baselineGeneration: string | null - gpuEval: string | null - cpuLog: string | null -} - -/** Resolve log labels for an evaluation block (container + validator log lists). */ -export function findEvalRunLogLabels( - containerLabels: string[], - validatorLabels: string[], - evaluationBlock: number, -): EvalRunLogLabels { - const baselineScoring = findBaselineScoringLogLabel(containerLabels, evaluationBlock) - const baselineGeneration = findBaselineGenerationLogLabel(containerLabels, evaluationBlock) - const gpuEval = findValidatorLogForBlock(validatorLabels, evaluationBlock, 'gpu') - const cpuLog = findValidatorLogForBlock(validatorLabels, evaluationBlock, 'cpu') - return { baselineScoring, baselineGeneration, gpuEval, cpuLog } -} - -export function validatorLogHref(label: string | null): string { - if (!label) return '/dashboard/validator-logs' - return `/dashboard/validator-logs?label=${encodeURIComponent(label)}` -} - -export function containerLogHref(label: string | null): string { - if (!label) return '/dashboard/logs' - return `/dashboard/logs?label=${encodeURIComponent(label)}` -} diff --git a/app/lib/use-poll.ts b/app/lib/use-poll.ts deleted file mode 100644 index da734fa..0000000 --- a/app/lib/use-poll.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { useCallback, useEffect, useRef, useState } from 'react' -import { ApiError } from './api.client' - -export interface PollState { - data: T | null - error: string | null - /** True when the last failure was an HTTP 429 (rate-limited), not a real outage. */ - rateLimited: boolean - loading: boolean - refetch: () => void -} - -export function usePoll(fetcher: () => Promise, intervalMs: number): PollState { - const [data, setData] = useState(null) - const [error, setError] = useState(null) - const [rateLimited, setRateLimited] = useState(false) - const [loading, setLoading] = useState(true) - const mounted = useRef(true) - - const run = useCallback(async () => { - try { - const result = await fetcher() - if (mounted.current) { - setData(result) - setError(null) - setRateLimited(false) - } - } catch (e) { - if (mounted.current) { - const isRL = e instanceof ApiError && e.isRateLimit - setRateLimited(isRL) - if (isRL) { - // Rate-limit: API is alive, keep previous data and clear any stale hard error. - setError(null) - } else { - setError(e instanceof Error ? e.message : String(e)) - } - } - } finally { - if (mounted.current) setLoading(false) - } - }, [fetcher]) - - useEffect(() => { - mounted.current = true - run() - const id = setInterval(run, intervalMs) - return () => { - mounted.current = false - clearInterval(id) - } - }, [run, intervalMs]) - - return { data, error, rateLimited, loading, refetch: run } -} diff --git a/app/routes.ts b/app/routes.ts index 3cc3e75..4f844ad 100644 --- a/app/routes.ts +++ b/app/routes.ts @@ -2,15 +2,6 @@ import { type RouteConfig, index, route } from '@react-router/dev/routes' export default [ index('routes/home.tsx'), - route('dashboard', 'routes/dashboard.tsx', [ - index('routes/dashboard._index.tsx'), - route('pulse', 'routes/dashboard.pulse.tsx'), - route('leader', 'routes/dashboard.leader.tsx'), - route('evaluations', 'routes/dashboard.evaluations.tsx'), - route('rounds', 'routes/dashboard.rounds.tsx'), - route('logs', 'routes/dashboard.logs.tsx'), - route('validator-logs', 'routes/dashboard.validator-logs.tsx'), - ]), route('llms.txt', 'routes/llms.ts'), route('llms-full.txt', 'routes/llms-full.ts'), route('docs-markdown', 'routes/docs-markdown.ts', { id: 'docs-markdown-index' }), diff --git a/app/routes/dashboard._index.tsx b/app/routes/dashboard._index.tsx deleted file mode 100644 index a47f3f4..0000000 --- a/app/routes/dashboard._index.tsx +++ /dev/null @@ -1,5 +0,0 @@ -import { Navigate } from 'react-router' - -export default function DashboardIndexRedirect() { - return -} diff --git a/app/routes/dashboard.evaluations.tsx b/app/routes/dashboard.evaluations.tsx deleted file mode 100644 index 3812b97..0000000 --- a/app/routes/dashboard.evaluations.tsx +++ /dev/null @@ -1,10 +0,0 @@ -import type { Route } from './+types/dashboard.evaluations' -import { EvaluationsSection } from '~/components/dashboard/evaluations-section' - -export function meta(_: Route.MetaArgs) { - return [{ title: 'Evaluations · Dashboard · Cacheon' }] -} - -export default function DashboardEvaluationsRoute() { - return -} diff --git a/app/routes/dashboard.leader.tsx b/app/routes/dashboard.leader.tsx deleted file mode 100644 index 9189748..0000000 --- a/app/routes/dashboard.leader.tsx +++ /dev/null @@ -1,10 +0,0 @@ -import type { Route } from './+types/dashboard.leader' -import { LeaderSection } from '~/components/dashboard/leader-section' - -export function meta(_: Route.MetaArgs) { - return [{ title: 'Leader · Dashboard · Cacheon' }] -} - -export default function DashboardLeaderRoute() { - return -} diff --git a/app/routes/dashboard.logs.tsx b/app/routes/dashboard.logs.tsx deleted file mode 100644 index 1578fac..0000000 --- a/app/routes/dashboard.logs.tsx +++ /dev/null @@ -1,10 +0,0 @@ -import type { Route } from './+types/dashboard.logs' -import { LogsSection } from '~/components/dashboard/logs-section' - -export function meta(_: Route.MetaArgs) { - return [{ title: 'Logs · Dashboard · Cacheon' }] -} - -export default function DashboardLogsRoute() { - return -} diff --git a/app/routes/dashboard.pulse.tsx b/app/routes/dashboard.pulse.tsx deleted file mode 100644 index d740f4b..0000000 --- a/app/routes/dashboard.pulse.tsx +++ /dev/null @@ -1,10 +0,0 @@ -import type { Route } from './+types/dashboard.pulse' -import { PulseSection } from '~/components/dashboard/pulse-section' - -export function meta(_: Route.MetaArgs) { - return [{ title: 'Pulse · Dashboard · Cacheon' }] -} - -export default function DashboardPulseRoute() { - return -} diff --git a/app/routes/dashboard.rounds.tsx b/app/routes/dashboard.rounds.tsx deleted file mode 100644 index b8e0c1f..0000000 --- a/app/routes/dashboard.rounds.tsx +++ /dev/null @@ -1,10 +0,0 @@ -import type { Route } from './+types/dashboard.rounds' -import { RoundsSection } from '~/components/dashboard/rounds-section' - -export function meta(_: Route.MetaArgs) { - return [{ title: 'Rounds · Dashboard · Cacheon' }] -} - -export default function DashboardRoundsRoute() { - return -} diff --git a/app/routes/dashboard.tsx b/app/routes/dashboard.tsx deleted file mode 100644 index 9a403d0..0000000 --- a/app/routes/dashboard.tsx +++ /dev/null @@ -1,53 +0,0 @@ -import { Outlet, NavLink } from 'react-router' -import type { Route } from './+types/dashboard' -import Nav from '~/components/Nav' -import Footer from '~/components/Footer' -import { DASHBOARD_TABS } from '~/components/dashboard/shared' -import { cn } from '~/lib/cn' - -export function meta(_: Route.MetaArgs) { - return [ - { title: 'Dashboard · Cacheon' }, - { - name: 'description', - content: 'Live monitoring dashboard for the Cacheon inference arena.', - }, - ] -} - -export default function DashboardLayout() { - const tabCls = ({ isActive }: { isActive: boolean }) => - cn( - 'cursor-pointer whitespace-nowrap rounded-md px-3 py-1.5 font-mono text-[0.8rem] font-semibold uppercase tracking-[0.12em] no-underline transition-colors', - isActive - ? 'bg-accent/12 text-accent' - : 'text-secondary hover:bg-white/[0.03] hover:text-primary', - ) - - return ( - <> -