Skip to content

Commit 07e3e60

Browse files
Jamie Nuchoclaude
andcommitted
Hide broken scatter plots for judge spaces, show metrics-only layout
Judge spaces (Claude, DeepSeek, Grok) use pairwise similarity scores, not embeddings — scatter plots were meaningless. Now shows explanation panel + metrics for judges, scatter plot + metrics for embeddings. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent b9af35a commit 07e3e60

1 file changed

Lines changed: 86 additions & 67 deletions

File tree

web/src/components/PathInvariance.jsx

Lines changed: 86 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,7 @@ export default function PathInvariance() {
165165
const currentSpace = selectedSpace in data.per_embedding_space && !data.per_embedding_space[selectedSpace].error
166166
? selectedSpace : spaces[0]
167167

168+
const isJudge = space?.dimensions === 'judgment'
168169
const points = space?.pca_2d || []
169170
const responses = data.responses || []
170171
const clustering = space?.clustering || {}
@@ -199,9 +200,9 @@ export default function PathInvariance() {
199200
</div>
200201
)}
201202

202-
{/* Embedding space selector + color toggle */}
203-
<div className="flex items-center gap-4 mb-6">
204-
<div className="flex gap-2">
203+
{/* Space selector + color toggle */}
204+
<div className="flex items-center gap-4 mb-6 flex-wrap">
205+
<div className="flex gap-2 flex-wrap">
205206
{spaces.map(s => (
206207
<button
207208
key={s}
@@ -216,76 +217,94 @@ export default function PathInvariance() {
216217
</button>
217218
))}
218219
</div>
219-
<div className="flex gap-2 ml-auto">
220-
<button
221-
onClick={() => setColorBy('phase')}
222-
className={`px-3 py-1 text-xs rounded border ${
223-
colorBy === 'phase' ? 'border-accent text-accent' : 'border-border text-muted'
224-
}`}
225-
>
226-
Color by Phase
227-
</button>
228-
<button
229-
onClick={() => setColorBy('model')}
230-
className={`px-3 py-1 text-xs rounded border ${
231-
colorBy === 'model' ? 'border-accent text-accent' : 'border-border text-muted'
232-
}`}
233-
>
234-
Color by Model
235-
</button>
236-
</div>
220+
{!isJudge && (
221+
<div className="flex gap-2 ml-auto">
222+
<button
223+
onClick={() => setColorBy('phase')}
224+
className={`px-3 py-1 text-xs rounded border ${
225+
colorBy === 'phase' ? 'border-accent text-accent' : 'border-border text-muted'
226+
}`}
227+
>
228+
Color by Phase
229+
</button>
230+
<button
231+
onClick={() => setColorBy('model')}
232+
className={`px-3 py-1 text-xs rounded border ${
233+
colorBy === 'model' ? 'border-accent text-accent' : 'border-border text-muted'
234+
}`}
235+
>
236+
Color by Model
237+
</button>
238+
</div>
239+
)}
237240
</div>
238241

239-
{/* Main layout: scatter + metrics */}
240-
<div className="grid grid-cols-1 lg:grid-cols-5 gap-6 mb-10">
241-
{/* Scatter plot */}
242-
<div className="lg:col-span-3">
243-
<ScatterPlot
244-
points={points}
245-
responses={responses}
246-
colorBy={colorBy}
247-
space={currentSpace}
248-
hoveredPoint={hoveredPoint}
249-
setHoveredPoint={setHoveredPoint}
250-
/>
251-
{/* Legend */}
252-
<div className="flex flex-wrap gap-3 mt-3">
253-
{colorBy === 'model'
254-
? Object.entries(MODEL_COLORS)
255-
.filter(([m]) => responses.some(r => r.model === m))
256-
.map(([m, c]) => (
257-
<div key={m} className="flex items-center gap-1.5">
258-
<div className="w-3 h-3 rounded-full" style={{ backgroundColor: c }} />
259-
<span className="text-xs text-muted">{m}</span>
260-
</div>
261-
))
262-
: Object.entries(PHASE_COLORS)
263-
.filter(([p]) => responses.some(r => r.phase === p))
264-
.map(([p, c]) => (
265-
<div key={p} className="flex items-center gap-1.5">
266-
<div className="w-3 h-3 rounded-full" style={{ backgroundColor: c }} />
267-
<span className="text-xs text-muted">{p}</span>
268-
</div>
269-
))
270-
}
242+
{/* Main layout */}
243+
<div className={`grid grid-cols-1 ${isJudge ? 'lg:grid-cols-3' : 'lg:grid-cols-5'} gap-6 mb-10`}>
244+
{/* Scatter plot — embedding spaces only */}
245+
{!isJudge && (
246+
<div className="lg:col-span-3">
247+
<ScatterPlot
248+
points={points}
249+
responses={responses}
250+
colorBy={colorBy}
251+
space={currentSpace}
252+
hoveredPoint={hoveredPoint}
253+
setHoveredPoint={setHoveredPoint}
254+
/>
255+
{/* Legend */}
256+
<div className="flex flex-wrap gap-3 mt-3">
257+
{colorBy === 'model'
258+
? Object.entries(MODEL_COLORS)
259+
.filter(([m]) => responses.some(r => r.model === m))
260+
.map(([m, c]) => (
261+
<div key={m} className="flex items-center gap-1.5">
262+
<div className="w-3 h-3 rounded-full" style={{ backgroundColor: c }} />
263+
<span className="text-xs text-muted">{m}</span>
264+
</div>
265+
))
266+
: Object.entries(PHASE_COLORS)
267+
.filter(([p]) => responses.some(r => r.phase === p))
268+
.map(([p, c]) => (
269+
<div key={p} className="flex items-center gap-1.5">
270+
<div className="w-3 h-3 rounded-full" style={{ backgroundColor: c }} />
271+
<span className="text-xs text-muted">{p}</span>
272+
</div>
273+
))
274+
}
275+
</div>
276+
{/* Hover info */}
277+
{hovered && (
278+
<div className="mt-3 p-3 border border-border rounded bg-panel text-xs">
279+
<span className="text-gray-200 font-medium">Q{hovered.question_num}: {hovered.question_title}</span>
280+
<span className="text-muted">{hovered.model}{hovered.phase}</span>
281+
</div>
282+
)}
283+
<p className="text-xs text-muted/60 mt-2">
284+
PCA projection of {space?.dimensions}d embeddings to 2D.
285+
{colorBy === 'phase'
286+
? ' Tight clusters = same phase, same endpoint. Switch to "Color by Model" — if clusters break apart, it\'s training bias. If they hold, it\'s structural.'
287+
: ' If model colors are scattered (no model-specific clusters), model identity doesn\'t predict position in embedding space.'}
288+
</p>
271289
</div>
272-
{/* Hover info */}
273-
{hovered && (
274-
<div className="mt-3 p-3 border border-border rounded bg-panel text-xs">
275-
<span className="text-gray-200 font-medium">Q{hovered.question_num}: {hovered.question_title}</span>
276-
<span className="text-muted">{hovered.model}{hovered.phase}</span>
290+
)}
291+
292+
{/* Judge space description */}
293+
{isJudge && (
294+
<div className="lg:col-span-1">
295+
<div className="p-4 border border-border rounded-lg bg-panel">
296+
<p className="text-xs text-gray-200 font-medium mb-2">Pairwise LLM Judgment</p>
297+
<p className="text-xs text-muted">
298+
{space.judge_model} scored {space.dimensions === 'judgment' ? '663' : ''} response pairs for semantic similarity.
299+
No scatter plot — pairwise scores don't have spatial positions.
300+
The metrics are computed from the scored pairs directly.
301+
</p>
277302
</div>
278-
)}
279-
<p className="text-xs text-muted/60 mt-2">
280-
{space?.dimensions === 'judgment' ? 'PCA projection of LLM similarity judgments to 2D.' : `PCA projection of ${space?.dimensions}d embeddings to 2D.`}
281-
{colorBy === 'phase'
282-
? ' Tight clusters = same phase, same endpoint. Switch to "Color by Model" — if clusters break apart, it\'s training bias. If they hold, it\'s structural.'
283-
: ' If model colors are scattered (no model-specific clusters), model identity doesn\'t predict position in embedding space.'}
284-
</p>
285-
</div>
303+
</div>
304+
)}
286305

287306
{/* Metrics */}
288-
<div className="lg:col-span-2">
307+
<div className={isJudge ? 'lg:col-span-2' : 'lg:col-span-2'}>
289308
<div className="p-4 border border-border rounded-lg bg-panel mb-4">
290309
<h3 className="text-xs font-semibold text-accent mb-3 tracking-wide">
291310
KNN Purity (k=5)

0 commit comments

Comments
 (0)