import type { CompoundExample, Robustness, TermRow } from "../types"; const W = 660; const H = 266; // Left and bottom carry a tick column plus an axis title. const PAD = { l: 64, r: 16, t: 16, b: 58 }; const AXIS = "rgba(9,15,21,.55)"; /** Recall against description coverage. CLAP is flat; it never read the text. */ function RecallChart({ points, clap, chance, }: { points: { setting: number; names: boolean; recall: number }[]; clap: number; chance: number; }) { const max = Math.max(clap, ...points.map((p) => p.recall)) * 1.3; const x = (i: number) => PAD.l + (i * (W - PAD.l - PAD.r)) / (points.length - 1); const y = (v: number) => H - PAD.b - (v / max) * (H - PAD.t - PAD.b); const bm = points.map((p, i) => `${i ? "L" : "M"}${x(i)},${y(p.recall)}`).join(" "); return (
{[0, 0.25, 0.5, 0.75, 1].map((f) => ( {(max * f).toFixed(2)} ))} chance {points.map((p, i) => ( ))} {points.map((p, i) => ( {Math.round(p.setting * 100)}%{p.names ? "" : " ✂"} ))} description metadata remaining recall — share of the 205 matches found
✂ marks the stages where names are deleted too, not just descriptions CLAP BM25
); } /** P@10 per query term, against the prevalence of the label it asks for. */ function Terms({ rows }: { rows: TermRow[] }) { return (
{rows.map((r) => { const joint = r.truth === "conjunction"; return ( ); })}
query term ground-truth CLAP BM25 prevalence
{r.term} {r.truth ?? "no label"} {r.clap === undefined ? "—" : r.clap.toFixed(2)} {r.bm25 === undefined ? "—" : r.bm25.toFixed(2)} {r.prevalence === undefined ? "—" : r.prevalence.toFixed(3)}
); } function Examples({ rows }: { rows: CompoundExample[] }) { return (
{rows.map((e) => ( ))}
voice gender age use case
{e.match ? "✓" : "✗"} {e.name} {e.gender} {(e.age ?? "").replace(/_/g, " ")} {(e.use_case ?? "").replace(/_/g, " ")}
); } export function CompoundCard({ data }: { data: Robustness["compound"] }) { if (!data) return null; return (

Comparison with BM25 under metadata ablation

Vector search (CLAP) is compared against text-based BM25 search. Query: “{data.query}”. BM25 degrades as descriptions are deleted first, then names.

Precision@10, term by term

The conjunction is{" "} {(data.clap.p10 / data.chance).toFixed(1)}× chance.

Recall as metadata is deleted

Vector search is stable even when metadata is sparse.

What each returns

CLAP

BM25, descriptions deleted

With descriptions gone, BM25 returns voices with narrator or{" "} audiobook in their title.

BM25, descriptions and names deleted

Nothing left to match on: every score is zero.

); }