// Registry-driven model presentation. No backend IDs, weights or readiness are invented. const esc = value => String(value).replace(/[&<>"']/g,x=>({'&':'&','<':'<','>':'>','"':'"',"'":'''}[x])); export function validateRegistry(result) { if(!Array.isArray(result?.models)||!result.models.length||(result.default!==undefined&&!result.models.some(m=>m.id===result.default)))throw Error('Invalid model registry.'); const text=(v,max)=>typeof v==='string'&&v.trim()&&v.length<=max; const ids=new Set(),wireIds=new Set(); for(const m of result.models){ if(!text(m.id,128)||!text(m.wire_id,128)||ids.has(m.id)||wireIds.has(m.wire_id)||!text(m.label,64)||!text(m.version,32)||!/^[0-9a-f]{64}$/.test(m.manifest_sha256)||!Number.isInteger(m.complete_input_tokens)||m.complete_input_tokens<1)throw Error('Invalid model identity.'); ids.add(m.id);wireIds.add(m.wire_id); if(m.description!==undefined&&!text(m.description,240)||m.parameter_label!==undefined&&!text(m.parameter_label,32))throw Error('Invalid model description.'); if(m.example_ids!==undefined&&(!Array.isArray(m.example_ids)||!m.example_ids.length||m.example_ids.some(x=>!text(x,128))||new Set(m.example_ids).size!==m.example_ids.length))throw Error('Invalid model examples.'); if(m.default_example_id!==undefined&&!text(m.default_example_id,128))throw Error('Invalid default example.'); if(m.release_date!==undefined&&!/^\d{4}-\d{2}-\d{2}$/.test(m.release_date))throw Error('Invalid model release date.'); } return result; } // Presentation facts come only from registry fields and the canonical Hub ID // (Decision---[-Reasoning]). export function modelPresentation(model) { const repo=String(model.repo_id||model.id||'').split('/').pop(); const parsed=/^Decision-(\d+(?:\.\d+)*)-(.+?)-(\d+(?:\.\d+)?[BM])(?:-(Reasoning))?$/i.exec(repo); const reasoning=/-reasoning$/i.test(repo)||/\breasoning\b/i.test(model.label||''); const codename=String(model.label||parsed?.[2]||repo).replace(/[\s-]*reasoning$/i,'').trim()||repo; const size=model.parameter_label||parsed?.[3]||''; const generation=model.version||parsed?.[1]||''; const name=[codename,size].filter(Boolean).join(' ')+(reasoning?' Reasoning':''); return {codename,size,generation,reasoning,name,description:model.description||'', release:model.release_date||'',releaseLabel:formatRelease(model.release_date)}; } export function formatRelease(iso) { if(typeof iso!=='string'||!/^\d{4}-\d{2}-\d{2}$/.test(iso))return ''; const date=new Date(iso+'T00:00:00Z'); return Number.isNaN(date.getTime())?'':new Intl.DateTimeFormat('en',{month:'short',day:'numeric',year:'numeric',timeZone:'UTC'}).format(date); } const versionParts=v=>String(v).split('.').map(x=>Number.parseInt(x,10)||0); export function compareVersions(a,b){const x=versionParts(a),y=versionParts(b);for(let i=0;icompareVersions(a[0],b[0])).map(([generation,items])=>({generation,label:generation?`Decision ${generation}`:'Decision',models:items})); } export function orderedModels(models){return groupModels(models).flatMap(group=>group.models);} // The first base (non-Reasoning) model of the newest generation. A server // default is honoured only inside that generation. export function preferredModel(models,serverDefault) { const newest=groupModels(models)[0]?.models||[]; const named=newest.find(m=>m.id===serverDefault&&!modelPresentation(m).reasoning); return named||newest.find(m=>!modelPresentation(m).reasoning)||newest[0]||null; } export function statusName(model,models) { const p=modelPresentation(model),newest=groupModels(models)[0]?.generation; return p.generation&&newest&&p.generation!==newest?`${p.name} (${p.generation})`:p.name; } export function examplesForModel(model,catalog) { if(!Array.isArray(catalog)||!catalog.length||new Set(catalog.map(e=>e.id)).size!==catalog.length)throw Error('Invalid example catalog.'); const byId=new Map(catalog.map(e=>[e.id,e])); const examples=model.example_ids===undefined?catalog:model.example_ids.map(id=>{if(!byId.has(id))throw Error('Model example is missing.');return byId.get(id);}); if(model.default_example_id!==undefined&&!examples.some(e=>e.id===model.default_example_id))throw Error('Default example is outside this model’s catalog.'); return {examples,defaultExample:examples.find(e=>e.id===model.default_example_id)||examples[0]}; } export const exampleMode = example => example.surface === 'routing' ? 'routing' : Array.isArray(example.states) ? 'batch' : 'single'; // These are presentation prompts only. Runtime identity and answer semantics stay unchanged. function routingExampleForModel(example,model) { const modelId=model.wire_id||model.id; const variant=example.model_variants?.[modelId]; if(!variant)return example; if(variant&&( variant.state!==undefined&&(typeof variant.state!=='string'||!variant.state.trim()) ||Object.keys(variant).some(key=>!['state','questions'].includes(key)) ||Object.keys(variant.questions||{}).some(key=>!example.questions[key]) ))throw Error('Invalid model-specific routing example.'); const questions=Object.fromEntries(Object.entries(example.questions).map(([key,question])=>{ const override=variant?.questions?.[key]||{}; if(Object.keys(override).some(field=>!['instructions','criteria'].includes(field)))throw Error('Invalid routing question override.'); if(override.instructions!==undefined&&(typeof override.instructions!=='string'||!override.instructions.trim()))throw Error('Invalid routing question wording.'); if(override.criteria!==undefined&&( !question.criteria||Array.isArray(question.criteria)||!override.criteria||typeof override.criteria!=='object' ||Object.keys(override.criteria).some(candidate=>!(candidate in question.criteria)||typeof override.criteria[candidate]!=='string'||!override.criteria[candidate].trim()) ))throw Error('Invalid routing choice override.'); return [key,{...question,...override, ...(override.criteria?{criteria:{...question.criteria,...override.criteria}}:{})}]; })); return {...example,state:variant?.state||example.state,questions}; } export function studioExamplesForModel(model,catalog) { const modelCatalog=examplesForModel(model,catalog); const routing=catalog.filter(example=>exampleMode(example)==='routing').map(example=>routingExampleForModel(example,model)); // Without an allowlist the catalog holds one model-tuned copy of each // scenario per model; show each title once. const titles=new Set(); const scenarios=modelCatalog.examples.filter(example=>exampleMode(example)!=='routing'&&(model.example_ids!==undefined||!titles.has(example.title)&&titles.add(example.title))); return { examples:[...routing,...scenarios], defaultExample:routing[0]||modelCatalog.defaultExample, }; } export function modelReadiness(status) { if(!status)return {label:'Checking',state:'checking'}; if(!status.configured||status.phase==='unavailable')return {label:'Unavailable',state:'offline'}; if(!status.loaded||status.phase==='loading')return {label:'Connecting',state:'loading'}; if(status.running||status.phase==='running')return {label:'Working',state:'working'}; return {label:'Ready',state:'ready'}; } const tokenLabel=n=>n%1024===0?`${n/1024}K`:n.toLocaleString('en'); function optionHTML(m,selected) { const p=modelPresentation(m); const meta=[p.description,p.releaseLabel&&`Released ${p.releaseLabel}`].filter(Boolean).join(' · '); return ``; } export function createModelMenu({getModels,getSelected,getStatus,onSelect,onOpen,reducedMotion}) { const $=id=>document.getElementById(id),trigger=$('model-trigger'),popover=$('model-popover'),options=$('model-options'); let open=false,focusIndex=0; const buttons=()=>Array.from(options.querySelectorAll('[role=option]')); function position(){ if(!open)return; const r=trigger.getBoundingClientRect(),margin=12,gap=8,width=Math.min(368,innerWidth-margin*2); const below=innerHeight-r.bottom-gap-margin,above=r.top-gap-margin,side=below>=Math.min(420,above)?'below':'above'; popover.style.width=width+'px';popover.style.maxHeight=Math.max(160,Math.min(560,side==='below'?below:above))+'px'; const height=popover.offsetHeight,left=Math.max(margin,Math.min(innerWidth-width-margin,r.right-width)); popover.style.left=left+'px';popover.style.top=(side==='below'?r.bottom+gap:r.top-gap-height)+'px';popover.dataset.side=side; } function focusAt(index){const list=buttons();focusIndex=(index+list.length)%list.length;list.forEach((b,i)=>b.tabIndex=i===focusIndex?0:-1);list[focusIndex]?.focus({preventScroll:true});list[focusIndex]?.scrollIntoView({block:'nearest'});} function updateStatuses(){ for(const button of buttons()){ const status=modelReadiness(getStatus(button.dataset.model));button.dataset.readiness=status.state;button.setAttribute('aria-description',status.label); button.querySelector('.model-option-state').textContent=status.state==='ready'||status.state==='checking'?'':status.label; button.setAttribute('aria-selected',String(button.dataset.model===getSelected())); } } function render(){ const selected=getSelected(); options.innerHTML=groupModels(getModels()).map((group,index)=>`
${group.models.map(m=>optionHTML(m,selected)).join('')}
`).join(''); updateStatuses(); } function close(returnFocus=false){open=false;popover.hidden=true;trigger.setAttribute('aria-expanded','false');if(returnFocus)trigger.focus({preventScroll:true});} function show(index){ if(getModels().length<2||trigger.disabled)return; render();open=true;popover.hidden=false;trigger.setAttribute('aria-expanded','true');position(); const selected=buttons().findIndex(b=>b.dataset.model===getSelected());focusAt(index??Math.max(0,selected));onOpen?.(); } function choose(button){const id=button.dataset.model;close(true);if(id!==getSelected())onSelect(id);} trigger.addEventListener('click',()=>open?close(true):show()); trigger.addEventListener('keydown',e=>{if(e.key==='ArrowDown'||e.key==='ArrowUp'){e.preventDefault();show(e.key==='ArrowUp'?buttons().length-1:undefined);}else if(e.key==='Escape')close(true);}); options.addEventListener('click',e=>{const b=e.target.closest('[role=option]');if(b)choose(b);}); options.addEventListener('keydown',e=>{ if(['ArrowDown','ArrowUp','Home','End'].includes(e.key)){e.preventDefault();const index=e.key==='Home'?0:e.key==='End'?buttons().length-1:focusIndex+(e.key==='ArrowDown'?1:-1);focusAt(index);} else if(e.key==='Escape'){e.preventDefault();e.stopPropagation();close(true);} else if(e.key==='Tab')close(true); else if(e.key==='Enter'||e.key===' '){e.preventDefault();const b=document.activeElement.closest('[role=option]');if(b)choose(b);} }); document.addEventListener('pointerdown',e=>{if(open&&!popover.contains(e.target)&&!trigger.contains(e.target))close(false);}); document.addEventListener('focusin',e=>{if(open&&!popover.contains(e.target)&&!trigger.contains(e.target))close(false);}); addEventListener('resize',position);addEventListener('scroll',e=>{if(!popover.contains(e.target))position();},true); function identity(model,{animate=false,enabled=true}={}){ const p=modelPresentation(model),name=$('model-name'),previous=name.textContent; document.querySelectorAll('.model-name-ghost').forEach(e=>e.remove());name.getAnimations().forEach(a=>a.cancel()); name.textContent=p.codename;$('model-size').textContent=p.size; $('model-version').textContent=p.generation;$('model-version').hidden=!p.generation;$('model-badge').hidden=!p.reasoning; $('model-spec').textContent=[p.size,`Complete ${tokenLabel(model.complete_input_tokens)}`].filter(Boolean).join(' · '); $('input-budget').textContent=`Complete ${model.complete_input_tokens.toLocaleString('en')}-token input per question. Nothing is truncated.`; const many=getModels().length>1; trigger.disabled=!enabled||!many;trigger.dataset.single=String(!many);trigger.setAttribute('aria-label',`${p.name}${p.generation?`, Decision ${p.generation}`:''}. ${many?'Switch decision model.':'Decision model.'}`); if(animate&&!reducedMotion.matches&&previous!==p.codename){ const ghost=document.createElement('span');ghost.className='model-name-ghost';ghost.textContent=previous;ghost.setAttribute('aria-hidden','true');name.parentNode.append(ghost); const a=ghost.animate([{opacity:1,transform:'translateY(0)'},{opacity:0,transform:'translateY(-6px)'}],{duration:170,easing:'ease-out'});a.onfinish=()=>ghost.remove(); name.animate([{opacity:0,transform:'translateY(5px)'},{opacity:1,transform:'translateY(0)'}],{duration:190,easing:'ease-out'}); } updateStatuses(); } return {identity,updateStatuses,close,isOpen:()=>open}; }