import { useEffect, useMemo, useRef, useState } from "react"; import { useNavigate, useParams } from "react-router-dom"; import api from "@/lib/api"; import ActivationTimelineHeatmap from "@/components/ActivationTimelineHeatmap"; import SaeFeatureDriftChart from "@/components/SaeFeatureDriftChart"; import HallucinationRiskTimeline from "@/components/HallucinationRiskTimeline"; import PatchMatrix from "@/components/PatchMatrix"; import CircuitQueryBox from "@/components/CircuitQueryBox"; import StepList from "@/components/StepList"; import ReproSnippet from "@/components/ReproSnippet"; import VizPanel from "@/components/VizPanel"; import LoadingProgress from "@/components/LoadingProgress"; import StatusDot from "@/components/StatusDot"; import { Wand2, Loader2 } from "lucide-react"; import { toast } from "sonner"; export default function RunAnalysis({ presetRun = null }) { const params = useParams(); const navigate = useNavigate(); const id = params.id || presetRun?.id; const [run, setRun] = useState(presetRun); const [activeStep, setActiveStep] = useState(1); const [pickedFeatureId, setPickedFeatureId] = useState(null); const [patchLayer, setPatchLayer] = useState(12); const [patchInspector, setPatchInspector] = useState(null); const [matrixLoading, setMatrixLoading] = useState(false); const timer = useRef(null); // poll while running useEffect(() => { if (!id || presetRun) return; let alive = true; async function tick() { try { const r = await api.getRun(id); if (!alive) return; setRun(r); if (r.status !== "done" && r.status !== "error") { timer.current = setTimeout(tick, 2000); } } catch { timer.current = setTimeout(tick, 4000); } } tick(); return () => { alive = false; if (timer.current) clearTimeout(timer.current); }; }, [id, presetRun]); const trajectoryReady = run?.status === "done"; const steps = run?.steps || []; const timelines = run?.feature_timelines || []; const patchMatrix = run?.patch_matrix || []; const filteredTimelines = useMemo(() => { if (!pickedFeatureId) return timelines; return timelines.filter((t) => t.feature_id === pickedFeatureId).concat(timelines.filter((t) => t.feature_id !== pickedFeatureId)); }, [timelines, pickedFeatureId]); async function runMatrix() { setMatrixLoading(true); try { const res = await api.patchMatrix(id, { layers: [6, 12, 18] }); setRun((r) => ({ ...r, patch_matrix: res.patch_matrix })); toast.success(`patch matrix swept · ${res.patch_matrix.length} cells`); } catch (e) { toast.error(`patch matrix failed: ${e.message}`); } finally { setMatrixLoading(false); } } async function runPatch(cell) { // If viewing a pre-computed experiment, look up the cell in the cached matrix. if (presetRun) { const cached = (run.patch_matrix || []).find( (m) => m.source_step === cell.source && m.target_step === cell.target && m.patch_layer === cell.layer, ); if (cached) { setPatchInspector({ source_step: cell.source, target_step: cell.target, patch_layer: cell.layer, kl: cached.kl, significant: cached.significant, token_changes: cached.top_token_change ? [cached.top_token_change] : [], interpretation: `Patching layer ${cell.layer} activations from step ${cell.source} into step ${cell.target} ` + `${cached.significant ? "significantly changes" : "does not significantly change"} ` + `the output distribution (KL=${cached.kl.toFixed(4)}). ` + `${cached.significant ? "Causal link between these steps confirmed." : "No strong causal link at this layer."} ` + `(Precomputed from experiment cache.)`, }); return; } } try { const res = await api.patch(id, { source_step: cell.source, target_step: cell.target, patch_layer: cell.layer, }); setPatchInspector({ ...cell, ...res }); } catch (e) { toast.error(`patch failed: ${e.message}`); } } if (!run) { return (
loading run…
); } return (
{/* header */}
run {id?.slice(0, 8)}… · n_steps {run.n_steps} · layer {run.sae_layer} {run.total_elapsed_ms ? ( · {(run.total_elapsed_ms / 1000).toFixed(1)}s ) : null}

{run.task}

{/* error banner */} {run.status === "error" && (
run failed: {run.error}
)} {/* loading state */} {run.status !== "done" && run.status !== "error" && ( )} {/* main grid (3-pane) */}
{/* activation timeline heatmap */}
setActiveStep(c.step)} />
{/* SAE drift */} {steps.length ? ( ) : (
waiting for steps…
)}
{/* hallucination + patch matrix */}
{steps.length ? ( ) : (
no risk data yet
)}
{matrixLoading ? ( ) : ( )} {patchMatrix.length ? "re-sweep" : "sweep matrix"} } > {patchMatrix.length ? ( ) : (
No patch sweep yet. Click “sweep matrix” to run all (source, target, layer) cross-step patches — ≈18 cells, ~{(run.n_steps * (run.n_steps - 1) * 3 * 1.5).toFixed(0)}s on CPU.
)}
{/* NL query box — full width below */}
); }