File size: 12,789 Bytes
61a25fe | 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 | 'use strict';
// Static dashboard presenting locally stored and already executed Python metrics.
// No user input leaves the browser. There is no neural inference on this page.
const $ = selector => document.querySelector(selector);
const $$ = selector => [...document.querySelectorAll(selector)];
const state = {db:null, split:'monolingual', view:'overview', metric:'accuracy_at_1', method:'char-tfidf', group:'by_language', caseMethod:'char-tfidf', selectedCase:null, search:'', language:'all', domain:'all'};
const METHOD_LABELS={'char-tfidf':'Character TF-IDF','word-tfidf':'Word TF-IDF','hybrid-tfidf':'Hybrid TF-IDF','bm25':'BM25'};
const LANG_LABELS={en:'English',es:'Español',pt:'Português'};
const DOMAIN_LABELS={ai:'Artificial intelligence',business:'Business',education:'Education','latin-america':'Latin America',research:'Research'};
const esc=s=>String(s??'').replace(/[&<>"']/g,c=>({'&':'&','<':'<','>':'>','"':'"',"'":'''}[c]));
const percent=x=>x==null?'Not evaluated':`${(x*100).toFixed(1)}%`;
const fixed=(x,n=3)=>x==null?'—':Number(x).toFixed(n);
const activeData=()=>state.db?.splits[state.split];
const executed=()=>activeData().results.models.filter(m=>m.status==='executed'&&m.kind==='lexical');
function updateTop(){
$('#splitMono').classList.toggle('active',state.split==='monolingual');
$('#splitCross').classList.toggle('active',state.split==='cross-language');
$('#splitMono').setAttribute('aria-pressed',state.split==='monolingual');
$('#splitCross').setAttribute('aria-pressed',state.split==='cross-language');
const desc=state.split==='monolingual'?'30 within-language questions across English, Spanish and Portuguese':'18 cross-language questions across six translation directions';
$('#splitDescription').textContent=desc;
const group=$('#groupSelect');
if(state.split==='cross-language'&&group.value==='by_language') group.value='by_language_pair';
if(state.split==='monolingual'&&group.value==='by_language_pair') group.value='by_language';
[...group.options].find(x=>x.value==='by_language_pair').disabled=state.split==='monolingual';
state.group=group.value;
}
function renderStats(){
const d=activeData(), method=executed().slice().sort((a,b)=>b.accuracy_at_1-a.accuracy_at_1)[0];
const cells=[['Evaluation tasks',String(d.rows.length)],['Measured baselines',String(executed().length)],['Best Accuracy@1',percent(method.accuracy_at_1)],['Best MRR',fixed(Math.max(...executed().map(m=>m.mrr)))]];
$('#stats').innerHTML=cells.map(([title,value])=>`<div class="stat"><strong>${esc(value)}</strong><small>${esc(title)}</small></div>`).join('');
}
function renderLeaderboard(){
const models=executed().slice().sort((a,b)=>b[state.metric]-a[state.metric]);
$('#leaderboard').innerHTML=models.map((m,i)=>`<div class="bar-item"><div class="bar-label"><span>${esc(METHOD_LABELS[m.name]||m.name)}</span><strong>${state.metric==='mrr'?fixed(m.mrr):percent(m.accuracy_at_1)}</strong></div><div class="bar-track"><div class="bar-fill ${i%2?'alt':''}" style="width:${Math.max(1,Math.min(100,(m[state.metric]||0)*100)).toFixed(2)}%"></div></div></div>`).join('');
$('#metricSelect').value=state.metric;
}
function renderInsight(){
const d=activeData(), lookup=name=>d.results.models.find(m=>m.name===name), char=lookup('char-tfidf'), word=lookup('word-tfidf');
const diff=(char.accuracy_at_1-word.accuracy_at_1)*100;
if(state.split==='cross-language'){
$('#insight').innerHTML=`<p><strong>Character TF-IDF: ${percent(char.accuracy_at_1)}</strong> versus Word TF-IDF: ${percent(word.accuracy_at_1)}. On this small cross-language test set, character patterns outperform exact token overlap by <strong>${diff.toFixed(1)} percentage points</strong>.</p><p>Shared roots and technical vocabulary between the three languages can make these test cases easier for character matching.</p>`;
}else{
$('#insight').innerHTML=`<p><strong>${percent(char.accuracy_at_1)} Accuracy@1</strong> for character TF-IDF on 30 monolingual tests. Word TF-IDF achieves ${percent(word.accuracy_at_1)}.</p><p>All methods use three candidate passages per question. The high scores are diagnostic, not evidence of production reliability.</p>`;
}
}
function renderMethodSelectors(){
for(const id of ['#modelSelect','#caseModelSelect']){
const el=$(id); const chosen=id==='#modelSelect'?state.method:state.caseMethod;
el.innerHTML=executed().map(m=>`<option value="${esc(m.name)}">${esc(METHOD_LABELS[m.name]||m.name)}</option>`).join('');
el.value=chosen;
}
}
function renderBreakdown(){
const m=executed().find(m=>m.name===state.method)||executed()[0];
const segments=m[state.group]||{};
const entries=Object.entries(segments).sort((a,b)=>a[0].localeCompare(b[0]));
$('#breakdown').innerHTML=entries.length?entries.map(([key,r])=>{
const lbl=state.group==='by_language'?LANG_LABELS[key]||key:state.group==='by_domain'?DOMAIN_LABELS[key]||key:key.replace('->',' → ').toUpperCase();
return `<div class="breakdown-item"><div class="heading"><span>${esc(lbl)}</span><b>${percent(r.accuracy_at_1)}</b></div><div class="bar-track"><div class="bar-fill" style="width:${(r.accuracy_at_1*100).toFixed(1)}%"></div></div><p>n = ${Number(r.n)} · MRR ${fixed(r.mrr)}</p></div>`;
}).join(''):'<p class="muted">No subgroup data available.</p>';
}
function renderSemantic(){
const models=activeData().results.models.filter(m=>m.kind==='semantic');
$('#semanticRows').innerHTML=models.map(m=>`<article class="semantic"><b>${esc(m.metadata?.repo_id||m.name)}</b><small>${esc(m.metadata?.role||'Multilingual embeddings')}</small><span class="pending">NOT EVALUATED</span></article>`).join('');
}
function renderOverview(){updateTop();renderStats();renderMethodSelectors();renderLeaderboard();renderInsight();renderBreakdown();renderSemantic();}
function filtersForCurrent(){
let rows=activeData().rows;
if(state.search){const q=state.search.toLowerCase();rows=rows.filter(r=>[r.id,r.query,r.domain].some(v=>String(v).toLowerCase().includes(q)));}
if(state.language!=='all')rows=rows.filter(r=>(r.language||r.query_language)===state.language);
if(state.domain!=='all')rows=rows.filter(r=>r.domain===state.domain);
return rows;
}
function filtersUI(){
const lang=$('#langFilter');lang.innerHTML='<option value="all">All languages</option>'+['en','es','pt'].map(k=>`<option value="${k}">${LANG_LABELS[k]}</option>`).join('');lang.value=state.language;
const domain=$('#domainFilter');domain.innerHTML='<option value="all">All domains</option>'+Object.entries(DOMAIN_LABELS).map(([k,v])=>`<option value="${k}">${v}</option>`).join('');domain.value=state.domain;
$('#querySearch').value=state.search;
}
function renderCaseList(){
const rows=filtersForCurrent();
$('#caseCount').textContent=`${rows.length} / ${activeData().rows.length}`;
if(!rows.some(r=>r.id===state.selectedCase))state.selectedCase=rows[0]?.id||null;
$('#caseList').innerHTML=rows.length?rows.map(r=>`<button type="button" class="case-btn ${state.selectedCase===r.id?'active':''}" data-id="${esc(r.id)}" role="listitem"><span class="case-id">${esc(r.id.toUpperCase())}</span><span class="case-q">${esc(r.query)}</span><span class="case-meta">${esc(DOMAIN_LABELS[r.domain]||r.domain)} · ${esc(r.language||`${r.query_language} → ${r.passage_language}`)}</span></button>`).join(''):'<p class="empty">No cases match those filters. Clear the search or change filters.</p>';
$$('#caseList .case-btn').forEach(b=>b.addEventListener('click',()=>{state.selectedCase=b.dataset.id;renderCaseList();renderCaseDetail();}));
renderCaseDetail();
}
function renderCaseDetail(){
const d=activeData(), r=d.rows.find(x=>x.id===state.selectedCase);
if(!r){$('#caseDetail').innerHTML='<p class="empty">Select a case to see its original passages and scores.</p>';return;}
const saved=(d.case_results[state.caseMethod]||[]).find(x=>x.id===r.id);
if(!saved){$('#caseDetail').innerHTML='<p class="empty">No stored result for this method.</p>';return;}
const scores=saved.scores;
const candidates=[{label:'Relevant passage',type:'positive',text:r.positive_passage,score:scores[0],original:0},{label:'Hard negative',type:'negative',text:r.hard_negative,score:scores[1],original:1},{label:'Easy negative',type:'negative',text:r.easy_negative,score:scores[2],original:2}];
candidates.sort((a,b)=>b.score-a.score||b.original-a.original);
const winner=saved.positive_rank===1;
const max=Math.max(...scores,0.000001),min=Math.min(...scores,0);
$('#caseDetail').innerHTML=`<div class="case-query">${esc(r.query)}<small>${esc(r.id)} · ${esc(DOMAIN_LABELS[r.domain]||r.domain)} · ${esc(r.language||r.query_language+' → '+r.passage_language)}</small></div><div class="case-topline"><span class="${winner?'good-tag':'bad-tag'}">${winner?'✓ Relevant passage ranked #1':'! Relevant passage ranked #'+saved.positive_rank}</span><span class="muted">Original Python experiment · ${esc(METHOD_LABELS[state.caseMethod])}</span></div>`+
candidates.map((c,i)=>`<div class="candidate ${c.type}"><h4><span>#${i+1} · ${esc(c.label)}</span><span>${c.type==='positive'?'★ Ground truth':'Contrast case'}</span></h4><p>${esc(c.text)}</p><div class="score-row"><span>Similarity score</span><div class="bar-track"><div class="bar-fill ${c.type==='positive'?'':'alt'}" style="width:${Math.max(0,Math.min(100,(c.score-min)/(max-min||1)*100)).toFixed(2)}%"></div></div><strong>${fixed(c.score,4)}</strong></div></div>`).join('')+`<p class="footnote">Scores are retrieval scores, not confidence probabilities. Bars rescale to each case's minimum and maximum. The correct passage is defined in the curated dataset.</p>`;
}
function renderExplorer(){filtersUI();renderMethodSelectors();renderCaseList();}
function render(){if(!state.db)return;renderOverview();renderExplorer();}
function setSplit(split){if(!state.db||!state.db.splits[split])return;state.split=split;state.selectedCase=null;state.search='';state.language='all';state.domain='all';render();}
function showView(which){state.view=which;$$('.tab').forEach(el=>{let selected=el.dataset.view===which;el.classList.toggle('active',selected);el.setAttribute('aria-selected',String(selected));});$$('.view').forEach(v=>v.classList.toggle('active',v.id===which));}
function download(name, text, mime){const blob=new Blob([text],{type:mime});const url=URL.createObjectURL(blob);const a=document.createElement('a');a.href=url;a.download=name;document.body.appendChild(a);a.click();a.remove();setTimeout(()=>URL.revokeObjectURL(url),1000);}
function exportCsv(){if(!state.db)return;const rows=[['split','method','status','n','accuracy_at_1','mrr','avg_latency_ms']];for(const [label,d] of Object.entries(state.db.splits)){for(const m of d.results.models){rows.push([label,m.name,m.status,m.n,m.accuracy_at_1??'',m.mrr??'',m.avg_latency_ms??'']);}}const csv=rows.map(x=>x.map(v=>'"'+String(v??'').replaceAll('"','""')+'"').join(',')).join('\r\n')+'\r\n';download('modelbench-verified-benchmarks.csv',csv,'text/csv;charset=utf-8');}
async function load(){try{const res=await fetch('./benchmark-data.json',{cache:'no-store'});if(!res.ok)throw Error('Dataset could not be loaded (HTTP '+res.status+').');const obj=await res.json();if(obj?.splits?.monolingual?.rows?.length!==30||obj?.splits?.['cross-language']?.rows?.length!==18)throw Error('Dataset is incomplete or invalid.');state.db=obj;$('#lastStatus').textContent='● Stored results verified · no server needed';$('#errorBanner').hidden=true;render();}catch(err){$('#errorBanner').textContent='Could not display the evaluation data: '+err.message+' Please confirm benchmark-data.json was uploaded alongside index.html.';$('#errorBanner').hidden=false;$('#lastStatus').textContent='Data unavailable';$('#lastStatus').classList.add('bad');}}
$('#splitMono').addEventListener('click',()=>setSplit('monolingual'));
$('#splitCross').addEventListener('click',()=>setSplit('cross-language'));
$$('.tab').forEach(b=>b.addEventListener('click',()=>showView(b.dataset.view)));
$('#metricSelect').addEventListener('change',e=>{state.metric=e.target.value;renderLeaderboard();});
$('#modelSelect').addEventListener('change',e=>{state.method=e.target.value;renderBreakdown();});
$('#groupSelect').addEventListener('change',e=>{state.group=e.target.value;renderBreakdown();});
$('#caseModelSelect').addEventListener('change',e=>{state.caseMethod=e.target.value;renderCaseDetail();});
$('#querySearch').addEventListener('input',e=>{state.search=e.target.value;renderCaseList();});
$('#langFilter').addEventListener('change',e=>{state.language=e.target.value;renderCaseList();});
$('#domainFilter').addEventListener('change',e=>{state.domain=e.target.value;renderCaseList();});
$('#exportBtn').addEventListener('click',exportCsv);
load();
|