Spaces:
Running
Running
Download experiments/lab.js from alphabell/Model-Observatory: direct link, hf CLI and curl.
- Browser
- Download file 27.5 kB
-
https://huggingface.co/spaces/alphabell/Model-Observatory/resolve/main/experiments/lab.js
- Command line
-
hf download hf://spaces/alphabell/Model-Observatory/experiments/lab.js
-
curl -L -o lab.js https://huggingface.co/spaces/alphabell/Model-Observatory/resolve/main/experiments/lab.js
27.5 kB
| (function (global) { | |
| 'use strict'; | |
| const DEFINITIONS = { | |
| reroute: { number: '01', title: 'One word changed.', short: 'Follow the routing', summary: 'Compare two prompts and the measured expert selections they produced.' }, | |
| quant: { number: '02', title: 'Spot the 4-bit model.', short: 'A blind precision test', summary: 'Read two answers before seeing their precision, timings and checks.' }, | |
| surgery: { number: '03', title: 'What else broke?', short: 'A controlled intervention', summary: 'Inspect a behavior intervention and its effects across the same test cases.' }, | |
| memory: { number: '04', title: 'Remembered, but outdated.', short: 'The correction detective', summary: 'Predict the right answer, then see whether the model used a correction or repeated an old fact.' }, | |
| sound: { number: '05', title: 'A model, heard.', short: 'Listen to real routing', summary: 'Turn measured model activity into a musical score you can inspect.' } | |
| }; | |
| function assert(condition, message) { if (!condition) throw new Error(message); } | |
| function validateResults(data) { | |
| assert(data && data.schema_version === 1, 'This results file uses an unsupported format.'); | |
| assert(data.experiments && typeof data.experiments === 'object', 'Experiment results are missing.'); | |
| for (const key of ['reroute', 'quant', 'surgery', 'memory']) { | |
| const exp = data.experiments[key]; | |
| assert(exp && Array.isArray(exp.cases) && exp.cases.length > 0, `The ${key} experiment has no cases.`); | |
| const seen = new Set(); | |
| for (const item of exp.cases) { | |
| assert(item && typeof item.id === 'string' && item.id.length > 0 && !seen.has(item.id), `Invalid or repeated case ID in ${key}.`); | |
| seen.add(item.id); | |
| assert(Array.isArray(item.conditions) && item.conditions.length >= 1 && item.conditions.length <= 12, `Conditions are missing in ${key}/${item.id}.`); | |
| const conditionIds = new Set(); | |
| for (const condition of item.conditions) { | |
| assert(condition && typeof condition.id === 'string' && !conditionIds.has(condition.id), `Invalid condition ID in ${key}/${item.id}.`); | |
| conditionIds.add(condition.id); | |
| assert(typeof condition.output === 'string', `A captured answer is missing in ${key}/${item.id}.`); | |
| if (condition.routing && condition.routing.tokens !== undefined) { | |
| assert(Array.isArray(condition.routing.tokens), `Routing tokens must be an array in ${key}/${item.id}.`); | |
| for (const frame of condition.routing.tokens) { | |
| assert(frame && typeof frame.token === 'string' && Array.isArray(frame.experts), 'A routing frame is invalid.'); | |
| assert(frame.experts.every(id => Number.isInteger(id) && id >= 0 && id < (condition.routing.expert_count || 256)), 'An expert index is outside this model.'); | |
| if (frame.weights !== undefined) assert(Array.isArray(frame.weights) && frame.weights.length === frame.experts.length && frame.weights.every(w => Number.isFinite(w) && w >= 0), 'Routing weights are invalid.'); | |
| } | |
| } | |
| } | |
| if (key === 'quant') assert(item.conditions.length === 2, 'The blind comparison needs exactly two answers.'); | |
| if (key === 'memory') assert(Array.isArray(item.choices) && item.choices.length >= 2 && item.choices.every(c => typeof c === 'string'), 'The memory case needs answer choices.'); | |
| } | |
| } | |
| return data; | |
| } | |
| function stablePairOrder(id, conditions) { | |
| let hash = 2166136261; | |
| for (const char of String(id)) hash = Math.imul(hash ^ char.charCodeAt(0), 16777619) >>> 0; | |
| return hash % 2 ? conditions.slice().reverse() : conditions.slice(); | |
| } | |
| function isLowBit(condition) { return /(?:^|[^0-9])q?4(?:[^0-9]|$)/i.test(String(condition && condition.quant || '')); } | |
| function formatMetric(value) { | |
| if (typeof value === 'number') return Number.isInteger(value) ? value.toLocaleString('en-US') : value.toLocaleString('en-US', { maximumFractionDigits: 5 }); | |
| if (typeof value === 'boolean') return value ? 'Yes' : 'No'; | |
| return typeof value === 'object' && value !== null ? JSON.stringify(value) : String(value ?? ''); | |
| } | |
| function readableKey(key) { return key.replace(/_/g, ' ').replace(/\bms\b/g, '(ms)').replace(/\bseconds\b/g, '(s)'); } | |
| function promptFor(item, condition) { | |
| if (typeof condition.prompt === 'string') return condition.prompt; | |
| const linked = Array.isArray(item.prompts) && item.prompts.find(p => p.id === condition.prompt_id); | |
| return linked ? linked.text : typeof item.prompt === 'string' ? item.prompt : ''; | |
| } | |
| function fixturesFrom(data) { | |
| const experiments = {}; | |
| for (const [key, exp] of Object.entries(data.experiments)) { | |
| experiments[key] = { | |
| method: exp.method, | |
| cases: exp.cases.map(item => ({ id: item.id, title: item.title, prompt: item.prompt, prompts: item.prompts, question: item.question, ground_truth: item.ground_truth, choices: item.choices, intervention: item.intervention, conditions: item.conditions.map(c => ({ id: c.id, model: c.model, quant: c.quant, prompt: promptFor(item, c), settings: c.settings, intervention: c.intervention })) })) | |
| }; | |
| } | |
| return { schema_version: 1, generated_at: data.generated_at, provenance: data.provenance, methodology: data.methodology, experiments }; | |
| } | |
| function routingFrame(condition, step) { | |
| const frames = condition.routing && condition.routing.tokens; | |
| return Array.isArray(frames) && step < frames.length ? frames[step] : null; | |
| } | |
| const api = { validateResults, stablePairOrder, isLowBit, formatMetric, promptFor, fixturesFrom, routingFrame }; | |
| if (typeof module !== 'undefined' && module.exports) module.exports = api; | |
| if (typeof document === 'undefined') return; | |
| const $ = id => document.getElementById(id); | |
| let data, active = 'reroute', currentCase, tokenStep = 0, toastTimer; | |
| const reveals = new Set(); | |
| const stateKey = 'alphabell-experiments-v1'; | |
| let guesses = { quant: {}, memory: {} }; | |
| try { const saved = JSON.parse(localStorage.getItem(stateKey) || 'null'); if (saved && saved.quant && saved.memory) guesses = saved; } catch (_) { /* Private browser storage may be unavailable. */ } | |
| function node(tag, className, content) { | |
| const element = document.createElement(tag); | |
| if (className) element.className = className; | |
| if (content !== undefined) element.textContent = String(content); | |
| return element; | |
| } | |
| function append(parent, ...children) { for (const child of children) if (child) parent.append(child); return parent; } | |
| function button(text, action, className) { const el = node('button', className, text); el.type = 'button'; el.addEventListener('click', action); return el; } | |
| function safeURL(url) { | |
| if (typeof url !== 'string') return null; | |
| try { const parsed = new URL(url, location.href); return ['http:', 'https:'].includes(parsed.protocol) ? parsed.href : null; } catch (_) { return null; } | |
| } | |
| function link(text, url, className) { | |
| const href = safeURL(url); | |
| if (!href) return null; | |
| const el = node('a', className, text); el.href = href; | |
| if (new URL(href).origin !== location.origin) { el.target = '_blank'; el.rel = 'noreferrer'; } | |
| return el; | |
| } | |
| function toast(message) { $('toast').textContent = message; $('toast').hidden = false; clearTimeout(toastTimer); toastTimer = setTimeout(() => { $('toast').hidden = true; }, 3000); } | |
| function save() { try { localStorage.setItem(stateKey, JSON.stringify(guesses)); } catch (_) { toast('This browser cannot save guesses. Your current answer is still recorded for this visit.'); } } | |
| function download(object, name) { | |
| const url = URL.createObjectURL(new Blob([JSON.stringify(object, null, 2) + '\n'], { type: 'application/json' })); | |
| const anchor = document.createElement('a'); anchor.href = url; anchor.download = name; anchor.click(); setTimeout(() => URL.revokeObjectURL(url), 1000); | |
| } | |
| function displayValue(parent, value) { | |
| if (value === undefined || value === null || value === '') return; | |
| if (typeof value === 'string') { parent.append(node('p', '', value)); return; } | |
| if (Array.isArray(value)) { const list = node('ul'); value.forEach(item => append(list, node('li', '', typeof item === 'object' ? JSON.stringify(item) : item))); parent.append(list); return; } | |
| parent.append(node('pre', '', JSON.stringify(value, null, 2))); | |
| } | |
| function metrics(object) { | |
| const list = node('dl', 'metrics'); | |
| for (const [key, value] of Object.entries(object || {})) { | |
| if (value === undefined || value === null) continue; | |
| list.append(append(node('div'), node('dt', '', readableKey(key)), node('dd', '', formatMetric(value)))); | |
| } | |
| return list.children.length ? list : null; | |
| } | |
| function setupNav() { | |
| $('experimentNav').replaceChildren(); | |
| for (const [key, def] of Object.entries(DEFINITIONS)) { | |
| const anchor = node('a', 'experiment-card'); anchor.href = key === 'sound' ? 'sound.html' : 'index.html?experiment=' + key; | |
| anchor.dataset.experiment = key; | |
| append(anchor, node('span', 'card-number', def.number), node('strong', '', def.title), node('small', '', def.short), node('span', 'card-arrow', '↗')); | |
| if (key !== 'sound') anchor.addEventListener('click', event => { event.preventDefault(); selectExperiment(key, null, true); }); | |
| $('experimentNav').append(anchor); | |
| } | |
| } | |
| function selectExperiment(key, id, push) { | |
| active = ['reroute', 'quant', 'surgery', 'memory'].includes(key) ? key : 'reroute'; | |
| if (!data) { | |
| const url = new URL(location.href); url.searchParams.set('experiment', active); url.searchParams.delete('case'); history.pushState(null, '', url); | |
| return; | |
| } | |
| const cases = data.experiments[active].cases; | |
| currentCase = cases.find(item => item.id === id) || cases[0]; | |
| tokenStep = 0; | |
| if (push) { | |
| const url = new URL(location.href); url.searchParams.set('experiment', active); url.searchParams.set('case', currentCase.id); history.pushState(null, '', url); | |
| } | |
| document.querySelectorAll('[data-experiment]').forEach(a => { if (a.dataset.experiment === active) a.setAttribute('aria-current', 'page'); else a.removeAttribute('aria-current'); }); | |
| render(); | |
| if (push) $('experiment').scrollIntoView({block:'start',behavior:'smooth'}); | |
| } | |
| function conditionCard(item, condition, options = {}) { | |
| const card = node('section', 'condition'); | |
| const heading = node('div', 'condition-heading'); | |
| append(heading, node('h4', '', options.label || condition.label || condition.id)); | |
| if (!options.blind) heading.append(node('span', 'tag', [condition.model, condition.quant].filter(Boolean).join(' · '))); | |
| const content = node('div', 'condition-content'); | |
| const prompt = promptFor(item, condition); | |
| if (options.showPrompt && prompt) content.append(node('div', 'prompt', prompt)); | |
| content.append(node('div', 'output', condition.output || '(The model produced no answer.)')); | |
| if (!options.blind) { | |
| append(content, metrics(condition.metrics)); | |
| if (condition.checks) { const detail = node('details'); append(detail, node('summary', '', 'Answer checks')); displayValue(detail, condition.checks); content.append(detail); } | |
| append(content, link('Open measured trace ↗', condition.trace_url, 'condition-link')); | |
| } | |
| append(card, heading, content); return card; | |
| } | |
| function shareButton() { | |
| return button('Share this case ↗', async () => { | |
| const url = new URL(location.href); url.searchParams.set('experiment', active); url.searchParams.set('case', currentCase.id); | |
| try { await navigator.clipboard.writeText(url.href); toast('Case link copied.'); } catch (_) { toast('Copy the address in your browser to share this case.'); } | |
| }, 'quiet share'); | |
| } | |
| function render() { | |
| const exp = data.experiments[active], def = DEFINITIONS[active], item = currentCase; | |
| document.title = def.title + ' | alphaBell Experiments'; | |
| const fragment = document.createDocumentFragment(), head = node('div', 'experiment-head'); | |
| const title = node('h2', '', exp.title || def.title); title.id = 'experimentTitle'; | |
| append(head, append(node('div'), node('p', 'eyebrow', 'EXPERIMENT ' + def.number), title, node('p', '', exp.summary || def.summary))); | |
| const picker = node('label', 'case-picker', 'Choose a recorded case'); const select = node('select'); select.id = 'caseSelect'; | |
| exp.cases.forEach((c, index) => { const option = node('option', '', String(index + 1).padStart(2, '0') + ' · ' + (c.title || c.id)); option.value = c.id; option.selected = c.id === item.id; select.append(option); }); | |
| select.addEventListener('change', () => selectExperiment(active, select.value, true)); append(picker, select); head.append(picker); fragment.append(head); | |
| const layout = node('article', 'case-layout'); | |
| const top = node('div', 'case-top'), context = append(node('div'), node('h3', '', item.title || item.id)); | |
| if (item.description) context.append(node('p', '', item.description)); | |
| if (item.prompt && active !== 'reroute') context.append(node('div', 'prompt', item.prompt)); | |
| append(top, context, node('span', 'tag', 'Recorded experiment')); | |
| const body = node('div', 'case-body'); | |
| if (active === 'reroute') renderReroute(body, item); | |
| if (active === 'quant') renderQuant(body, item); | |
| if (active === 'surgery') renderSurgery(body, item); | |
| if (active === 'memory') renderMemory(body, item); | |
| const controls = node('div', 'controls'); | |
| append(controls, button('Download this case ↓', () => download({ schema_version: data.schema_version, experiment: active, provenance: data.provenance, method: exp.method, case: item }, 'alphabell-' + active + '-' + item.id.replace(/[^a-z0-9_-]/gi, '_') + '.json'), 'quiet'), shareButton()); | |
| body.append(controls); | |
| const method = node('details'); append(method, node('summary', '', 'How to read this experiment')); displayValue(method, exp.method); displayValue(method, exp.limitations); displayValue(method, exp.reproduce); if (item.provenance) displayValue(method, item.provenance); body.append(method); | |
| append(layout, top, body); fragment.append(layout); $('experiment').replaceChildren(fragment); | |
| } | |
| function renderReroute(body, item) { | |
| if (item.conditions.every(c => !c.routing)) body.append(node('p', 'annotation', 'Qwen Open uses recurrent heads and dense feed-forward blocks. Compare measured head activity in the full traces; expert grids apply to Flint.')); | |
| body.append(node('div', 'notice', 'A routing change is a measured change in expert selection. It does not identify what an expert means, or prove that an expert caused a particular answer.')); | |
| const frames = item.conditions.map(c => c.routing && c.routing.tokens || []), maxFrames = Math.max(...frames.map(f => f.length)); | |
| const columns = node('div', item.conditions.length > 2 ? 'columns three' : 'columns'); | |
| const panels = []; | |
| if (maxFrames > 0) { | |
| const controls = node('div', 'token-controls'), label = node('label', '', 'Generated token step'); label.htmlFor = 'routingStep'; | |
| const slider = node('input'); slider.type = 'range'; slider.id = 'routingStep'; slider.min = '0'; slider.max = String(maxFrames - 1); slider.value = String(tokenStep); | |
| const readout = node('span', 'token-readout'); readout.setAttribute('aria-live', 'polite'); | |
| const update = step => { tokenStep = Math.max(0, Math.min(maxFrames - 1, step)); slider.value = String(tokenStep); readout.textContent = (tokenStep + 1) + ' / ' + maxFrames; panels.forEach(panel => paintRouting(panel, tokenStep)); }; | |
| const previous = button('‹', () => update(tokenStep - 1)); previous.setAttribute('aria-label', 'Previous token'); | |
| const next = button('›', () => update(tokenStep + 1)); next.setAttribute('aria-label', 'Next token'); | |
| slider.addEventListener('input', () => update(Number(slider.value))); append(controls, label, previous, slider, next, readout); body.append(controls); | |
| body.append(node('p', 'annotation', 'The slider pairs tokens by generated position. Different answers may use different token boundaries and stop at different lengths.')); | |
| readout.textContent = (tokenStep + 1) + ' / ' + maxFrames; | |
| } | |
| for (const condition of item.conditions) { | |
| const card = conditionCard(item, condition, { showPrompt: true }); | |
| if (condition.routing && Array.isArray(condition.routing.tokens) && condition.routing.tokens.length) { | |
| const panel = node('div', 'routing-panel'); panel.condition = condition; | |
| const label = 'Selected experts' + (Number.isInteger(condition.routing.layer) ? ', block ' + condition.routing.layer : ''); | |
| append(panel, node('p', 'routing-label', label), node('code', 'routing-token'), node('div', 'expert-grid'), node('p', 'expert-list'), node('p', 'routing-key', 'Each square is an expert ID. Dark squares were selected in this captured token.')); | |
| const grid = panel.querySelector('.expert-grid'); | |
| for (let id = 0; id < (condition.routing.expert_count || 256); id++) { const dot = node('span', 'expert-dot'); dot.title = 'Expert ' + id; dot.setAttribute('aria-hidden', 'true'); grid.append(dot); } | |
| card.querySelector('.condition-content').prepend(panel); panels.push(panel); paintRouting(panel, tokenStep); | |
| } | |
| columns.append(card); | |
| } | |
| body.append(columns); | |
| if (item.comparison) { const details = node('details'); append(details, node('summary', '', 'Measured comparison')); displayValue(details, item.comparison); body.append(details); } | |
| } | |
| function paintRouting(panel, step) { | |
| const frame = routingFrame(panel.condition, step), token = panel.querySelector('.routing-token'), list = panel.querySelector('.expert-list'); | |
| token.textContent = frame ? frame.token : 'This answer has ended.'; | |
| const experts = new Set(frame ? frame.experts : []); | |
| Array.from(panel.querySelector('.expert-grid').children).forEach((dot, id) => dot.classList.toggle('active', experts.has(id))); | |
| list.textContent = frame ? frame.experts.map((id, index) => '#' + id + (frame.weights ? ' (' + formatMetric(frame.weights[index]) + ')' : '')).join(' · ') : 'No captured frame at this position.'; | |
| } | |
| function renderQuant(body, item) { | |
| const ordered = stablePairOrder(item.id, item.conditions), key = 'quant:' + item.id, revealed = reveals.has(key); | |
| body.append(node('p', 'annotation', 'Both answers use the same case prompt. Model names, precision and measurements stay hidden until you reveal the evidence. Answer order is deterministic for each case.')); | |
| const columns = node('div', 'columns'); ordered.forEach((condition, index) => columns.append(conditionCard(item, condition, { blind: !revealed, label: revealed ? 'Answer ' + String.fromCharCode(65 + index) + ' · ' + (condition.label || condition.id) : 'Answer ' + String.fromCharCode(65 + index) }))); body.append(columns); | |
| const panel = node('section', 'guess-panel'); append(panel, node('h4', 'question', 'Which answer came from the 4-bit model?')); | |
| const choices = node('div', 'choice-row'); | |
| ordered.forEach((condition, index) => { | |
| const pick = button('Answer ' + String.fromCharCode(65 + index) + ' is 4-bit', () => { guesses.quant[item.id] = condition.id; save(); reveals.add(key); render(); }); | |
| pick.disabled = revealed; pick.setAttribute('aria-pressed', String(guesses.quant[item.id] === condition.id)); choices.append(pick); | |
| }); panel.append(choices); | |
| if (!revealed) { const controls = node('div', 'controls'); controls.append(button('Reveal without guessing', () => { reveals.add(key); render(); }, 'quiet')); panel.append(controls); } | |
| else { | |
| const low = ordered.find(isLowBit), guess = guesses.quant[item.id], chosen = ordered.find(c => c.id === guess); | |
| const message = !low ? 'Precision labels are shown above.' : guess ? (isLowBit(chosen) ? 'You spotted it.' : 'The other answer was 4-bit.') : 'The precision is revealed.'; | |
| const result = node('div', 'result-note', message); | |
| if (low) result.append(node('p', '', 'Answer ' + String.fromCharCode(65 + ordered.indexOf(low)) + ' used ' + low.quant + '. A single answer is not a quality benchmark. Check the full outputs and measured results.')); | |
| append(panel, result); if (item.checks) displayValue(panel, item.checks); | |
| } | |
| let total = 0, correct = 0; | |
| for (const test of data.experiments.quant.cases) { const guess = guesses.quant[test.id]; if (guess) { total++; const cond = test.conditions.find(c => c.id === guess); if (cond && isLowBit(cond)) correct++; } } | |
| panel.append(node('p', 'local-tally', 'LOCAL ONLY · ' + correct + ' / ' + total + ' correct guesses saved in this browser. No votes are uploaded.')); | |
| body.append(panel); | |
| } | |
| function renderSurgery(body, item) { | |
| const intervention = item.intervention || {}; | |
| const notice = node('div', 'notice'); append(notice, node('strong', '', intervention.kind ? readableKey(intervention.kind) : 'Recorded intervention')); | |
| if (intervention.description) notice.append(node('div', '', intervention.description)); | |
| if (/prompt/i.test(intervention.kind || '')) notice.append(node('div', '', 'This intervention changes the prompt. It does not modify model weights.')); | |
| if (/activation/i.test(intervention.kind || '')) notice.append(node('div', '', 'This intervention changes activations during execution. It does not rewrite the model weights.')); | |
| body.append(notice); | |
| const columns = node('div', item.conditions.length > 2 ? 'columns three' : 'columns'); item.conditions.forEach(c => columns.append(conditionCard(item, c, { showPrompt: true }))); body.append(columns); | |
| if (item.checks) { const results = node('div', 'result-note'); results.append(node('strong', '', 'Behavior and regression checks')); displayValue(results, item.checks); body.append(results); } | |
| if (item.comparison) { const results = node('details'); results.append(node('summary', '', 'Intervention comparison')); displayValue(results, item.comparison); body.append(results); } | |
| body.append(node('p', 'annotation', 'A visible change is a result for this case and intervention. Check the other cases before generalizing about the model. Negative and unchanged results are useful evidence too.')); | |
| } | |
| function truthText(item) { | |
| return typeof item.ground_truth === 'string' ? item.ground_truth : item.ground_truth && (item.ground_truth.answer || item.ground_truth.value) || JSON.stringify(item.ground_truth); | |
| } | |
| function renderMemory(body, item) { | |
| const key = 'memory:' + item.id, revealed = reveals.has(key), selected = guesses.memory[item.id]; | |
| const panel = node('section', 'guess-panel'); append(panel, node('h4', 'question', item.question || 'What should the model answer after the correction?')); | |
| const choices = node('div', 'choice-row'); | |
| item.choices.forEach(choice => { | |
| const pick = button(choice, () => { guesses.memory[item.id] = choice; save(); render(); }); pick.disabled = revealed; pick.setAttribute('aria-pressed', String(selected === choice)); choices.append(pick); | |
| }); panel.append(choices); | |
| if (!revealed) { | |
| const reveal = button('Reveal model answers', () => { reveals.add(key); render(); }, 'primary'); reveal.disabled = !selected; | |
| const controls = node('div', 'controls'); controls.append(reveal); panel.append(controls); | |
| panel.append(node('p', 'local-tally', 'Choose the answer the model should give. Your choice stays in this browser.')); | |
| body.append(panel, node('div', 'reveal-blank', 'The captured answers and scoring stay hidden until you make a prediction.')); | |
| } else { | |
| const truth = truthText(item), correct = String(selected).trim().toLowerCase() === String(truth).trim().toLowerCase(); | |
| const result = node('div', 'result-note', (correct ? 'Your answer matches the ground truth. ' : 'Compare your prediction with the ground truth. ') + 'Expected answer: ' + truth); | |
| result.append(node('p', '', 'Your prediction: ' + selected + '. The model answers below are separate measured results.')); | |
| panel.append(result); body.append(panel); | |
| const columns = node('div', item.conditions.length > 2 ? 'columns three' : 'columns'); item.conditions.forEach(c => columns.append(conditionCard(item, c, { showPrompt: true }))); body.append(columns); | |
| if (item.checks) { const check = node('div', 'result-note'); check.append(node('strong', '', 'Correction and recall checks')); displayValue(check, item.checks); body.append(check); } | |
| } | |
| body.append(node('p', 'annotation', 'Using an in-context correction and remembering a fact are different tests. The prompts show which information each condition received. These results do not measure a permanent change to model memory.')); | |
| } | |
| function renderMethods() { | |
| const target = $('methodBody'); target.replaceChildren(); | |
| if (data.generated_at) target.append(node('p', '', 'Results prepared: ' + new Intl.DateTimeFormat('en-US', {dateStyle:'medium',timeStyle:'short',timeZone:'America/Los_Angeles'}).format(new Date(data.generated_at)) + ' Pacific time.')); | |
| displayValue(target, data.methodology); | |
| const provenance = data.provenance || {}; | |
| if (Array.isArray(provenance.model_sources)) { | |
| target.append(node('h3', '', 'Source models')); | |
| const list = node('ul'); provenance.model_sources.forEach(source => { const li = node('li'); append(li, link(source.name || source.url, source.url)); if (source.revision) li.append(document.createTextNode(' · revision ' + source.revision)); list.append(li); }); target.append(list); | |
| } | |
| const detail = { ...provenance }; delete detail.model_sources; displayValue(target, detail); | |
| } | |
| async function load() { | |
| $('experiment').replaceChildren(node('div', 'loading', 'Opening measured experiments…')); | |
| try { | |
| const response = await fetch('data/results.json', { cache: 'no-cache' }); | |
| if (!response.ok) throw new Error('The results could not be downloaded (HTTP ' + response.status + ').'); | |
| data = validateResults(await response.json()); | |
| renderMethods(); $('evidence').hidden = false; | |
| const params = new URLSearchParams(location.search); selectExperiment(params.get('experiment'), params.get('case'), false); if (params.has('experiment')) requestAnimationFrame(() => $('experiment').scrollIntoView({block:'start'})); | |
| } catch (error) { | |
| $('evidence').hidden = true; | |
| const panel = node('div', 'error'); panel.setAttribute('role', 'alert'); | |
| append(panel, node('h2', '', 'The measured results could not be opened.'), node('p', '', error.message || 'Please try loading the experiment again.'), button('Try again', load)); | |
| $('experiment').replaceChildren(panel); | |
| } | |
| } | |
| setupNav(); | |
| $('downloadResults').addEventListener('click', () => { if (data) download(data, 'alphabell-experiment-results.json'); }); | |
| $('downloadFixtures').addEventListener('click', () => { if (data) download(fixturesFrom(data), 'alphabell-experiment-prompts.json'); }); | |
| $('resetGuesses').addEventListener('click', () => { guesses = { quant: {}, memory: {} }; reveals.clear(); save(); if (data) render(); toast('Local guesses cleared.'); }); | |
| window.addEventListener('popstate', () => { if (data) { const params = new URLSearchParams(location.search); selectExperiment(params.get('experiment'), params.get('case'), false); } }); | |
| load(); | |
| })(typeof window !== 'undefined' ? window : globalThis); | |