Download browser-reader.mjs from Reza2kn/Bina-0.4-Rizeh-ONNX-INT4: direct link, hf CLI and curl.
- Browser
- Download file 3.71 kB
-
https://huggingface.co/Reza2kn/Bina-0.4-Rizeh-ONNX-INT4/resolve/main/browser-reader.mjs
- Command line
-
hf download hf://Reza2kn/Bina-0.4-Rizeh-ONNX-INT4/browser-reader.mjs
-
curl -L -o browser-reader.mjs https://huggingface.co/Reza2kn/Bina-0.4-Rizeh-ONNX-INT4/resolve/main/browser-reader.mjs
3.71 kB
| // Line-level ONNX Runtime Web loader. Full document routing is a separate layer. | |
| const sessions=new Map(); | |
| export function flipVisual(text,tables){ | |
| const marks=new Set(tables.marks),kind=c=>['R','AL'].includes(tables.directions[c])?'R':['L','EN','AN'].includes(tables.directions[c])?'L':'N'; | |
| const clusters=[];for(const c of text){if(clusters.length&&marks.has(c))clusters[clusters.length-1]+=c;else clusters.push(c);} | |
| if(!clusters.some(c=>kind([...c][0])==='R'))return text; | |
| const mirror=new Map([..."()[]{}<>«»‹›"].map((c,i)=>[c,[...")(][}{><»«›‹"][i]])); | |
| const units=[];let i=0; | |
| while(i<clusters.length){ | |
| let first=-1,last=-1,j=i; | |
| for(;j<clusters.length;j++){ | |
| const k=kind([...clusters[j]][0]);if(k==='R')break;if(k==='L'){if(first<0)first=j;last=j;} | |
| } | |
| if(first>=0){ | |
| for(let k=i;k<first;k++){const [c,...rest]=[...clusters[k]];units.push((mirror.get(c)||c)+rest.join(''));} | |
| units.push(clusters.slice(first,last+1).join(''));i=last+1; | |
| }else{const [c,...rest]=[...clusters[i++]];units.push((mirror.get(c)||c)+rest.join(''));} | |
| } | |
| return units.reverse().join(''); | |
| } | |
| export async function createReader({ort,baseURL,role='body',backend='webgpu',cacheEntries=512}){ | |
| const base=new URL(baseURL,location.href),modelURL=new URL(`components/${role}/models/recognizer-w4-block16.onnx`,base); | |
| const [vocabulary,tables,bytes]=await Promise.all([fetch(new URL('vocab.json',base)).then(r=>r.json()),fetch(new URL('unicode-directions.json',base)).then(r=>r.json()),fetch(modelURL).then(r=>r.arrayBuffer())]); | |
| const modelHash=[...new Uint8Array(await crypto.subtle.digest('SHA-256',bytes))].map(x=>x.toString(16).padStart(2,'0')).join(''),identity=modelHash+':'+backend; | |
| if(!sessions.has(identity))sessions.set(identity,ort.InferenceSession.create(bytes,{executionProviders:[backend],graphOptimizationLevel:'all'})); | |
| const session=await sessions.get(identity),cache=new Map(),pending=new Map(); | |
| async function infer(ink,width){ | |
| if(!(ink instanceof Float32Array)||ink.length!==64*width||width<1)throw Error('Float32 ink map64×true_width required'); | |
| const hash=[...new Uint8Array(await crypto.subtle.digest('SHA-256',ink))].map(x=>x.toString(16).padStart(2,'0')).join(''),key=identity+':'+width+':'+hash; | |
| if(cache.has(key)){const value=cache.get(key);cache.delete(key);cache.set(key,value);return {...value,cached:true};} | |
| if(pending.has(key))return pending.get(key); | |
| const task=(async()=>{ | |
| const bucket=Math.max(512,2**Math.ceil(Math.log2(width)));if(bucket>8192)throw Error('Width exceeds validated8192 bound; never truncated'); | |
| const pixels=new Float32Array(64*bucket);for(let y=0;y<64;y++)pixels.set(ink.subarray(y*width,(y+1)*width),y*bucket); | |
| const inputs={pixels:new ort.Tensor('float32',pixels,[1,1,64,bucket]),widths:new ort.Tensor('int64',BigInt64Array.of(BigInt(width)),[1])};let outputs; | |
| try{ | |
| outputs=await session.run(inputs);const n=Number(outputs.lengths.data[0]);let visual='',confidence=0,count=0,previous=-1; | |
| for(let i=0;i<n;i++){const token=Number(outputs.tokens.data[i]);if(token&&token!==previous){visual+=vocabulary.chars[token-1];confidence+=outputs.confidence.data[i];count++;}previous=token;} | |
| const result={text:flipVisual(visual,tables),visualText:visual,confidence:count?confidence/count:0,role,modelHash,cached:false}; | |
| if(cacheEntries>0){cache.set(key,result);while(cache.size>cacheEntries)cache.delete(cache.keys().next().value);}return result; | |
| }finally{if(outputs)Object.values(outputs).forEach(t=>t.dispose());Object.values(inputs).forEach(t=>t.dispose());} | |
| })();pending.set(key,task);try{return await task;}finally{pending.delete(key);} | |
| } | |
| return {read:infer,modelHash,clearCache:()=>cache.clear()}; | |
| } | |