Download app.js from Hugdrigo/multilingual-modelbench: direct link, hf CLI and curl.
- Browser
- Download file 12.8 kB
-
https://huggingface.co/spaces/Hugdrigo/multilingual-modelbench/resolve/main/app.js
- Command line
-
hf download hf://spaces/Hugdrigo/multilingual-modelbench/app.js
-
curl -L -o app.js https://huggingface.co/spaces/Hugdrigo/multilingual-modelbench/resolve/main/app.js
12.8 kB
| ; | |
| // 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(); | |