Spaces:
Running
Running
File size: 9,035 Bytes
34d520a 83ebad7 34d520a | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 | <!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Random ICA Components</title>
<style>
:root { --bg:#f8fafc; --panel:#fff; --text:#0f172a; --muted:#64748b; --border:#cbd5e1; --accent:#2563eb; --subtle:#f1f5f9; --shadow:0 1px 2px rgb(15 23 42 / .08),0 8px 24px rgb(15 23 42 / .05); }
* { box-sizing:border-box; }
body { margin:0; background:var(--bg); color:var(--text); font:14px/1.45 system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif; }
header { position:sticky; top:0; z-index:5; background:#fff; border-bottom:1px solid var(--border); padding:12px 16px; box-shadow:var(--shadow); }
.top { display:flex; justify-content:space-between; align-items:center; gap:12px; flex-wrap:wrap; }
h1 { margin:0; font-size:20px; }
nav { display:flex; gap:6px; }
nav a { color:var(--muted); text-decoration:none; padding:7px 10px; border-radius:7px; font-weight:650; }
nav a:hover, nav a.active { color:var(--accent); background:#eff6ff; }
.toolbar { display:flex; align-items:end; flex-wrap:wrap; gap:10px; margin-top:10px; }
label { color:#475569; font-size:12px; font-weight:700; }
select,input,button { font:inherit; border:1px solid var(--border); border-radius:7px; padding:7px 9px; background:#fff; color:var(--text); }
button { cursor:pointer; background:var(--subtle); font-weight:750; }
button:hover { color:var(--accent); border-color:var(--accent); background:#eff6ff; }
main { padding:14px; }
.panel { background:var(--panel); border:1px solid var(--border); border-radius:8px; box-shadow:var(--shadow); padding:12px; }
.summary { color:var(--muted); margin-bottom:10px; }
.run { margin-top:14px; }
.run h2 { display:flex; align-items:baseline; justify-content:space-between; gap:10px; margin:0 0 8px; font-size:16px; }
.run-meta { color:var(--muted); font-size:12px; font-weight:500; }
table { width:100%; border-collapse:collapse; background:#fff; }
th,td { border-bottom:1px solid #e2e8f0; padding:8px 9px; text-align:left; vertical-align:top; }
th { position:sticky; top:94px; background:#f8fafc; color:#475569; font-size:12px; text-transform:uppercase; letter-spacing:.02em; z-index:1; }
tr:hover td { background:#f8fafc; }
a { color:var(--accent); text-decoration:none; font-weight:800; }
a:hover { text-decoration:underline; }
.muted { color:var(--muted); }
.pill { display:inline-flex; border:1px solid var(--border); border-radius:999px; padding:1px 7px; color:var(--muted); background:#fff; font-size:12px; font-weight:750; white-space:nowrap; }
.label { max-width:360px; color:#334155; }
.label-badge { display:inline-flex; align-items:center; border-radius:999px; padding:2px 8px; font-size:12px; font-weight:850; line-height:1.3; }
.label-badge.high { color:#166534; background:#dcfce7; border:1px solid #86efac; }
.label-badge.medium { color:#854d0e; background:#fef3c7; border:1px solid #fde68a; }
.label-badge.low { color:#9f1239; background:#ffe4e6; border:1px solid #f9a8d4; }
.label-badge.unclear { color:#475569; background:#e5e7eb; border:1px solid #cbd5e1; }
.status { margin-left:auto; color:var(--muted); }
.empty,.error { border:1px dashed var(--border); border-radius:8px; padding:18px; color:var(--muted); background:#fff; }
.error { color:#991b1b; border-color:#fecaca; background:#fef2f2; }
@media(max-width:760px){ th:nth-child(5),td:nth-child(5),th:nth-child(6),td:nth-child(6){display:none;} }
</style>
<script defer src="https://analytics.liusida.com/umami/script.js" data-website-id="64322a37-ae7f-4635-ac78-8869ef79997b"></script>
</head>
<body>
<header>
<div class="top">
<h1>Random ICA Components</h1>
<nav aria-label="Primary">
<a href="/">Explorer</a>
<a href="/sae-explorer">SAE Explorer</a>
<a href="/stats">Stats</a>
<a href="/annotate">Annotate</a>
<a href="/random-components" class="active">Random</a>
</nav>
</div>
<div class="toolbar">
<label>Model<br><select id="model"><option value="">all models</option></select></label>
<label style="flex:1;min-width:260px">Selection<br><input id="selection" placeholder="optional: random_components_n50_seed20260524" /></label>
<button id="load">Load</button>
<span id="status" class="status"></span>
</div>
</header>
<main>
<section class="panel">
<div id="summary" class="summary">Loading random component selections...</div>
<div id="content"></div>
</section>
</main>
<script>
const state = { models: [] };
const el = Object.fromEntries(["model","selection","load","status","summary","content"].map(id=>[id,document.getElementById(id)]));
async function api(path){ const r=await fetch(path); if(!r.ok){let msg=await r.text(); try{msg=JSON.parse(msg).detail||msg}catch{} throw new Error(msg)} return r.json(); }
function esc(s){return String(s??"").replace(/[&<>"']/g,c=>({"&":"&","<":"<",">":">","\"":""","'":"'"}[c]));}
function setStatus(text){ el.status.textContent = text || ""; }
async function init(){
const params = new URLSearchParams(location.search);
el.selection.value = params.get("selection") || "random_components_n50_seed0";
const requestedModel = params.get("model") || "qwen3_5_2b_base";
try {
const models = await api("/api/models");
state.models = models.models || [];
el.model.innerHTML = `<option value="">all models</option>` + state.models.map(m=>`<option value="${esc(m.model_name)}">${esc(m.display_name || m.model_name)}</option>`).join("");
el.model.value = requestedModel;
} catch {}
el.load.onclick = () => loadRuns(true);
el.model.onchange = () => loadRuns(true);
el.selection.onkeydown = event => { if(event.key === "Enter") loadRuns(true); };
await loadRuns(false);
}
async function loadRuns(push){
setStatus("Loading...");
el.content.innerHTML = "";
try {
const params = new URLSearchParams();
if(el.model.value) params.set("model", el.model.value);
if(el.selection.value.trim()) params.set("selection", el.selection.value.trim());
if(push) history.replaceState(null, "", `/random-components${params.toString() ? `?${params}` : ""}`);
const data = await api(`/api/random-components${params.toString() ? `?${params}` : ""}`);
render(data);
setStatus("");
} catch (error) {
el.summary.textContent = "";
el.content.innerHTML = `<div class="error">${esc(error.message)}</div>`;
setStatus("");
}
}
function render(data){
const runs = data.runs || [];
const total = runs.reduce((sum, run) => sum + Number(run.selected_size || 0), 0);
el.summary.textContent = `${runs.length} selection file${runs.length===1?"":"s"} - ${total} selected components`;
el.content.innerHTML = runs.map(renderRun).join("") || '<div class="empty">No random component selections found.</div>';
}
function renderRun(run){
const settings = run.settings || {};
const rows = run.selected_components || [];
return `<section class="run">
<h2>
<span>${esc(run.model)}</span>
<span class="run-meta">n=${esc(settings.n)} seed=${esc(settings.seed)} inventory=${esc(run.inventory_size)} selection=${esc(run.selection_name || "")}</span>
</h2>
<table>
<thead><tr><th>#</th><th>Component</th><th>Annotate</th><th>K</th><th>Current label</th></tr></thead>
<tbody>${rows.map((item,index)=>renderRow(item,index)).join("")}</tbody>
</table>
</section>`;
}
function renderRow(item,index){
const annotation = item.annotation || {};
const sign = Number(item.top_abs_sign) < 0 ? -1 : 1;
const rawLabel = sign < 0 ? annotation.negative_label : annotation.positive_label;
const confidence = normalizeConfidence(sign < 0 ? annotation.negative_confidence : annotation.positive_confidence);
const label = visibleLabel(rawLabel);
const labels = label
? `<span class="label-badge ${esc(confidence)}">${label}</span>`
: '<span class="label-badge unclear">unlabeled</span>';
const k = Number(item.excess_kurtosis);
const erf = Number(item.effective_context_mean);
const metrics = `${Number.isFinite(erf) ? `<span class="pill">ERF=${erf.toFixed(1)}</span>` : ""} ${Number.isFinite(k) ? `<span class="pill">K=${k.toFixed(1)}</span>` : ""}`;
return `<tr>
<td class="muted">${index + 1}</td>
<td><strong>${esc(item.layer)} C${esc(item.component_index)}</strong><br><span class="muted">${esc(item.component_id || "")}</span></td>
<td><a href="${esc(item.annotate_url)}">Annotate</a></td>
<td>${metrics || '<span class="muted">?</span>'}</td>
<td class="label">${labels}</td>
</tr>`;
}
function visibleLabel(value){
const text = String(value || "").trim();
if(!text) return "";
return esc(text);
}
function normalizeConfidence(value){
const text = String(value || "unclear").trim().toLowerCase();
return ["high","medium","low","unclear"].includes(text) ? text : "unclear";
}
init();
</script>
</body>
</html>
|