// Strict browser-side mirror of the bounded local System One request contract. // The server remains authoritative; this module never edits unsupported fields. const object = v => v !== null && typeof v === 'object' && !Array.isArray(v); const keys = (value, allowed, label) => { if (!object(value) || Object.keys(value).some(k => !allowed.includes(k))) throw Error(`${label}: unsupported fields or invalid object.`); }; const content = (v,label) => { if (typeof v === 'string') { if(!v.trim()) throw Error(`${label} must not be empty.`); } else if(!object(v) && !Array.isArray(v)) throw Error(`${label} must be text, an object, or an array.`); }; const id = (v,label,maxLength) => { if(typeof v!=='string'||!v.trim())throw Error(`${label} needs a nonempty string.`); if(maxLength!==undefined&&v.length>maxLength)throw Error(`${label} needs at most ${maxLength} characters.`); }; function finiteJSON(value) { if(typeof value==='number'&&!Number.isFinite(value))throw Error('JSON numbers must be finite.'); if(value && typeof value==='object')Object.values(value).forEach(finiteJSON); } export function validateRequest(body, expectedModel) { finiteJSON(body); keys(body,['state','states','model','questions'],'Request'); if(typeof expectedModel!=='string'||!expectedModel||body.model!==expectedModel)throw Error('Request model does not match the selected model.'); if(Object.hasOwn(body,'state')===Object.hasOwn(body,'states'))throw Error('Use one context or batch contexts, not both.'); if(Object.hasOwn(body,'states')){ if(!Array.isArray(body.states)||body.states.length<1)throw Error('Provide at least one context.'); const seen=new Set(); for(const row of body.states){keys(row,['id','state'],'Context');id(row.id,'Context ID',128);if(seen.has(row.id))throw Error('Context IDs must be unique.');seen.add(row.id);content(row.state,`Context ${row.id}`);} }else content(body.state,'State'); if(!object(body.questions)||Object.keys(body.questions).length<1)throw Error('Provide at least one named question.'); for(const[qid,q]of Object.entries(body.questions)) { id(qid,'Question ID');keys(q,['type','instructions','criteria'],qid); if(!['choice','noul','score'].includes(q.type))throw Error(`${qid}: unsupported question type.`); content(q.instructions,`${qid} instructions`); if(q.type==='choice') { if(!object(q.criteria)||Object.keys(q.criteria).length<2||Object.keys(q.criteria).length>255)throw Error(`${qid}: Choice needs 2–255 named options.`); for(const[name,v]of Object.entries(q.criteria)){id(name,'Option name');if(v!==null)content(v,`${qid} option description`);} } else if(q.type==='score') { if(!Array.isArray(q.criteria)||q.criteria.length<2||q.criteria.length>10)throw Error(`${qid}: Score needs 2–10 ordered levels.`); q.criteria.forEach(v=>content(v,`${qid} level`)); } else if(q.criteria!==undefined&&q.criteria!==null) { keys(q.criteria,['false','true'],`${qid} criteria`); Object.values(q.criteria).forEach(v=>{if(v!==null)content(v,`${qid} criterion`);}); } } const requestLimit=Object.hasOwn(body,'states')?2*1024*1024:256*1024; if(new TextEncoder().encode(JSON.stringify(body)).length>requestLimit)throw Error(`The complete request must fit within ${requestLimit===256*1024?'256 KiB':'2 MiB'}.`); const utf8=new TextEncoder(), states=body.states?body.states.map(x=>x.state):[body.state]; const stateBytes=states.reduce((n,v)=>n+utf8.encode(typeof v==='string'?v:JSON.stringify(v)).length,0); const expanded=stateBytes*Object.keys(body.questions).length+utf8.encode(JSON.stringify(body.questions)).length*states.length; if(expanded>16*1024*1024)throw Error('Expanded context/question input exceeds 16 MiB; split this workload into requests.'); return body; } export function validateResult(result,request,canonicalModel=request.model){ if(!object(result))throw Error('The server returned an invalid response.'); const legacy=Object.hasOwn(result,'source'); if(legacy){ if(result.model!==request.model||result.source!=='live_native')throw Error('The server returned a different model or prediction source.'); }else{ if(result.model!==canonicalModel)throw Error('The server returned a different model.'); const fields=request.states?['model','results','usage']:['model','answers','usage']; if(Object.keys(result).length!==fields.length||fields.some(key=>!Object.hasOwn(result,key)))throw Error('The server returned a non-standard response.'); } const usage=value=>object(value)&&Object.keys(value).length===2&&Number.isInteger(value.input_tokens)&&value.input_tokens>=0&&Number.isInteger(value.output_tokens)&&value.output_tokens>=0; if(!usage(result.usage))throw Error('Invalid response usage.'); const expectedIds=Object.keys(request.questions); const answerSet=answers=>{ if(!object(answers)||Object.keys(answers).length!==expectedIds.length||expectedIds.some(id=>!Object.hasOwn(answers,id))||legacy&&JSON.stringify(Object.keys(answers))!==JSON.stringify(expectedIds))throw Error('The response does not match the shared questions.'); for(const id of expectedIds){const a=answers[id],q=request.questions[id]; if(!object(a)||a.type!==q.type)throw Error('Invalid decision identity.'); if(legacy){if(!Number.isInteger(a.input_tokens)||a.input_tokens<1)throw Error('Invalid decision token usage.');} else{ const fields=q.type==='noul'?['type','noul']:q.type==='choice'?['type','choice','confidence','probabilities']:['type','score','confidence','legend','probabilities']; if(Object.keys(a).length!==fields.length||fields.some(key=>!Object.hasOwn(a,key)))throw Error('The server returned non-standard answer fields.'); } if(q.type==='noul'){if(!Number.isFinite(a.noul)||a.noul<0||a.noul>1)throw Error('Invalid yes probability.');} else {const ids=q.type==='choice'?Object.keys(q.criteria):q.criteria.map((_,i)=>String(i)); if(!object(a.probabilities)||Object.keys(a.probabilities).length!==ids.length||ids.some(k=>!Object.hasOwn(a.probabilities,k)))throw Error('Invalid candidate identity.'); const p=ids.map(k=>a.probabilities[k]);if(p.some(v=>!Number.isFinite(v)||v<0||v>1)||Math.abs(p.reduce((a,b)=>a+b,0)-1)>2e-5)throw Error('Invalid candidate probabilities.'); if(!legacy&&(!Number.isFinite(a.confidence)||a.confidence<0||a.confidence>1))throw Error('Invalid decision confidence.'); if(q.type==='choice'&&!ids.includes(a.choice)||q.type==='score'&&(!Number.isFinite(a.score)||a.score<0||a.score>ids.length-1))throw Error('Invalid decision value.'); if(q.type==='score'&&(!object(a.legend)||JSON.stringify(Object.keys(a.legend))!==JSON.stringify(ids)))throw Error('Invalid score legend.'); } } }; if(request.states){ if(!Array.isArray(result.results)||result.results.length!==request.states.length)throw Error('Incomplete context results.'); result.results.forEach((r,i)=>{if(!object(r)||r.id!==request.states[i].id||!usage(r.usage))throw Error('Context order or identity mismatch.');if(!legacy&&(Object.keys(r).length!==3||['id','answers','usage'].some(key=>!Object.hasOwn(r,key))))throw Error('The server returned non-standard context fields.');answerSet(r.answers);}); }else answerSet(result.answers); return result; } export function validateServingResult(result,request,canonicalModel,backend){ const native=result!==null&&typeof result==='object'&&Object.hasOwn(result,'source'); if(backend==='native_rocm'&&!native||backend==='direct'&&native||!['native_rocm','direct','pull_queue'].includes(backend))throw Error('The server returned an unexpected prediction format.'); return validateResult(result,request,canonicalModel); }