"use strict"; const RESULTS_REPO = "c3aiia3c/read-bench-results"; // Column definitions. Row type is shown via the leading symbol (hover = label); // no separate text "Type" column. Metrics include the three pollution levels // plus full-corpus scale. const COLUMNS = [ { key: "rank", label: "#", kind: "rank", always: true }, { key: "sym", label: "", kind: "sym", always: true }, { key: "name", label: "Method", kind: "name", always: true }, { key: "supervision", label: "Supervision", kind: "tag", on: true }, { key: "reranker", label: "Reranker", kind: "tag", on: true }, { key: "rho0", label: "NDCG@10 ฯ=0%", kind: "metric",on: true }, { key: "rho10", label: "ฯ=10%", kind: "metric",on: true }, { key: "rho20", label: "ฯ=20%", kind: "metric",on: true }, { key: "code", label: "Code", kind: "code", on: true }, ]; const FACETS = { type: [["embedder","๐ŸŸข Foundation embedder"],["distance","๐Ÿ”ถ Shape distance"],["symbolic","๐Ÿ”ท Symbolic (BM25)"], ["fusion","๐ŸŸฃ Hybrid (fusion)"],["supervised","๐Ÿ”ด Supervised ceiling"],["floor","โšช Trivial baseline"]], supervision: [["label-free","label-free"],["neighbour-labels","neighbour-labels"],["supervised","supervised"]], reranker: [["โ€”","none"],["GPC","GPC"],["PRF","PRF"],["Purity","Purity"],["MajVote","MajVote"],["QPurity","QPurity"],["LM","LM"]], }; // Where submitted method code lives (per-method folder on the results repo). const CODE_BASE = "https://huggingface.co/datasets/c3aiia3c/read-bench-results"; const state = { view: "overall", rho: "rho0", sortKey: "rho0", sortDir: -1, search: "", active: { type: new Set(), supervision: new Set(), reranker: new Set() }, visible: new Set(COLUMNS.filter(c => c.always || c.on).map(c => c.key)), rows: [], }; // ---- toolbar builders ---- function buildFacets() { for (const [facet, opts] of Object.entries(FACETS)) { const host = document.getElementById("f-" + facet); host.innerHTML = ""; for (const [val, label] of opts) { const id = `cb-${facet}-${val}`; const wrap = document.createElement("label"); wrap.className = "cb"; wrap.innerHTML = ` ${label}`; wrap.querySelector("input").onchange = e => { const s = state.active[facet]; e.target.checked ? s.add(val) : s.delete(val); render(); }; host.appendChild(wrap); } } } function buildColumnPicker() { const host = document.getElementById("colbox"); host.innerHTML = ""; for (const c of COLUMNS) { if (c.always) continue; const wrap = document.createElement("label"); wrap.className = "cb"; wrap.innerHTML = ` ${c.label}`; wrap.querySelector("input").onchange = e => { e.target.checked ? state.visible.add(c.key) : state.visible.delete(c.key); render(); }; host.appendChild(wrap); } } // ---- submissions ---- async function loadSubmissions() { const api = `https://huggingface.co/api/datasets/${RESULTS_REPO}/tree/main/submissions?recursive=1`; let tree; try { tree = await (await fetch(api)).json(); } catch (e) { return []; } if (!Array.isArray(tree)) return []; const slugs = new Set(); for (const f of tree) { const m = f.path && f.path.match(/^submissions\/([^/]+)\/results\.json$/); if (m) slugs.add(m[1]); } const out = []; for (const slug of slugs) { const base = `https://huggingface.co/datasets/${RESULTS_REPO}/resolve/main/submissions/${slug}`; try { const res = await (await fetch(`${base}/results.json`)).json(); let meta = {}; try { meta = parseYaml(await (await fetch(`${base}/metadata.yaml`)).text()); } catch (e) {} const h = res.headline || {}; const famToType = { "Foundation embedder":"embedder", "Shape distance":"distance", "Symbolic (BM25)":"symbolic", "Hybrid (fusion)":"fusion", "Supervised ceiling":"supervised", "Trivial baseline":"floor", "FM embedder":"embedder", "Raw-window distance":"distance", "Symbolic":"symbolic", "Fusion + reranker":"fusion", "Supervised":"supervised", "Trivial floor":"floor" }; out.push({ name: meta.name || slug, type: famToType[meta.family] || "embedder", family: meta.family || "Other", supervision: meta.supervision || "label-free", reranker: meta.reranker || "โ€”", nr: /nr|normal-residual/i.test(meta.name||""), code: true, link: meta.code || meta.paper || "", slug: slug, rho0: h.rho0, rho10: h.rho10, rho20: h.rho20, full: (res.headline||{}).full ?? null, source: "submission", }); } catch (e) {} } return out; } function parseYaml(txt) { const o = {}; for (const line of txt.split("\n")) { const m = line.match(/^([a-zA-Z_]+):\s*(.*)$/); if (!m) continue; let v = m[2].trim(); if (!/^["']/.test(v)) v = v.replace(/\s+#.*$/, "").trim(); v = v.replace(/^["']|["']$/g, ""); if (v) o[m[1]] = v; } return o; } // ---- render ---- function passes(r) { if (state.active.type.size && !state.active.type.has(r.type)) return false; if (state.active.supervision.size && !state.active.supervision.has(r.supervision)) return false; if (state.active.reranker.size && !state.active.reranker.has(r.reranker)) return false; if (state.search && !r.name.toLowerCase().includes(state.search)) return false; return true; } function render() { if (state.view === "domain") { renderDomain(); return; } const cols = COLUMNS.filter(c => c.always || state.visible.has(c.key)); let rows = state.rows.filter(r => typeof r[state.rho] === "number").filter(passes); rows.sort((a, b) => { const x = a[state.sortKey], y = b[state.sortKey]; if (typeof x === "number" || typeof y === "number") return ((x ?? -1) - (y ?? -1)) * state.sortDir; return String(x).localeCompare(String(y)) * state.sortDir; }); const best = rows.length ? Math.max(...rows.map(r => r[state.rho] ?? 0)) : -1; const head = document.getElementById("head"); head.innerHTML = "" + cols.map(c => { if (c.kind === "rank" || c.kind === "sym") return ``; const ar = c.key === state.sortKey ? `${state.sortDir < 0 ? "โ–ผ" : "โ–ฒ"}` : ""; const cls = (c.kind === "metric" || c.kind === "code") ? "num" : ""; return `${c.label} ${ar}`; }).join("") + ""; head.querySelectorAll("th[data-k]").forEach(th => { th.onclick = () => { const k = th.dataset.k; if (state.sortKey === k) state.sortDir *= -1; else { state.sortKey = k; state.sortDir = ["rho0","rho10","rho20"].includes(k) ? -1 : 1; } render(); }; }); const tb = document.getElementById("rows"); tb.innerHTML = ""; rows.forEach((r, i) => { const tr = document.createElement("tr"); const cells = cols.map(c => { switch (c.kind) { case "rank": return `${i + 1}`; case "sym": { const t = window.TYPE_SYMBOLS[r.type] || {}; return `${t.symbol || "?"}`; } case "name": return `${r.link ? `${r.name}` : r.name}`; case "tag": return `${r[c.key] === "โ€”" ? "โ€”" : r[c.key]}`; case "code": { const href = r.link || (r.slug ? `${CODE_BASE}/tree/main/submissions/${r.slug}` : CODE_BASE); return `</>`; } case "metric": { const v = r[c.key]; const cls = (c.key === state.rho && v === best) ? "num best" : "num"; return `${typeof v === "number" ? v.toFixed(3) : "โ€”"}`; } } }); tr.innerHTML = cells.join(""); tb.appendChild(tr); }); document.getElementById("count").textContent = `${rows.length} methods ยท sorted by ${["rho0","rho10","rho20"].includes(state.sortKey) ? "NDCG@10 " + state.sortKey.replace("rho","ฯ=") + "%" : state.sortKey}`; document.getElementById("legend").innerHTML = "Legend: " + Object.values(window.TYPE_SYMBOLS).map(t => `${t.symbol} ${t.label}`).join(" ยท "); } function renderDomain() { const domains = window.DOMAINS || []; // only methods with a complete per-domain breakdown (the fusion-grid rows // report macro only, so they'd show gaps โ€” exclude them from this view). let rows = state.rows .filter(r => r.domains && domains.every(d => typeof r.domains[d] === "number")) .filter(passes); rows.sort((a, b) => (b.rho0 ?? 0) - (a.rho0 ?? 0)); const dv = document.getElementById("domain-view"); const head = "#Method" + domains.map(d => `${d}`).join("") + ""; const body = rows.map((r, i) => { const sym = (window.TYPE_SYMBOLS[r.type] || {}).symbol || "?"; const nm = r.link ? `${r.name}` : r.name; // best per domain (column) across shown rows const cells = domains.map(d => { const v = r.domains[d]; return `${typeof v === "number" ? v.toFixed(3) : "โ€”"}`; }).join(""); return `${i + 1}${sym}${nm}${cells}`; }).join(""); dv.querySelector(".tablewrap").innerHTML = `${head}${body}
`; } function setView(view) { state.view = view; document.querySelectorAll(".tab").forEach(t => t.classList.toggle("active", t.dataset.view === view)); const overallWrap = document.getElementById("board").parentElement; // .tablewrap const show = (el, on) => { if (el) el.style.display = on ? "" : "none"; }; let dv = document.getElementById("domain-view"); overallWrap.style.display = "none"; show(document.getElementById("count"), false); show(document.getElementById("legend"), false); show(dv, false); if (view === "domain") { if (!dv) { dv = document.createElement("div"); dv.id = "domain-view"; dv.innerHTML = `

NDCG@10 (ฯ=0%) macro-averaged within each diagnostic domain. Filters and search above apply here too.

`; overallWrap.after(dv); } dv.style.display = "block"; renderDomain(); } else { overallWrap.style.display = ""; show(document.getElementById("count"), true); show(document.getElementById("legend"), true); render(); } } document.getElementById("rho").onchange = e => { state.rho = e.target.value; state.sortKey = e.target.value; state.sortDir = -1; state.visible.add(e.target.value); // make sure the active metric column shows buildColumnPicker(); render(); }; document.getElementById("search").oninput = e => { state.search = e.target.value.toLowerCase(); render(); }; document.querySelectorAll(".tab").forEach(t => t.onclick = () => setView(t.dataset.view)); (async () => { buildFacets(); buildColumnPicker(); const subs = await loadSubmissions(); state.rows = window.PAPER_ROWS.concat(subs); render(); })();