Download index.html from danielharkin21/ncm-chat-experimental: direct link, hf CLI and curl.
- Browser
- Download file 4.34 kB
-
https://huggingface.co/spaces/danielharkin21/ncm-chat-experimental/resolve/main/index.html
- Command line
-
hf download hf://spaces/danielharkin21/ncm-chat-experimental/index.html
-
curl -L -o index.html https://huggingface.co/spaces/danielharkin21/ncm-chat-experimental/resolve/main/index.html
4.34 kB
| <html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"> | |
| <title>NCM chat (experimental, rough)</title> | |
| <style> | |
| body{font-family:system-ui,sans-serif;margin:0;background:#111;color:#eee;display:flex;flex-direction:column;height:100dvh} | |
| header{padding:10px 14px;background:#1b1b1b;font-size:13px;line-height:1.35} | |
| header b{font-size:15px} | |
| #log{flex:1;overflow:auto;padding:12px;display:flex;flex-direction:column;gap:8px} | |
| .m{max-width:85%;padding:8px 12px;border-radius:14px;white-space:pre-wrap;word-wrap:break-word} | |
| .u{align-self:flex-end;background:#2b5fd9}.b{align-self:flex-start;background:#2a2a2a}.s{align-self:center;color:#aaa;font-size:12px} | |
| form{display:flex;gap:8px;padding:10px;background:#1b1b1b} | |
| input{flex:1;font-size:16px;padding:10px;border-radius:10px;border:1px solid #444;background:#222;color:#eee} | |
| button{font-size:16px;padding:10px 16px;border-radius:10px;border:0;background:#2b5fd9;color:#fff}button:disabled{opacity:.5} | |
| </style></head><body> | |
| <header><b>NCM chat</b> (experimental, rough) · Next-Concept Modeling, Stanford Hazy Research (arXiv 2610.14162). Runs fully in your browser: the released XSum NCM generator, briefly fine-tuned on short dialogue, fp16-stored-weights ONNX, fp32 compute, verified identical to the PyTorch model on test prompts. Replies are short and often wrong or garbled. Not a capable assistant. First load downloads the model (<span id="mb">~375</span> MB, needs roughly 1 GB free RAM; desktop browser recommended).</header> | |
| <div id="log"></div> | |
| <form id="f"><input id="t" autocomplete="off" placeholder="Loading model..." disabled><button id="b" disabled>Send</button></form> | |
| <script type="module"> | |
| import { AutoTokenizer } from "https://cdn.jsdelivr.net/npm/@huggingface/transformers@3.0.2"; | |
| import * as ort from "https://cdn.jsdelivr.net/npm/onnxruntime-web@1.20.1/dist/ort.min.mjs"; | |
| const MODEL="ncm_fp16w.onnx", P=128, EOS=50256, PAD=50257, PPAD=50257, TOTAL=200, V=4096, T=0.7; | |
| const log=document.getElementById("log"), inp=document.getElementById("t"), btn=document.getElementById("b"); | |
| const add=(c,txt)=>{const d=document.createElement("div");d.className="m "+c;d.textContent=txt;log.appendChild(d);log.scrollTop=1e9;return d}; | |
| let tok,sess,hist=[]; | |
| async function load(){ | |
| const s=add("s","Loading tokenizer and model..."); | |
| tok=await AutoTokenizer.from_pretrained("Xenova/gpt2"); | |
| ort.env.wasm.wasmPaths="https://cdn.jsdelivr.net/npm/onnxruntime-web@1.20.1/dist/"; | |
| ort.env.wasm.numThreads=crossOriginIsolated?Math.min(4,navigator.hardwareConcurrency||2):1; | |
| const buf=await (await fetch(MODEL)).arrayBuffer(); | |
| document.getElementById("mb").textContent=Math.round(buf.byteLength/1e6); | |
| sess=await ort.InferenceSession.create(buf,{executionProviders:["wasm"]}); | |
| s.textContent="Model ready. Say something."; inp.disabled=btn.disabled=false; inp.placeholder="Type a message"; inp.focus(); | |
| } | |
| function gumbel(n){const a=new Float32Array(n);for(let i=0;i<n;i++){const u=Math.random()*(1-1e-7)+1e-7;a[i]=-Math.log(-Math.log(u));}return a} | |
| async function reply(){ | |
| const turns=hist.slice(-4); let p=""; for(const [r,t] of turns) p+=(r==="u"?"User: ":"Assistant: ")+t+"\n"; p+="Assistant:"; | |
| let ids=Array.from(tok.encode(p)).slice(-P); while(ids.length<P) ids.push(PPAD); | |
| const prefix=new ort.Tensor("int64",BigInt64Array.from(ids.map(BigInt)),[1,P]); | |
| const noise=new ort.Tensor("float32",gumbel(TOTAL*V),[1,TOTAL,V]); | |
| const temp=new ort.Tensor("float32",new Float32Array([T]),[]); | |
| const out=await sess.run({prefix,noise,temp}); let o=Array.from(out.ids.data,Number); | |
| const e=o.indexOf(EOS); if(e>=0) o=o.slice(0,e); o=o.filter(x=>x!==PAD&&x!==EOS); | |
| return tok.decode(o,{skip_special_tokens:true}).trim()||"(empty reply)"; | |
| } | |
| document.getElementById("f").addEventListener("submit",async ev=>{ | |
| ev.preventDefault(); const m=inp.value.trim(); if(!m) return; inp.value=""; add("u",m); hist.push(["u",m]); | |
| inp.disabled=btn.disabled=true; const w=add("b","..."); const t0=performance.now(); | |
| try{const r=await reply(); w.textContent=r; hist.push(["a",r]); w.title=((performance.now()-t0)/1000).toFixed(1)+"s";} | |
| catch(e){w.textContent="Error: "+e.message} | |
| inp.disabled=btn.disabled=false; inp.focus(); | |
| }); | |
| load().catch(e=>add("s","Load failed: "+e.message)); | |
| </script></body></html> | |