Lumbridge Bench
This commit is contained in:
@@ -0,0 +1,6 @@
|
||||
# Copy to .env.local. Never commit .env.local.
|
||||
#
|
||||
# Server-side destination for inert model suggestions. On web-host, Lumbridge
|
||||
# Bench and the control plane share the host, so the private loopback endpoint
|
||||
# avoids a public round trip.
|
||||
LUMBRIDGE_CONTROL_PLANE_URL=http://127.0.0.1:8902
|
||||
@@ -0,0 +1,171 @@
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
import { ImageResponse } from "next/og";
|
||||
import { fmtParams, getRun, getRuns, limitOf } from "@/lib/results";
|
||||
|
||||
/**
|
||||
* Shareable score card as a PNG.
|
||||
*
|
||||
* One image serves two jobs: the download button and the OpenGraph tag, so a
|
||||
* pasted link unfurls as the card itself. Fonts are bundled rather than
|
||||
* fetched at request time — satori needs real font buffers, and a network
|
||||
* dependency inside image generation fails in exactly the situations where
|
||||
* you want a share link to work.
|
||||
*
|
||||
* Note: satori supports flexbox only. Every container needs an explicit
|
||||
* display value; CSS grid silently renders nothing.
|
||||
*/
|
||||
|
||||
export const dynamic = "force-static";
|
||||
|
||||
export function generateStaticParams() {
|
||||
return getRuns().map((r) => ({ id: r.run_id.slice(0, 8) }));
|
||||
}
|
||||
|
||||
const FONT_DIR = path.join(process.cwd(), "assets", "fonts");
|
||||
const font = (file: string) => fs.readFileSync(path.join(FONT_DIR, file));
|
||||
|
||||
const VOID = "#08090b";
|
||||
const RULE = "#1e232b";
|
||||
const INK = "#e8e6e1";
|
||||
const DIM = "#79818d";
|
||||
const DIMMER = "#4a515b";
|
||||
const SIGNAL = "#f2b134";
|
||||
const REFERENCE = "#5896ae";
|
||||
|
||||
function Stat({
|
||||
label,
|
||||
value,
|
||||
unit,
|
||||
color = INK,
|
||||
hint,
|
||||
}: {
|
||||
label: string;
|
||||
value: string;
|
||||
unit?: string;
|
||||
color?: string;
|
||||
hint?: string;
|
||||
}) {
|
||||
return (
|
||||
<div style={{ display: "flex", flexDirection: "column", flex: 1 }}>
|
||||
<div style={{ fontSize: 15, letterSpacing: 3, color: DIMMER, textTransform: "uppercase" }}>
|
||||
{label}
|
||||
</div>
|
||||
<div style={{ display: "flex", alignItems: "baseline", marginTop: 12 }}>
|
||||
<span style={{ fontFamily: "Instrument", fontSize: 62, color, lineHeight: 1 }}>{value}</span>
|
||||
{unit && <span style={{ fontSize: 17, color: DIMMER, marginLeft: 7 }}>{unit}</span>}
|
||||
</div>
|
||||
{hint && <div style={{ fontSize: 14, color: DIMMER, marginTop: 8 }}>{hint}</div>}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export async function GET(
|
||||
_req: Request,
|
||||
{ params }: { params: Promise<{ id: string }> },
|
||||
) {
|
||||
const { id } = await params;
|
||||
const run = getRun(id);
|
||||
if (!run) return new Response("not found", { status: 404 });
|
||||
|
||||
const v = run.verdict;
|
||||
const limit = limitOf(run);
|
||||
const perf = run.perf?.points ?? [];
|
||||
const single = perf.find((p) => p.concurrency === 1);
|
||||
const peak = perf.length
|
||||
? perf.reduce((a, b) => (b.output_tps_total > a.output_tps_total ? b : a))
|
||||
: null;
|
||||
|
||||
return new ImageResponse(
|
||||
(
|
||||
<div
|
||||
style={{
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
background: VOID,
|
||||
fontFamily: "Plex",
|
||||
color: INK,
|
||||
padding: 56,
|
||||
}}
|
||||
>
|
||||
{/* header */}
|
||||
<div style={{ display: "flex", justifyContent: "space-between", alignItems: "baseline" }}>
|
||||
<div style={{ display: "flex", alignItems: "baseline" }}>
|
||||
<span style={{ fontFamily: "Instrument", fontSize: 30 }}>bench</span>
|
||||
<span style={{ fontFamily: "Instrument", fontSize: 30, color: DIMMER }}>.karti.ai</span>
|
||||
</div>
|
||||
<span style={{ fontSize: 15, letterSpacing: 3, color: DIMMER, textTransform: "uppercase" }}>
|
||||
{run.timestamp.slice(0, 10)}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div style={{ display: "flex", height: 1, background: RULE, marginTop: 24, marginBottom: 40 }} />
|
||||
|
||||
{/* title */}
|
||||
<div style={{ display: "flex", flexDirection: "column" }}>
|
||||
<span style={{ fontFamily: "Instrument", fontSize: 68, lineHeight: 1.05 }}>
|
||||
{run.target.display_name}
|
||||
</span>
|
||||
<span style={{ fontSize: 18, color: DIM, marginTop: 16 }}>
|
||||
{run.host.hardware} · {fmtParams(run.target)} ·{" "}
|
||||
{run.target.quant?.toUpperCase()} · {run.target.serving.engine}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{/* stats */}
|
||||
<div style={{ display: "flex", marginTop: "auto", gap: 24 }}>
|
||||
<Stat
|
||||
label="signal"
|
||||
value={v.signal_score != null ? v.signal_score.toFixed(3) : "—"}
|
||||
color={SIGNAL}
|
||||
hint="our workloads"
|
||||
/>
|
||||
<Stat
|
||||
label="reference"
|
||||
value={v.reference_score != null ? v.reference_score.toFixed(3) : "—"}
|
||||
color={REFERENCE}
|
||||
hint={limit ? `capped ${limit}` : "calibration"}
|
||||
/>
|
||||
<Stat
|
||||
label="single stream"
|
||||
value={single ? single.output_tps_per_stream.toFixed(1) : "—"}
|
||||
unit="tok/s"
|
||||
hint={single?.ttft_p50_ms ? `ttft ${Math.round(single.ttft_p50_ms)}ms` : undefined}
|
||||
/>
|
||||
<Stat
|
||||
label="peak"
|
||||
value={peak ? peak.output_tps_total.toFixed(0) : "—"}
|
||||
unit="tok/s"
|
||||
hint={peak ? `at ×${peak.concurrency}` : undefined}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* tick strip — a measurement scale across the full plate */}
|
||||
<div style={{ display: "flex", marginTop: 40, justifyContent: "space-between", width: "100%" }}>
|
||||
{Array.from({ length: 96 }).map((_, i) => (
|
||||
<div
|
||||
key={i}
|
||||
style={{
|
||||
display: "flex",
|
||||
width: 1,
|
||||
height: 8,
|
||||
background: i % 6 === 0 ? SIGNAL : RULE,
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
),
|
||||
{
|
||||
width: 1200,
|
||||
height: 630,
|
||||
fonts: [
|
||||
{ name: "Instrument", data: font("InstrumentSerif-Regular.ttf"), style: "normal", weight: 400 },
|
||||
{ name: "Plex", data: font("IBMPlexMono-Regular.ttf"), style: "normal", weight: 400 },
|
||||
{ name: "Plex", data: font("IBMPlexMono-SemiBold.ttf"), style: "normal", weight: 600 },
|
||||
],
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import { getRuns } from "@/lib/results";
|
||||
|
||||
export const dynamic = "force-dynamic";
|
||||
|
||||
/** Liveness probe used by the deploy verify step. */
|
||||
export async function GET() {
|
||||
const runs = getRuns();
|
||||
return Response.json({
|
||||
ok: true,
|
||||
runs: runs.length,
|
||||
latest: runs[0]?.timestamp ?? null,
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
import { NextResponse } from "next/server";
|
||||
import { parseHuggingFaceRef } from "@/lib/model-ref";
|
||||
|
||||
export const runtime = "nodejs";
|
||||
export const dynamic = "force-dynamic";
|
||||
|
||||
const controlPlaneUrl = (
|
||||
process.env.LUMBRIDGE_CONTROL_PLANE_URL ?? "http://127.0.0.1:8902"
|
||||
).replace(/\/+$/, "");
|
||||
|
||||
/**
|
||||
* Record a Hugging Face model suggestion for Karti to review manually.
|
||||
*
|
||||
* This endpoint only forwards an inert review record to the Lumbridge control
|
||||
* plane. Neither service downloads, imports, schedules, or executes a model.
|
||||
*/
|
||||
|
||||
export async function POST(req: Request) {
|
||||
let body: { model?: string; notes?: string };
|
||||
try {
|
||||
body = await req.json();
|
||||
} catch {
|
||||
return NextResponse.json({ error: "invalid JSON" }, { status: 400 });
|
||||
}
|
||||
|
||||
const ref = parseHuggingFaceRef(body.model ?? "");
|
||||
if (!ref) {
|
||||
return NextResponse.json(
|
||||
{ error: "Expected a Hugging Face model reference like `owner/model`." },
|
||||
{ status: 400 },
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch(`${controlPlaneUrl}/api/model-suggestions`, {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({
|
||||
sourceUrl: `https://huggingface.co/${ref}`,
|
||||
reason: "Suggested via Lumbridge Bench for manual model review.",
|
||||
notes: (body.notes ?? "").slice(0, 2000) || undefined,
|
||||
}),
|
||||
cache: "no-store",
|
||||
signal: AbortSignal.timeout(5_000),
|
||||
});
|
||||
if (!response.ok) {
|
||||
const result = await response.json().catch(() => null) as {
|
||||
error?: string;
|
||||
} | null;
|
||||
return NextResponse.json(
|
||||
{
|
||||
error:
|
||||
result?.error ??
|
||||
"The Lumbridge review inbox could not accept that suggestion.",
|
||||
},
|
||||
{ status: response.status },
|
||||
);
|
||||
}
|
||||
} catch {
|
||||
return NextResponse.json(
|
||||
{ error: "The Lumbridge review inbox is temporarily unavailable." },
|
||||
{ status: 503 },
|
||||
);
|
||||
}
|
||||
|
||||
return NextResponse.json({
|
||||
ok: true,
|
||||
model: ref,
|
||||
message:
|
||||
"Suggestion received. Karti reviews every model manually. Nothing is " +
|
||||
"downloaded or run automatically; approved results appear on the board later.",
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,166 @@
|
||||
@import "tailwindcss";
|
||||
|
||||
/* ---------------------------------------------------------------------------
|
||||
Lumbridge Bench — model evidence lab.
|
||||
|
||||
This is a measurement tool, so it reads like a bench instrument rather than
|
||||
a dashboard: near-black panel, hairline rules, tabular figures everywhere,
|
||||
and a phosphor palette that carries meaning rather than decoration.
|
||||
|
||||
Colour is semantic and must stay that way:
|
||||
signal (amber) — our private measurement. The real number.
|
||||
reference (cyan) — public benchmark. Calibration only, never a ranking.
|
||||
Anything that dilutes that distinction on screen undermines the whole point
|
||||
of the two-tier design. See docs/DECISIONS.md#d5.
|
||||
--------------------------------------------------------------------------- */
|
||||
|
||||
@theme {
|
||||
--color-void: #08090b;
|
||||
--color-panel: #0d0f13;
|
||||
--color-panel-2: #121519;
|
||||
--color-rule: #1e232b;
|
||||
--color-rule-bright: #2c333d;
|
||||
--color-ink: #e8e6e1;
|
||||
--color-dim: #79818d;
|
||||
--color-dimmer: #4a515b;
|
||||
|
||||
--color-signal: #f2b134;
|
||||
--color-signal-dim: #6b4f18;
|
||||
--color-reference: #5896ae;
|
||||
--color-reference-dim: #23414d;
|
||||
|
||||
--color-pass: #52a86e;
|
||||
--color-fail: #cb5a54;
|
||||
|
||||
--font-display: var(--font-instrument), ui-serif, Georgia, serif;
|
||||
--font-mono: var(--font-plex-mono), ui-monospace, monospace;
|
||||
}
|
||||
|
||||
:root {
|
||||
color-scheme: dark;
|
||||
}
|
||||
|
||||
html {
|
||||
background: var(--color-void);
|
||||
}
|
||||
|
||||
body {
|
||||
color: var(--color-ink);
|
||||
font-family: var(--font-mono);
|
||||
font-feature-settings: "tnum" 1, "zero" 1;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
|
||||
/* Graph-paper substrate. Two grids at different scales, like a scope screen,
|
||||
plus a vignette so the panel edges fall away rather than ending abruptly. */
|
||||
.substrate {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
z-index: -2;
|
||||
background-color: var(--color-void);
|
||||
background-image:
|
||||
linear-gradient(rgba(120, 140, 160, 0.028) 1px, transparent 1px),
|
||||
linear-gradient(90deg, rgba(120, 140, 160, 0.028) 1px, transparent 1px),
|
||||
linear-gradient(rgba(120, 140, 160, 0.055) 1px, transparent 1px),
|
||||
linear-gradient(90deg, rgba(120, 140, 160, 0.055) 1px, transparent 1px);
|
||||
background-size: 22px 22px, 22px 22px, 110px 110px, 110px 110px;
|
||||
}
|
||||
|
||||
.substrate::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background: radial-gradient(
|
||||
ellipse 120% 80% at 50% 0%,
|
||||
transparent 20%,
|
||||
rgba(8, 9, 11, 0.75) 75%,
|
||||
var(--color-void) 100%
|
||||
);
|
||||
}
|
||||
|
||||
/* Film grain. Keeps large dark fields from banding and gives the panel a
|
||||
physical, slightly analogue quality. */
|
||||
.grain {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
z-index: -1;
|
||||
pointer-events: none;
|
||||
opacity: 0.16;
|
||||
mix-blend-mode: overlay;
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='200' height='200'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3'/%3E%3C/filter%3E%3Crect width='200' height='200' filter='url(%23n)' opacity='0.55'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
/* Instrument label: the small stamped caps used on panel headings. */
|
||||
.label {
|
||||
font-size: 0.6875rem;
|
||||
letter-spacing: 0.18em;
|
||||
text-transform: uppercase;
|
||||
color: var(--color-dimmer);
|
||||
}
|
||||
|
||||
/* Numbers that should read as a live readout. */
|
||||
.readout {
|
||||
font-variant-numeric: tabular-nums;
|
||||
letter-spacing: -0.02em;
|
||||
}
|
||||
|
||||
/* Hairline panel. Border colour is deliberately close to the background —
|
||||
structure should be felt more than seen. */
|
||||
.panel {
|
||||
background: linear-gradient(180deg, var(--color-panel) 0%, var(--color-void) 100%);
|
||||
border: 1px solid var(--color-rule);
|
||||
}
|
||||
|
||||
/* Tick strip used as a section divider — evokes a measurement scale. */
|
||||
.ticks {
|
||||
height: 6px;
|
||||
background-image: repeating-linear-gradient(
|
||||
90deg,
|
||||
var(--color-rule-bright) 0 1px,
|
||||
transparent 1px 9px
|
||||
);
|
||||
}
|
||||
|
||||
@keyframes rise {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(8px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
/* Staggered page-load reveal. One orchestrated entrance rather than scattered
|
||||
micro-interactions. */
|
||||
.rise {
|
||||
animation: rise 0.5s cubic-bezier(0.16, 1, 0.3, 1) both;
|
||||
}
|
||||
|
||||
@keyframes trace {
|
||||
from {
|
||||
stroke-dashoffset: var(--trace-len);
|
||||
}
|
||||
to {
|
||||
stroke-dashoffset: 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* The throughput curve draws itself like a scope sweep. */
|
||||
.trace {
|
||||
stroke-dasharray: var(--trace-len);
|
||||
animation: trace 1.1s cubic-bezier(0.4, 0, 0.2, 1) 0.25s both;
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.rise,
|
||||
.trace {
|
||||
animation: none;
|
||||
}
|
||||
}
|
||||
|
||||
::selection {
|
||||
background: var(--color-signal);
|
||||
color: var(--color-void);
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
|
||||
<rect width="32" height="32" fill="#08090b"/>
|
||||
<!-- a rising throughput trace: the mark is the measurement -->
|
||||
<path d="M4 24 L11 20 L18 11 L28 7" fill="none" stroke="#f2b134" stroke-width="2.5"
|
||||
stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M4 28 h24" stroke="#2c333d" stroke-width="2" stroke-linecap="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 406 B |
@@ -0,0 +1,88 @@
|
||||
import type { Metadata } from "next";
|
||||
import { IBM_Plex_Mono, Instrument_Serif } from "next/font/google";
|
||||
import Link from "next/link";
|
||||
import "./globals.css";
|
||||
|
||||
const instrument = Instrument_Serif({
|
||||
weight: "400",
|
||||
subsets: ["latin"],
|
||||
variable: "--font-instrument",
|
||||
display: "swap",
|
||||
});
|
||||
|
||||
const plexMono = IBM_Plex_Mono({
|
||||
weight: ["400", "500", "600"],
|
||||
subsets: ["latin"],
|
||||
variable: "--font-plex-mono",
|
||||
display: "swap",
|
||||
});
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Lumbridge Bench",
|
||||
description:
|
||||
"Evidence for models on Lumbridge Compute: quality, serving performance, and exact hardware provenance.",
|
||||
};
|
||||
|
||||
export default function RootLayout({
|
||||
children,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
}) {
|
||||
return (
|
||||
<html lang="en" className={`${instrument.variable} ${plexMono.variable}`}>
|
||||
<body className="min-h-screen antialiased">
|
||||
<div className="substrate" />
|
||||
<div className="grain" />
|
||||
|
||||
<header className="border-b border-rule">
|
||||
<div className="mx-auto flex max-w-6xl items-baseline justify-between px-6 py-5">
|
||||
<Link href="/" className="group flex items-baseline gap-3">
|
||||
<span className="font-display text-2xl leading-none tracking-tight">
|
||||
Lumbridge
|
||||
<span className="text-dimmer"> Bench</span>
|
||||
</span>
|
||||
<span className="label hidden transition-colors group-hover:text-signal sm:inline">
|
||||
evidence lab
|
||||
</span>
|
||||
</Link>
|
||||
<nav className="flex items-center gap-6 text-xs">
|
||||
<Link
|
||||
href="/"
|
||||
className="text-dim transition-colors hover:text-ink"
|
||||
>
|
||||
board
|
||||
</Link>
|
||||
<Link
|
||||
href="/method"
|
||||
className="text-dim transition-colors hover:text-ink"
|
||||
>
|
||||
method
|
||||
</Link>
|
||||
<Link
|
||||
href="/submit"
|
||||
className="border border-rule-bright px-3 py-1.5 text-ink transition-colors hover:border-signal hover:text-signal"
|
||||
>
|
||||
suggest a model
|
||||
</Link>
|
||||
</nav>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
{children}
|
||||
|
||||
<footer className="mt-24 border-t border-rule">
|
||||
<div className="mx-auto max-w-6xl px-6 py-8">
|
||||
<div className="ticks mb-6 opacity-40" />
|
||||
<div className="flex flex-col gap-3 text-xs text-dimmer sm:flex-row sm:justify-between">
|
||||
<p>
|
||||
Measured on our own hardware. Reference scores are public
|
||||
benchmarks and are calibration only.
|
||||
</p>
|
||||
<p className="font-display text-sm text-dim">Lumbridge Bench</p>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,142 @@
|
||||
export const dynamic = "force-static";
|
||||
|
||||
export const metadata = {
|
||||
title: "Method — Lumbridge Bench",
|
||||
description:
|
||||
"How these numbers are produced, and what they do and do not mean.",
|
||||
};
|
||||
|
||||
function Section({
|
||||
n,
|
||||
title,
|
||||
children,
|
||||
}: {
|
||||
n: string;
|
||||
title: string;
|
||||
children: React.ReactNode;
|
||||
}) {
|
||||
return (
|
||||
<section className="border-t border-rule py-10">
|
||||
<div className="grid gap-6 md:grid-cols-[auto_1fr] md:gap-12">
|
||||
<div className="label pt-1 md:w-16">{n}</div>
|
||||
<div>
|
||||
<h2 className="font-display mb-4 text-2xl">{title}</h2>
|
||||
<div className="max-w-2xl space-y-4 text-xs leading-relaxed text-dim">
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
export default function MethodPage() {
|
||||
return (
|
||||
<main className="mx-auto max-w-5xl px-6 py-14">
|
||||
<div className="rise mb-10 max-w-2xl">
|
||||
<h1 className="font-display text-4xl leading-tight sm:text-5xl">
|
||||
Method
|
||||
</h1>
|
||||
<p className="mt-5 text-sm leading-relaxed text-dim">
|
||||
What these numbers are, how they are produced, and — the part most
|
||||
leaderboards skip — what they cannot tell you.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<Section n="01" title="A target is not a model">
|
||||
<p>
|
||||
Every card measures a{" "}
|
||||
<span className="text-ink">
|
||||
model × host × quantization × serving config × checkpoint
|
||||
</span>
|
||||
, not a model name. On unified-memory hardware the serving flags move
|
||||
throughput more than swapping the model does — disabling CUDA graphs,
|
||||
or capping memory utilization at 55%, changes the answer by more than
|
||||
the difference between two model families.
|
||||
</p>
|
||||
<p>
|
||||
So the flags are recorded from the live server on every run and shown
|
||||
on the card. Two rows are only comparable if their configurations
|
||||
match.
|
||||
</p>
|
||||
</Section>
|
||||
|
||||
<Section n="02" title="Two tiers, and only one is a ranking">
|
||||
<p>
|
||||
<span className="text-signal">Signal</span> tasks are private, built
|
||||
from work we actually do. They are the measurement.
|
||||
</p>
|
||||
<p>
|
||||
<span className="text-reference">Reference</span> tasks are public
|
||||
benchmarks run unmodified. They exist to calibrate the harness, not to
|
||||
rank models: if our number lands far from the published value, our
|
||||
harness is broken. Public benchmarks have been in training data for
|
||||
years, and a high reference score is not evidence of much.
|
||||
</p>
|
||||
<p>
|
||||
A bench made only of private tasks would be unfalsifiable — no reader
|
||||
could distinguish a bad model from a broken harness. That is what the
|
||||
reference tier is for.
|
||||
</p>
|
||||
</Section>
|
||||
|
||||
<Section n="03" title="Why the private set stays private">
|
||||
<p>
|
||||
The usual reason given is confidentiality. The real reason is
|
||||
contamination: a published test set gets scraped, trained on, and
|
||||
stops measuring capability. That is how MMLU, GSM8K and HumanEval
|
||||
stopped being informative.
|
||||
</p>
|
||||
<p>
|
||||
Samples carry embedded canary strings, so if a future model reproduces
|
||||
them we can demonstrate contamination rather than suspect it.
|
||||
</p>
|
||||
</Section>
|
||||
|
||||
<Section n="04" title="Performance is measured, not quoted">
|
||||
<p>
|
||||
Each run drives real concurrent load at the serving endpoint and
|
||||
records time-to-first-token, inter-token latency, per-stream decode
|
||||
rate, and aggregate throughput at several concurrency levels.
|
||||
</p>
|
||||
<p>
|
||||
Two details do most of the work. Every request carries a unique prefix
|
||||
so <span className="text-ink">prefix caching</span> cannot make
|
||||
prefill free and inflate the result. And output length is pinned, so
|
||||
concurrency levels finish at identical token counts and remain
|
||||
comparable. Without both, the numbers look considerably better and
|
||||
mean nothing.
|
||||
</p>
|
||||
<p>
|
||||
Single-stream and aggregate figures are both reported because they
|
||||
disagree. On our hardware, throughput rises roughly 12× from one
|
||||
stream to thirty-two while per-stream decode falls to a third — batch
|
||||
serving and interactive use want opposite configurations.
|
||||
</p>
|
||||
</Section>
|
||||
|
||||
<Section n="05" title="What a capped run means">
|
||||
<p>
|
||||
Long reference datasets against a slow local model take hours, so some
|
||||
runs are capped at a sample limit. When that happens the cap is
|
||||
recorded in the result and displayed on the card. A capped score is
|
||||
not comparable to a full-dataset score and is never presented as one.
|
||||
</p>
|
||||
</Section>
|
||||
|
||||
<Section n="06" title="Known limits">
|
||||
<p>
|
||||
Single hardware sample per host — no variance across identical
|
||||
machines. Perf runs are single-shot rather than averaged over repeats,
|
||||
so treat small differences between runs as noise; the run-to-run
|
||||
spread on aggregate throughput is meaningful.
|
||||
</p>
|
||||
<p>
|
||||
Rubric-graded samples depend on a judge model, which drifts as that
|
||||
model changes. Deterministic scorers are preferred wherever the
|
||||
property can be checked mechanically.
|
||||
</p>
|
||||
</Section>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,280 @@
|
||||
import Link from "next/link";
|
||||
import { ScopeTrace } from "@/components/ScopeTrace";
|
||||
import {
|
||||
fmtParams,
|
||||
getRuns,
|
||||
latestPerTarget,
|
||||
limitOf,
|
||||
type Run,
|
||||
} from "@/lib/results";
|
||||
|
||||
export const dynamic = "force-static";
|
||||
|
||||
function Metric({
|
||||
label,
|
||||
value,
|
||||
unit,
|
||||
tone = "ink",
|
||||
hint,
|
||||
}: {
|
||||
label: string;
|
||||
value: string;
|
||||
unit?: string;
|
||||
tone?: "ink" | "signal" | "reference" | "dim";
|
||||
hint?: string;
|
||||
}) {
|
||||
const toneClass = {
|
||||
ink: "text-ink",
|
||||
signal: "text-signal",
|
||||
reference: "text-reference",
|
||||
dim: "text-dim",
|
||||
}[tone];
|
||||
return (
|
||||
<div className="min-w-0">
|
||||
<div className="label mb-2">{label}</div>
|
||||
<div
|
||||
className={`readout font-display text-4xl leading-none ${toneClass}`}
|
||||
>
|
||||
{value}
|
||||
{unit && (
|
||||
<span className="ml-1 font-mono text-sm text-dimmer">{unit}</span>
|
||||
)}
|
||||
</div>
|
||||
{hint && (
|
||||
<div className="mt-2 text-[11px] leading-snug text-dimmer">{hint}</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function PrimaryCard({ run }: { run: Run }) {
|
||||
const v = run.verdict;
|
||||
const limit = limitOf(run);
|
||||
const perf = run.perf?.points ?? [];
|
||||
const single = perf.find((p) => p.concurrency === 1);
|
||||
const peak = perf.length
|
||||
? perf.reduce((a, b) => (b.output_tps_total > a.output_tps_total ? b : a))
|
||||
: null;
|
||||
|
||||
return (
|
||||
<section className="rise panel relative overflow-hidden">
|
||||
<div className="absolute right-0 top-0 border-b border-l border-rule px-3 py-1.5">
|
||||
<span className="label text-signal">in production</span>
|
||||
</div>
|
||||
|
||||
<div className="border-b border-rule p-6 sm:p-8">
|
||||
<div className="label mb-3">primary target</div>
|
||||
<h2 className="font-display text-3xl leading-tight sm:text-4xl">
|
||||
{run.target.display_name}
|
||||
</h2>
|
||||
<p className="mt-3 max-w-2xl text-xs leading-relaxed text-dim">
|
||||
{run.host.hardware} · {fmtParams(run.target)} ·{" "}
|
||||
{run.target.quant?.toUpperCase()} · {run.target.serving.engine} · ctx{" "}
|
||||
{run.target.serving.max_model_len?.toLocaleString()}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="grid gap-8 border-b border-rule p-6 sm:grid-cols-2 sm:p-8 lg:grid-cols-4">
|
||||
<Metric
|
||||
label="signal score"
|
||||
value={v.signal_score != null ? v.signal_score.toFixed(3) : "—"}
|
||||
tone="signal"
|
||||
hint={
|
||||
v.signal_score == null
|
||||
? "no private tasks authored yet"
|
||||
: "our own workloads"
|
||||
}
|
||||
/>
|
||||
<Metric
|
||||
label="reference"
|
||||
value={v.reference_score != null ? v.reference_score.toFixed(3) : "—"}
|
||||
tone="reference"
|
||||
hint={
|
||||
limit
|
||||
? `capped at ${limit} samples · calibration only`
|
||||
: "calibration only"
|
||||
}
|
||||
/>
|
||||
<Metric
|
||||
label="single stream"
|
||||
value={single ? single.output_tps_per_stream.toFixed(1) : "—"}
|
||||
unit="tok/s"
|
||||
hint={
|
||||
single
|
||||
? `TTFT ${Math.round(single.ttft_p50_ms ?? 0)}ms · what one user feels`
|
||||
: undefined
|
||||
}
|
||||
/>
|
||||
<Metric
|
||||
label="peak throughput"
|
||||
value={peak ? peak.output_tps_total.toFixed(0) : "—"}
|
||||
unit="tok/s"
|
||||
hint={
|
||||
peak
|
||||
? `at concurrency ${peak.concurrency} · what the box serves`
|
||||
: undefined
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{perf.length > 1 && (
|
||||
<div className="p-6 sm:p-8">
|
||||
<ScopeTrace points={perf} />
|
||||
<div className="mt-4 flex flex-wrap items-baseline justify-between gap-3">
|
||||
<p className="max-w-xl text-[11px] leading-relaxed text-dimmer">
|
||||
Throughput scales{" "}
|
||||
{peak && single
|
||||
? `${(peak.output_tps_total / single.output_tps_per_stream).toFixed(1)}×`
|
||||
: "—"}{" "}
|
||||
from one stream to {peak?.concurrency ?? "—"}, but per-stream
|
||||
decode falls to {peak?.output_tps_per_stream.toFixed(1) ?? "—"}{" "}
|
||||
tok/s. Batch work and interactive work want opposite settings on
|
||||
this box.
|
||||
</p>
|
||||
<Link
|
||||
href={`/run/${run.run_id.slice(0, 8)}`}
|
||||
className="shrink-0 border border-rule-bright px-3 py-1.5 text-xs transition-colors hover:border-signal hover:text-signal"
|
||||
>
|
||||
full score card →
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
export default function Home() {
|
||||
const runs = getRuns();
|
||||
const board = latestPerTarget(runs);
|
||||
const primary = board.find((r) => r.target.tier === "production") ?? board[0];
|
||||
const lastMeasured = runs[0]?.timestamp?.slice(0, 10);
|
||||
|
||||
return (
|
||||
<main className="mx-auto max-w-6xl px-6 py-14">
|
||||
<div className="rise mb-14 max-w-3xl">
|
||||
<h1 className="font-display text-5xl leading-[1.05] sm:text-6xl">
|
||||
Does this model earn
|
||||
<br />
|
||||
its place on our hardware?
|
||||
</h1>
|
||||
<p className="mt-6 max-w-xl text-sm leading-relaxed text-dim">
|
||||
Lumbridge Bench is the evidence layer for Lumbridge Compute. Two
|
||||
numbers decide a self-hosting call: whether a model is good enough at
|
||||
the work we actually do, and whether it is fast enough on the box we
|
||||
would run it on. Most leaderboards report only the first. Every card
|
||||
here reports both, measured together, on the same machine.
|
||||
</p>
|
||||
<div className="mt-8 flex flex-wrap items-center gap-x-8 gap-y-3 text-xs text-dimmer">
|
||||
<span>
|
||||
<span className="text-ink">{board.length}</span> target
|
||||
{board.length === 1 ? "" : "s"}
|
||||
</span>
|
||||
<span>
|
||||
<span className="text-ink">{runs.length}</span> run
|
||||
{runs.length === 1 ? "" : "s"}
|
||||
</span>
|
||||
{lastMeasured && (
|
||||
<span>
|
||||
last measured <span className="text-ink">{lastMeasured}</span>
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{primary && <PrimaryCard run={primary} />}
|
||||
|
||||
<div className="ticks mt-16 mb-8 opacity-40" />
|
||||
|
||||
<section>
|
||||
<div className="mb-6 flex items-baseline justify-between">
|
||||
<h2 className="font-display text-2xl">The board</h2>
|
||||
<span className="label">latest run per target</span>
|
||||
</div>
|
||||
|
||||
<div className="-mx-6 overflow-x-auto px-6">
|
||||
<table className="w-full min-w-[720px] border-collapse text-xs">
|
||||
<thead>
|
||||
<tr className="border-b border-rule-bright text-left">
|
||||
<th className="label py-3 pr-4 font-normal">target</th>
|
||||
<th className="label py-3 pr-4 font-normal">host</th>
|
||||
<th className="label py-3 pr-4 text-right font-normal">
|
||||
signal
|
||||
</th>
|
||||
<th className="label py-3 pr-4 text-right font-normal">
|
||||
reference
|
||||
</th>
|
||||
<th className="label py-3 pr-4 text-right font-normal">
|
||||
tok/s ×1
|
||||
</th>
|
||||
<th className="label py-3 pr-4 text-right font-normal">
|
||||
peak tok/s
|
||||
</th>
|
||||
<th className="label py-3 text-right font-normal">measured</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{board.map((run) => {
|
||||
const v = run.verdict;
|
||||
return (
|
||||
<tr
|
||||
key={run.run_id}
|
||||
className="group border-b border-rule transition-colors hover:bg-panel-2"
|
||||
>
|
||||
<td className="py-4 pr-4">
|
||||
<Link
|
||||
href={`/run/${run.run_id.slice(0, 8)}`}
|
||||
className="block"
|
||||
>
|
||||
<span className="text-ink transition-colors group-hover:text-signal">
|
||||
{run.target.display_name}
|
||||
</span>
|
||||
<span className="mt-1 block text-[11px] text-dimmer">
|
||||
{fmtParams(run.target)} ·{" "}
|
||||
{run.target.quant?.toUpperCase()}
|
||||
</span>
|
||||
</Link>
|
||||
</td>
|
||||
<td className="py-4 pr-4 text-dim">{run.host.id}</td>
|
||||
<td className="readout py-4 pr-4 text-right text-signal">
|
||||
{v.signal_score != null ? v.signal_score.toFixed(3) : "—"}
|
||||
</td>
|
||||
<td className="readout py-4 pr-4 text-right text-reference">
|
||||
{v.reference_score != null
|
||||
? v.reference_score.toFixed(3)
|
||||
: "—"}
|
||||
</td>
|
||||
<td className="readout py-4 pr-4 text-right">
|
||||
{v.single_stream_tps?.toFixed(1) ?? "—"}
|
||||
</td>
|
||||
<td className="readout py-4 pr-4 text-right">
|
||||
{v.peak_throughput_tps?.toFixed(0) ?? "—"}
|
||||
</td>
|
||||
<td className="py-4 text-right text-dimmer">
|
||||
{run.timestamp.slice(0, 10)}
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div className="mt-8 grid gap-4 text-[11px] leading-relaxed text-dimmer sm:grid-cols-2">
|
||||
<p className="border-l border-signal-dim pl-4">
|
||||
<span className="text-signal">Signal</span> scores come from private
|
||||
tasks built from our own workloads. They are the measurement. The
|
||||
set is not published — a public test set gets scraped into the next
|
||||
training run and stops measuring anything.
|
||||
</p>
|
||||
<p className="border-l border-reference-dim pl-4">
|
||||
<span className="text-reference">Reference</span> scores come from
|
||||
public benchmarks, unmodified. They are calibration, not a ranking:
|
||||
if one lands far from its published value, our harness is wrong, not
|
||||
the model.
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,219 @@
|
||||
import Link from "next/link";
|
||||
import { notFound } from "next/navigation";
|
||||
import { ScopeTrace } from "@/components/ScopeTrace";
|
||||
import { fmtParams, getRun, getRuns, limitOf, type QualityResult } from "@/lib/results";
|
||||
|
||||
export const dynamic = "force-static";
|
||||
|
||||
export function generateStaticParams() {
|
||||
return getRuns().map((r) => ({ id: r.run_id.slice(0, 8) }));
|
||||
}
|
||||
|
||||
const TIER_TONE = {
|
||||
signal: { text: "text-signal", border: "border-signal-dim", chip: "bg-signal-dim/30" },
|
||||
reference: { text: "text-reference", border: "border-reference-dim", chip: "bg-reference-dim/30" },
|
||||
example: { text: "text-dim", border: "border-rule", chip: "bg-rule" },
|
||||
} as const;
|
||||
|
||||
/**
|
||||
* Per-sample outcome grid.
|
||||
*
|
||||
* This is the reason the result schema stores samples and not just aggregates:
|
||||
* an aggregate says a checkpoint got worse, this says which samples broke.
|
||||
*/
|
||||
function SampleGrid({ q }: { q: QualityResult }) {
|
||||
if (!q.samples.length) return null;
|
||||
return (
|
||||
<div className="mt-5">
|
||||
<div className="label mb-3">
|
||||
per-sample outcomes · {q.samples.filter((s) => s.passed).length}/{q.samples.length} passed
|
||||
</div>
|
||||
<div className="flex flex-wrap gap-[3px]">
|
||||
{q.samples.map((s) => (
|
||||
<span
|
||||
key={s.sample_id}
|
||||
title={`${s.sample_id} — ${s.passed ? "pass" : "fail"}`}
|
||||
className={`h-3 w-3 ${s.passed ? "bg-pass/70" : "bg-fail/80"}`}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default async function RunPage({ params }: { params: Promise<{ id: string }> }) {
|
||||
const { id } = await params;
|
||||
const run = getRun(id);
|
||||
if (!run) notFound();
|
||||
|
||||
const limit = limitOf(run);
|
||||
const perf = run.perf?.points ?? [];
|
||||
const flags = Object.entries(run.target.serving.flags ?? {});
|
||||
|
||||
return (
|
||||
<main className="mx-auto max-w-6xl px-6 py-14">
|
||||
<Link href="/" className="label transition-colors hover:text-signal">
|
||||
← board
|
||||
</Link>
|
||||
|
||||
<header className="rise mt-6 mb-10">
|
||||
<h1 className="font-display text-4xl leading-tight sm:text-5xl">
|
||||
{run.target.display_name}
|
||||
</h1>
|
||||
<p className="mt-3 font-mono text-xs text-dim">{run.target_id}</p>
|
||||
<div className="mt-6 flex flex-wrap gap-x-6 gap-y-2 text-[11px] text-dimmer">
|
||||
<span>{run.host.hardware}</span>
|
||||
<span>{fmtParams(run.target)}</span>
|
||||
<span>{run.target.quant?.toUpperCase()}</span>
|
||||
<span>ctx {run.target.serving.max_model_len?.toLocaleString()}</span>
|
||||
<span>measured {run.timestamp.slice(0, 16).replace("T", " ")}Z</span>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
{limit && (
|
||||
<div className="mb-10 border-l-2 border-signal bg-signal-dim/10 px-4 py-3 text-xs text-dim">
|
||||
<span className="text-signal">Capped run.</span> Reference tasks were
|
||||
limited to {limit} samples. Not comparable to a full-dataset score.
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="grid gap-10 lg:grid-cols-[1.15fr_1fr]">
|
||||
<section>
|
||||
<h2 className="font-display mb-5 text-2xl">Quality</h2>
|
||||
<div className="space-y-4">
|
||||
{run.quality.length === 0 && (
|
||||
<p className="text-xs text-dimmer">
|
||||
Perf-only run — no quality tasks were executed.
|
||||
</p>
|
||||
)}
|
||||
{run.quality.map((q) => {
|
||||
const tone = TIER_TONE[q.tier] ?? TIER_TONE.example;
|
||||
const acc = q.metrics?.accuracy;
|
||||
return (
|
||||
<div key={q.task} className={`panel border-l-2 ${tone.border} p-5`}>
|
||||
<div className="flex items-baseline justify-between gap-4">
|
||||
<div>
|
||||
<span className="text-sm text-ink">{q.task}</span>
|
||||
<span className={`ml-3 px-2 py-0.5 text-[10px] uppercase tracking-widest ${tone.chip} ${tone.text}`}>
|
||||
{q.tier}
|
||||
</span>
|
||||
</div>
|
||||
<span className={`readout font-display text-3xl ${tone.text}`}>
|
||||
{q.error ? "ERR" : acc != null ? acc.toFixed(3) : "—"}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{q.error ? (
|
||||
<p className="mt-3 font-mono text-[11px] leading-relaxed text-fail">
|
||||
{q.error.slice(0, 240)}
|
||||
</p>
|
||||
) : (
|
||||
<>
|
||||
<div className="mt-3 flex flex-wrap gap-x-5 gap-y-1 text-[11px] text-dimmer">
|
||||
<span>{q.n_samples} samples</span>
|
||||
{q.duration_s && <span>{Math.round(q.duration_s)}s</span>}
|
||||
{Object.entries(q.metrics)
|
||||
.filter(([k]) => !k.startsWith("_") && k !== "accuracy")
|
||||
.slice(0, 4)
|
||||
.map(([k, val]) => (
|
||||
<span key={k}>
|
||||
{k} <span className="text-dim">{val.toFixed(3)}</span>
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
<SampleGrid q={q} />
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2 className="font-display mb-5 text-2xl">Serving performance</h2>
|
||||
|
||||
{perf.length > 1 && (
|
||||
<div className="panel mb-5 p-5">
|
||||
<ScopeTrace points={perf} />
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="overflow-x-auto">
|
||||
<table className="w-full border-collapse text-[11px]">
|
||||
<thead>
|
||||
<tr className="border-b border-rule-bright text-left">
|
||||
<th className="label py-2 pr-3 font-normal">conc</th>
|
||||
<th className="label py-2 pr-3 text-right font-normal">tok/s all</th>
|
||||
<th className="label py-2 pr-3 text-right font-normal">/stream</th>
|
||||
<th className="label py-2 pr-3 text-right font-normal">ttft p50</th>
|
||||
<th className="label py-2 pr-3 text-right font-normal">ttft p95</th>
|
||||
<th className="label py-2 text-right font-normal">tpot</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{perf.map((p) => (
|
||||
<tr key={p.concurrency} className="border-b border-rule">
|
||||
<td className="readout py-2.5 pr-3">{p.concurrency}</td>
|
||||
<td className="readout py-2.5 pr-3 text-right text-signal">
|
||||
{p.output_tps_total.toFixed(1)}
|
||||
</td>
|
||||
<td className="readout py-2.5 pr-3 text-right">
|
||||
{p.output_tps_per_stream.toFixed(1)}
|
||||
</td>
|
||||
<td className="readout py-2.5 pr-3 text-right text-dim">
|
||||
{p.ttft_p50_ms ? `${Math.round(p.ttft_p50_ms)}ms` : "—"}
|
||||
</td>
|
||||
<td className="readout py-2.5 pr-3 text-right text-dim">
|
||||
{p.ttft_p95_ms ? `${Math.round(p.ttft_p95_ms)}ms` : "—"}
|
||||
</td>
|
||||
<td className="readout py-2.5 text-right text-dim">
|
||||
{p.tpot_p50_ms ? `${p.tpot_p50_ms.toFixed(1)}ms` : "—"}
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
{flags.length > 0 && (
|
||||
<div className="mt-8">
|
||||
<div className="label mb-3">serving configuration</div>
|
||||
<p className="mb-4 text-[11px] leading-relaxed text-dimmer">
|
||||
Results are only comparable between targets with identical flags.
|
||||
These are recorded from the live server, not from memory.
|
||||
</p>
|
||||
<dl className="grid grid-cols-[auto_1fr] gap-x-4 gap-y-1.5 font-mono text-[11px]">
|
||||
{flags.map(([k, v]) => (
|
||||
<div key={k} className="contents">
|
||||
<dt className="text-dimmer">{k}</dt>
|
||||
<dd className="text-dim">{String(v)}</dd>
|
||||
</div>
|
||||
))}
|
||||
</dl>
|
||||
</div>
|
||||
)}
|
||||
</section>
|
||||
</div>
|
||||
|
||||
{run.target.notes && (
|
||||
<section className="mt-14 border-t border-rule pt-8">
|
||||
<div className="label mb-3">notes</div>
|
||||
<p className="max-w-3xl text-xs leading-relaxed text-dim">{run.target.notes}</p>
|
||||
</section>
|
||||
)}
|
||||
|
||||
<section className="mt-10 flex flex-wrap items-center gap-4 border-t border-rule pt-8">
|
||||
<a
|
||||
href={`/api/card/${run.run_id.slice(0, 8)}`}
|
||||
className="border border-rule-bright px-4 py-2 text-xs transition-colors hover:border-signal hover:text-signal"
|
||||
>
|
||||
download score card ↓
|
||||
</a>
|
||||
<span className="text-[11px] text-dimmer">
|
||||
PNG, sized for sharing. Same image the link preview uses.
|
||||
</span>
|
||||
</section>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
"use client";
|
||||
|
||||
import { useState } from "react";
|
||||
|
||||
type State =
|
||||
| { kind: "idle" }
|
||||
| { kind: "sending" }
|
||||
| { kind: "ok"; message: string; model: string }
|
||||
| { kind: "error"; message: string };
|
||||
|
||||
export default function SubmitPage() {
|
||||
const [model, setModel] = useState("");
|
||||
const [notes, setNotes] = useState("");
|
||||
const [state, setState] = useState<State>({ kind: "idle" });
|
||||
|
||||
async function onSubmit(e: React.FormEvent) {
|
||||
e.preventDefault();
|
||||
setState({ kind: "sending" });
|
||||
try {
|
||||
const res = await fetch("/api/submit", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ model, notes }),
|
||||
});
|
||||
const data = await res.json();
|
||||
if (!res.ok) {
|
||||
setState({
|
||||
kind: "error",
|
||||
message: data.error ?? "Something went wrong.",
|
||||
});
|
||||
return;
|
||||
}
|
||||
setState({ kind: "ok", message: data.message, model: data.model });
|
||||
setModel("");
|
||||
setNotes("");
|
||||
} catch {
|
||||
setState({ kind: "error", message: "Network error." });
|
||||
}
|
||||
}
|
||||
|
||||
const busy = state.kind === "sending";
|
||||
|
||||
return (
|
||||
<main className="mx-auto max-w-3xl px-6 py-14">
|
||||
<div className="rise">
|
||||
<h1 className="font-display text-4xl leading-tight sm:text-5xl">
|
||||
Suggest a model
|
||||
</h1>
|
||||
<p className="mt-5 max-w-xl text-sm leading-relaxed text-dim">
|
||||
Share a Hugging Face model you think belongs in the lab. Karti reviews
|
||||
every suggestion, inspects the files and license, and decides what to
|
||||
run. Nothing is downloaded or executed automatically. No account
|
||||
needed.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<form
|
||||
onSubmit={onSubmit}
|
||||
className="rise panel mt-10 p-6 sm:p-8"
|
||||
style={{ animationDelay: "80ms" }}
|
||||
>
|
||||
<label className="label block" htmlFor="model">
|
||||
huggingface model
|
||||
</label>
|
||||
<input
|
||||
id="model"
|
||||
value={model}
|
||||
onChange={(e) => setModel(e.target.value)}
|
||||
placeholder="Qwen/Qwen3-8B"
|
||||
required
|
||||
spellCheck={false}
|
||||
className="mt-3 w-full border border-rule bg-void px-4 py-3 font-mono text-sm text-ink outline-none transition-colors placeholder:text-dimmer focus:border-signal"
|
||||
/>
|
||||
<p className="mt-2 text-[11px] text-dimmer">
|
||||
`owner/model`, or paste the full huggingface.co URL.
|
||||
</p>
|
||||
|
||||
<label className="label mt-8 block" htmlFor="notes">
|
||||
anything we should know{" "}
|
||||
<span className="text-dimmer">(optional)</span>
|
||||
</label>
|
||||
<textarea
|
||||
id="notes"
|
||||
value={notes}
|
||||
onChange={(e) => setNotes(e.target.value)}
|
||||
rows={3}
|
||||
placeholder="Recommended quant, chat template quirks, what it is meant to be good at…"
|
||||
className="mt-3 w-full resize-y border border-rule bg-void px-4 py-3 font-mono text-xs text-ink outline-none transition-colors placeholder:text-dimmer focus:border-signal"
|
||||
/>
|
||||
|
||||
<button
|
||||
type="submit"
|
||||
disabled={busy || !model.trim()}
|
||||
className="mt-8 w-full border border-rule-bright px-4 py-3 text-xs uppercase tracking-widest transition-colors hover:border-signal hover:text-signal disabled:cursor-not-allowed disabled:opacity-40 disabled:hover:border-rule-bright disabled:hover:text-ink"
|
||||
>
|
||||
{busy ? "sending suggestion…" : "send suggestion"}
|
||||
</button>
|
||||
|
||||
{state.kind === "error" && (
|
||||
<p className="mt-5 border-l-2 border-fail px-4 py-2 text-xs leading-relaxed text-fail">
|
||||
{state.message}
|
||||
</p>
|
||||
)}
|
||||
{state.kind === "ok" && (
|
||||
<div className="mt-5 border-l-2 border-pass px-4 py-2">
|
||||
<p className="text-xs text-pass">{state.model} suggested.</p>
|
||||
<p className="mt-1 text-[11px] leading-relaxed text-dim">
|
||||
{state.message}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</form>
|
||||
|
||||
<section className="mt-12 grid gap-8 sm:grid-cols-2">
|
||||
<div>
|
||||
<h2 className="label mb-4">what helps review</h2>
|
||||
<ul className="space-y-3 text-[11px] leading-relaxed text-dim">
|
||||
<li className="border-l border-rule pl-4">
|
||||
<span className="text-ink">safetensors weights.</span> We do not
|
||||
load `.bin` checkpoints — they are pickles, and loading one is
|
||||
remote code execution on our hardware.
|
||||
</li>
|
||||
<li className="border-l border-rule pl-4">
|
||||
<span className="text-ink">A clear source.</span> Public or gated
|
||||
is fine to suggest; access is reviewed manually.
|
||||
</li>
|
||||
<li className="border-l border-rule pl-4">
|
||||
<span className="text-ink">Useful context.</span> Tell us the
|
||||
recommended quant, license, and what the model is meant to do.
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div>
|
||||
<h2 className="label mb-4">what happens next</h2>
|
||||
<ul className="space-y-3 text-[11px] leading-relaxed text-dim">
|
||||
<li className="border-l border-rule pl-4">
|
||||
If Karti approves and schedules it, Lumbridge Bench records
|
||||
quality and serving performance on named hardware with the exact
|
||||
serving flags.
|
||||
</li>
|
||||
<li className="border-l border-rule pl-4">
|
||||
Private eval samples and internal review notes never become
|
||||
public. Selected aggregate score cards can be published after
|
||||
review.
|
||||
</li>
|
||||
<li className="border-l border-rule pl-4">
|
||||
A suggestion is not a promise to run a model. There is no
|
||||
automatic queue or public-code worker.
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,134 @@
|
||||
import type { PerfPoint } from "@/lib/results";
|
||||
|
||||
/**
|
||||
* Aggregate throughput plotted against concurrency, drawn as a scope sweep.
|
||||
*
|
||||
* The x axis is logarithmic because concurrency is sampled in octaves
|
||||
* (1, 8, 32). On a linear axis the low-concurrency points — the ones that
|
||||
* describe what a single user actually feels — collapse against the origin
|
||||
* and the most important part of the curve becomes unreadable.
|
||||
*/
|
||||
export function ScopeTrace({
|
||||
points,
|
||||
width = 520,
|
||||
height = 150,
|
||||
showAxis = true,
|
||||
}: {
|
||||
points: PerfPoint[];
|
||||
width?: number;
|
||||
height?: number;
|
||||
showAxis?: boolean;
|
||||
}) {
|
||||
if (points.length < 2) return null;
|
||||
|
||||
const pad = { top: 14, right: 14, bottom: showAxis ? 22 : 10, left: showAxis ? 46 : 10 };
|
||||
const w = width - pad.left - pad.right;
|
||||
const h = height - pad.top - pad.bottom;
|
||||
|
||||
const xs = points.map((p) => Math.log2(Math.max(1, p.concurrency)));
|
||||
const ys = points.map((p) => p.output_tps_total);
|
||||
const xMax = Math.max(...xs) || 1;
|
||||
const yMax = Math.max(...ys) * 1.15;
|
||||
|
||||
const px = (i: number) => pad.left + (xs[i] / xMax) * w;
|
||||
const py = (v: number) => pad.top + h - (v / yMax) * h;
|
||||
|
||||
const path = points.map((p, i) => `${i === 0 ? "M" : "L"}${px(i)},${py(p.output_tps_total)}`).join(" ");
|
||||
const area = `${path} L${px(points.length - 1)},${pad.top + h} L${px(0)},${pad.top + h} Z`;
|
||||
|
||||
// Rough path length so the draw-on animation has a dash offset to work with.
|
||||
const traceLen = Math.round(w * 1.6);
|
||||
|
||||
const gridLines = [0, 0.25, 0.5, 0.75, 1];
|
||||
|
||||
return (
|
||||
<svg
|
||||
viewBox={`0 0 ${width} ${height}`}
|
||||
className="w-full"
|
||||
role="img"
|
||||
aria-label="Aggregate throughput against concurrency"
|
||||
>
|
||||
<defs>
|
||||
<linearGradient id="trace-fill" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0%" stopColor="var(--color-signal)" stopOpacity="0.22" />
|
||||
<stop offset="100%" stopColor="var(--color-signal)" stopOpacity="0" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
|
||||
{gridLines.map((g) => (
|
||||
<line
|
||||
key={g}
|
||||
x1={pad.left}
|
||||
x2={pad.left + w}
|
||||
y1={pad.top + h - g * h}
|
||||
y2={pad.top + h - g * h}
|
||||
stroke="var(--color-rule)"
|
||||
strokeWidth="1"
|
||||
/>
|
||||
))}
|
||||
|
||||
{showAxis &&
|
||||
gridLines
|
||||
.filter((g) => g > 0)
|
||||
.map((g) => (
|
||||
<text
|
||||
key={`l${g}`}
|
||||
x={pad.left - 8}
|
||||
y={pad.top + h - g * h + 3}
|
||||
textAnchor="end"
|
||||
className="fill-[var(--color-dimmer)]"
|
||||
style={{ fontSize: 9, fontFamily: "var(--font-mono)" }}
|
||||
>
|
||||
{Math.round(yMax * g)}
|
||||
</text>
|
||||
))}
|
||||
|
||||
<path d={area} fill="url(#trace-fill)" />
|
||||
<path
|
||||
d={path}
|
||||
fill="none"
|
||||
stroke="var(--color-signal)"
|
||||
strokeWidth="1.75"
|
||||
strokeLinejoin="round"
|
||||
strokeLinecap="round"
|
||||
className="trace"
|
||||
style={{ ["--trace-len" as string]: traceLen }}
|
||||
/>
|
||||
|
||||
{points.map((p, i) => (
|
||||
<g key={p.concurrency}>
|
||||
<circle
|
||||
cx={px(i)}
|
||||
cy={py(p.output_tps_total)}
|
||||
r="3"
|
||||
fill="var(--color-void)"
|
||||
stroke="var(--color-signal)"
|
||||
strokeWidth="1.5"
|
||||
/>
|
||||
{showAxis && (
|
||||
<text
|
||||
x={px(i)}
|
||||
y={height - 6}
|
||||
textAnchor="middle"
|
||||
className="fill-[var(--color-dimmer)]"
|
||||
style={{ fontSize: 9, fontFamily: "var(--font-mono)" }}
|
||||
>
|
||||
{p.concurrency}
|
||||
</text>
|
||||
)}
|
||||
</g>
|
||||
))}
|
||||
|
||||
{showAxis && (
|
||||
<text
|
||||
x={pad.left}
|
||||
y={11}
|
||||
className="fill-[var(--color-dimmer)]"
|
||||
style={{ fontSize: 9, letterSpacing: "0.14em", fontFamily: "var(--font-mono)" }}
|
||||
>
|
||||
TOK/S AGGREGATE ↑ / CONCURRENCY →
|
||||
</text>
|
||||
)}
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
/**
|
||||
* Normalize a public Hugging Face reference before it becomes an inert
|
||||
* Lumbridge model-suggestion record.
|
||||
*/
|
||||
export function parseHuggingFaceRef(input: string): string | null {
|
||||
const trimmed = input.trim().replace(/\/+$/, "");
|
||||
const url = trimmed
|
||||
.replace(/^https?:\/\/(www\.)?huggingface\.co\//i, "")
|
||||
.replace(/^hf:\/\//i, "");
|
||||
// owner/model — letters, digits, dot, dash, underscore.
|
||||
return /^[\w.-]+\/[\w.-]+$/.test(url) ? url : null;
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
|
||||
/**
|
||||
* The leaderboard is a view over committed JSON, not a database.
|
||||
*
|
||||
* results/*.json is the record of every run and it lives in git, so the site
|
||||
* has no state of its own and cannot drift from what was actually measured.
|
||||
* Supabase enters only for submissions and auth, which are genuinely mutable.
|
||||
*/
|
||||
|
||||
const RESULTS_DIR = path.join(process.cwd(), "..", "results");
|
||||
|
||||
export type SampleOutcome = {
|
||||
sample_id: string;
|
||||
score: number;
|
||||
passed: boolean;
|
||||
excerpt: string | null;
|
||||
metadata: Record<string, unknown>;
|
||||
};
|
||||
|
||||
export type QualityResult = {
|
||||
task: string;
|
||||
tier: "reference" | "signal" | "example";
|
||||
dataset_version: string | null;
|
||||
n_samples: number;
|
||||
metrics: Record<string, number>;
|
||||
samples: SampleOutcome[];
|
||||
duration_s: number | null;
|
||||
error: string | null;
|
||||
};
|
||||
|
||||
export type PerfPoint = {
|
||||
concurrency: number;
|
||||
input_tokens: number;
|
||||
output_tokens: number;
|
||||
completed: number;
|
||||
failed: number;
|
||||
output_tps_total: number;
|
||||
output_tps_per_stream: number;
|
||||
ttft_p50_ms: number | null;
|
||||
ttft_p95_ms: number | null;
|
||||
tpot_p50_ms: number | null;
|
||||
prefill_tps: number | null;
|
||||
};
|
||||
|
||||
export type Run = {
|
||||
run_id: string;
|
||||
timestamp: string;
|
||||
target_id: string;
|
||||
target: {
|
||||
display_name: string;
|
||||
family: string | null;
|
||||
params_b: number | null;
|
||||
active_params_b: number | null;
|
||||
quant: string | null;
|
||||
checkpoint: string | null;
|
||||
tier: string;
|
||||
host: string;
|
||||
slug: string;
|
||||
notes: string | null;
|
||||
serving: {
|
||||
engine: string;
|
||||
model_name: string;
|
||||
max_model_len: number | null;
|
||||
flags: Record<string, unknown>;
|
||||
};
|
||||
};
|
||||
host: { id: string; hardware: string | null; memory_gb: number | null };
|
||||
quality: QualityResult[];
|
||||
perf: { engine: string; points: PerfPoint[] } | null;
|
||||
verdict: {
|
||||
signal_score: number | null;
|
||||
reference_score: number | null;
|
||||
single_stream_tps?: number;
|
||||
interactive_viable?: boolean;
|
||||
peak_throughput_tps?: number;
|
||||
peak_throughput_concurrency?: number;
|
||||
};
|
||||
schema_version: number;
|
||||
};
|
||||
|
||||
export function getRuns(): Run[] {
|
||||
if (!fs.existsSync(RESULTS_DIR)) return [];
|
||||
return fs
|
||||
.readdirSync(RESULTS_DIR)
|
||||
.filter((f) => f.endsWith(".json"))
|
||||
.map((f) => JSON.parse(fs.readFileSync(path.join(RESULTS_DIR, f), "utf8")) as Run)
|
||||
.sort((a, b) => b.timestamp.localeCompare(a.timestamp));
|
||||
}
|
||||
|
||||
export function getRun(id: string): Run | undefined {
|
||||
return getRuns().find((r) => r.run_id.startsWith(id));
|
||||
}
|
||||
|
||||
/**
|
||||
* Latest run per target — what the leaderboard ranks.
|
||||
*
|
||||
* Ranking every run would let a target dominate the board simply by being
|
||||
* measured more often.
|
||||
*/
|
||||
export function latestPerTarget(runs: Run[]): Run[] {
|
||||
const seen = new Map<string, Run>();
|
||||
for (const run of runs) {
|
||||
if (!seen.has(run.target_id)) seen.set(run.target_id, run);
|
||||
}
|
||||
return [...seen.values()];
|
||||
}
|
||||
|
||||
/** Was any task in this run capped with --limit? Must never be hidden. */
|
||||
export function limitOf(run: Run): number | null {
|
||||
for (const q of run.quality) {
|
||||
if (q.metrics?._limit) return q.metrics._limit;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
export function fmtParams(t: Run["target"]): string {
|
||||
if (!t.params_b) return "—";
|
||||
const base = `${t.params_b}B`;
|
||||
return t.active_params_b ? `${base}·${t.active_params_b}B active` : base;
|
||||
}
|
||||
Vendored
+6
@@ -0,0 +1,6 @@
|
||||
/// <reference types="next" />
|
||||
/// <reference types="next/image-types/global" />
|
||||
import "./.next/types/routes.d.ts";
|
||||
|
||||
// NOTE: This file should not be edited
|
||||
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
|
||||
@@ -0,0 +1,17 @@
|
||||
import path from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
const siteDir = path.dirname(fileURLToPath(import.meta.url));
|
||||
|
||||
/** @type {import('next').NextConfig} */
|
||||
const nextConfig = {
|
||||
outputFileTracingRoot: path.join(siteDir, ".."),
|
||||
outputFileTracingIncludes: {
|
||||
// Score cards are read from the repo's results/ directory at build time.
|
||||
// There is no database behind the leaderboard: the git history IS the
|
||||
// record, so the site is a view over committed JSON.
|
||||
"/**": ["../results/**"],
|
||||
},
|
||||
};
|
||||
|
||||
export default nextConfig;
|
||||
Generated
+1676
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"name": "lumbridge-bench-site",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "next dev -p 8909",
|
||||
"build": "next build",
|
||||
"start": "next start -p 8909",
|
||||
"lint": "tsc --noEmit"
|
||||
},
|
||||
"dependencies": {
|
||||
"next": "16.2.11",
|
||||
"react": "19.2.3",
|
||||
"react-dom": "19.2.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tailwindcss/postcss": "^4.0.0",
|
||||
"@types/node": "^22",
|
||||
"@types/react": "^19",
|
||||
"@types/react-dom": "^19",
|
||||
"postcss": "8.5.25",
|
||||
"tailwindcss": "^4.0.0",
|
||||
"typescript": "^5"
|
||||
},
|
||||
"overrides": {
|
||||
"postcss": "8.5.25",
|
||||
"sharp": "0.35.3"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
export default {
|
||||
plugins: {
|
||||
"@tailwindcss/postcss": {},
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,33 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2022",
|
||||
"lib": ["dom", "dom.iterable", "esnext"],
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"strict": true,
|
||||
"noEmit": true,
|
||||
"esModuleInterop": true,
|
||||
"module": "esnext",
|
||||
"moduleResolution": "bundler",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"jsx": "react-jsx",
|
||||
"incremental": true,
|
||||
"plugins": [
|
||||
{
|
||||
"name": "next"
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"@/*": ["./*"]
|
||||
}
|
||||
},
|
||||
"include": [
|
||||
"next-env.d.ts",
|
||||
"**/*.ts",
|
||||
"**/*.tsx",
|
||||
".next/types/**/*.ts",
|
||||
".next/dev/types/**/*.ts"
|
||||
],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||
Reference in New Issue
Block a user