Spaces:
Running
Running
| /* Oolong viewer — vanilla JS. Two tabs: Corpus (context windows) + Eval | |
| (questions). A dataset selector (synth / real) reloads both. Eval filters are | |
| built generically from each set's `facets` in sets.json. */ | |
| (function () { | |
| "use strict"; | |
| var $ = function (id) { return document.getElementById(id); }; | |
| function esc(s) { | |
| if (s === null || s === undefined) return ""; | |
| return String(s) | |
| .replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """); | |
| } | |
| function truncate(s, n) { s = String(s || ""); return s.length > n ? s.slice(0, n - 1) + "…" : s; } | |
| var state = { | |
| sets: [], // manifest | |
| setName: null, | |
| mode: "corpus", | |
| corpus: [], // context windows for current set | |
| corpusView: [], | |
| corpusIdx: 0, | |
| docByCwid: {}, // cwid -> index into state.corpus | |
| questions: [], | |
| evalView: [], | |
| evalIdx: 0, | |
| facets: [], // current set's facets | |
| }; | |
| /* ---------------- loading ---------------- */ | |
| function loadManifest() { | |
| return fetch("sets.json").then(function (r) { return r.json(); }).then(function (sets) { | |
| state.sets = sets || []; | |
| var sel = $("setSelect"); | |
| sel.innerHTML = ""; | |
| state.sets.forEach(function (s) { | |
| var o = document.createElement("option"); | |
| o.value = s.set; | |
| o.textContent = s.set + " (" + s.n_questions + " Q · " + s.n_contexts + " ctx)"; | |
| sel.appendChild(o); | |
| }); | |
| if (!state.sets.length) throw new Error("no sets"); | |
| return loadSet(state.sets[0].set); | |
| }); | |
| } | |
| function currentManifest() { | |
| for (var i = 0; i < state.sets.length; i++) if (state.sets[i].set === state.setName) return state.sets[i]; | |
| return null; | |
| } | |
| function loadSet(name) { | |
| state.setName = name; | |
| $("setSelect").value = name; | |
| var m = currentManifest(); | |
| state.facets = m.facets || []; | |
| $("loading").style.display = "block"; | |
| $("loading").textContent = "Loading " + name + " …"; | |
| $("corpusView").style.display = "none"; | |
| $("evalView").style.display = "none"; | |
| return Promise.all([ | |
| fetch(m.corpus_file).then(function (r) { return r.json(); }), | |
| fetch(m.eval_file).then(function (r) { return r.json(); }), | |
| ]).then(function (res) { | |
| state.corpus = res[0] || []; | |
| state.questions = res[1] || []; | |
| state.docByCwid = {}; | |
| state.corpus.forEach(function (d, i) { state.docByCwid[d.cwid] = i; }); | |
| state.corpusIdx = 0; | |
| state.evalIdx = 0; | |
| buildFacetFilters(); | |
| $("corpusFilter").value = ""; | |
| $("evalSearch").value = ""; | |
| $("loading").style.display = "none"; | |
| setMode(state.mode); | |
| filterCorpus(); | |
| filterEval(); | |
| renderFooter(); | |
| }); | |
| } | |
| /* ---------------- corpus tab ---------------- */ | |
| function filterCorpus() { | |
| var q = $("corpusFilter").value.trim().toLowerCase(); | |
| state.corpusView = state.corpus.filter(function (d) { | |
| return !q || d.title.toLowerCase().indexOf(q) !== -1 || String(d.cwid).indexOf(q) !== -1; | |
| }); | |
| if (state.corpusIdx >= state.corpusView.length) state.corpusIdx = 0; | |
| rebuildDocSelect(); | |
| renderCorpus(); | |
| } | |
| function rebuildDocSelect() { | |
| var sel = $("docSelect"); | |
| sel.innerHTML = ""; | |
| state.corpusView.forEach(function (d, i) { | |
| var o = document.createElement("option"); | |
| o.value = String(i); | |
| o.textContent = truncate(d.title, 54); | |
| sel.appendChild(o); | |
| }); | |
| sel.value = String(state.corpusIdx); | |
| } | |
| function renderCorpus() { | |
| var meta = $("docMetaCard"), body = $("docContent"); | |
| if (!state.corpusView.length) { | |
| meta.innerHTML = '<div class="empty">No context windows match the filter.</div>'; | |
| body.textContent = ""; | |
| $("corpusCounter").textContent = "0 / 0"; | |
| return; | |
| } | |
| var d = state.corpusView[state.corpusIdx]; | |
| $("docSelect").value = String(state.corpusIdx); | |
| $("corpusCounter").textContent = (state.corpusIdx + 1) + " / " + state.corpusView.length; | |
| var pills = []; | |
| if (d.source) pills.push('<span class="meta-pill">Source: <b>' + esc(d.source) + "</b></span>"); | |
| if (d.campaign) pills.push('<span class="meta-pill">Campaign: <b>' + esc(d.campaign) + "</b></span>"); | |
| if (d.episodes) pills.push('<span class="meta-pill">Episodes: <b>' + esc(JSON.stringify(d.episodes)) + "</b></span>"); | |
| if (d.context_len) pills.push('<span class="meta-pill">Length: <b>' + esc(d.context_len) + "</b> tok</span>"); | |
| pills.push('<span class="meta-pill">Used in <b>' + esc(d.n_questions) + "</b> questions</span>"); | |
| pills.push('<span class="meta-pill"><b>' + Math.round(d.size / 1024) + "</b> KB</span>"); | |
| if (d.truncated) pills.push('<span class="meta-pill warn">truncated in viewer</span>'); | |
| pills.push('<span class="meta-pill">cwid <b>' + esc(d.cwid) + "</b></span>"); | |
| meta.innerHTML = "<h2>" + esc(d.title) + "</h2>" + '<div class="meta-grid">' + pills.join("") + "</div>"; | |
| renderDocBody(d, body); | |
| } | |
| function renderDocBody(d, body) { | |
| if (typeof d.content === "string") { body.textContent = d.content; body.scrollTop = 0; return; } | |
| body.textContent = "Loading context window…"; | |
| var target = d; | |
| fetch(d.file).then(function (r) { return r.text(); }).then(function (text) { | |
| target.content = text; | |
| if (state.corpusView[state.corpusIdx] === target) { body.textContent = text; body.scrollTop = 0; } | |
| }).catch(function (err) { body.textContent = "Failed to load context: " + err; }); | |
| } | |
| function selectDocByCwid(cwid) { | |
| $("corpusFilter").value = ""; | |
| state.corpusView = state.corpus.slice(); | |
| var idx = 0; | |
| for (var i = 0; i < state.corpusView.length; i++) { | |
| if (state.corpusView[i].cwid === cwid) { idx = i; break; } | |
| } | |
| state.corpusIdx = idx; | |
| rebuildDocSelect(); | |
| renderCorpus(); | |
| } | |
| /* ---------------- eval tab ---------------- */ | |
| function buildFacetFilters() { | |
| var host = $("facetFilters"); | |
| host.innerHTML = ""; | |
| state.facets.forEach(function (f) { | |
| var sel = document.createElement("select"); | |
| sel.className = "facet-select"; | |
| sel.setAttribute("data-key", f.key); | |
| var all = document.createElement("option"); | |
| all.value = ""; all.textContent = "All " + f.key.toLowerCase(); | |
| sel.appendChild(all); | |
| (f.values || []).forEach(function (v) { | |
| var o = document.createElement("option"); | |
| o.value = v; o.textContent = v; | |
| sel.appendChild(o); | |
| }); | |
| sel.addEventListener("change", filterEval); | |
| host.appendChild(sel); | |
| }); | |
| } | |
| function activeFacetFilters() { | |
| var out = []; | |
| Array.prototype.forEach.call(document.querySelectorAll("#facetFilters .facet-select"), function (sel) { | |
| if (sel.value) out.push([sel.getAttribute("data-key"), sel.value]); | |
| }); | |
| return out; | |
| } | |
| function filterEval() { | |
| var q = $("evalSearch").value.trim().toLowerCase(); | |
| var facs = activeFacetFilters(); | |
| state.evalView = state.questions.filter(function (x) { | |
| for (var i = 0; i < facs.length; i++) { | |
| if (String((x.meta || {})[facs[i][0]]) !== facs[i][1]) return false; | |
| } | |
| if (q) { | |
| var hay = [x.question, x.answer, x.id, x.context_window_id].join(" ").toLowerCase(); | |
| if (hay.indexOf(q) === -1) return false; | |
| } | |
| return true; | |
| }); | |
| if (state.evalIdx >= state.evalView.length) state.evalIdx = 0; | |
| rebuildQuestionSelect(); | |
| renderEval(); | |
| } | |
| function rebuildQuestionSelect() { | |
| var sel = $("questionSelect"); | |
| sel.innerHTML = ""; | |
| state.evalView.forEach(function (x, i) { | |
| var o = document.createElement("option"); | |
| o.value = String(i); | |
| o.textContent = truncate(x.question || x.id, 50); | |
| sel.appendChild(o); | |
| }); | |
| sel.value = String(state.evalIdx); | |
| } | |
| function field(lbl, valHtml) { | |
| return '<div class="field"><div class="lbl">' + esc(lbl) + '</div><div class="val">' + valHtml + "</div></div>"; | |
| } | |
| function renderEval() { | |
| var card = $("evalCard"); | |
| if (!state.evalView.length) { | |
| card.innerHTML = '<div class="empty">No questions match the filter.</div>'; | |
| $("evalCounter").textContent = "0 / 0"; | |
| return; | |
| } | |
| var x = state.evalView[state.evalIdx]; | |
| $("questionSelect").value = String(state.evalIdx); | |
| $("evalCounter").textContent = (state.evalIdx + 1) + " / " + state.evalView.length; | |
| var badges = '<span class="badge id">' + esc(x.id) + "</span>" + | |
| '<span class="badge set">' + esc(state.setName) + "</span>"; | |
| // a couple of meta values as headline badges | |
| var meta = x.meta || {}; | |
| ["Task group", "Answer type", "Question type", "Split"].forEach(function (k) { | |
| if (meta[k]) badges += '<span class="badge">' + esc(meta[k]) + "</span>"; | |
| }); | |
| // supporting context window | |
| var cwid = x.context_window_id; | |
| var known = state.docByCwid.hasOwnProperty(cwid); | |
| var cwTitle = known ? state.corpus[state.docByCwid[cwid]].title : ("cwid " + cwid); | |
| var support = '<a class="doclink' + (known ? "" : " missing") + '"' + | |
| (known ? ' data-cwid="' + esc(cwid) + '"' : "") + ">📚 " + esc(cwTitle) + (known ? " ↗" : "") + "</a>"; | |
| // metadata table | |
| var rows = ""; | |
| Object.keys(meta).forEach(function (k) { | |
| if (meta[k] !== "" && meta[k] !== "None" && meta[k] !== null && meta[k] !== undefined) { | |
| rows += '<div class="mk">' + esc(k) + '</div><div class="mv">' + esc(meta[k]) + "</div>"; | |
| } | |
| }); | |
| card.innerHTML = | |
| '<div class="badges">' + badges + "</div>" + | |
| "<h2>" + esc(x.question) + "</h2>" + | |
| '<div class="answer-banner"><div class="lbl">Gold answer</div><div class="val">' + | |
| (x.answer ? esc(x.answer) : '<span class="empty-answer">(empty)</span>') + "</div></div>" + | |
| field("Supporting context window", support) + | |
| field("Metadata", '<div class="meta-table">' + rows + "</div>"); | |
| var link = card.querySelector("a.doclink[data-cwid]"); | |
| if (link) link.addEventListener("click", function () { | |
| setMode("corpus"); | |
| selectDocByCwid(this.getAttribute("data-cwid")); | |
| }); | |
| } | |
| /* ---------------- mode / footer / wiring ---------------- */ | |
| function setMode(mode) { | |
| state.mode = mode; | |
| $("modeCorpusBtn").classList.toggle("active", mode === "corpus"); | |
| $("modeEvalBtn").classList.toggle("active", mode === "eval"); | |
| $("corpusControls").style.display = mode === "corpus" ? "" : "none"; | |
| $("evalControls").style.display = mode === "eval" ? "" : "none"; | |
| $("corpusView").style.display = mode === "corpus" ? "flex" : "none"; | |
| $("evalView").style.display = mode === "eval" ? "block" : "none"; | |
| } | |
| function renderFooter() { | |
| var m = currentManifest() || {}; | |
| $("sidebarFooter").innerHTML = | |
| "Dataset <b>" + esc(state.setName) + "</b> · " + (m.n_contexts || 0) + " context windows · " + | |
| (m.n_questions || 0) + " questions<br>" + | |
| '<a href="https://github.com/abertsch72/oolong" target="_blank" rel="noopener">GitHub</a> · ' + | |
| '<a href="https://huggingface.co/oolongbench" target="_blank" rel="noopener">HF datasets</a> · ' + | |
| '<a href="https://arxiv.org/abs/2511.02817" target="_blank" rel="noopener">paper</a>'; | |
| } | |
| function wire() { | |
| $("setSelect").addEventListener("change", function () { loadSet(this.value); }); | |
| $("modeCorpusBtn").addEventListener("click", function () { setMode("corpus"); }); | |
| $("modeEvalBtn").addEventListener("click", function () { setMode("eval"); }); | |
| $("corpusFilter").addEventListener("input", filterCorpus); | |
| $("docSelect").addEventListener("change", function () { state.corpusIdx = parseInt(this.value, 10) || 0; renderCorpus(); }); | |
| $("corpusPrevBtn").addEventListener("click", function () { if (state.corpusIdx > 0) { state.corpusIdx--; renderCorpus(); } }); | |
| $("corpusNextBtn").addEventListener("click", function () { if (state.corpusIdx < state.corpusView.length - 1) { state.corpusIdx++; renderCorpus(); } }); | |
| $("evalSearch").addEventListener("input", filterEval); | |
| $("questionSelect").addEventListener("change", function () { state.evalIdx = parseInt(this.value, 10) || 0; renderEval(); }); | |
| $("evalPrevBtn").addEventListener("click", function () { if (state.evalIdx > 0) { state.evalIdx--; renderEval(); } }); | |
| $("evalNextBtn").addEventListener("click", function () { if (state.evalIdx < state.evalView.length - 1) { state.evalIdx++; renderEval(); } }); | |
| document.addEventListener("keydown", function (e) { | |
| var tag = (e.target.tagName || "").toLowerCase(); | |
| if (tag === "input" || tag === "select" || tag === "textarea") return; | |
| if (e.key === "ArrowLeft") (state.mode === "corpus" ? $("corpusPrevBtn") : $("evalPrevBtn")).click(); | |
| else if (e.key === "ArrowRight") (state.mode === "corpus" ? $("corpusNextBtn") : $("evalNextBtn")).click(); | |
| }); | |
| } | |
| wire(); | |
| loadManifest().catch(function (err) { $("loading").textContent = "Failed to load data: " + err; }); | |
| })(); | |