'use strict';
const $=s=>document.querySelector(s), esc=s=>String(s??'').replace(/[&<>"']/g,c=>({'&':'&','<':'<','>':'>','"':'"',"'":'''}[c]));
const fmt=n=>Number(n).toLocaleString('en-US'), pct=(a,b)=>b?`${(100*a/b).toFixed(1)}%`:'—';
const kindNames={chat:'Slack',tracker:'Notion · Tracker',claim:'Notion · Claim',decision:'Notion · Decision',meeting:'회의 기록',email:'Email',doc:'문서',image:'그림'};
const modNames={text:'Text',image:'Image',multi:'Multi-image'};
const taskOrder=['Fact','Attribution','Computation','Selection','Interpretation','Discrepancy','Resolution','State','Change','Abstention'];
const taskNames={Fact:'Fact · 사실 조회',Attribution:'Attribution · 출처 추적',Computation:'Computation · 수량 계산',Selection:'Selection · 비교·선택',Interpretation:'Interpretation · 해석',Discrepancy:'Discrepancy · 불일치 식별',Resolution:'Resolution · 유효값 결정',State:'State · 상태',Change:'Change · 변경'};
let INDEX,UNITS,unitMap,projectMap,recordUnits,questionMap,RANKS=null,rankError=false,renderGeneration=0;
taskNames.Abstention='Abstention · 답변 유보';
const qaCache=new Map();
let S={view:'qa',project:'',task:'',result:'',text:'',kind:'',id:'',page:1,tab:'retrieval',hitkind:'all',unit:''};
async function getJSON(path){const r=await fetch(path+'.gz');if(!r.ok)throw new Error(`${path} (${r.status})`);const b=new Uint8Array(await r.arrayBuffer());if(b[0]!==31||b[1]!==139)return JSON.parse(new TextDecoder().decode(b));if(typeof DecompressionStream==='undefined')throw new Error('최신 브라우저에서 열어 주세요. 무손실 압축 데이터 지원이 필요합니다.');return new Response(new Blob([b]).stream().pipeThrough(new DecompressionStream('gzip'))).json();}
function url(view,params={}){const q=new URLSearchParams();for(const[k,v]of Object.entries(params))if(v!==''&&v!==null&&v!==undefined&&v!==false)q.set(k,v);return '#'+view+(q.size?'?'+q:'');}
function readState(){const raw=location.hash.slice(1),parts=raw.includes('?')?raw.split('?'):(raw.includes('=')?['clean',raw]:[raw,'']);const p=new URLSearchParams(parts[1]||''),aliases={capability:'group',family:'task',world:'project',evidence:'evidence_bucket'};let head=p.get('head')||'group';head=aliases[head]||head;let result=p.get('result')||p.get('verdict')||'';if(result==='wrong')result='incorrect';let project=p.get('project')||p.get('world')||p.get('f_world')||'';project=INDEX?.projects.find(x=>x.id===project||x.run===project)?.id||project;const requestedTask=p.get('task')||p.get('f_family')||'',requestedFacet=p.get('facet')||'';return {view:['clean','qa','world','results','stats'].includes(parts[0])?parts[0]:'clean',project,task:taskOrder.includes(requestedTask)?requestedTask:'',group:p.get('group')||p.get('f_capability')||'',result,model:p.get('model')||'luna',condition:p.get('condition')||'rag100',text:p.get('text')||'',kind:p.get('kind')||'',id:p.get('id')||'',page:Math.max(1,parseInt(p.get('page')||'1',10)||1),tab:p.get('tab')||'gold',hitkind:p.get('hitkind')||'all',unit:p.get('unit')||'',document:p.get('document')||'',head,facet:head==='task'&&!taskOrder.includes(requestedFacet)?'':requestedFacet,inclusion:p.get('inclusion')||'',interesting:p.get('interesting')||'',app:p.get('app')||'slack',room:p.get('room')||''};}
function go(patch,replace=false){const next={...S,...patch};const h=url(next.view,Object.fromEntries(Object.entries(next).filter(([k])=>k!=='view')));if(replace){history.replaceState(null,'',h);S=next;render();}else if(location.hash===h){S=next;render();}else location.hash=h;}
function projectLabel(id){return projectMap.get(id)?.label||id;}
function shortSource(u){const lines=u.text.split('\n').filter(Boolean);return lines[1]||lines[0]||u.record_id;}
function time(t){return (t||'').replace('T',' ');}
function imageSrc(path){if(!/^media\/[a-f0-9]{64}\.(?:png|jpe?g|webp|gif)$/i.test(path||''))return '';return typeof LAYOUT_IMAGE_PATHS!=='undefined'&&LAYOUT_IMAGE_PATHS.has(path)?'https://huggingface.co/datasets/MMOrgBench/MMOrgBench/resolve/'+LAYOUT_REFRESH.hf_commit+'/'+path:path;}
function badge(ok){return `${ok?'✓ 정답':'× 오답'}`;}
function overview(){const d=INDEX.summary;$('#overview').innerHTML=`
Luna · 전역 RAG top100${pct(d.correct,d.questions)}${fmt(d.correct)} / ${fmt(d.questions)} 정답 · 채점 완료
통합 프로젝트${d.projects} 프로젝트${fmt(d.records)}개 원문 기록
검색 단위${fmt(d.units)}텍스트 ${fmt(d.text_units)} · 이미지 ${fmt(d.images)}
질문 관점Omni프로젝트 사전 필터 없음
`;}
function projectOptions(){return ''+INDEX.projects.map(p=>``).join('');}
function taskOptions(){return ''+taskOrder.map(t=>``).join('');}
function filterControls(world=false){return `
${world?``:``}
`;}
function bindFilters(world=false){let timer;$('#search')?.addEventListener('input',e=>{const pos=e.target.selectionStart,value=e.target.value;clearTimeout(timer);timer=setTimeout(()=>{go({text:value,page:1,id:'',unit:''},true);setTimeout(()=>{const el=$('#search');el?.focus();if(el&&pos!==null)el.setSelectionRange(pos,pos);},0);},250);});$('#project-filter')?.addEventListener('change',e=>go({project:e.target.value,page:1,id:'',unit:''}));$('#task-filter')?.addEventListener('change',e=>go({task:e.target.value,page:1,id:''}));$('#kind-filter')?.addEventListener('change',e=>go({kind:e.target.value,page:1,unit:''}));if(!world)$('#result-filter')?.addEventListener('change',e=>go({result:e.target.value,page:1,id:''}));}
function pageControls(page,pages,extra=''){return `
${page} / ${pages}
`;}
function bindPages(){$$('[data-page]').forEach(b=>b.addEventListener('click',()=>go({page:Number(b.dataset.page),id:'',unit:''})));}
function $$(s,base=document){return [...base.querySelectorAll(s)];}
function filteredQuestions(){const needle=S.text.trim().toLowerCase();return INDEX.questions.filter(q=>(!S.project||q.project===S.project)&&(!S.task||q.task===S.task)&&(!needle||[q.q,q.id,projectLabel(q.project)].join(' ').toLowerCase().includes(needle))&&(!S.result||S.result==='correct'&&q.correct||S.result==='incorrect'&&!q.correct||S.result==='no-image'&&q.modality!=='text'&&q.images===0));}
async function loadQuestion(id){const p=id.split('::')[0];if(!qaCache.has(p))qaCache.set(p,getJSON('data/qa/'+encodeURIComponent(p)+'.json').catch(e=>{qaCache.delete(p);throw e;}));const data=await qaCache.get(p);return data.items.find(q=>q.id===id);}
async function renderQA(generation){const all=filteredQuestions(),pageSize=25,pages=Math.max(1,Math.ceil(all.length/pageSize));let page=Math.min(S.page,pages);let active=all.find(q=>q.id===S.id);if(active){page=Math.floor(all.indexOf(active)/pageSize)+1;}else active=all[(page-1)*pageSize];const start=(page-1)*pageSize;const shown=all.slice(start,start+pageSize);const good=all.filter(q=>q.correct).length;
$('#workspace').innerHTML=filterControls()+`
표시 ${fmt(all.length)}문항 · 정답 ${fmt(good)} · ${pct(good,all.length)}저장된 Luna 판정 · 질문 단위
${active?'
답변과 검색 기록을 불러오는 중입니다…
':'
필터를 변경해 주세요.
'}
`;
bindFilters();bindPages();const imageCounts=OrgBenchImageStats.summarize(all);$('.filter-foot').insertAdjacentHTML('beforeend',`현재 문항들의 검색 이미지: 평균 ${imageCounts.mean===null?'—':imageCounts.mean.toFixed(2)}장 / 100개 · 전체 분포 보기`);$$('.question-link .q-meta').forEach((el,i)=>el.insertAdjacentHTML('afterend',`검색 이미지 ${shown[i].images} / 100`));if(!active)return;try{const q=await loadQuestion(active.id);if(generation!==renderGeneration)return;if(!q)throw new Error('문항 데이터를 찾을 수 없습니다.');S.id=q.id;S.page=page;renderQuestion(q);}catch(e){if(generation===renderGeneration)$('#question-detail').innerHTML=`
문항을 불러오지 못했습니다. ${esc(e.message)}
`;}}
function renderQuestion(q){const projects=new Set(q.hits.map(h=>unitMap.get(h.id)?.project));$('#question-detail').innerHTML=`
${esc(projectLabel(u.project))} · ${esc(time(u.timestamp))} ${esc(u.record_id)}${opts.imageRank?' · 이미지 중 '+fmt(opts.imageRank)+'위':''}${seq&&originalRank?' · 검색 #'+originalRank:''}
${chronological?'유사도 상위 100개를 먼저 선택한 뒤 시간순으로 배열한 실제 기록 순서입니다. 마지막에 질문을 붙여 Luna에 전달했습니다.':'32개 프로젝트 전체에서 질문과의 유사도가 높은 순서로 선택한 100개입니다. 텍스트와 이미지가 같은 순위에서 경쟁하며, 첨부 파일은 자동으로 추가하지 않습니다.'}
100개 검색 단위이미지 ${hitImages}장텍스트 전용 ${100-hitImages}개${projectCount}개 프로젝트
${q.modality!=='text'&&!hitImages?'
이미지 관련 문항이지만 이 요청에는 이미지가 한 장도 전달되지 않았습니다. Gold 근거 탭에서 제외된 이미지의 순위를 확인할 수 있습니다.
`:''}`;}
function renderWorld(){const needle=S.text.trim().toLowerCase();const all=UNITS.filter(u=>(!S.project||u.project===S.project)&&(!S.kind||(S.kind==='pixels'?!!u.image:u.kind===S.kind))&&(!needle||[u.text,u.record_id,projectLabel(u.project)].join(' ').toLowerCase().includes(needle)));const size=24,pages=Math.max(1,Math.ceil(all.length/size)),page=Math.min(S.page,pages),shown=all.slice((page-1)*size,page*size);$('#workspace').innerHTML=`
통합 월드 원문
32개 프로젝트 · 검색에 사용한 원문과 페이지 이미지
${filterControls(true)}
${fmt(all.length)}개 검색 단위 · ${fmt(new Set(all.map(u=>u.record_id)).size)}개 논리 기록
${projectIntro()}
${shown.map(u=>recordHTML(u)).join('')||'
조건에 맞는 기록이 없습니다.
'}
${pageControls(page,pages,'bottom-pages')}`;bindFilters(true);bindPages();bindRecords();if(S.unit&&unitMap.has(S.unit))openUnit(S.unit);}
function groupStats(field,order){return order.map(k=>{const qs=INDEX.questions.filter(q=>q[field]===k);return {key:k,n:qs.length,ok:qs.filter(q=>q.correct).length};});}
function statsTable(rows,label,href){return `
`;}
function renderResults(){const s=INDEX.summary;const mod=groupStats('modality',['text','image','multi']).map(r=>({...r,label:modNames[r.key]}));const groups=groupStats('group',['Retrieval','Synthesis','Alignment']);const tasks=groupStats('task',taskOrder).map(r=>({...r,label:taskNames[r.key]}));const im=INDEX.questions.filter(q=>q.modality!=='text');const buckets=[{key:'with',label:'이미지가 1장 이상 검색됨',rows:im.filter(q=>q.images>0)},{key:'without',label:'검색된 이미지 0장',rows:im.filter(q=>!q.images)}].map(r=>({...r,n:r.rows.length,ok:r.rows.filter(q=>q.correct).length}));$('#workspace').innerHTML=`
최종 평가 결과
${fmt(s.questions)} / ${fmt(s.questions)} 채점 완료
GPT-5.6 Luna · Qwen3-VL-Embedding-2B 전역 top100 · ${pct(s.correct,s.questions)} (${fmt(s.correct)} / ${fmt(s.questions)})
Task 상위 그룹
${statsTable(groups,'그룹')}
모두 Luna 답변 정확도입니다. 검색기의 Recall@100 지표가 아닙니다.
근거 모달리티
${statsTable(mod,'문항 라벨')}
문항에 부여된 라벨 기준입니다. 실제 검색된 이미지 수와는 다릅니다.
세부 Task
${statsTable(tasks,'Task',k=>url('qa',{task:k}))}
이미지 관련 문항 · 검색 결과
${statsTable(buckets,'검색된 이미지')}
서로 다른 문항 집단의 비교입니다. 이미지 제공 여부만 바꾼 대조 실험이 아니므로 인과적 효과로 해석하지 않습니다.
그림이 있어도 Luna가 못 본 사례
‘child-in-red’ 그림은 이미지끼리 비교하면 1위지만, 텍스트까지 합친 전체 순위는 191위입니다. 따라서 top100에 포함되지 않았습니다.
이미지 입력PDF·PPT 페이지와 별도 그림의 실제 픽셀 · OCR / 생성 설명 추가 없음
Luna 컨텍스트전역 top100 선택 후 시간순 배열 · 첨부 / 문서 전체 자동 확장 없음
채점짧은 Luna 의미 판정 · 질문 / 관점 / Gold / 모델 답 비교
응답 모델과 채점 모델은 모두 Luna입니다. 판정은 사람의 확정 검수가 아니며 이견이 생길 수 있습니다. 이 사이트는 저장된 결과를 읽기만 하고 모델을 새로 실행하지 않습니다.
`;}
function imageStatisticsHTML(){
const summarize=OrgBenchImageStats.summarize,s=summarize(INDEX.questions),visual=summarize(INDEX.questions.filter(q=>q.modality!=='text'));
const rows=[{label:'전체 문항',s},...['text','image','multi'].map(m=>({label:modNames[m],s:summarize(INDEX.questions.filter(q=>q.modality===m))}))];
return `
Top100에 포함된 이미지 수
각 문항의 실제 검색 결과 100개 중 이미지로 전달된 항목 수입니다. 문서의 페이지 이미지와 별도 그림을 모두 셉니다.
문항당 평균${s.mean.toFixed(2)}장 / 100개
중앙값${s.median}장
최소–최대${s.min}–${s.max}장
90백분위수${s.p90.toFixed(1)}장
이미지 0장인 문항${fmt(s.zero)}개 · ${pct(s.zero,s.n)}
이미지 수별 문항 분포
Top100의 이미지 수
문항
비율
${s.bins.map(b=>`
${b.label}
${fmt(b.n)}
${pct(b.n,s.n)}
`).join('')}
문항 모달리티별
문항 라벨
문항
평균 이미지 수
중앙값
이미지 0장 문항
${rows.map(r=>`
${r.label}
${fmt(r.s.n)}
${r.s.mean===null?'—':r.s.mean.toFixed(2)}
${r.s.median??'—'}
${fmt(r.s.zero)} · ${pct(r.s.zero,r.s.n)}
`).join('')}
이미지 관련 문항(Image + Multi-image) ${fmt(visual.n)}개: 평균 ${visual.mean.toFixed(2)}장, 중앙값 ${visual.median}장. 이 중 ${fmt(visual.zero)}개(${pct(visual.zero,visual.n)})에는 이미지가 전달되지 않았습니다.
전체 ${fmt(s.slots)}개 검색 슬롯 중 이미지 ${fmt(s.total)}개(${pct(s.total,s.slots)}). 동일 이미지가 여러 문항에 검색되면 각각 집계하므로, 고유 이미지 수와 다릅니다. 이미지가 검색됐다는 것만으로 정답에 필요한 그림이 포함됐다는 뜻은 아닙니다.
`;
}
function render(){
if(['qa','results'].includes(S.view))S.view='clean';
const generation=++renderGeneration;document.body.classList.toggle('corpus-view',S.view==='stats');$('#notice').textContent='';$('#overview').innerHTML='';
$$('[data-nav]').forEach(a=>{a.classList.toggle('active',a.dataset.nav===S.view);if(a.dataset.nav===S.view)a.setAttribute('aria-current','page');else a.removeAttribute('aria-current');});
if(S.view==='stats'){BenchmarkStats.show(generation);return;}
if(S.view==='world'){renderWorldBrowser(generation);return;}
renderClean(generation);
}
async function init(){try{[INDEX,UNITS]=await Promise.all([getJSON('data/index.json'),getJSON('data/units.json')]);unitMap=new Map(UNITS.map(u=>[u.id,u]));projectMap=new Map(INDEX.projects.map(p=>[p.id,p]));questionMap=new Map(INDEX.questions.map(q=>[q.id,q]));recordUnits=new Map();for(const u of UNITS){if(!recordUnits.has(u.record_id))recordUnits.set(u.record_id,[]);recordUnits.get(u.record_id).push(u);}S=readState();overview();render();getJSON('data/rank_checks.json').then(r=>{RANKS=r;refreshOpenReviewEvidence();if(S.view==='qa'&&S.tab==='gold'&&S.id){const id=S.id;loadQuestion(id).then(q=>{if(q&&S.id===id&&S.view==='qa'&&S.tab==='gold'&&$('#tab-body'))renderEvidence(q);});}}).catch(()=>{rankError=true;});}catch(e){$('#notice').className='notice error';$('#notice').innerHTML=`데이터를 불러오지 못했습니다. ${esc(e.message)} `;}}
window.addEventListener('hashchange',()=>{if(!INDEX)return;S=readState();if($('#record-dialog').open)$('#record-dialog').close();render();});$('.skip').addEventListener('click',e=>{e.preventDefault();$('#main').focus();$('#main').scrollIntoView({block:'start'});});$('#close-dialog').addEventListener('click',()=>$('#record-dialog').close());$('#record-dialog').addEventListener('click',e=>{if(e.target===$('#record-dialog'))$('#record-dialog').close();});init();