MedASR-Bench / src /components /model /GeneralizationStrip.tsx
ngoan
MedASR Bench: full platform + Hugging Face Docker Space packaging
d70f132
Raw
History Blame Contribute Delete
4.92 kB
import clsx from "clsx";
import { FlaskConical } from "lucide-react";
import { fmt } from "@/lib/data";
import type { ModelRow, Provenance, SplitInfo, SplitMetrics } from "@/lib/types";
import SectionLabel from "./SectionLabel";
export interface GeneralizationStripProps {
model: ModelRow;
hardInfo: SplitInfo;
}
function hasNumbers(m?: SplitMetrics): boolean {
return !!m && Object.values(m).some((v) => typeof v === "number");
}
function DeltaTile({
label,
test,
hard,
}: {
label: string;
test?: number;
hard?: number;
}) {
const d =
typeof test === "number" && typeof hard === "number" ? hard - test : null;
const degrading = d !== null && d > 0;
return (
<div className="hairline depth-1 hover-lift flex min-w-[160px] flex-col gap-1 rounded-sm bg-surface px-4 py-3">
<span className="text-[11px] uppercase tracking-[0.14em] text-muted">
Δ {label}
</span>
<span
className={clsx(
"num text-2xl leading-none",
d === null ? "text-muted/50" : degrading ? "text-warn" : "text-text",
)}
>
{d === null ? "—" : `${d >= 0 ? "+" : "−"}${Math.abs(d).toFixed(2)}`}
</span>
<span className="num text-[11px] text-muted">
{fmt(test)} → {fmt(hard)}
</span>
</div>
);
}
function GhostTile({ label }: { label: string }) {
return (
<div
aria-hidden
className="flex min-w-[160px] flex-col gap-1 rounded-sm border border-dashed border-line px-4 py-3"
>
<span className="text-[11px] uppercase tracking-[0.14em] text-muted/70">
Δ {label}
</span>
<span className="num text-2xl leading-none text-muted/50"></span>
<span className="num text-[11px] text-muted/50">test → hard</span>
</div>
);
}
function gapCopy(provenance: Provenance): string {
if (provenance === "running")
return "This model’s pilot run is executing on Test + Hard right now — the delta lands here the moment it commits.";
if (provenance === "planned")
return "A platform run covering Test + Hard is on the roadmap for this model.";
return "Published results cover Test only; the platform pilot re-runs every model on Test + Hard, and this gap closes when its run lands.";
}
/**
* Test → Hard generalization: signed delta tiles when both splits are
* measured (amber when degrading), otherwise a dashed evidence-gap callout
* with ghost tiles showing the shape of what will appear.
*/
export default function GeneralizationStrip({
model,
hardInfo,
}: GeneralizationStripProps) {
const test = model.metrics?.test;
const hard = model.metrics?.hard;
const measured = hasNumbers(test) && hasNumbers(hard);
return (
<section className="flex flex-col gap-4">
<SectionLabel
index="03"
title="Generalization"
hint="Test → Hard delta"
/>
{measured ? (
<div className="flex flex-col gap-5 md:flex-row md:items-center md:justify-between">
<div className="flex flex-wrap gap-3">
<DeltaTile label="WER" test={test?.wer} hard={hard?.wer} />
<DeltaTile label="CS-WER" test={test?.csWer} hard={hard?.csWer} />
</div>
<p className="max-w-sm text-[13px] leading-relaxed text-muted">
The Hard split holds{" "}
<span className="num">{fmt(hardInfo.hours)}</span>
<span className="num text-[11px]">{" "}h</span> of rare and
unseen code-switched terms. A positive Δ (amber) means the model
degrades off the common-term distribution.
</p>
</div>
) : (
<div className="flex flex-col gap-5 rounded-sm border border-dashed border-line p-5 md:flex-row md:items-center md:justify-between md:p-6">
<div className="flex max-w-xl items-start gap-4">
<span aria-hidden className="mt-1 shrink-0 text-muted">
<FlaskConical size={18} />
</span>
<div>
<h3 className="font-display text-lg italic text-text">
Evidence gap — no Hard-split measurement yet
</h3>
<p className="mt-1.5 text-[13px] leading-relaxed text-muted">
The Hard split holds{" "}
<span className="num">{fmt(hardInfo.hours)}</span>
<span className="num text-[11px]">{" "}h</span> /{" "}
<span className="num">
{hardInfo.utts.toLocaleString("en-US")}
</span>{" "}
utterances of rare and unseen code-switched terms — the
generalization leaderboard. {gapCopy(model.provenance)}
</p>
</div>
</div>
<div className="flex shrink-0 flex-wrap gap-3">
<GhostTile label="WER" />
<GhostTile label="CS-WER" />
</div>
</div>
)}
</section>
);
}