Leon4gr45 commited on
Commit
b2db539
·
verified ·
1 Parent(s): 1cf0807

Delete web/src/app/experiments/ExperimentsView.tsx with huggingface_hub

Browse files
web/src/app/experiments/ExperimentsView.tsx DELETED
@@ -1,260 +0,0 @@
1
- "use client";
2
-
3
- import { useEffect, useMemo, useState } from "react";
4
- import { getJSON, type Experiments, type GridRow, type PerQuery, type View } from "@/lib/api";
5
- import { bytes, fmtTs, pct } from "@/lib/format";
6
- import { VIEW_INFO, VIEW_ORDER } from "@/lib/views";
7
- import { ClipPlayer, type ClipRef } from "@/components/ClipPlayer";
8
- import { Segmented, ViewSwatch } from "@/components/Controls";
9
- import { Heatmap } from "@/components/charts/Heatmap";
10
- import { MetricByDimChart, type Point } from "@/components/charts/MetricByDimChart";
11
-
12
- const METRICS = ["recall@1", "recall@5", "recall@10", "mrr@10", "neighbor_recall@5", "video_hit@1"] as const;
13
- type Metric = (typeof METRICS)[number];
14
- const METRIC_HELP: Record<Metric, string> = {
15
- "recall@1": "Exact source segment ranked first",
16
- "recall@5": "Exact source segment in the top 5",
17
- "recall@10": "Exact source segment in the top 10",
18
- "mrr@10": "Mean reciprocal rank of the source segment (0 if outside top 10)",
19
- "neighbor_recall@5": "Source segment or an adjacent one (±30s) in the top 5",
20
- "video_hit@1": "Top result comes from the right video",
21
- };
22
- const QTYPES = ["all", "spoken", "visual"] as const;
23
- type QType = (typeof QTYPES)[number];
24
-
25
- function Tile({ label, value, sub }: { label: string; value: string; sub?: string }) {
26
- return (
27
- <div className="rounded-xl border border-line bg-surface p-4">
28
- <div className="text-xs text-muted">{label}</div>
29
- <div className="mt-1 text-2xl font-semibold">{value}</div>
30
- {sub && <div className="mt-0.5 text-xs text-ink-2">{sub}</div>}
31
- </div>
32
- );
33
- }
34
-
35
- function rankCell(rank: number | undefined) {
36
- if (rank == null) return <td />;
37
- const good = rank === 1 ? "bg-[#1c5cab] text-white" : rank <= 5 ? "bg-[#104281] text-white" : rank <= 10 ? "bg-surface-2 text-ink-2" : "text-muted";
38
- return <td className={`rounded px-2 py-1 text-right text-xs tabular ${good}`}>{rank > 99 ? "99+" : rank}</td>;
39
- }
40
-
41
- export function ExperimentsView() {
42
- const [data, setData] = useState<Experiments | null>(null);
43
- const [error, setError] = useState<string | null>(null);
44
- const [metric, setMetric] = useState<Metric>("recall@5");
45
- const [qtype, setQtype] = useState<QType>("all");
46
- const [heatMode, setHeatMode] = useState<"absolute" | "retained">("absolute");
47
- const [hoverView, setHoverView] = useState<View | null>(null);
48
- const [tableDim, setTableDim] = useState(768);
49
- const [filter, setFilter] = useState("");
50
- const [clip, setClip] = useState<ClipRef | null>(null);
51
- const [transcripts, setTranscripts] = useState<string | null>(null);
52
-
53
- useEffect(() => {
54
- getJSON<Experiments>(`/api/experiments${transcripts ? `?transcripts=${transcripts}` : ""}`)
55
- .then(setData)
56
- .catch((e) => setError(String(e)));
57
- }, [transcripts]);
58
-
59
- const views = useMemo(() => VIEW_ORDER.filter((v) => data?.views.includes(v)), [data]);
60
- const dimsAsc = useMemo(() => [...(data?.dims ?? [])].sort((a, b) => a - b), [data]);
61
- const cell = useMemo(() => {
62
- const m = new Map<string, GridRow>();
63
- data?.grid.forEach((g) => m.set(`${g.view}|${g.dim}|${g.query_type}`, g));
64
- return (view: View, dim: number) => m.get(`${view}|${dim}|${qtype}`);
65
- }, [data, qtype]);
66
-
67
- if (error) return <p className="text-sm text-ink-2">{error}</p>;
68
- if (!data) return <p className="text-sm text-muted">Loading…</p>;
69
-
70
- const points: Point[] = dimsAsc.map((d) => ({ dim: d, ...Object.fromEntries(views.map((v) => [v, cell(v, d)?.[metric] ?? 0])) }));
71
- const best = data.grid.filter((g) => g.query_type === qtype).sort((a, b) => b[metric] - a[metric])[0];
72
- const best768 = (v: View) => cell(v, 768)?.[metric] ?? 0;
73
- const fused256 = cell("fused", 256)?.[metric];
74
- const fused768 = best768("fused");
75
- const bestView = views.reduce((a, v) => (best768(v) > best768(a) ? v : a), views[0]);
76
-
77
- const queries = data.per_query.filter(
78
- (q) => (qtype === "all" || q.type === qtype) && (!filter || q.query.toLowerCase().includes(filter.toLowerCase())),
79
- );
80
-
81
- return (
82
- <div className="space-y-8">
83
- <section>
84
- <h1 className="text-2xl font-semibold">Experiments</h1>
85
- <p className="mt-1 max-w-3xl text-sm text-ink-2">
86
- {data.num_queries} queries ({data.query_types.spoken} spoken, {data.query_types.visual} visual) written by Gemma 4 12B after
87
- watching sampled segments, each with one known source segment, run against {data.num_segments} segments for every embedding
88
- view and Matryoshka dimension, with {data.transcripts === "whisper" ? "Whisper" : data.transcripts === "gemma" ? "Gemma 4" : "unlabelled"}{" "}
89
- transcripts behind the text and fused views. Run {new Date(data.created_at).toLocaleString()}.
90
- </p>
91
- </section>
92
-
93
- {/* Filters: one row, scope everything below */}
94
- <div className="flex flex-wrap items-center gap-x-6 gap-y-3 rounded-xl border border-line bg-surface p-3">
95
- <Segmented label="Metric" value={metric} options={METRICS} onChange={setMetric} />
96
- <Segmented label="Queries" value={qtype} options={QTYPES} onChange={setQtype} />
97
- {data.variants && data.variants.length > 1 && data.transcripts && (
98
- <Segmented
99
- label="Transcripts"
100
- value={data.transcripts}
101
- options={data.variants}
102
- onChange={setTranscripts}
103
- render={(e) => (e === "gemma" ? "Gemma 4" : e === "whisper" ? "Whisper" : e)}
104
- />
105
- )}
106
- <span className="text-xs text-muted">{METRIC_HELP[metric]}</span>
107
- </div>
108
-
109
- <section className="grid grid-cols-2 gap-3 md:grid-cols-4">
110
- <Tile label={`Best config · ${metric}`} value={best ? best[metric].toFixed(3) : "–"} sub={best ? `${VIEW_INFO[best.view].label} @ ${best.dim}d` : ""} />
111
- <Tile label="Best view at 768d" value={VIEW_INFO[bestView].label} sub={`${best768(bestView).toFixed(3)} ${metric}`} />
112
- <Tile
113
- label="Fused at 256d keeps"
114
- value={fused256 != null && fused768 ? pct(fused256 / fused768) : "–"}
115
- sub={`of 768d ${metric} at 1/3 the storage`}
116
- />
117
- <Tile label="Query embed" value={`${data.query_embed_ms.toFixed(1)} ms`} sub="per query (batched, bf16)" />
118
- </section>
119
-
120
- <section className="rounded-xl border border-line bg-surface p-5">
121
- <div className="flex flex-wrap items-baseline justify-between gap-2">
122
- <h2 className="font-medium">
123
- {metric} by Matryoshka dimension <span className="text-sm font-normal text-muted">· {qtype} queries</span>
124
- </h2>
125
- </div>
126
- {/* Legend (always present for >1 series); hover a key to emphasise its line. */}
127
- <div className="mt-3 flex flex-wrap gap-x-5 gap-y-1">
128
- {views.map((v) => (
129
- <button
130
- key={v}
131
- onMouseEnter={() => setHoverView(v)}
132
- onMouseLeave={() => setHoverView(null)}
133
- onFocus={() => setHoverView(v)}
134
- onBlur={() => setHoverView(null)}
135
- className="flex items-center gap-1.5 text-sm text-ink-2"
136
- >
137
- <span className="inline-block h-0.5 w-4 rounded" style={{ background: VIEW_INFO[v].color }} />
138
- {VIEW_INFO[v].label}
139
- <span className="text-xs text-muted">{VIEW_INFO[v].blurb}</span>
140
- </button>
141
- ))}
142
- </div>
143
- <MetricByDimChart data={points} views={views} metric={metric} highlight={hoverView} />
144
- </section>
145
-
146
- <section className="grid gap-6 lg:grid-cols-[1fr_380px]">
147
- <div className="rounded-xl border border-line bg-surface p-5">
148
- <div className="mb-3 flex flex-wrap items-center justify-between gap-2">
149
- <h2 className="font-medium">
150
- {heatMode === "absolute" ? metric : `% of 768d ${metric} retained`}{" "}
151
- <span className="text-sm font-normal text-muted">· {qtype} queries</span>
152
- </h2>
153
- <Segmented label="Show" value={heatMode} options={["absolute", "retained"] as const} onChange={setHeatMode} />
154
- </div>
155
- <Heatmap
156
- views={views}
157
- dims={[...dimsAsc].reverse()}
158
- value={(v, d) => {
159
- const x = cell(v, d)?.[metric];
160
- if (x == null) return undefined;
161
- if (heatMode === "absolute") return x;
162
- const base = cell(v, 768)?.[metric];
163
- return base ? x / base : undefined;
164
- }}
165
- format={(x) => (heatMode === "absolute" ? x.toFixed(3) : pct(x))}
166
- />
167
- </div>
168
- <div className="rounded-xl border border-line bg-surface p-5">
169
- <h2 className="mb-3 font-medium">Storage (float32)</h2>
170
- <table className="w-full text-sm">
171
- <thead>
172
- <tr className="text-xs text-muted">
173
- <th className="pb-2 text-left font-normal">Dims</th>
174
- <th className="pb-2 text-right font-normal">Per vector</th>
175
- <th className="pb-2 text-right font-normal">Per view</th>
176
- <th className="pb-2 text-right font-normal">All views</th>
177
- </tr>
178
- </thead>
179
- <tbody className="tabular">
180
- {data.storage.map((s) => (
181
- <tr key={s.dim} className="border-t border-line">
182
- <td className="py-2">
183
- {s.dim}d <span className="text-xs text-muted">{s.dim === 768 ? "" : `÷${(768 / s.dim).toFixed(1)}`}</span>
184
- </td>
185
- <td className="py-2 text-right">{bytes(s.bytes_per_vector)}</td>
186
- <td className="py-2 text-right">{bytes(s.bytes_per_view)}</td>
187
- <td className="py-2 text-right">{bytes(s.bytes_all_views)}</td>
188
- </tr>
189
- ))}
190
- </tbody>
191
- </table>
192
- <p className="mt-3 text-xs text-muted">
193
- Tiny at this corpus size; the ratios are what matter at scale. Model card: ~lossless to 256d, 128d drops sharply for
194
- multimodal inputs.
195
- </p>
196
- </div>
197
- </section>
198
-
199
- <section className="rounded-xl border border-line bg-surface p-5">
200
- <div className="mb-3 flex flex-wrap items-center gap-4">
201
- <h2 className="font-medium">Per-query ranks</h2>
202
- <Segmented label="Dims" value={tableDim} options={data.dims} onChange={setTableDim} render={(d) => `${d}d`} />
203
- <input
204
- value={filter}
205
- onChange={(e) => setFilter(e.target.value)}
206
- placeholder="Filter queries…"
207
- className="ml-auto w-64 rounded-lg border border-line bg-page px-3 py-1.5 text-sm outline-none focus:border-accent"
208
- />
209
- </div>
210
- <p className="mb-3 text-xs text-muted">Rank of the source segment for each view (1 = perfect). Click a row to play the source segment.</p>
211
- <div className="max-h-[560px] overflow-y-auto">
212
- <table className="w-full border-separate text-sm" style={{ borderSpacing: "2px" }}>
213
- <thead className="sticky top-0 bg-surface">
214
- <tr className="text-xs text-muted">
215
- <th className="py-1 text-left font-normal">Query</th>
216
- <th className="py-1 text-left font-normal">Type</th>
217
- {views.map((v) => (
218
- <th key={v} className="py-1 text-right font-normal">
219
- <span className="inline-flex items-center gap-1">
220
- <ViewSwatch view={v} />
221
- {VIEW_INFO[v].label}
222
- </span>
223
- </th>
224
- ))}
225
- </tr>
226
- </thead>
227
- <tbody>
228
- {queries.map((q: PerQuery, i) => (
229
- <tr
230
- key={i}
231
- onClick={() => q.segment && setClip({ ...q.segment, text: q.evidence ? `On screen: ${q.evidence}` : undefined })}
232
- className="cursor-pointer hover:bg-surface-2"
233
- >
234
- <td className="py-1 pr-3">
235
- <div>{q.query}</div>
236
- {q.segment && (
237
- <div className="text-xs text-muted">
238
- {q.segment.title} @ {fmtTs(q.segment.start)}
239
- </div>
240
- )}
241
- </td>
242
- <td className="py-1 pr-2 text-xs text-ink-2">{q.type}</td>
243
- {views.map((v) => (
244
- <RankTd key={v} rank={q.ranks[`${v}@${tableDim}`]} />
245
- ))}
246
- </tr>
247
- ))}
248
- </tbody>
249
- </table>
250
- </div>
251
- </section>
252
-
253
- <ClipPlayer clip={clip} onClose={() => setClip(null)} />
254
- </div>
255
- );
256
- }
257
-
258
- function RankTd({ rank }: { rank?: number }) {
259
- return rankCell(rank);
260
- }