project-nomad/admin/inertia/components/benchmark/CoreGrid.tsx
chriscrosstalk 8d6b2045ef
feat(benchmark): live telemetry during benchmark runs (#1082) (#1084)
Phase 1 of the live benchmark-run experience. Replaces the opaque (and
in sync mode, simulated) progress bar with a real-time run view driven by
actual host telemetry.

- Async run path: the UI now dispatches to the queue worker and keys off
  SSE instead of faking stage progress with client-side timers.
- New BenchmarkTelemetrySampler broadcasts per-core CPU load, CPU temp
  (best-effort, hidden when unavailable), and disk MB/s at 1 Hz over a new
  benchmark-telemetry SSE channel. Runs in the orchestration process, never
  the sysbench container, so it cannot affect scores.
- BenchmarkProgress carries the ordered stage plan + index so the frontend
  renders a live stage rail.
- AI benchmark streams /api/generate for live tokens/sec and true TTFT; the
  scored numbers still come from Ollama's authoritative final eval fields.
- Frontend: useBenchmarkRun hook owns both subscriptions; self-contained SVG
  components (StageRail, CoreGrid, Sparkline, LiveReadout) + BenchmarkRunView,
  styled in the desert palette. No chart library added.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-04 03:37:39 +00:00

60 lines
1.7 KiB
TypeScript

import classNames from '~/lib/classNames'
interface CoreGridProps {
/** Per-thread load, 0-100. */
loads: number[]
}
const MAX_CELLS = 64
// Colour ramp by load: idle olive -> warm orange -> hot red. Cells transition
// smoothly as load climbs, so a single-thread pass lights one cell and an
// all-threads pass lights the whole grid.
function cellClass(load: number): string {
if (load >= 80) return 'bg-desert-red'
if (load >= 55) return 'bg-desert-orange'
if (load >= 25) return 'bg-desert-olive'
if (load >= 8) return 'bg-desert-green'
return 'bg-desert-green/15'
}
export default function CoreGrid({ loads }: CoreGridProps) {
// Fold very high core counts down to MAX_CELLS by averaging groups.
let cells = loads
let grouped = 1
if (loads.length > MAX_CELLS) {
grouped = Math.ceil(loads.length / MAX_CELLS)
cells = []
for (let i = 0; i < loads.length; i += grouped) {
const slice = loads.slice(i, i + grouped)
cells.push(slice.reduce((a, b) => a + b, 0) / slice.length)
}
}
const cols = Math.min(cells.length, 16) || 1
return (
<div className="space-y-2">
<div
className="grid gap-1.5"
style={{ gridTemplateColumns: `repeat(${cols}, minmax(0, 1fr))` }}
>
{cells.map((load, i) => (
<div
key={i}
title={`Thread ${i + 1}: ${Math.round(load)}%`}
className={classNames(
'aspect-square rounded-sm transition-colors duration-300',
cellClass(load)
)}
/>
))}
</div>
<div className="text-xs text-desert-stone-dark font-mono">
{loads.length} thread{loads.length === 1 ? '' : 's'}
{grouped > 1 ? ` (${grouped}/cell)` : ''}
</div>
</div>
)
}