Spaces:
Running
Running
Remove orphaned old-layout files (_shared vendor dup + old diagnostics)
Browse files- _shared/voice/vendor/kokoro/kokoro.js +0 -1
- _shared/voice/vendor/kokoro/phonemizer.js +0 -0
- _shared/voice/vendor/kokoro/stub.js +0 -6
- _shared/voice/vendor/kokoro/transformers/ort-wasm-simd-threaded.jsep.mjs +0 -125
- _shared/voice/vendor/kokoro/transformers/ort-wasm-simd-threaded.jsep.wasm +0 -3
- _shared/voice/vendor/kokoro/transformers/transformers.js +0 -0
- _shared/voice/vendor/models/onnx-community/Kokoro-82M-v1.0-ONNX/config.json +0 -3
- _shared/voice/vendor/models/onnx-community/Kokoro-82M-v1.0-ONNX/tokenizer.json +0 -175
- _shared/voice/vendor/models/onnx-community/Kokoro-82M-v1.0-ONNX/tokenizer_config.json +0 -6
- _shared/voice/vendor/models/onnx-community/Kokoro-82M-v1.0-ONNX/voices/af_bella.bin +0 -3
- _shared/voice/vendor/models/onnx-community/Kokoro-82M-v1.0-ONNX/voices/af_heart.bin +0 -3
- _shared/voice/vendor/models/onnx-community/Kokoro-82M-v1.0-ONNX/voices/af_nicole.bin +0 -3
- _shared/voice/vendor/models/onnx-community/Kokoro-82M-v1.0-ONNX/voices/am_fenrir.bin +0 -3
- _shared/voice/vendor/models/onnx-community/Kokoro-82M-v1.0-ONNX/voices/am_michael.bin +0 -3
- _shared/voice/vendor/models/onnx-community/Kokoro-82M-v1.0-ONNX/voices/am_puck.bin +0 -3
- _shared/voice/vendor/models/onnx-community/Kokoro-82M-v1.0-ONNX/voices/bf_emma.bin +0 -3
- _shared/voice/vendor/models/onnx-community/Kokoro-82M-v1.0-ONNX/voices/bm_george.bin +0 -3
- _shared/voice/vendor/models/onnx-community/silero-vad/onnx/model.onnx +0 -3
- _shared/voice/vendor/models/onnx-community/silero-vad/onnx/model_quantized.onnx +0 -3
- _shared/voice/vendor/transformers/ort-wasm-simd-threaded.jsep.wasm +0 -3
- _shared/voice/vendor/transformers/transformers.js +0 -0
- _shared/voice/vendor/transformers/transformers.mjs +0 -0
- blake3-gpu-parallel.html +0 -145
- blake3-gpu-test.html +0 -195
- core/agents.js +0 -74
- core/backend.js +0 -23
- core/code-tools.js +0 -130
- core/keymap.js +0 -29
- core/mcp.js +0 -144
- core/mcphub.js +0 -107
- core/memory.js +0 -69
- core/presets.js +0 -54
- core/promptsLib.js +0 -109
- core/q-brain-fast.mjs +0 -153
- core/schema.js +0 -85
- core/semantic-witness.mjs +0 -33
- core/skills.js +0 -92
- core/state.js +0 -39
- core/store.js +0 -185
- core/tools.js +0 -112
- gemv-bench.html +0 -183
- gpu-blake3.mjs +0 -107
- holo-blake3.mjs +0 -128
- holo-gpu-device.mjs +0 -27
- kernel-profile.html +0 -147
- q-chat.html +0 -120
- rb-bench.html +0 -130
- roofline.html +0 -126
- style.css +0 -28
_shared/voice/vendor/kokoro/kokoro.js
DELETED
|
@@ -1 +0,0 @@
|
|
| 1 |
-
import{StyleTextToSpeech2Model as e,AutoTokenizer as a,Tensor as t,RawAudio as r,env as n}from"@huggingface/transformers";import{phonemize as l}from"phonemizer";import s from"path";import i from"fs/promises";function o(e){if(e.includes("."))return e;if(e.includes(":")){let[a,t]=e.split(":").map(Number);return 0===t?`${a} o'clock`:t<10?`${a} oh ${t}`:`${a} ${t}`}let a=parseInt(e.slice(0,4),10);if(a<1100||a%1e3<10)return e;let t=e.slice(0,2),r=parseInt(e.slice(2,4),10),n=e.endsWith("s")?"s":"";if(a%1e3>=100&&a%1e3<=999){if(0===r)return`${t} hundred${n}`;if(r<10)return`${t} oh ${r}${n}`}return`${t} ${r}${n}`}function c(e){const a="$"===e[0]?"dollar":"pound";if(isNaN(Number(e.slice(1))))return`${e.slice(1)} ${a}s`;if(!e.includes(".")){let t="1"===e.slice(1)?"":"s";return`${e.slice(1)} ${a}${t}`}const[t,r]=e.slice(1).split("."),n=parseInt(r.padEnd(2,"0"),10);return`${t} ${a}${"1"===t?"":"s"} and ${n} ${"$"===e[0]?1===n?"cent":"cents":1===n?"penny":"pence"}`}function g(e){let[a,t]=e.split(".");return`${a} point ${t.split("").join(" ")}`}const u=new RegExp(`(\\s*[${d=';:,.!?¡¿—…"«»“”(){}[]',d.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}]+\\s*)+`,"g");var d;async function m(e,a="a",t=!0){t&&(e=function(e){return e.replace(/[‘’]/g,"'").replace(/«/g,"“").replace(/»/g,"”").replace(/[“”]/g,'"').replace(/\(/g,"«").replace(/\)/g,"»").replace(/、/g,", ").replace(/。/g,". ").replace(/!/g,"! ").replace(/,/g,", ").replace(/:/g,": ").replace(/;/g,"; ").replace(/?/g,"? ").replace(/[^\S \n]/g," ").replace(/ +/," ").replace(/(?<=\n) +(?=\n)/g,"").replace(/\bD[Rr]\.(?= [A-Z])/g,"Doctor").replace(/\b(?:Mr\.|MR\.(?= [A-Z]))/g,"Mister").replace(/\b(?:Ms\.|MS\.(?= [A-Z]))/g,"Miss").replace(/\b(?:Mrs\.|MRS\.(?= [A-Z]))/g,"Mrs").replace(/\betc\.(?! [A-Z])/gi,"etc").replace(/\b(y)eah?\b/gi,"$1e'a").replace(/\d*\.\d+|\b\d{4}s?\b|(?<!:)\b(?:[1-9]|1[0-2]):[0-5]\d\b(?!:)/g,o).replace(/(?<=\d),(?=\d)/g,"").replace(/[$£]\d+(?:\.\d+)?(?: hundred| thousand| (?:[bm]|tr)illion)*\b|[$£]\d+\.\d\d?\b/gi,c).replace(/\d*\.\d+/g,g).replace(/(?<=\d)-(?=\d)/g," to ").replace(/(?<=\d)S/g," S").replace(/(?<=[BCDFGHJ-NP-TV-Z])'?s\b/g,"'S").replace(/(?<=X')S\b/g,"s").replace(/(?:[A-Za-z]\.){2,} [a-z]/g,(e=>e.replace(/\./g,"-"))).replace(/(?<=[A-Z])\.(?=[A-Z])/gi,"-").trim()}(e));const r=function(e,a){const t=[];let r=0;for(const n of e.matchAll(a)){const a=n[0];r<n.index&&t.push({match:!1,text:e.slice(r,n.index)}),a.length>0&&t.push({match:!0,text:a}),r=n.index+a.length}return r<e.length&&t.push({match:!1,text:e.slice(r)}),t}(e,u),n="a"===a?"en-us":"en",s=(await Promise.all(r.map((async({match:e,text:a})=>e?a:(await l(a,n)).join(" "))))).join("");let i=s.replace(/kəkˈoːɹoʊ/g,"kˈoʊkəɹoʊ").replace(/kəkˈɔːɹəʊ/g,"kˈəʊkəɹəʊ").replace(/ʲ/g,"j").replace(/r/g,"ɹ").replace(/x/g,"k").replace(/ɬ/g,"l").replace(/(?<=[a-zɹː])(?=hˈʌndɹɪd)/g," ").replace(/ z(?=[;:,.!?¡¿—…"«»“” ]|$)/g,"z");return"a"===a&&(i=i.replace(/(?<=nˈaɪn)ti(?!ː)/g,"di")),i.trim()}function p(e,a=!0){return".!?…。?!".includes(e)||a&&"\n"===e}function f(e,a){let t=a;for(;t<e.length&&!/\s/.test(e[t]);)++t;return e.substring(a,t)}const h=new Set(["mr","mrs","ms","dr","prof","sr","jr","sgt","col","gen","rep","sen","gov","lt","maj","capt","st","mt","etc","co","inc","ltd","dept","vs","p","pg","jan","feb","mar","apr","jun","jul","aug","sep","sept","oct","nov","dec","sun","mon","tu","tue","tues","wed","th","thu","thur","thurs","fri","sat"]);function _(e){return e=e.replace(/['’]s$/i,"").replace(/\.+$/,""),h.has(e.toLowerCase())}const v=new Map([[")","("],["]","["],["}","{"],["》","《"],["〉","〈"],["›","‹"],["»","«"],["〉","〈"],["」","「"],["』","『"],["〕","〔"],["】","【"]]),b=new Set(v.values());function y(e,a,t,r){if('"'===e||"'"===e){if("'"===e&&t>0&&t<r.length-1&&/[A-Za-z]/.test(r[t-1])&&/[A-Za-z]/.test(r[t+1]))return;return void(a.length&&a.at(-1)===e?a.pop():a.push(e))}if(b.has(e))return void a.push(e);const n=v.get(e);n&&a.length&&a.at(-1)===n&&a.pop()}class w{constructor(){this._buffer="",this._sentences=[],this._resolver=null,this._closed=!1}push(...e){for(const a of e)this._buffer+=a,this._process()}close(){if(this._closed)throw new Error("Stream is already closed.");this._closed=!0,this.flush()}flush(){const e=this._buffer.trim();e.length>0&&this._sentences.push(e),this._buffer="",this._resolve()}_resolve(){this._resolver&&(this._resolver(),this._resolver=null)}_process(){let e=0;const a=this._buffer,t=a.length;let r=0,n=[];const l=e=>{let r=e;for(;r+1<t&&p(a[r+1],!1);)++r;for(;r+1<t&&(n=a[r+1],"\"')]}」』".includes(n));)++r;var n;let l=r+1;for(;l<t&&/\s/.test(a[l]);)++l;return{end:r,nextNonSpace:l}};for(;r<t;){const s=a[r];if(y(s,n,r,a),0===n.length&&p(s)){const n=a.slice(e,r);if(/(^|\n)\d+$/.test(n)){++r;continue}const{end:i,nextNonSpace:o}=l(r);if(r===o-1&&"\n"!==s){++r;continue}if(o===t)break;let c=r-1;for(;c>=0&&/\S/.test(a[c]);)c--;c=Math.max(e,c+1);const g=f(a,c);if(!g){++r;continue}if((/https?[,:]\/\//.test(g)||g.includes("@"))&&!p(g.at(-1))){r=c+g.length;continue}if(_(g)){++r;continue}if(/^([A-Za-z]\.)+$/.test(g)&&o<t&&/[A-Z]/.test(a[o])){++r;continue}if("."===s&&o<t&&/[a-z]/.test(a[o])){++r;continue}const u=a.substring(e,i+1).trim();if("..."===u||"…"===u){++r;continue}u&&this._sentences.push(u),r=e=i+1}else++r}this._buffer=a.substring(e),this._sentences.length>0&&this._resolve()}async*[Symbol.asyncIterator](){if(this._resolver)throw new Error("Another iterator is already active.");for(;;)if(this._sentences.length>0)yield this._sentences.shift();else{if(this._closed)break;await new Promise((e=>{this._resolver=e}))}}[Symbol.iterator](){this.flush();const e=this._sentences[Symbol.iterator]();return this._sentences=[],e}get sentences(){return this._sentences}}const $=Object.freeze({af_heart:{name:"Heart",language:"en-us",gender:"Female",traits:"❤️",targetQuality:"A",overallGrade:"A"},af_alloy:{name:"Alloy",language:"en-us",gender:"Female",targetQuality:"B",overallGrade:"C"},af_aoede:{name:"Aoede",language:"en-us",gender:"Female",targetQuality:"B",overallGrade:"C+"},af_bella:{name:"Bella",language:"en-us",gender:"Female",traits:"🔥",targetQuality:"A",overallGrade:"A-"},af_jessica:{name:"Jessica",language:"en-us",gender:"Female",targetQuality:"C",overallGrade:"D"},af_kore:{name:"Kore",language:"en-us",gender:"Female",targetQuality:"B",overallGrade:"C+"},af_nicole:{name:"Nicole",language:"en-us",gender:"Female",traits:"🎧",targetQuality:"B",overallGrade:"B-"},af_nova:{name:"Nova",language:"en-us",gender:"Female",targetQuality:"B",overallGrade:"C"},af_river:{name:"River",language:"en-us",gender:"Female",targetQuality:"C",overallGrade:"D"},af_sarah:{name:"Sarah",language:"en-us",gender:"Female",targetQuality:"B",overallGrade:"C+"},af_sky:{name:"Sky",language:"en-us",gender:"Female",targetQuality:"B",overallGrade:"C-"},am_adam:{name:"Adam",language:"en-us",gender:"Male",targetQuality:"D",overallGrade:"F+"},am_echo:{name:"Echo",language:"en-us",gender:"Male",targetQuality:"C",overallGrade:"D"},am_eric:{name:"Eric",language:"en-us",gender:"Male",targetQuality:"C",overallGrade:"D"},am_fenrir:{name:"Fenrir",language:"en-us",gender:"Male",targetQuality:"B",overallGrade:"C+"},am_liam:{name:"Liam",language:"en-us",gender:"Male",targetQuality:"C",overallGrade:"D"},am_michael:{name:"Michael",language:"en-us",gender:"Male",targetQuality:"B",overallGrade:"C+"},am_onyx:{name:"Onyx",language:"en-us",gender:"Male",targetQuality:"C",overallGrade:"D"},am_puck:{name:"Puck",language:"en-us",gender:"Male",targetQuality:"B",overallGrade:"C+"},am_santa:{name:"Santa",language:"en-us",gender:"Male",targetQuality:"C",overallGrade:"D-"},bf_emma:{name:"Emma",language:"en-gb",gender:"Female",traits:"🚺",targetQuality:"B",overallGrade:"B-"},bf_isabella:{name:"Isabella",language:"en-gb",gender:"Female",targetQuality:"B",overallGrade:"C"},bm_george:{name:"George",language:"en-gb",gender:"Male",targetQuality:"B",overallGrade:"C"},bm_lewis:{name:"Lewis",language:"en-gb",gender:"Male",targetQuality:"C",overallGrade:"D+"},bf_alice:{name:"Alice",language:"en-gb",gender:"Female",traits:"🚺",targetQuality:"C",overallGrade:"D"},bf_lily:{name:"Lily",language:"en-gb",gender:"Female",traits:"🚺",targetQuality:"C",overallGrade:"D"},bm_daniel:{name:"Daniel",language:"en-gb",gender:"Male",traits:"🚹",targetQuality:"C",overallGrade:"D"},bm_fable:{name:"Fable",language:"en-gb",gender:"Male",traits:"🚹",targetQuality:"B",overallGrade:"C"}});const G=new Map;async function k(e){if(G.has(e))return G.get(e);const a=new Float32Array(await async function(e){if(i&&Object.hasOwn(i,"readFile")){const a="undefined"!=typeof __dirname?__dirname:import.meta.dirname,t=s.resolve(a,`../voices/${e}.bin`),{buffer:r}=await i.readFile(t);return r}const a=`https://huggingface.co/onnx-community/Kokoro-82M-v1.0-ONNX/resolve/main/voices/${e}.bin`;let t;try{t=await caches.open("kokoro-voices");const e=await t.match(a);if(e)return await e.arrayBuffer()}catch(e){console.warn("Unable to open cache",e)}const r=await fetch(a),n=await r.arrayBuffer();if(t)try{await t.put(a,new Response(n,{headers:r.headers}))}catch(e){console.warn("Unable to cache file",e)}return n}(e));return G.set(e,a),a}class M{constructor(e,a){this.model=e,this.tokenizer=a}static async from_pretrained(t,{dtype:r="fp32",device:n=null,progress_callback:l=null}={}){const s=e.from_pretrained(t,{progress_callback:l,dtype:r,device:n}),i=a.from_pretrained(t,{progress_callback:l}),o=await Promise.all([s,i]);return new M(...o)}get voices(){return $}list_voices(){console.table($)}_validate_voice(e){if(!$.hasOwnProperty(e))throw console.error(`Voice "${e}" not found. Available voices:`),console.table($),new Error(`Voice "${e}" not found. Should be one of: ${Object.keys($).join(", ")}.`);return e.at(0)}async generate(e,{voice:a="af_heart",speed:t=1}={}){const r=this._validate_voice(a),n=await m(e,r),{input_ids:l}=this.tokenizer(n,{truncation:!0});return this.generate_from_ids(l,{voice:a,speed:t})}async generate_from_ids(e,{voice:a="af_heart",speed:n=1}={}){const l=256*Math.min(Math.max(e.dims.at(-1)-2,0),509),s=(await k(a)).slice(l,l+256),i={input_ids:e,style:new t("float32",s,[1,256]),speed:new t("float32",[n],[1])},{waveform:o}=await this.model(i);return new r(o.data,24e3)}async*stream(e,{voice:a="af_heart",speed:t=1,split_pattern:r=null}={}){const n=this._validate_voice(a);let l;if(e instanceof w)l=e;else{if("string"!=typeof e)throw new Error("Invalid input type. Expected string or TextSplitterStream.");{l=new w;const a=r?e.split(r).map((e=>e.trim())).filter((e=>e.length>0)):[e];l.push(...a)}}for await(const e of l){const r=await m(e,n),{input_ids:l}=this.tokenizer(r,{truncation:!0}),s=await this.generate_from_ids(l,{voice:a,speed:t});yield{text:e,phonemes:r,audio:s}}}}const Q={set wasmPaths(e){n.backends.onnx.wasm.wasmPaths=e},get wasmPaths(){return n.backends.onnx.wasm.wasmPaths}};export{M as KokoroTTS,w as TextSplitterStream,Q as env};
|
|
|
|
|
|
_shared/voice/vendor/kokoro/phonemizer.js
DELETED
|
The diff for this file is too large to render.
See raw diff
|
|
|
_shared/voice/vendor/kokoro/stub.js
DELETED
|
@@ -1,6 +0,0 @@
|
|
| 1 |
-
// browser stub for node built-ins kokoro-js imports but doesn't use client-side.
|
| 2 |
-
export const join = (...a) => a.join('/');
|
| 3 |
-
export const resolve = (...a) => a.join('/');
|
| 4 |
-
export const dirname = (p) => String(p).replace(/\/[^/]*$/, '');
|
| 5 |
-
export const readFile = async () => { throw new Error('fs unavailable in browser'); };
|
| 6 |
-
export default {};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
_shared/voice/vendor/kokoro/transformers/ort-wasm-simd-threaded.jsep.mjs
DELETED
|
@@ -1,125 +0,0 @@
|
|
| 1 |
-
var ortWasmThreaded = (() => {
|
| 2 |
-
var _scriptName = import.meta.url;
|
| 3 |
-
|
| 4 |
-
return (
|
| 5 |
-
async function(moduleArg = {}) {
|
| 6 |
-
var moduleRtn;
|
| 7 |
-
|
| 8 |
-
var e=moduleArg,aa,ca,da=new Promise((a,b)=>{aa=a;ca=b}),ea="object"==typeof window,k="undefined"!=typeof WorkerGlobalScope,n="object"==typeof process&&"object"==typeof process.versions&&"string"==typeof process.versions.node&&"renderer"!=process.type,q=k&&self.name?.startsWith("em-pthread");if(n){const {createRequire:a}=await import("module");var require=a(import.meta.url),fa=require("worker_threads");global.Worker=fa.Worker;q=(k=!fa.oc)&&"em-pthread"==fa.workerData}
|
| 9 |
-
e.mountExternalData=(a,b)=>{a.startsWith("./")&&(a=a.substring(2));(e.Eb||(e.Eb=new Map)).set(a,b)};e.unmountExternalData=()=>{delete e.Eb};var SharedArrayBuffer=globalThis.SharedArrayBuffer??(new WebAssembly.Memory({initial:0,maximum:0,pc:!0})).buffer.constructor;
|
| 10 |
-
const ha=a=>async(...b)=>{try{if(e.Fb)throw Error("Session already started");const c=e.Fb={dc:b[0],errors:[]},d=await a(...b);if(e.Fb!==c)throw Error("Session mismatch");e.Jb?.flush();const f=c.errors;if(0<f.length){let g=await Promise.all(f);g=g.filter(h=>h);if(0<g.length)throw Error(g.join("\n"));}return d}finally{e.Fb=null}};
|
| 11 |
-
e.jsepInit=(a,b)=>{if("webgpu"===a){[e.Jb,e.Ub,e.Yb,e.Kb,e.Xb,e.jb,e.Zb,e.ac,e.Vb,e.Wb,e.$b]=b;const c=e.Jb;e.jsepRegisterBuffer=(d,f,g,h)=>c.registerBuffer(d,f,g,h);e.jsepGetBuffer=d=>c.getBuffer(d);e.jsepCreateDownloader=(d,f,g)=>c.createDownloader(d,f,g);e.jsepOnCreateSession=d=>{c.onCreateSession(d)};e.jsepOnReleaseSession=d=>{c.onReleaseSession(d)};e.jsepOnRunStart=d=>c.onRunStart(d);e.bc=(d,f)=>{c.upload(d,f)}}else if("webnn"===a){const c=b[0];[e.nc,e.Nb,e.webnnEnsureTensor,e.Ob,e.webnnDownloadTensor]=
|
| 12 |
-
b.slice(1);e.webnnReleaseTensorId=e.Nb;e.webnnUploadTensor=e.Ob;e.webnnOnRunStart=d=>c.onRunStart(d);e.webnnOnRunEnd=c.onRunEnd.bind(c);e.webnnRegisterMLContext=(d,f)=>{c.registerMLContext(d,f)};e.webnnOnReleaseSession=d=>{c.onReleaseSession(d)};e.webnnCreateMLTensorDownloader=(d,f)=>c.createMLTensorDownloader(d,f);e.webnnRegisterMLTensor=(d,f,g,h)=>c.registerMLTensor(d,f,g,h);e.webnnCreateMLContext=d=>c.createMLContext(d);e.webnnRegisterMLConstant=(d,f,g,h,l,m)=>c.registerMLConstant(d,f,g,h,l,e.Eb,
|
| 13 |
-
m);e.webnnRegisterGraphInput=c.registerGraphInput.bind(c);e.webnnIsGraphInput=c.isGraphInput.bind(c);e.webnnCreateTemporaryTensor=c.createTemporaryTensor.bind(c);e.webnnIsInt64Supported=c.isInt64Supported.bind(c)}};
|
| 14 |
-
let ja=()=>{const a=(b,c,d)=>(...f)=>{const g=t,h=c?.();f=b(...f);const l=c?.();h!==l&&(b=l,d(h),c=d=null);return t!=g?ia():f};(b=>{for(const c of b)e[c]=a(e[c],()=>e[c],d=>e[c]=d)})(["_OrtAppendExecutionProvider","_OrtCreateSession","_OrtRun","_OrtRunWithBinding","_OrtBindInput"]);"undefined"!==typeof ha&&(e._OrtRun=ha(e._OrtRun),e._OrtRunWithBinding=ha(e._OrtRunWithBinding));ja=void 0};e.asyncInit=()=>{ja?.()};var ka=Object.assign({},e),la="./this.program",ma=(a,b)=>{throw b;},v="",na,oa;
|
| 15 |
-
if(n){var fs=require("fs"),pa=require("path");import.meta.url.startsWith("data:")||(v=pa.dirname(require("url").fileURLToPath(import.meta.url))+"/");oa=a=>{a=qa(a)?new URL(a):a;return fs.readFileSync(a)};na=async a=>{a=qa(a)?new URL(a):a;return fs.readFileSync(a,void 0)};!e.thisProgram&&1<process.argv.length&&(la=process.argv[1].replace(/\\/g,"/"));process.argv.slice(2);ma=(a,b)=>{process.exitCode=a;throw b;}}else if(ea||k)k?v=self.location.href:"undefined"!=typeof document&&
|
| 16 |
-
document.currentScript&&(v=document.currentScript.src),_scriptName&&(v=_scriptName),v.startsWith("blob:")?v="":v=v.slice(0,v.replace(/[?#].*/,"").lastIndexOf("/")+1),n||(k&&(oa=a=>{var b=new XMLHttpRequest;b.open("GET",a,!1);b.responseType="arraybuffer";b.send(null);return new Uint8Array(b.response)}),na=async a=>{if(qa(a))return new Promise((c,d)=>{var f=new XMLHttpRequest;f.open("GET",a,!0);f.responseType="arraybuffer";f.onload=()=>{200==f.status||0==f.status&&f.response?c(f.response):d(f.status)};
|
| 17 |
-
f.onerror=d;f.send(null)});var b=await fetch(a,{credentials:"same-origin"});if(b.ok)return b.arrayBuffer();throw Error(b.status+" : "+b.url);});var ra=console.log.bind(console),sa=console.error.bind(console);n&&(ra=(...a)=>fs.writeSync(1,a.join(" ")+"\n"),sa=(...a)=>fs.writeSync(2,a.join(" ")+"\n"));var ta=ra,x=sa;Object.assign(e,ka);ka=null;var ua=e.wasmBinary,z,va,A=!1,wa,B,xa,ya,za,Aa,Ba,Ca,C,Da,Ea,qa=a=>a.startsWith("file://");function D(){z.buffer!=B.buffer&&E();return B}
|
| 18 |
-
function F(){z.buffer!=B.buffer&&E();return xa}function G(){z.buffer!=B.buffer&&E();return ya}function Fa(){z.buffer!=B.buffer&&E();return za}function H(){z.buffer!=B.buffer&&E();return Aa}function I(){z.buffer!=B.buffer&&E();return Ba}function Ga(){z.buffer!=B.buffer&&E();return Ca}function J(){z.buffer!=B.buffer&&E();return Ea}
|
| 19 |
-
if(q){var Ha;if(n){var Ia=fa.parentPort;Ia.on("message",b=>onmessage({data:b}));Object.assign(globalThis,{self:global,postMessage:b=>Ia.postMessage(b)})}var Ja=!1;x=function(...b){b=b.join(" ");n?fs.writeSync(2,b+"\n"):console.error(b)};self.alert=function(...b){postMessage({Bb:"alert",text:b.join(" "),ic:Ka()})};self.onunhandledrejection=b=>{throw b.reason||b;};function a(b){try{var c=b.data,d=c.Bb;if("load"===d){let f=[];self.onmessage=g=>f.push(g);self.startWorker=()=>{postMessage({Bb:"loaded"});
|
| 20 |
-
for(let g of f)a(g);self.onmessage=a};for(const g of c.Rb)if(!e[g]||e[g].proxy)e[g]=(...h)=>{postMessage({Bb:"callHandler",Qb:g,args:h})},"print"==g&&(ta=e[g]),"printErr"==g&&(x=e[g]);z=c.kc;E();Ha(c.lc)}else if("run"===d){La(c.Ab);Ma(c.Ab,0,0,1,0,0);Na();Oa(c.Ab);Ja||(Pa(),Ja=!0);try{Qa(c.fc,c.Hb)}catch(f){if("unwind"!=f)throw f;}}else"setimmediate"!==c.target&&("checkMailbox"===d?Ja&&Ra():d&&(x(`worker: received unknown command ${d}`),x(c)))}catch(f){throw Sa(),f;}}self.onmessage=a}
|
| 21 |
-
function E(){var a=z.buffer;e.HEAP8=B=new Int8Array(a);e.HEAP16=ya=new Int16Array(a);e.HEAPU8=xa=new Uint8Array(a);e.HEAPU16=za=new Uint16Array(a);e.HEAP32=Aa=new Int32Array(a);e.HEAPU32=Ba=new Uint32Array(a);e.HEAPF32=Ca=new Float32Array(a);e.HEAPF64=Ea=new Float64Array(a);e.HEAP64=C=new BigInt64Array(a);e.HEAPU64=Da=new BigUint64Array(a)}q||(z=new WebAssembly.Memory({initial:256,maximum:65536,shared:!0}),E());function Ta(){q?startWorker(e):K.Ca()}var Ua=0,Va=null;
|
| 22 |
-
function Wa(){Ua--;if(0==Ua&&Va){var a=Va;Va=null;a()}}function L(a){a="Aborted("+a+")";x(a);A=!0;a=new WebAssembly.RuntimeError(a+". Build with -sASSERTIONS for more info.");ca(a);throw a;}var Xa;async function Ya(a){if(!ua)try{var b=await na(a);return new Uint8Array(b)}catch{}if(a==Xa&&ua)a=new Uint8Array(ua);else if(oa)a=oa(a);else throw"both async and sync fetching of the wasm failed";return a}
|
| 23 |
-
async function Za(a,b){try{var c=await Ya(a);return await WebAssembly.instantiate(c,b)}catch(d){x(`failed to asynchronously prepare wasm: ${d}`),L(d)}}async function $a(a){var b=Xa;if(!ua&&"function"==typeof WebAssembly.instantiateStreaming&&!qa(b)&&!n)try{var c=fetch(b,{credentials:"same-origin"});return await WebAssembly.instantiateStreaming(c,a)}catch(d){x(`wasm streaming compile failed: ${d}`),x("falling back to ArrayBuffer instantiation")}return Za(b,a)}
|
| 24 |
-
function ab(){bb={L:cb,Aa:db,b:eb,$:fb,A:gb,pa:hb,X:ib,Z:jb,qa:kb,na:lb,ga:mb,ma:nb,J:ob,Y:pb,V:qb,oa:rb,W:sb,va:tb,E:ub,Q:vb,O:wb,D:xb,u:yb,r:zb,P:Ab,z:Bb,R:Cb,ja:Db,T:Eb,aa:Fb,M:Gb,F:Hb,ia:Oa,sa:Ib,t:Jb,Ba:Kb,w:Lb,o:Mb,l:Nb,c:Ob,n:Pb,j:Qb,v:Rb,p:Sb,f:Tb,s:Ub,m:Vb,e:Wb,k:Xb,i:Yb,g:Zb,d:$b,da:ac,ea:bc,fa:cc,ba:dc,ca:ec,N:fc,xa:gc,ua:hc,h:ic,C:jc,G:kc,ta:lc,x:mc,ra:nc,U:oc,q:pc,y:qc,K:rc,S:sc,za:tc,ya:uc,ka:vc,la:wc,_:xc,B:yc,I:zc,ha:Ac,H:Bc,a:z,wa:Cc};return{a:bb}}
|
| 25 |
-
var Dc={829644:(a,b,c,d,f)=>{if("undefined"==typeof e||!e.Eb)return 1;a=M(Number(a>>>0));a.startsWith("./")&&(a=a.substring(2));a=e.Eb.get(a);if(!a)return 2;b=Number(b>>>0);c=Number(c>>>0);d=Number(d>>>0);if(b+c>a.byteLength)return 3;try{const g=a.subarray(b,b+c);switch(f){case 0:F().set(g,d>>>0);break;case 1:e.mc?e.mc(d,g):e.bc(d,g);break;default:return 4}return 0}catch{return 4}},830468:(a,b,c)=>{e.Ob(a,F().subarray(b>>>0,b+c>>>0))},830532:()=>e.nc(),830574:a=>{e.Nb(a)},830611:()=>{e.Vb()},830642:()=>
|
| 26 |
-
{e.Wb()},830671:()=>{e.$b()},830696:a=>e.Ub(a),830729:a=>e.Yb(a),830761:(a,b,c)=>{e.Kb(Number(a),Number(b),Number(c),!0)},830824:(a,b,c)=>{e.Kb(Number(a),Number(b),Number(c))},830881:()=>"undefined"!==typeof wasmOffsetConverter,830938:a=>{e.jb("Abs",a,void 0)},830989:a=>{e.jb("Neg",a,void 0)},831040:a=>{e.jb("Floor",a,void 0)},831093:a=>{e.jb("Ceil",a,void 0)},831145:a=>{e.jb("Reciprocal",a,void 0)},831203:a=>{e.jb("Sqrt",a,void 0)},831255:a=>{e.jb("Exp",a,void 0)},831306:a=>{e.jb("Erf",a,void 0)},
|
| 27 |
-
831357:a=>{e.jb("Sigmoid",a,void 0)},831412:(a,b,c)=>{e.jb("HardSigmoid",a,{alpha:b,beta:c})},831491:a=>{e.jb("Log",a,void 0)},831542:a=>{e.jb("Sin",a,void 0)},831593:a=>{e.jb("Cos",a,void 0)},831644:a=>{e.jb("Tan",a,void 0)},831695:a=>{e.jb("Asin",a,void 0)},831747:a=>{e.jb("Acos",a,void 0)},831799:a=>{e.jb("Atan",a,void 0)},831851:a=>{e.jb("Sinh",a,void 0)},831903:a=>{e.jb("Cosh",a,void 0)},831955:a=>{e.jb("Asinh",a,void 0)},832008:a=>{e.jb("Acosh",a,void 0)},832061:a=>{e.jb("Atanh",a,void 0)},
|
| 28 |
-
832114:a=>{e.jb("Tanh",a,void 0)},832166:a=>{e.jb("Not",a,void 0)},832217:(a,b,c)=>{e.jb("Clip",a,{min:b,max:c})},832286:a=>{e.jb("Clip",a,void 0)},832338:(a,b)=>{e.jb("Elu",a,{alpha:b})},832396:a=>{e.jb("Gelu",a,void 0)},832448:a=>{e.jb("Relu",a,void 0)},832500:(a,b)=>{e.jb("LeakyRelu",a,{alpha:b})},832564:(a,b)=>{e.jb("ThresholdedRelu",a,{alpha:b})},832634:(a,b)=>{e.jb("Cast",a,{to:b})},832692:a=>{e.jb("Add",a,void 0)},832743:a=>{e.jb("Sub",a,void 0)},832794:a=>{e.jb("Mul",a,void 0)},832845:a=>
|
| 29 |
-
{e.jb("Div",a,void 0)},832896:a=>{e.jb("Pow",a,void 0)},832947:a=>{e.jb("Equal",a,void 0)},833E3:a=>{e.jb("Greater",a,void 0)},833055:a=>{e.jb("GreaterOrEqual",a,void 0)},833117:a=>{e.jb("Less",a,void 0)},833169:a=>{e.jb("LessOrEqual",a,void 0)},833228:(a,b,c,d,f)=>{e.jb("ReduceMean",a,{keepDims:!!b,noopWithEmptyAxes:!!c,axes:d?Array.from(H().subarray(Number(d)>>>0,Number(f)>>>0)):[]})},833403:(a,b,c,d,f)=>{e.jb("ReduceMax",a,{keepDims:!!b,noopWithEmptyAxes:!!c,axes:d?Array.from(H().subarray(Number(d)>>>
|
| 30 |
-
0,Number(f)>>>0)):[]})},833577:(a,b,c,d,f)=>{e.jb("ReduceMin",a,{keepDims:!!b,noopWithEmptyAxes:!!c,axes:d?Array.from(H().subarray(Number(d)>>>0,Number(f)>>>0)):[]})},833751:(a,b,c,d,f)=>{e.jb("ReduceProd",a,{keepDims:!!b,noopWithEmptyAxes:!!c,axes:d?Array.from(H().subarray(Number(d)>>>0,Number(f)>>>0)):[]})},833926:(a,b,c,d,f)=>{e.jb("ReduceSum",a,{keepDims:!!b,noopWithEmptyAxes:!!c,axes:d?Array.from(H().subarray(Number(d)>>>0,Number(f)>>>0)):[]})},834100:(a,b,c,d,f)=>{e.jb("ReduceL1",a,{keepDims:!!b,
|
| 31 |
-
noopWithEmptyAxes:!!c,axes:d?Array.from(H().subarray(Number(d)>>>0,Number(f)>>>0)):[]})},834273:(a,b,c,d,f)=>{e.jb("ReduceL2",a,{keepDims:!!b,noopWithEmptyAxes:!!c,axes:d?Array.from(H().subarray(Number(d)>>>0,Number(f)>>>0)):[]})},834446:(a,b,c,d,f)=>{e.jb("ReduceLogSum",a,{keepDims:!!b,noopWithEmptyAxes:!!c,axes:d?Array.from(H().subarray(Number(d)>>>0,Number(f)>>>0)):[]})},834623:(a,b,c,d,f)=>{e.jb("ReduceSumSquare",a,{keepDims:!!b,noopWithEmptyAxes:!!c,axes:d?Array.from(H().subarray(Number(d)>>>
|
| 32 |
-
0,Number(f)>>>0)):[]})},834803:(a,b,c,d,f)=>{e.jb("ReduceLogSumExp",a,{keepDims:!!b,noopWithEmptyAxes:!!c,axes:d?Array.from(H().subarray(Number(d)>>>0,Number(f)>>>0)):[]})},834983:a=>{e.jb("Where",a,void 0)},835036:(a,b,c)=>{e.jb("Transpose",a,{perm:b?Array.from(H().subarray(Number(b)>>>0,Number(c)>>>0)):[]})},835160:(a,b,c,d)=>{e.jb("DepthToSpace",a,{blocksize:b,mode:M(c),format:d?"NHWC":"NCHW"})},835293:(a,b,c,d)=>{e.jb("DepthToSpace",a,{blocksize:b,mode:M(c),format:d?"NHWC":"NCHW"})},835426:(a,
|
| 33 |
-
b,c,d,f,g,h,l,m,p,r,u,w,y,ba)=>{e.jb("ConvTranspose",a,{format:m?"NHWC":"NCHW",autoPad:b,dilations:[c],group:d,kernelShape:[f],pads:[g,h],strides:[l],wIsConst:()=>!!D()[p>>>0],outputPadding:r?Array.from(H().subarray(Number(r)>>>0,Number(u)>>>0)):[],outputShape:w?Array.from(H().subarray(Number(w)>>>0,Number(y)>>>0)):[],activation:M(ba)})},835859:(a,b,c,d,f,g,h,l,m,p,r,u,w,y)=>{e.jb("ConvTranspose",a,{format:l?"NHWC":"NCHW",autoPad:b,dilations:Array.from(H().subarray(Number(c)>>>0,(Number(c)>>>0)+2>>>
|
| 34 |
-
0)),group:d,kernelShape:Array.from(H().subarray(Number(f)>>>0,(Number(f)>>>0)+2>>>0)),pads:Array.from(H().subarray(Number(g)>>>0,(Number(g)>>>0)+4>>>0)),strides:Array.from(H().subarray(Number(h)>>>0,(Number(h)>>>0)+2>>>0)),wIsConst:()=>!!D()[m>>>0],outputPadding:p?Array.from(H().subarray(Number(p)>>>0,Number(r)>>>0)):[],outputShape:u?Array.from(H().subarray(Number(u)>>>0,Number(w)>>>0)):[],activation:M(y)})},836520:(a,b,c,d,f,g,h,l,m,p,r,u,w,y,ba)=>{e.jb("ConvTranspose",a,{format:m?"NHWC":"NCHW",
|
| 35 |
-
autoPad:b,dilations:[c],group:d,kernelShape:[f],pads:[g,h],strides:[l],wIsConst:()=>!!D()[p>>>0],outputPadding:r?Array.from(H().subarray(Number(r)>>>0,Number(u)>>>0)):[],outputShape:w?Array.from(H().subarray(Number(w)>>>0,Number(y)>>>0)):[],activation:M(ba)})},836953:(a,b,c,d,f,g,h,l,m,p,r,u,w,y)=>{e.jb("ConvTranspose",a,{format:l?"NHWC":"NCHW",autoPad:b,dilations:Array.from(H().subarray(Number(c)>>>0,(Number(c)>>>0)+2>>>0)),group:d,kernelShape:Array.from(H().subarray(Number(f)>>>0,(Number(f)>>>0)+
|
| 36 |
-
2>>>0)),pads:Array.from(H().subarray(Number(g)>>>0,(Number(g)>>>0)+4>>>0)),strides:Array.from(H().subarray(Number(h)>>>0,(Number(h)>>>0)+2>>>0)),wIsConst:()=>!!D()[m>>>0],outputPadding:p?Array.from(H().subarray(Number(p)>>>0,Number(r)>>>0)):[],outputShape:u?Array.from(H().subarray(Number(u)>>>0,Number(w)>>>0)):[],activation:M(y)})},837614:(a,b)=>{e.jb("GlobalAveragePool",a,{format:b?"NHWC":"NCHW"})},837705:(a,b,c,d,f,g,h,l,m,p,r,u,w,y)=>{e.jb("AveragePool",a,{format:y?"NHWC":"NCHW",auto_pad:b,ceil_mode:c,
|
| 37 |
-
count_include_pad:d,storage_order:f,dilations:g?Array.from(H().subarray(Number(g)>>>0,Number(h)>>>0)):[],kernel_shape:l?Array.from(H().subarray(Number(l)>>>0,Number(m)>>>0)):[],pads:p?Array.from(H().subarray(Number(p)>>>0,Number(r)>>>0)):[],strides:u?Array.from(H().subarray(Number(u)>>>0,Number(w)>>>0)):[]})},838184:(a,b)=>{e.jb("GlobalAveragePool",a,{format:b?"NHWC":"NCHW"})},838275:(a,b,c,d,f,g,h,l,m,p,r,u,w,y)=>{e.jb("AveragePool",a,{format:y?"NHWC":"NCHW",auto_pad:b,ceil_mode:c,count_include_pad:d,
|
| 38 |
-
storage_order:f,dilations:g?Array.from(H().subarray(Number(g)>>>0,Number(h)>>>0)):[],kernel_shape:l?Array.from(H().subarray(Number(l)>>>0,Number(m)>>>0)):[],pads:p?Array.from(H().subarray(Number(p)>>>0,Number(r)>>>0)):[],strides:u?Array.from(H().subarray(Number(u)>>>0,Number(w)>>>0)):[]})},838754:(a,b)=>{e.jb("GlobalMaxPool",a,{format:b?"NHWC":"NCHW"})},838841:(a,b,c,d,f,g,h,l,m,p,r,u,w,y)=>{e.jb("MaxPool",a,{format:y?"NHWC":"NCHW",auto_pad:b,ceil_mode:c,count_include_pad:d,storage_order:f,dilations:g?
|
| 39 |
-
Array.from(H().subarray(Number(g)>>>0,Number(h)>>>0)):[],kernel_shape:l?Array.from(H().subarray(Number(l)>>>0,Number(m)>>>0)):[],pads:p?Array.from(H().subarray(Number(p)>>>0,Number(r)>>>0)):[],strides:u?Array.from(H().subarray(Number(u)>>>0,Number(w)>>>0)):[]})},839316:(a,b)=>{e.jb("GlobalMaxPool",a,{format:b?"NHWC":"NCHW"})},839403:(a,b,c,d,f,g,h,l,m,p,r,u,w,y)=>{e.jb("MaxPool",a,{format:y?"NHWC":"NCHW",auto_pad:b,ceil_mode:c,count_include_pad:d,storage_order:f,dilations:g?Array.from(H().subarray(Number(g)>>>
|
| 40 |
-
0,Number(h)>>>0)):[],kernel_shape:l?Array.from(H().subarray(Number(l)>>>0,Number(m)>>>0)):[],pads:p?Array.from(H().subarray(Number(p)>>>0,Number(r)>>>0)):[],strides:u?Array.from(H().subarray(Number(u)>>>0,Number(w)>>>0)):[]})},839878:(a,b,c,d,f)=>{e.jb("Gemm",a,{alpha:b,beta:c,transA:d,transB:f})},839982:a=>{e.jb("MatMul",a,void 0)},840036:(a,b,c,d)=>{e.jb("ArgMax",a,{keepDims:!!b,selectLastIndex:!!c,axis:d})},840144:(a,b,c,d)=>{e.jb("ArgMin",a,{keepDims:!!b,selectLastIndex:!!c,axis:d})},840252:(a,
|
| 41 |
-
b)=>{e.jb("Softmax",a,{axis:b})},840315:(a,b)=>{e.jb("Concat",a,{axis:b})},840375:(a,b,c,d,f)=>{e.jb("Split",a,{axis:b,numOutputs:c,splitSizes:d?Array.from(H().subarray(Number(d)>>>0,Number(f)>>>0)):[]})},840531:a=>{e.jb("Expand",a,void 0)},840585:(a,b)=>{e.jb("Gather",a,{axis:Number(b)})},840656:(a,b)=>{e.jb("GatherElements",a,{axis:Number(b)})},840735:(a,b)=>{e.jb("GatherND",a,{batch_dims:Number(b)})},840814:(a,b,c,d,f,g,h,l,m,p,r)=>{e.jb("Resize",a,{antialias:b,axes:c?Array.from(H().subarray(Number(c)>>>
|
| 42 |
-
0,Number(d)>>>0)):[],coordinateTransformMode:M(f),cubicCoeffA:g,excludeOutside:h,extrapolationValue:l,keepAspectRatioPolicy:M(m),mode:M(p),nearestMode:M(r)})},841176:(a,b,c,d,f,g,h)=>{e.jb("Slice",a,{starts:b?Array.from(H().subarray(Number(b)>>>0,Number(c)>>>0)):[],ends:d?Array.from(H().subarray(Number(d)>>>0,Number(f)>>>0)):[],axes:g?Array.from(H().subarray(Number(g)>>>0,Number(h)>>>0)):[]})},841440:a=>{e.jb("Tile",a,void 0)},841492:(a,b,c)=>{e.jb("InstanceNormalization",a,{epsilon:b,format:c?"NHWC":
|
| 43 |
-
"NCHW"})},841606:(a,b,c)=>{e.jb("InstanceNormalization",a,{epsilon:b,format:c?"NHWC":"NCHW"})},841720:a=>{e.jb("Range",a,void 0)},841773:(a,b)=>{e.jb("Einsum",a,{equation:M(b)})},841854:(a,b,c,d,f)=>{e.jb("Pad",a,{mode:b,value:c,pads:d?Array.from(H().subarray(Number(d)>>>0,Number(f)>>>0)):[]})},841997:(a,b,c,d,f,g)=>{e.jb("BatchNormalization",a,{epsilon:b,momentum:c,spatial:!!f,trainingMode:!!d,format:g?"NHWC":"NCHW"})},842166:(a,b,c,d,f,g)=>{e.jb("BatchNormalization",a,{epsilon:b,momentum:c,spatial:!!f,
|
| 44 |
-
trainingMode:!!d,format:g?"NHWC":"NCHW"})},842335:(a,b,c)=>{e.jb("CumSum",a,{exclusive:Number(b),reverse:Number(c)})},842432:(a,b,c)=>{e.jb("DequantizeLinear",a,{axis:b,blockSize:c})},842522:(a,b,c,d,f)=>{e.jb("GridSample",a,{align_corners:b,mode:M(c),padding_mode:M(d),format:f?"NHWC":"NCHW"})},842692:(a,b,c,d,f)=>{e.jb("GridSample",a,{align_corners:b,mode:M(c),padding_mode:M(d),format:f?"NHWC":"NCHW"})},842862:(a,b)=>{e.jb("ScatterND",a,{reduction:M(b)})},842947:(a,b,c,d,f,g,h,l,m)=>{e.jb("Attention",
|
| 45 |
-
a,{numHeads:b,isUnidirectional:c,maskFilterValue:d,scale:f,doRotary:g,qkvHiddenSizes:h?Array.from(H().subarray(Number(l)>>>0,Number(l)+h>>>0)):[],pastPresentShareBuffer:!!m})},843219:a=>{e.jb("BiasAdd",a,void 0)},843274:a=>{e.jb("BiasSplitGelu",a,void 0)},843335:a=>{e.jb("FastGelu",a,void 0)},843391:(a,b,c,d,f,g,h,l,m,p,r,u,w,y,ba,Vd)=>{e.jb("Conv",a,{format:u?"NHWC":"NCHW",auto_pad:b,dilations:c?Array.from(H().subarray(Number(c)>>>0,Number(d)>>>0)):[],group:f,kernel_shape:g?Array.from(H().subarray(Number(g)>>>
|
| 46 |
-
0,Number(h)>>>0)):[],pads:l?Array.from(H().subarray(Number(l)>>>0,Number(m)>>>0)):[],strides:p?Array.from(H().subarray(Number(p)>>>0,Number(r)>>>0)):[],w_is_const:()=>!!D()[Number(w)>>>0],activation:M(y),activation_params:ba?Array.from(Ga().subarray(Number(ba)>>>0,Number(Vd)>>>0)):[]})},843975:a=>{e.jb("Gelu",a,void 0)},844027:(a,b,c,d,f,g,h,l,m)=>{e.jb("GroupQueryAttention",a,{numHeads:b,kvNumHeads:c,scale:d,softcap:f,doRotary:g,rotaryInterleaved:h,smoothSoftmax:l,localWindowSize:m})},844244:(a,
|
| 47 |
-
b,c,d)=>{e.jb("LayerNormalization",a,{axis:b,epsilon:c,simplified:!!d})},844355:(a,b,c,d)=>{e.jb("LayerNormalization",a,{axis:b,epsilon:c,simplified:!!d})},844466:(a,b,c,d,f,g)=>{e.jb("MatMulNBits",a,{k:b,n:c,accuracyLevel:d,bits:f,blockSize:g})},844593:(a,b,c,d,f,g)=>{e.jb("MultiHeadAttention",a,{numHeads:b,isUnidirectional:c,maskFilterValue:d,scale:f,doRotary:g})},844752:(a,b)=>{e.jb("QuickGelu",a,{alpha:b})},844816:(a,b,c,d,f)=>{e.jb("RotaryEmbedding",a,{interleaved:!!b,numHeads:c,rotaryEmbeddingDim:d,
|
| 48 |
-
scale:f})},844955:(a,b,c)=>{e.jb("SkipLayerNormalization",a,{epsilon:b,simplified:!!c})},845057:(a,b,c)=>{e.jb("SkipLayerNormalization",a,{epsilon:b,simplified:!!c})},845159:(a,b,c,d)=>{e.jb("GatherBlockQuantized",a,{gatherAxis:b,quantizeAxis:c,blockSize:d})},845280:a=>{e.Zb(a)},845314:(a,b)=>e.ac(Number(a),Number(b),e.Fb.dc,e.Fb.errors)};function db(a,b,c){return Ec(async()=>{await e.Xb(Number(a),Number(b),Number(c))})}function cb(){return"undefined"!==typeof wasmOffsetConverter}
|
| 49 |
-
class Fc{name="ExitStatus";constructor(a){this.message=`Program terminated with exit(${a})`;this.status=a}}
|
| 50 |
-
var Gc=a=>{a.terminate();a.onmessage=()=>{}},Hc=[],Lc=a=>{0==N.length&&(Ic(),Jc(N[0]));var b=N.pop();if(!b)return 6;Kc.push(b);O[a.Ab]=b;b.Ab=a.Ab;var c={Bb:"run",fc:a.ec,Hb:a.Hb,Ab:a.Ab};n&&b.unref();b.postMessage(c,a.Mb);return 0},P=0,Q=(a,b,...c)=>{for(var d=2*c.length,f=Mc(),g=Nc(8*d),h=g>>>3,l=0;l<c.length;l++){var m=c[l];"bigint"==typeof m?(C[h+2*l]=1n,C[h+2*l+1]=m):(C[h+2*l]=0n,J()[h+2*l+1>>>0]=m)}a=Oc(a,0,d,g,b);Pc(f);return a};
|
| 51 |
-
function Cc(a){if(q)return Q(0,1,a);wa=a;if(!(0<P)){for(var b of Kc)Gc(b);for(b of N)Gc(b);N=[];Kc=[];O={};A=!0}ma(a,new Fc(a))}function Qc(a){if(q)return Q(1,0,a);xc(a)}var xc=a=>{wa=a;if(q)throw Qc(a),"unwind";Cc(a)},N=[],Kc=[],Rc=[],O={};function Sc(){for(var a=e.numThreads-1;a--;)Ic();Hc.unshift(()=>{Ua++;Tc(()=>Wa())})}var Vc=a=>{var b=a.Ab;delete O[b];N.push(a);Kc.splice(Kc.indexOf(a),1);a.Ab=0;Uc(b)};function Na(){Rc.forEach(a=>a())}
|
| 52 |
-
var Jc=a=>new Promise(b=>{a.onmessage=g=>{g=g.data;var h=g.Bb;if(g.Gb&&g.Gb!=Ka()){var l=O[g.Gb];l?l.postMessage(g,g.Mb):x(`Internal error! Worker sent a message "${h}" to target pthread ${g.Gb}, but that thread no longer exists!`)}else if("checkMailbox"===h)Ra();else if("spawnThread"===h)Lc(g);else if("cleanupThread"===h)Vc(O[g.hc]);else if("loaded"===h)a.loaded=!0,n&&!a.Ab&&a.unref(),b(a);else if("alert"===h)alert(`Thread ${g.ic}: ${g.text}`);else if("setimmediate"===g.target)a.postMessage(g);else if("callHandler"===
|
| 53 |
-
h)e[g.Qb](...g.args);else h&&x(`worker sent an unknown command ${h}`)};a.onerror=g=>{x(`${"worker sent an error!"} ${g.filename}:${g.lineno}: ${g.message}`);throw g;};n&&(a.on("message",g=>a.onmessage({data:g})),a.on("error",g=>a.onerror(g)));var c=[],d=[],f;for(f of d)e.propertyIsEnumerable(f)&&c.push(f);a.postMessage({Bb:"load",Rb:c,kc:z,lc:va})});function Tc(a){q?a():Promise.all(N.map(Jc)).then(a)}
|
| 54 |
-
function Ic(){var a=new Worker(new URL(import.meta.url),{type:"module",workerData:"em-pthread",name:"em-pthread"});N.push(a)}var La=a=>{E();var b=I()[a+52>>>2>>>0];a=I()[a+56>>>2>>>0];Wc(b,b-a);Pc(b)},Qa=(a,b)=>{P=0;a=Xc(a,b);0<P?wa=a:Yc(a)};class Zc{constructor(a){this.Ib=a-24}}var $c=0,ad=0;function eb(a,b,c){a>>>=0;var d=new Zc(a);b>>>=0;c>>>=0;I()[d.Ib+16>>>2>>>0]=0;I()[d.Ib+4>>>2>>>0]=b;I()[d.Ib+8>>>2>>>0]=c;$c=a;ad++;throw $c;}
|
| 55 |
-
function bd(a,b,c,d){return q?Q(2,1,a,b,c,d):fb(a,b,c,d)}function fb(a,b,c,d){a>>>=0;b>>>=0;c>>>=0;d>>>=0;if("undefined"==typeof SharedArrayBuffer)return 6;var f=[];if(q&&0===f.length)return bd(a,b,c,d);a={ec:c,Ab:a,Hb:d,Mb:f};return q?(a.Bb="spawnThread",postMessage(a,f),0):Lc(a)}
|
| 56 |
-
var cd="undefined"!=typeof TextDecoder?new TextDecoder:void 0,dd=(a,b=0,c=NaN)=>{b>>>=0;var d=b+c;for(c=b;a[c]&&!(c>=d);)++c;if(16<c-b&&a.buffer&&cd)return cd.decode(a.buffer instanceof ArrayBuffer?a.subarray(b,c):a.slice(b,c));for(d="";b<c;){var f=a[b++];if(f&128){var g=a[b++]&63;if(192==(f&224))d+=String.fromCharCode((f&31)<<6|g);else{var h=a[b++]&63;f=224==(f&240)?(f&15)<<12|g<<6|h:(f&7)<<18|g<<12|h<<6|a[b++]&63;65536>f?d+=String.fromCharCode(f):(f-=65536,d+=String.fromCharCode(55296|f>>10,56320|
|
| 57 |
-
f&1023))}}else d+=String.fromCharCode(f)}return d},M=(a,b)=>(a>>>=0)?dd(F(),a,b):"";function gb(a,b,c){return q?Q(3,1,a,b,c):0}function hb(a,b){if(q)return Q(4,1,a,b)}
|
| 58 |
-
var ed=a=>{for(var b=0,c=0;c<a.length;++c){var d=a.charCodeAt(c);127>=d?b++:2047>=d?b+=2:55296<=d&&57343>=d?(b+=4,++c):b+=3}return b},fd=(a,b,c)=>{var d=F();b>>>=0;if(0<c){var f=b;c=b+c-1;for(var g=0;g<a.length;++g){var h=a.charCodeAt(g);if(55296<=h&&57343>=h){var l=a.charCodeAt(++g);h=65536+((h&1023)<<10)|l&1023}if(127>=h){if(b>=c)break;d[b++>>>0]=h}else{if(2047>=h){if(b+1>=c)break;d[b++>>>0]=192|h>>6}else{if(65535>=h){if(b+2>=c)break;d[b++>>>0]=224|h>>12}else{if(b+3>=c)break;d[b++>>>0]=240|h>>18;
|
| 59 |
-
d[b++>>>0]=128|h>>12&63}d[b++>>>0]=128|h>>6&63}d[b++>>>0]=128|h&63}}d[b>>>0]=0;a=b-f}else a=0;return a};function ib(a,b){if(q)return Q(5,1,a,b)}function jb(a,b,c){if(q)return Q(6,1,a,b,c)}function kb(a,b,c){return q?Q(7,1,a,b,c):0}function lb(a,b){if(q)return Q(8,1,a,b)}function mb(a,b,c){if(q)return Q(9,1,a,b,c)}function nb(a,b,c,d){if(q)return Q(10,1,a,b,c,d)}function ob(a,b,c,d){if(q)return Q(11,1,a,b,c,d)}function pb(a,b,c,d){if(q)return Q(12,1,a,b,c,d)}function qb(a){if(q)return Q(13,1,a)}
|
| 60 |
-
function rb(a,b){if(q)return Q(14,1,a,b)}function sb(a,b,c){if(q)return Q(15,1,a,b,c)}var tb=()=>L(""),gd,R=a=>{for(var b="";F()[a>>>0];)b+=gd[F()[a++>>>0]];return b},hd={},jd={},kd={},S;function ld(a,b,c={}){var d=b.name;if(!a)throw new S(`type "${d}" must have a positive integer typeid pointer`);if(jd.hasOwnProperty(a)){if(c.Sb)return;throw new S(`Cannot register type '${d}' twice`);}jd[a]=b;delete kd[a];hd.hasOwnProperty(a)&&(b=hd[a],delete hd[a],b.forEach(f=>f()))}
|
| 61 |
-
function T(a,b,c={}){return ld(a,b,c)}var md=(a,b,c)=>{switch(b){case 1:return c?d=>D()[d>>>0]:d=>F()[d>>>0];case 2:return c?d=>G()[d>>>1>>>0]:d=>Fa()[d>>>1>>>0];case 4:return c?d=>H()[d>>>2>>>0]:d=>I()[d>>>2>>>0];case 8:return c?d=>C[d>>>3]:d=>Da[d>>>3];default:throw new TypeError(`invalid integer width (${b}): ${a}`);}};
|
| 62 |
-
function ub(a,b,c){a>>>=0;c>>>=0;b=R(b>>>0);T(a,{name:b,fromWireType:d=>d,toWireType:function(d,f){if("bigint"!=typeof f&&"number"!=typeof f)throw null===f?f="null":(d=typeof f,f="object"===d||"array"===d||"function"===d?f.toString():""+f),new TypeError(`Cannot convert "${f}" to ${this.name}`);"number"==typeof f&&(f=BigInt(f));return f},Cb:U,readValueFromPointer:md(b,c,-1==b.indexOf("u")),Db:null})}var U=8;
|
| 63 |
-
function vb(a,b,c,d){a>>>=0;b=R(b>>>0);T(a,{name:b,fromWireType:function(f){return!!f},toWireType:function(f,g){return g?c:d},Cb:U,readValueFromPointer:function(f){return this.fromWireType(F()[f>>>0])},Db:null})}var nd=[],V=[];function Ob(a){a>>>=0;9<a&&0===--V[a+1]&&(V[a]=void 0,nd.push(a))}
|
| 64 |
-
var W=a=>{if(!a)throw new S("Cannot use deleted val. handle = "+a);return V[a]},X=a=>{switch(a){case void 0:return 2;case null:return 4;case !0:return 6;case !1:return 8;default:const b=nd.pop()||V.length;V[b]=a;V[b+1]=1;return b}};function od(a){return this.fromWireType(I()[a>>>2>>>0])}var pd={name:"emscripten::val",fromWireType:a=>{var b=W(a);Ob(a);return b},toWireType:(a,b)=>X(b),Cb:U,readValueFromPointer:od,Db:null};function wb(a){return T(a>>>0,pd)}
|
| 65 |
-
var qd=(a,b)=>{switch(b){case 4:return function(c){return this.fromWireType(Ga()[c>>>2>>>0])};case 8:return function(c){return this.fromWireType(J()[c>>>3>>>0])};default:throw new TypeError(`invalid float width (${b}): ${a}`);}};function xb(a,b,c){a>>>=0;c>>>=0;b=R(b>>>0);T(a,{name:b,fromWireType:d=>d,toWireType:(d,f)=>f,Cb:U,readValueFromPointer:qd(b,c),Db:null})}
|
| 66 |
-
function yb(a,b,c,d,f){a>>>=0;c>>>=0;b=R(b>>>0);-1===f&&(f=4294967295);f=l=>l;if(0===d){var g=32-8*c;f=l=>l<<g>>>g}var h=b.includes("unsigned")?function(l,m){return m>>>0}:function(l,m){return m};T(a,{name:b,fromWireType:f,toWireType:h,Cb:U,readValueFromPointer:md(b,c,0!==d),Db:null})}
|
| 67 |
-
function zb(a,b,c){function d(g){var h=I()[g>>>2>>>0];g=I()[g+4>>>2>>>0];return new f(D().buffer,g,h)}a>>>=0;var f=[Int8Array,Uint8Array,Int16Array,Uint16Array,Int32Array,Uint32Array,Float32Array,Float64Array,BigInt64Array,BigUint64Array][b];c=R(c>>>0);T(a,{name:c,fromWireType:d,Cb:U,readValueFromPointer:d},{Sb:!0})}
|
| 68 |
-
function Ab(a,b){a>>>=0;b=R(b>>>0);T(a,{name:b,fromWireType:function(c){for(var d=I()[c>>>2>>>0],f=c+4,g,h=f,l=0;l<=d;++l){var m=f+l;if(l==d||0==F()[m>>>0])h=M(h,m-h),void 0===g?g=h:(g+=String.fromCharCode(0),g+=h),h=m+1}Y(c);return g},toWireType:function(c,d){d instanceof ArrayBuffer&&(d=new Uint8Array(d));var f="string"==typeof d;if(!(f||d instanceof Uint8Array||d instanceof Uint8ClampedArray||d instanceof Int8Array))throw new S("Cannot pass non-string to std::string");var g=f?ed(d):d.length;var h=
|
| 69 |
-
rd(4+g+1),l=h+4;I()[h>>>2>>>0]=g;if(f)fd(d,l,g+1);else if(f)for(f=0;f<g;++f){var m=d.charCodeAt(f);if(255<m)throw Y(h),new S("String has UTF-16 code units that do not fit in 8 bits");F()[l+f>>>0]=m}else for(f=0;f<g;++f)F()[l+f>>>0]=d[f];null!==c&&c.push(Y,h);return h},Cb:U,readValueFromPointer:od,Db(c){Y(c)}})}
|
| 70 |
-
var sd="undefined"!=typeof TextDecoder?new TextDecoder("utf-16le"):void 0,td=(a,b)=>{var c=a>>1;for(var d=c+b/2;!(c>=d)&&Fa()[c>>>0];)++c;c<<=1;if(32<c-a&&sd)return sd.decode(F().slice(a,c));c="";for(d=0;!(d>=b/2);++d){var f=G()[a+2*d>>>1>>>0];if(0==f)break;c+=String.fromCharCode(f)}return c},ud=(a,b,c)=>{c??=2147483647;if(2>c)return 0;c-=2;var d=b;c=c<2*a.length?c/2:a.length;for(var f=0;f<c;++f){var g=a.charCodeAt(f);G()[b>>>1>>>0]=g;b+=2}G()[b>>>1>>>0]=0;return b-d},vd=a=>2*a.length,wd=(a,b)=>{for(var c=
|
| 71 |
-
0,d="";!(c>=b/4);){var f=H()[a+4*c>>>2>>>0];if(0==f)break;++c;65536<=f?(f-=65536,d+=String.fromCharCode(55296|f>>10,56320|f&1023)):d+=String.fromCharCode(f)}return d},xd=(a,b,c)=>{b>>>=0;c??=2147483647;if(4>c)return 0;var d=b;c=d+c-4;for(var f=0;f<a.length;++f){var g=a.charCodeAt(f);if(55296<=g&&57343>=g){var h=a.charCodeAt(++f);g=65536+((g&1023)<<10)|h&1023}H()[b>>>2>>>0]=g;b+=4;if(b+4>c)break}H()[b>>>2>>>0]=0;return b-d},yd=a=>{for(var b=0,c=0;c<a.length;++c){var d=a.charCodeAt(c);55296<=d&&57343>=
|
| 72 |
-
d&&++c;b+=4}return b};
|
| 73 |
-
function Bb(a,b,c){a>>>=0;b>>>=0;c>>>=0;c=R(c);if(2===b){var d=td;var f=ud;var g=vd;var h=l=>Fa()[l>>>1>>>0]}else 4===b&&(d=wd,f=xd,g=yd,h=l=>I()[l>>>2>>>0]);T(a,{name:c,fromWireType:l=>{for(var m=I()[l>>>2>>>0],p,r=l+4,u=0;u<=m;++u){var w=l+4+u*b;if(u==m||0==h(w))r=d(r,w-r),void 0===p?p=r:(p+=String.fromCharCode(0),p+=r),r=w+b}Y(l);return p},toWireType:(l,m)=>{if("string"!=typeof m)throw new S(`Cannot pass non-string to C++ string type ${c}`);var p=g(m),r=rd(4+p+b);I()[r>>>2>>>0]=p/b;f(m,r+4,p+b);
|
| 74 |
-
null!==l&&l.push(Y,r);return r},Cb:U,readValueFromPointer:od,Db(l){Y(l)}})}function Cb(a,b){a>>>=0;b=R(b>>>0);T(a,{Tb:!0,name:b,Cb:0,fromWireType:()=>{},toWireType:()=>{}})}function Db(a){Ma(a>>>0,!k,1,!ea,131072,!1);Na()}var zd=a=>{if(!A)try{if(a(),!(0<P))try{q?Yc(wa):xc(wa)}catch(b){b instanceof Fc||"unwind"==b||ma(1,b)}}catch(b){b instanceof Fc||"unwind"==b||ma(1,b)}};
|
| 75 |
-
function Oa(a){a>>>=0;"function"===typeof Atomics.jc&&(Atomics.jc(H(),a>>>2,a).value.then(Ra),a+=128,Atomics.store(H(),a>>>2,1))}var Ra=()=>{var a=Ka();a&&(Oa(a),zd(Ad))};function Eb(a,b){a>>>=0;a==b>>>0?setTimeout(Ra):q?postMessage({Gb:a,Bb:"checkMailbox"}):(a=O[a])&&a.postMessage({Bb:"checkMailbox"})}var Bd=[];function Fb(a,b,c,d,f){b>>>=0;d/=2;Bd.length=d;c=f>>>0>>>3;for(f=0;f<d;f++)Bd[f]=C[c+2*f]?C[c+2*f+1]:J()[c+2*f+1>>>0];return(b?Dc[b]:Cd[a])(...Bd)}var Gb=()=>{P=0};
|
| 76 |
-
function Hb(a){a>>>=0;q?postMessage({Bb:"cleanupThread",hc:a}):Vc(O[a])}function Ib(a){n&&O[a>>>0].ref()}var Ed=(a,b)=>{var c=jd[a];if(void 0===c)throw a=Dd(a),c=R(a),Y(a),new S(`${b} has unknown type ${c}`);return c},Fd=(a,b,c)=>{var d=[];a=a.toWireType(d,c);d.length&&(I()[b>>>2>>>0]=X(d));return a};function Jb(a,b,c){b>>>=0;c>>>=0;a=W(a>>>0);b=Ed(b,"emval::as");return Fd(b,c,a)}function Kb(a,b){b>>>=0;a=W(a>>>0);b=Ed(b,"emval::as");return b.toWireType(null,a)}var Gd=a=>{try{a()}catch(b){L(b)}};
|
| 77 |
-
function Hd(){var a=K,b={};for(let [c,d]of Object.entries(a))b[c]="function"==typeof d?(...f)=>{Id.push(c);try{return d(...f)}finally{A||(Id.pop(),t&&1===Z&&0===Id.length&&(Z=0,P+=1,Gd(Jd),"undefined"!=typeof Fibers&&Fibers.rc()))}}:d;return b}var Z=0,t=null,Kd=0,Id=[],Ld={},Md={},Nd=0,Od=null,Pd=[];function ia(){return new Promise((a,b)=>{Od={resolve:a,reject:b}})}
|
| 78 |
-
function Qd(){var a=rd(65548),b=a+12;I()[a>>>2>>>0]=b;I()[a+4>>>2>>>0]=b+65536;b=Id[0];var c=Ld[b];void 0===c&&(c=Nd++,Ld[b]=c,Md[c]=b);b=c;H()[a+8>>>2>>>0]=b;return a}function Rd(){var a=H()[t+8>>>2>>>0];a=K[Md[a]];--P;return a()}
|
| 79 |
-
function Sd(a){if(!A){if(0===Z){var b=!1,c=!1;a((d=0)=>{if(!A&&(Kd=d,b=!0,c)){Z=2;Gd(()=>Td(t));"undefined"!=typeof MainLoop&&MainLoop.Pb&&MainLoop.resume();d=!1;try{var f=Rd()}catch(l){f=l,d=!0}var g=!1;if(!t){var h=Od;h&&(Od=null,(d?h.reject:h.resolve)(f),g=!0)}if(d&&!g)throw f;}});c=!0;b||(Z=1,t=Qd(),"undefined"!=typeof MainLoop&&MainLoop.Pb&&MainLoop.pause(),Gd(()=>Ud(t)))}else 2===Z?(Z=0,Gd(Wd),Y(t),t=null,Pd.forEach(zd)):L(`invalid state: ${Z}`);return Kd}}
|
| 80 |
-
function Ec(a){return Sd(b=>{a().then(b)})}function Lb(a){a>>>=0;return Ec(async()=>{var b=await W(a);return X(b)})}var Xd=[];function Mb(a,b,c,d){c>>>=0;d>>>=0;a=Xd[a>>>0];b=W(b>>>0);return a(null,b,c,d)}var Yd={},Zd=a=>{var b=Yd[a];return void 0===b?R(a):b};function Nb(a,b,c,d,f){c>>>=0;d>>>=0;f>>>=0;a=Xd[a>>>0];b=W(b>>>0);c=Zd(c);return a(b,b[c],d,f)}var $d=()=>"object"==typeof globalThis?globalThis:Function("return this")();
|
| 81 |
-
function Pb(a){a>>>=0;if(0===a)return X($d());a=Zd(a);return X($d()[a])}var ae=a=>{var b=Xd.length;Xd.push(a);return b},be=(a,b)=>{for(var c=Array(a),d=0;d<a;++d)c[d]=Ed(I()[b+4*d>>>2>>>0],"parameter "+d);return c},ce=(a,b)=>Object.defineProperty(b,"name",{value:a});
|
| 82 |
-
function de(a){var b=Function;if(!(b instanceof Function))throw new TypeError(`new_ called with constructor type ${typeof b} which is not a function`);var c=ce(b.name||"unknownFunctionName",function(){});c.prototype=b.prototype;c=new c;a=b.apply(c,a);return a instanceof Object?a:c}
|
| 83 |
-
function Qb(a,b,c){b=be(a,b>>>0);var d=b.shift();a--;var f="return function (obj, func, destructorsRef, args) {\n",g=0,h=[];0===c&&h.push("obj");for(var l=["retType"],m=[d],p=0;p<a;++p)h.push("arg"+p),l.push("argType"+p),m.push(b[p]),f+=` var arg${p} = argType${p}.readValueFromPointer(args${g?"+"+g:""});\n`,g+=b[p].Cb;f+=` var rv = ${1===c?"new func":"func.call"}(${h.join(", ")});\n`;d.Tb||(l.push("emval_returnValue"),m.push(Fd),f+=" return emval_returnValue(retType, destructorsRef, rv);\n");l.push(f+
|
| 84 |
-
"};\n");a=de(l)(...m);c=`methodCaller<(${b.map(r=>r.name).join(", ")}) => ${d.name}>`;return ae(ce(c,a))}function Rb(a){a=Zd(a>>>0);return X(e[a])}function Sb(a,b){b>>>=0;a=W(a>>>0);b=W(b);return X(a[b])}function Tb(a){a>>>=0;9<a&&(V[a+1]+=1)}function Ub(){return X([])}function Vb(a){a=W(a>>>0);for(var b=Array(a.length),c=0;c<a.length;c++)b[c]=a[c];return X(b)}function Wb(a){return X(Zd(a>>>0))}function Xb(){return X({})}
|
| 85 |
-
function Yb(a){a>>>=0;for(var b=W(a);b.length;){var c=b.pop();b.pop()(c)}Ob(a)}function Zb(a,b,c){b>>>=0;c>>>=0;a=W(a>>>0);b=W(b);c=W(c);a[b]=c}function $b(a,b){b>>>=0;a=Ed(a>>>0,"_emval_take_value");a=a.readValueFromPointer(b);return X(a)}
|
| 86 |
-
function ac(a,b){a=-9007199254740992>a||9007199254740992<a?NaN:Number(a);b>>>=0;a=new Date(1E3*a);H()[b>>>2>>>0]=a.getUTCSeconds();H()[b+4>>>2>>>0]=a.getUTCMinutes();H()[b+8>>>2>>>0]=a.getUTCHours();H()[b+12>>>2>>>0]=a.getUTCDate();H()[b+16>>>2>>>0]=a.getUTCMonth();H()[b+20>>>2>>>0]=a.getUTCFullYear()-1900;H()[b+24>>>2>>>0]=a.getUTCDay();a=(a.getTime()-Date.UTC(a.getUTCFullYear(),0,1,0,0,0,0))/864E5|0;H()[b+28>>>2>>>0]=a}
|
| 87 |
-
var ee=a=>0===a%4&&(0!==a%100||0===a%400),fe=[0,31,60,91,121,152,182,213,244,274,305,335],ge=[0,31,59,90,120,151,181,212,243,273,304,334];
|
| 88 |
-
function bc(a,b){a=-9007199254740992>a||9007199254740992<a?NaN:Number(a);b>>>=0;a=new Date(1E3*a);H()[b>>>2>>>0]=a.getSeconds();H()[b+4>>>2>>>0]=a.getMinutes();H()[b+8>>>2>>>0]=a.getHours();H()[b+12>>>2>>>0]=a.getDate();H()[b+16>>>2>>>0]=a.getMonth();H()[b+20>>>2>>>0]=a.getFullYear()-1900;H()[b+24>>>2>>>0]=a.getDay();var c=(ee(a.getFullYear())?fe:ge)[a.getMonth()]+a.getDate()-1|0;H()[b+28>>>2>>>0]=c;H()[b+36>>>2>>>0]=-(60*a.getTimezoneOffset());c=(new Date(a.getFullYear(),6,1)).getTimezoneOffset();
|
| 89 |
-
var d=(new Date(a.getFullYear(),0,1)).getTimezoneOffset();a=(c!=d&&a.getTimezoneOffset()==Math.min(d,c))|0;H()[b+32>>>2>>>0]=a}
|
| 90 |
-
function cc(a){a>>>=0;var b=new Date(H()[a+20>>>2>>>0]+1900,H()[a+16>>>2>>>0],H()[a+12>>>2>>>0],H()[a+8>>>2>>>0],H()[a+4>>>2>>>0],H()[a>>>2>>>0],0),c=H()[a+32>>>2>>>0],d=b.getTimezoneOffset(),f=(new Date(b.getFullYear(),6,1)).getTimezoneOffset(),g=(new Date(b.getFullYear(),0,1)).getTimezoneOffset(),h=Math.min(g,f);0>c?H()[a+32>>>2>>>0]=Number(f!=g&&h==d):0<c!=(h==d)&&(f=Math.max(g,f),b.setTime(b.getTime()+6E4*((0<c?h:f)-d)));H()[a+24>>>2>>>0]=b.getDay();c=(ee(b.getFullYear())?fe:ge)[b.getMonth()]+
|
| 91 |
-
b.getDate()-1|0;H()[a+28>>>2>>>0]=c;H()[a>>>2>>>0]=b.getSeconds();H()[a+4>>>2>>>0]=b.getMinutes();H()[a+8>>>2>>>0]=b.getHours();H()[a+12>>>2>>>0]=b.getDate();H()[a+16>>>2>>>0]=b.getMonth();H()[a+20>>>2>>>0]=b.getYear();a=b.getTime();return BigInt(isNaN(a)?-1:a/1E3)}function dc(a,b,c,d,f,g,h){return q?Q(16,1,a,b,c,d,f,g,h):-52}function ec(a,b,c,d,f,g){if(q)return Q(17,1,a,b,c,d,f,g)}var he={},pc=()=>performance.timeOrigin+performance.now();
|
| 92 |
-
function fc(a,b){if(q)return Q(18,1,a,b);he[a]&&(clearTimeout(he[a].id),delete he[a]);if(!b)return 0;var c=setTimeout(()=>{delete he[a];zd(()=>ie(a,performance.timeOrigin+performance.now()))},b);he[a]={id:c,qc:b};return 0}
|
| 93 |
-
function gc(a,b,c,d){a>>>=0;b>>>=0;c>>>=0;d>>>=0;var f=(new Date).getFullYear(),g=(new Date(f,0,1)).getTimezoneOffset();f=(new Date(f,6,1)).getTimezoneOffset();var h=Math.max(g,f);I()[a>>>2>>>0]=60*h;H()[b>>>2>>>0]=Number(g!=f);b=l=>{var m=Math.abs(l);return`UTC${0<=l?"-":"+"}${String(Math.floor(m/60)).padStart(2,"0")}${String(m%60).padStart(2,"0")}`};a=b(g);b=b(f);f<g?(fd(a,c,17),fd(b,d,17)):(fd(a,d,17),fd(b,c,17))}var lc=()=>Date.now(),je=1;
|
| 94 |
-
function hc(a,b,c){if(!(0<=a&&3>=a))return 28;if(0===a)a=Date.now();else if(je)a=performance.timeOrigin+performance.now();else return 52;C[c>>>0>>>3]=BigInt(Math.round(1E6*a));return 0}var ke=[],le=(a,b)=>{ke.length=0;for(var c;c=F()[a++>>>0];){var d=105!=c;d&=112!=c;b+=d&&b%8?4:0;ke.push(112==c?I()[b>>>2>>>0]:106==c?C[b>>>3]:105==c?H()[b>>>2>>>0]:J()[b>>>3>>>0]);b+=d?8:4}return ke};function ic(a,b,c){a>>>=0;b=le(b>>>0,c>>>0);return Dc[a](...b)}
|
| 95 |
-
function jc(a,b,c){a>>>=0;b=le(b>>>0,c>>>0);return Dc[a](...b)}var kc=()=>{};function mc(a,b){return x(M(a>>>0,b>>>0))}var nc=()=>{P+=1;throw"unwind";};function oc(){return 4294901760}var qc=()=>n?require("os").cpus().length:navigator.hardwareConcurrency;function rc(){L("Cannot use emscripten_pc_get_function without -sUSE_OFFSET_CONVERTER");return 0}
|
| 96 |
-
function sc(a){a>>>=0;var b=F().length;if(a<=b||4294901760<a)return!1;for(var c=1;4>=c;c*=2){var d=b*(1+.2/c);d=Math.min(d,a+100663296);a:{d=(Math.min(4294901760,65536*Math.ceil(Math.max(a,d)/65536))-z.buffer.byteLength+65535)/65536|0;try{z.grow(d);E();var f=1;break a}catch(g){}f=void 0}if(f)return!0}return!1}var me=()=>{L("Cannot use convertFrameToPC (needed by __builtin_return_address) without -sUSE_OFFSET_CONVERTER");return 0},ne={},oe=a=>{a.forEach(b=>{var c=me();c&&(ne[c]=b)})};
|
| 97 |
-
function tc(){var a=Error().stack.toString().split("\n");"Error"==a[0]&&a.shift();oe(a);ne.Lb=me();ne.cc=a;return ne.Lb}function uc(a,b,c){a>>>=0;b>>>=0;if(ne.Lb==a)var d=ne.cc;else d=Error().stack.toString().split("\n"),"Error"==d[0]&&d.shift(),oe(d);for(var f=3;d[f]&&me()!=a;)++f;for(a=0;a<c&&d[a+f];++a)H()[b+4*a>>>2>>>0]=me();return a}
|
| 98 |
-
var pe={},re=()=>{if(!qe){var a={USER:"web_user",LOGNAME:"web_user",PATH:"/",PWD:"/",HOME:"/home/web_user",LANG:("object"==typeof navigator&&navigator.languages&&navigator.languages[0]||"C").replace("-","_")+".UTF-8",_:la||"./this.program"},b;for(b in pe)void 0===pe[b]?delete a[b]:a[b]=pe[b];var c=[];for(b in a)c.push(`${b}=${a[b]}`);qe=c}return qe},qe;
|
| 99 |
-
function vc(a,b){if(q)return Q(19,1,a,b);a>>>=0;b>>>=0;var c=0;re().forEach((d,f)=>{var g=b+c;f=I()[a+4*f>>>2>>>0]=g;for(g=0;g<d.length;++g)D()[f++>>>0]=d.charCodeAt(g);D()[f>>>0]=0;c+=d.length+1});return 0}function wc(a,b){if(q)return Q(20,1,a,b);a>>>=0;b>>>=0;var c=re();I()[a>>>2>>>0]=c.length;var d=0;c.forEach(f=>d+=f.length+1);I()[b>>>2>>>0]=d;return 0}function yc(a){return q?Q(21,1,a):52}function zc(a,b,c,d){return q?Q(22,1,a,b,c,d):52}function Ac(a,b,c,d){return q?Q(23,1,a,b,c,d):70}
|
| 100 |
-
var se=[null,[],[]];function Bc(a,b,c,d){if(q)return Q(24,1,a,b,c,d);b>>>=0;c>>>=0;d>>>=0;for(var f=0,g=0;g<c;g++){var h=I()[b>>>2>>>0],l=I()[b+4>>>2>>>0];b+=8;for(var m=0;m<l;m++){var p=F()[h+m>>>0],r=se[a];0===p||10===p?((1===a?ta:x)(dd(r)),r.length=0):r.push(p)}f+=l}I()[d>>>2>>>0]=f;return 0}q||Sc();for(var te=Array(256),ue=0;256>ue;++ue)te[ue]=String.fromCharCode(ue);gd=te;S=e.BindingError=class extends Error{constructor(a){super(a);this.name="BindingError"}};
|
| 101 |
-
e.InternalError=class extends Error{constructor(a){super(a);this.name="InternalError"}};V.push(0,1,void 0,1,null,1,!0,1,!1,1);e.count_emval_handles=()=>V.length/2-5-nd.length;var Cd=[Cc,Qc,bd,gb,hb,ib,jb,kb,lb,mb,nb,ob,pb,qb,rb,sb,dc,ec,fc,vc,wc,yc,zc,Ac,Bc],bb,K;
|
| 102 |
-
(async function(){function a(d,f){K=d.exports;K=Hd();K=ve();Rc.push(K.ib);va=f;Wa();return K}Ua++;var b=ab();if(e.instantiateWasm)return new Promise(d=>{e.instantiateWasm(b,(f,g)=>{a(f,g);d(f.exports)})});if(q)return new Promise(d=>{Ha=f=>{var g=new WebAssembly.Instance(f,ab());d(a(g,f))}});Xa??=e.locateFile?e.locateFile?e.locateFile("ort-wasm-simd-threaded.jsep.wasm",v):v+"ort-wasm-simd-threaded.jsep.wasm":(new URL("ort-wasm-simd-threaded.jsep.wasm",import.meta.url)).href;try{var c=await $a(b);
|
| 103 |
-
return a(c.instance,c.module)}catch(d){return ca(d),Promise.reject(d)}})();var Dd=a=>(Dd=K.Da)(a),Pa=()=>(Pa=K.Ea)();e._OrtInit=(a,b)=>(e._OrtInit=K.Fa)(a,b);e._OrtGetLastError=(a,b)=>(e._OrtGetLastError=K.Ga)(a,b);e._OrtCreateSessionOptions=(a,b,c,d,f,g,h,l,m,p)=>(e._OrtCreateSessionOptions=K.Ha)(a,b,c,d,f,g,h,l,m,p);e._OrtAppendExecutionProvider=(a,b,c,d,f)=>(e._OrtAppendExecutionProvider=K.Ia)(a,b,c,d,f);e._OrtAddFreeDimensionOverride=(a,b,c)=>(e._OrtAddFreeDimensionOverride=K.Ja)(a,b,c);
|
| 104 |
-
e._OrtAddSessionConfigEntry=(a,b,c)=>(e._OrtAddSessionConfigEntry=K.Ka)(a,b,c);e._OrtReleaseSessionOptions=a=>(e._OrtReleaseSessionOptions=K.La)(a);e._OrtCreateSession=(a,b,c)=>(e._OrtCreateSession=K.Ma)(a,b,c);e._OrtReleaseSession=a=>(e._OrtReleaseSession=K.Na)(a);e._OrtGetInputOutputCount=(a,b,c)=>(e._OrtGetInputOutputCount=K.Oa)(a,b,c);e._OrtGetInputOutputMetadata=(a,b,c,d)=>(e._OrtGetInputOutputMetadata=K.Pa)(a,b,c,d);e._OrtFree=a=>(e._OrtFree=K.Qa)(a);
|
| 105 |
-
e._OrtCreateTensor=(a,b,c,d,f,g)=>(e._OrtCreateTensor=K.Ra)(a,b,c,d,f,g);e._OrtGetTensorData=(a,b,c,d,f)=>(e._OrtGetTensorData=K.Sa)(a,b,c,d,f);e._OrtReleaseTensor=a=>(e._OrtReleaseTensor=K.Ta)(a);e._OrtCreateRunOptions=(a,b,c,d)=>(e._OrtCreateRunOptions=K.Ua)(a,b,c,d);e._OrtAddRunConfigEntry=(a,b,c)=>(e._OrtAddRunConfigEntry=K.Va)(a,b,c);e._OrtReleaseRunOptions=a=>(e._OrtReleaseRunOptions=K.Wa)(a);e._OrtCreateBinding=a=>(e._OrtCreateBinding=K.Xa)(a);
|
| 106 |
-
e._OrtBindInput=(a,b,c)=>(e._OrtBindInput=K.Ya)(a,b,c);e._OrtBindOutput=(a,b,c,d)=>(e._OrtBindOutput=K.Za)(a,b,c,d);e._OrtClearBoundOutputs=a=>(e._OrtClearBoundOutputs=K._a)(a);e._OrtReleaseBinding=a=>(e._OrtReleaseBinding=K.$a)(a);e._OrtRunWithBinding=(a,b,c,d,f)=>(e._OrtRunWithBinding=K.ab)(a,b,c,d,f);e._OrtRun=(a,b,c,d,f,g,h,l)=>(e._OrtRun=K.bb)(a,b,c,d,f,g,h,l);e._OrtEndProfiling=a=>(e._OrtEndProfiling=K.cb)(a);e._JsepOutput=(a,b,c)=>(e._JsepOutput=K.db)(a,b,c);
|
| 107 |
-
e._JsepGetNodeName=a=>(e._JsepGetNodeName=K.eb)(a);
|
| 108 |
-
var Ka=()=>(Ka=K.fb)(),Y=e._free=a=>(Y=e._free=K.gb)(a),rd=e._malloc=a=>(rd=e._malloc=K.hb)(a),Ma=(a,b,c,d,f,g)=>(Ma=K.kb)(a,b,c,d,f,g),Sa=()=>(Sa=K.lb)(),Oc=(a,b,c,d,f)=>(Oc=K.mb)(a,b,c,d,f),Uc=a=>(Uc=K.nb)(a),Yc=a=>(Yc=K.ob)(a),ie=(a,b)=>(ie=K.pb)(a,b),Ad=()=>(Ad=K.qb)(),Wc=(a,b)=>(Wc=K.rb)(a,b),Pc=a=>(Pc=K.sb)(a),Nc=a=>(Nc=K.tb)(a),Mc=()=>(Mc=K.ub)(),Xc=e.dynCall_ii=(a,b)=>(Xc=e.dynCall_ii=K.vb)(a,b),Ud=a=>(Ud=K.wb)(a),Jd=()=>(Jd=K.xb)(),Td=a=>(Td=K.yb)(a),Wd=()=>(Wd=K.zb)();
|
| 109 |
-
function ve(){var a=K;a=Object.assign({},a);var b=d=>f=>d(f)>>>0,c=d=>()=>d()>>>0;a.Da=b(a.Da);a.fb=c(a.fb);a.hb=b(a.hb);a.tb=b(a.tb);a.ub=c(a.ub);a.__cxa_get_exception_ptr=b(a.__cxa_get_exception_ptr);return a}e.stackSave=()=>Mc();e.stackRestore=a=>Pc(a);e.stackAlloc=a=>Nc(a);
|
| 110 |
-
e.setValue=function(a,b,c="i8"){c.endsWith("*")&&(c="*");switch(c){case "i1":D()[a>>>0]=b;break;case "i8":D()[a>>>0]=b;break;case "i16":G()[a>>>1>>>0]=b;break;case "i32":H()[a>>>2>>>0]=b;break;case "i64":C[a>>>3]=BigInt(b);break;case "float":Ga()[a>>>2>>>0]=b;break;case "double":J()[a>>>3>>>0]=b;break;case "*":I()[a>>>2>>>0]=b;break;default:L(`invalid type for setValue: ${c}`)}};
|
| 111 |
-
e.getValue=function(a,b="i8"){b.endsWith("*")&&(b="*");switch(b){case "i1":return D()[a>>>0];case "i8":return D()[a>>>0];case "i16":return G()[a>>>1>>>0];case "i32":return H()[a>>>2>>>0];case "i64":return C[a>>>3];case "float":return Ga()[a>>>2>>>0];case "double":return J()[a>>>3>>>0];case "*":return I()[a>>>2>>>0];default:L(`invalid type for getValue: ${b}`)}};e.UTF8ToString=M;e.stringToUTF8=fd;e.lengthBytesUTF8=ed;
|
| 112 |
-
function we(){if(0<Ua)Va=we;else if(q)aa(e),Ta();else{for(;0<Hc.length;)Hc.shift()(e);0<Ua?Va=we:(e.calledRun=!0,A||(Ta(),aa(e)))}}we();e.PTR_SIZE=4;moduleRtn=da;
|
| 113 |
-
|
| 114 |
-
|
| 115 |
-
return moduleRtn;
|
| 116 |
-
}
|
| 117 |
-
);
|
| 118 |
-
})();
|
| 119 |
-
export default ortWasmThreaded;
|
| 120 |
-
var isPthread = globalThis.self?.name?.startsWith('em-pthread');
|
| 121 |
-
var isNode = typeof globalThis.process?.versions?.node == 'string';
|
| 122 |
-
if (isNode) isPthread = (await import('worker_threads')).workerData === 'em-pthread';
|
| 123 |
-
|
| 124 |
-
// When running as a pthread, construct a new instance on startup
|
| 125 |
-
isPthread && ortWasmThreaded();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
_shared/voice/vendor/kokoro/transformers/ort-wasm-simd-threaded.jsep.wasm
DELETED
|
@@ -1,3 +0,0 @@
|
|
| 1 |
-
version https://git-lfs.github.com/spec/v1
|
| 2 |
-
oid sha256:c46655e8a94afc45338d4cb2b840475f88e5012d524509916e505079c00bfa39
|
| 3 |
-
size 21596019
|
|
|
|
|
|
|
|
|
|
|
|
_shared/voice/vendor/kokoro/transformers/transformers.js
DELETED
|
The diff for this file is too large to render.
See raw diff
|
|
|
_shared/voice/vendor/models/onnx-community/Kokoro-82M-v1.0-ONNX/config.json
DELETED
|
@@ -1,3 +0,0 @@
|
|
| 1 |
-
{
|
| 2 |
-
"model_type": "style_text_to_speech_2"
|
| 3 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
_shared/voice/vendor/models/onnx-community/Kokoro-82M-v1.0-ONNX/tokenizer.json
DELETED
|
@@ -1,175 +0,0 @@
|
|
| 1 |
-
{
|
| 2 |
-
"version": "1.0",
|
| 3 |
-
"truncation": null,
|
| 4 |
-
"padding": null,
|
| 5 |
-
"added_tokens": [],
|
| 6 |
-
"normalizer": {
|
| 7 |
-
"type": "Replace",
|
| 8 |
-
"pattern": {
|
| 9 |
-
"Regex": "[^$;:,.!?\u2014\u2026\"()\u201c\u201d \u0303\u02a3\u02a5\u02a6\u02a8\u1d5d\uab67AIOQSTWY\u1d4aabcdefhijklmnopqrstuvwxyz\u0251\u0250\u0252\u00e6\u03b2\u0254\u0255\u00e7\u0256\u00f0\u02a4\u0259\u025a\u025b\u025c\u025f\u0261\u0265\u0268\u026a\u029d\u026f\u0270\u014b\u0273\u0272\u0274\u00f8\u0278\u03b8\u0153\u0279\u027e\u027b\u0281\u027d\u0282\u0283\u0288\u02a7\u028a\u028b\u028c\u0263\u0264\u03c7\u028e\u0292\u0294\u02c8\u02cc\u02d0\u02b0\u02b2\u2193\u2192\u2197\u2198\u1d7b]"
|
| 10 |
-
},
|
| 11 |
-
"content": ""
|
| 12 |
-
},
|
| 13 |
-
"pre_tokenizer": {
|
| 14 |
-
"type": "Split",
|
| 15 |
-
"pattern": {
|
| 16 |
-
"Regex": ""
|
| 17 |
-
},
|
| 18 |
-
"behavior": "Isolated",
|
| 19 |
-
"invert": false
|
| 20 |
-
},
|
| 21 |
-
"post_processor": {
|
| 22 |
-
"type": "TemplateProcessing",
|
| 23 |
-
"single": [
|
| 24 |
-
{
|
| 25 |
-
"SpecialToken": {
|
| 26 |
-
"id": "$",
|
| 27 |
-
"type_id": 0
|
| 28 |
-
}
|
| 29 |
-
},
|
| 30 |
-
{
|
| 31 |
-
"Sequence": {
|
| 32 |
-
"id": "A",
|
| 33 |
-
"type_id": 0
|
| 34 |
-
}
|
| 35 |
-
},
|
| 36 |
-
{
|
| 37 |
-
"SpecialToken": {
|
| 38 |
-
"id": "$",
|
| 39 |
-
"type_id": 0
|
| 40 |
-
}
|
| 41 |
-
}
|
| 42 |
-
],
|
| 43 |
-
"special_tokens": {
|
| 44 |
-
"$": {
|
| 45 |
-
"id": "$",
|
| 46 |
-
"ids": [
|
| 47 |
-
0
|
| 48 |
-
],
|
| 49 |
-
"tokens": [
|
| 50 |
-
"$"
|
| 51 |
-
]
|
| 52 |
-
}
|
| 53 |
-
}
|
| 54 |
-
},
|
| 55 |
-
"decoder": null,
|
| 56 |
-
"model": {
|
| 57 |
-
"vocab": {
|
| 58 |
-
"$": 0,
|
| 59 |
-
";": 1,
|
| 60 |
-
":": 2,
|
| 61 |
-
",": 3,
|
| 62 |
-
".": 4,
|
| 63 |
-
"!": 5,
|
| 64 |
-
"?": 6,
|
| 65 |
-
"\u2014": 9,
|
| 66 |
-
"\u2026": 10,
|
| 67 |
-
"\"": 11,
|
| 68 |
-
"(": 12,
|
| 69 |
-
")": 13,
|
| 70 |
-
"\u201c": 14,
|
| 71 |
-
"\u201d": 15,
|
| 72 |
-
" ": 16,
|
| 73 |
-
"\u0303": 17,
|
| 74 |
-
"\u02a3": 18,
|
| 75 |
-
"\u02a5": 19,
|
| 76 |
-
"\u02a6": 20,
|
| 77 |
-
"\u02a8": 21,
|
| 78 |
-
"\u1d5d": 22,
|
| 79 |
-
"\uab67": 23,
|
| 80 |
-
"A": 24,
|
| 81 |
-
"I": 25,
|
| 82 |
-
"O": 31,
|
| 83 |
-
"Q": 33,
|
| 84 |
-
"S": 35,
|
| 85 |
-
"T": 36,
|
| 86 |
-
"W": 39,
|
| 87 |
-
"Y": 41,
|
| 88 |
-
"\u1d4a": 42,
|
| 89 |
-
"a": 43,
|
| 90 |
-
"b": 44,
|
| 91 |
-
"c": 45,
|
| 92 |
-
"d": 46,
|
| 93 |
-
"e": 47,
|
| 94 |
-
"f": 48,
|
| 95 |
-
"h": 50,
|
| 96 |
-
"i": 51,
|
| 97 |
-
"j": 52,
|
| 98 |
-
"k": 53,
|
| 99 |
-
"l": 54,
|
| 100 |
-
"m": 55,
|
| 101 |
-
"n": 56,
|
| 102 |
-
"o": 57,
|
| 103 |
-
"p": 58,
|
| 104 |
-
"q": 59,
|
| 105 |
-
"r": 60,
|
| 106 |
-
"s": 61,
|
| 107 |
-
"t": 62,
|
| 108 |
-
"u": 63,
|
| 109 |
-
"v": 64,
|
| 110 |
-
"w": 65,
|
| 111 |
-
"x": 66,
|
| 112 |
-
"y": 67,
|
| 113 |
-
"z": 68,
|
| 114 |
-
"\u0251": 69,
|
| 115 |
-
"\u0250": 70,
|
| 116 |
-
"\u0252": 71,
|
| 117 |
-
"\u00e6": 72,
|
| 118 |
-
"\u03b2": 75,
|
| 119 |
-
"\u0254": 76,
|
| 120 |
-
"\u0255": 77,
|
| 121 |
-
"\u00e7": 78,
|
| 122 |
-
"\u0256": 80,
|
| 123 |
-
"\u00f0": 81,
|
| 124 |
-
"\u02a4": 82,
|
| 125 |
-
"\u0259": 83,
|
| 126 |
-
"\u025a": 85,
|
| 127 |
-
"\u025b": 86,
|
| 128 |
-
"\u025c": 87,
|
| 129 |
-
"\u025f": 90,
|
| 130 |
-
"\u0261": 92,
|
| 131 |
-
"\u0265": 99,
|
| 132 |
-
"\u0268": 101,
|
| 133 |
-
"\u026a": 102,
|
| 134 |
-
"\u029d": 103,
|
| 135 |
-
"\u026f": 110,
|
| 136 |
-
"\u0270": 111,
|
| 137 |
-
"\u014b": 112,
|
| 138 |
-
"\u0273": 113,
|
| 139 |
-
"\u0272": 114,
|
| 140 |
-
"\u0274": 115,
|
| 141 |
-
"\u00f8": 116,
|
| 142 |
-
"\u0278": 118,
|
| 143 |
-
"\u03b8": 119,
|
| 144 |
-
"\u0153": 120,
|
| 145 |
-
"\u0279": 123,
|
| 146 |
-
"\u027e": 125,
|
| 147 |
-
"\u027b": 126,
|
| 148 |
-
"\u0281": 128,
|
| 149 |
-
"\u027d": 129,
|
| 150 |
-
"\u0282": 130,
|
| 151 |
-
"\u0283": 131,
|
| 152 |
-
"\u0288": 132,
|
| 153 |
-
"\u02a7": 133,
|
| 154 |
-
"\u028a": 135,
|
| 155 |
-
"\u028b": 136,
|
| 156 |
-
"\u028c": 138,
|
| 157 |
-
"\u0263": 139,
|
| 158 |
-
"\u0264": 140,
|
| 159 |
-
"\u03c7": 142,
|
| 160 |
-
"\u028e": 143,
|
| 161 |
-
"\u0292": 147,
|
| 162 |
-
"\u0294": 148,
|
| 163 |
-
"\u02c8": 156,
|
| 164 |
-
"\u02cc": 157,
|
| 165 |
-
"\u02d0": 158,
|
| 166 |
-
"\u02b0": 162,
|
| 167 |
-
"\u02b2": 164,
|
| 168 |
-
"\u2193": 169,
|
| 169 |
-
"\u2192": 171,
|
| 170 |
-
"\u2197": 172,
|
| 171 |
-
"\u2198": 173,
|
| 172 |
-
"\u1d7b": 177
|
| 173 |
-
}
|
| 174 |
-
}
|
| 175 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
_shared/voice/vendor/models/onnx-community/Kokoro-82M-v1.0-ONNX/tokenizer_config.json
DELETED
|
@@ -1,6 +0,0 @@
|
|
| 1 |
-
{
|
| 2 |
-
"model_max_length": 512,
|
| 3 |
-
"pad_token": "$",
|
| 4 |
-
"tokenizer_class": "PreTrainedTokenizer",
|
| 5 |
-
"unk_token": "$"
|
| 6 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
_shared/voice/vendor/models/onnx-community/Kokoro-82M-v1.0-ONNX/voices/af_bella.bin
DELETED
|
@@ -1,3 +0,0 @@
|
|
| 1 |
-
version https://git-lfs.github.com/spec/v1
|
| 2 |
-
oid sha256:f69d836209b78eb8c66e75e3cda491e26ea838a3674257e9d4e5703cbaf55c8b
|
| 3 |
-
size 522240
|
|
|
|
|
|
|
|
|
|
|
|
_shared/voice/vendor/models/onnx-community/Kokoro-82M-v1.0-ONNX/voices/af_heart.bin
DELETED
|
@@ -1,3 +0,0 @@
|
|
| 1 |
-
version https://git-lfs.github.com/spec/v1
|
| 2 |
-
oid sha256:d583ccff3cdca2f7fae535cb998ac07e9fcb90f09737b9a41fa2734ec44a8f0b
|
| 3 |
-
size 522240
|
|
|
|
|
|
|
|
|
|
|
|
_shared/voice/vendor/models/onnx-community/Kokoro-82M-v1.0-ONNX/voices/af_nicole.bin
DELETED
|
@@ -1,3 +0,0 @@
|
|
| 1 |
-
version https://git-lfs.github.com/spec/v1
|
| 2 |
-
oid sha256:cd2191ab31b914ed7b318416b0e4440fdf392ddad9106a060819aa600a64f59a
|
| 3 |
-
size 522240
|
|
|
|
|
|
|
|
|
|
|
|
_shared/voice/vendor/models/onnx-community/Kokoro-82M-v1.0-ONNX/voices/am_fenrir.bin
DELETED
|
@@ -1,3 +0,0 @@
|
|
| 1 |
-
version https://git-lfs.github.com/spec/v1
|
| 2 |
-
oid sha256:c27989f741f7ee34d273a39d8a595cc0837d35f5ced9a29b7cc162614616df43
|
| 3 |
-
size 522240
|
|
|
|
|
|
|
|
|
|
|
|
_shared/voice/vendor/models/onnx-community/Kokoro-82M-v1.0-ONNX/voices/am_michael.bin
DELETED
|
@@ -1,3 +0,0 @@
|
|
| 1 |
-
version https://git-lfs.github.com/spec/v1
|
| 2 |
-
oid sha256:1d1f21dd8da39c30705cd4c75d039d265e9bc4a2a93ed09bc9e1b1225eb95ba1
|
| 3 |
-
size 522240
|
|
|
|
|
|
|
|
|
|
|
|
_shared/voice/vendor/models/onnx-community/Kokoro-82M-v1.0-ONNX/voices/am_puck.bin
DELETED
|
@@ -1,3 +0,0 @@
|
|
| 1 |
-
version https://git-lfs.github.com/spec/v1
|
| 2 |
-
oid sha256:fcf73c989033e9233e0b98713eca600c8c74dcc1614b37009d5450ff4a2274a0
|
| 3 |
-
size 522240
|
|
|
|
|
|
|
|
|
|
|
|
_shared/voice/vendor/models/onnx-community/Kokoro-82M-v1.0-ONNX/voices/bf_emma.bin
DELETED
|
@@ -1,3 +0,0 @@
|
|
| 1 |
-
version https://git-lfs.github.com/spec/v1
|
| 2 |
-
oid sha256:669fe0647f9dd04fcab92f1439a40eeb4c8b4ab1f82e4996fe3d918ce4a63b73
|
| 3 |
-
size 522240
|
|
|
|
|
|
|
|
|
|
|
|
_shared/voice/vendor/models/onnx-community/Kokoro-82M-v1.0-ONNX/voices/bm_george.bin
DELETED
|
@@ -1,3 +0,0 @@
|
|
| 1 |
-
version https://git-lfs.github.com/spec/v1
|
| 2 |
-
oid sha256:c4b235a4c1f2cd3b939fed08b899ce9385638b763f7b73a59616c4fc9bd6c9bc
|
| 3 |
-
size 522240
|
|
|
|
|
|
|
|
|
|
|
|
_shared/voice/vendor/models/onnx-community/silero-vad/onnx/model.onnx
DELETED
|
@@ -1,3 +0,0 @@
|
|
| 1 |
-
version https://git-lfs.github.com/spec/v1
|
| 2 |
-
oid sha256:a4a068cd6cf1ea8355b84327595838ca748ec29a25bc91fc82e6c299ccdc5808
|
| 3 |
-
size 2243022
|
|
|
|
|
|
|
|
|
|
|
|
_shared/voice/vendor/models/onnx-community/silero-vad/onnx/model_quantized.onnx
DELETED
|
@@ -1,3 +0,0 @@
|
|
| 1 |
-
version https://git-lfs.github.com/spec/v1
|
| 2 |
-
oid sha256:982c96dc518784fb9d19bc7f56cc8252473b020e4f2099f32049e4ad0b3b43e7
|
| 3 |
-
size 639335
|
|
|
|
|
|
|
|
|
|
|
|
_shared/voice/vendor/transformers/ort-wasm-simd-threaded.jsep.wasm
DELETED
|
@@ -1,3 +0,0 @@
|
|
| 1 |
-
version https://git-lfs.github.com/spec/v1
|
| 2 |
-
oid sha256:0f6fe5c40378504d1a25a77f766133464bb15705af23e01c994f185719fb080e
|
| 3 |
-
size 21643825
|
|
|
|
|
|
|
|
|
|
|
|
_shared/voice/vendor/transformers/transformers.js
DELETED
|
The diff for this file is too large to render.
See raw diff
|
|
|
_shared/voice/vendor/transformers/transformers.mjs
DELETED
|
The diff for this file is too large to render.
See raw diff
|
|
|
blake3-gpu-parallel.html
DELETED
|
@@ -1,145 +0,0 @@
|
|
| 1 |
-
<!doctype html><html><head><meta charset=utf8><meta name=viewport content="width=device-width,initial-scale=1">
|
| 2 |
-
<title>BLAKE3 — parallel, on your GPU</title>
|
| 3 |
-
<style>
|
| 4 |
-
:root{--bg:#0a0d13;--panel:#111825;--ink:#e8ebf1;--dim:#8b95a7;--ac:#7c5cff;--ok:#48c26c;--no:#f0616d;--line:#1e2836}
|
| 5 |
-
*{box-sizing:border-box}body{margin:0;background:var(--bg);color:var(--ink);font:15px/1.55 -apple-system,Segoe UI,Roboto,monospace;padding:24px;max-width:820px;margin:0 auto}
|
| 6 |
-
h1{font-size:20px;margin:0 0 4px}.sub{color:var(--dim);font-size:13px;margin:0 0 18px}
|
| 7 |
-
#status,#speed{font-family:ui-monospace,monospace;padding:12px 14px;border:1px solid var(--line);border-radius:10px;background:var(--panel);margin-bottom:12px}
|
| 8 |
-
#speed{font-size:16px}.big{font-size:17px;font-weight:600;margin:14px 0 6px}
|
| 9 |
-
table{border-collapse:collapse;width:100%;font-family:ui-monospace,monospace;font-size:12.5px}
|
| 10 |
-
th,td{text-align:left;padding:6px 10px;border-bottom:1px solid var(--line)}
|
| 11 |
-
th{color:var(--dim);text-transform:uppercase;font-size:10.5px;letter-spacing:.08em}
|
| 12 |
-
td.n{text-align:right;font-variant-numeric:tabular-nums}.ok{color:var(--ok)}.no{color:var(--no)}.hx{color:var(--dim)}
|
| 13 |
-
</style></head><body>
|
| 14 |
-
<h1>BLAKE3 — <span style="color:var(--ac)">parallel</span>, on your GPU</h1>
|
| 15 |
-
<p class="sub">Phase 1: one GPU thread per 1024-byte chunk (parallel). Phase 2: stack tree-merge → root. Correctness vs the CPU oracle, plus throughput on a big block (the size of the 145 MB embed).</p>
|
| 16 |
-
<div id="status">initializing…</div>
|
| 17 |
-
<div id="speed"></div>
|
| 18 |
-
<div id="result"></div>
|
| 19 |
-
<table id="tbl"><thead><tr><th>bytes</th><th>match</th></tr></thead><tbody></tbody></table>
|
| 20 |
-
|
| 21 |
-
<script type="module">
|
| 22 |
-
// oracle (holo-blake3.mjs, inlined)
|
| 23 |
-
const IV=[0x6a09e667,0xbb67ae85,0x3c6ef372,0xa54ff53a,0x510e527f,0x9b05688c,0x1f83d9ab,0x5be0cd19];
|
| 24 |
-
const MSG=[2,6,3,10,7,0,4,13,1,11,12,5,9,14,15,8];const CS=1,CE=2,PA=4,RT=8,BL=64,CH=1024;
|
| 25 |
-
const rr=(x,n)=>((x>>>n)|(x<<(32-n)))>>>0;
|
| 26 |
-
function g(v,a,b,c,d,mx,my){v[a]=(v[a]+v[b]+mx)>>>0;v[d]=rr(v[d]^v[a],16);v[c]=(v[c]+v[d])>>>0;v[b]=rr(v[b]^v[c],12);v[a]=(v[a]+v[b]+my)>>>0;v[d]=rr(v[d]^v[a],8);v[c]=(v[c]+v[d])>>>0;v[b]=rr(v[b]^v[c],7);}
|
| 27 |
-
function rnd(v,m){g(v,0,4,8,12,m[0],m[1]);g(v,1,5,9,13,m[2],m[3]);g(v,2,6,10,14,m[4],m[5]);g(v,3,7,11,15,m[6],m[7]);g(v,0,5,10,15,m[8],m[9]);g(v,1,6,11,12,m[10],m[11]);g(v,2,7,8,13,m[12],m[13]);g(v,3,4,9,14,m[14],m[15]);}
|
| 28 |
-
function cmp(cv,m0,ct,bl,fl){const cl=ct>>>0,ch=Math.floor(ct/4294967296)>>>0;const v=[cv[0],cv[1],cv[2],cv[3],cv[4],cv[5],cv[6],cv[7],IV[0],IV[1],IV[2],IV[3],cl,ch,bl>>>0,fl>>>0];let m=m0.slice();for(let r=0;r<7;r++){rnd(v,m);if(r<6){const p=new Array(16);for(let i=0;i<16;i++)p[i]=m[MSG[i]];m=p;}}const o=new Array(16);for(let i=0;i<8;i++){o[i]=(v[i]^v[i+8])>>>0;o[i+8]=(v[i+8]^cv[i])>>>0;}return o;}
|
| 29 |
-
function wd(b,o,l){const m=new Array(16).fill(0);for(let i=0;i<l;i++)m[i>>2]|=b[o+i]<<((i&3)*8);for(let i=0;i<16;i++)m[i]>>>=0;return m;}
|
| 30 |
-
function ncv(o){return cmp(o.cv,o.m,o.counter,o.blockLen,o.flags).slice(0,8);}
|
| 31 |
-
function nrb(o){const out=cmp(o.cv,o.m,0,o.blockLen,o.flags|RT);const b=new Uint8Array(32);for(let i=0;i<8;i++){const w=out[i];b[i*4]=w&255;b[i*4+1]=(w>>>8)&255;b[i*4+2]=(w>>>16)&255;b[i*4+3]=(w>>>24)&255;}return b;}
|
| 32 |
-
function cn(b,s,l,ct){let cv=IV.slice();const nB=Math.max(1,Math.ceil(l/BL));let fl=CS;for(let i=0;i<nB-1;i++){cv=cmp(cv,wd(b,s+i*BL,BL),ct,BL,fl).slice(0,8);fl=0;}const lo=s+(nB-1)*BL,ll=l-(nB-1)*BL;return{cv,m:wd(b,lo,ll),counter:ct,blockLen:ll,flags:fl|CE};}
|
| 33 |
-
function pn(l,r){return{cv:IV.slice(),m:l.concat(r),counter:0,blockLen:BL,flags:PA};}
|
| 34 |
-
function st(b,s,l,ct){if(l<=CH)return cn(b,s,l,ct);let lf=CH;while(lf*2<l)lf*=2;const lc=ncv(st(b,s,lf,ct)),rc=ncv(st(b,s+lf,l-lf,ct+lf/CH));return pn(lc,rc);}
|
| 35 |
-
function b3hex(b){const d=nrb(st(b,0,b.length,0));let s='';for(let i=0;i<32;i++)s+=d[i].toString(16).padStart(2,'0');return s;}
|
| 36 |
-
|
| 37 |
-
// ── parallel WGSL: reuses the GPU-proven compress/chunkCV/chunkRoot/parentCV; adds two kernels ──
|
| 38 |
-
const WGSL=`
|
| 39 |
-
@group(0) @binding(0) var<storage, read> inp : array<u32>;
|
| 40 |
-
@group(0) @binding(1) var<storage, read_write> cvs : array<u32>; // phase1 out / phase2 in: 8 u32 per chunk
|
| 41 |
-
@group(0) @binding(2) var<storage, read_write> outp : array<u32>;
|
| 42 |
-
@group(0) @binding(3) var<uniform> P : vec4<u32>; // x=len y=N
|
| 43 |
-
const IV = array<u32,8>(0x6a09e667u,0xbb67ae85u,0x3c6ef372u,0xa54ff53au,0x510e527fu,0x9b05688cu,0x1f83d9abu,0x5be0cd19u);
|
| 44 |
-
fn rotr(x:u32,n:u32)->u32 { return (x>>n)|(x<<(32u-n)); }
|
| 45 |
-
fn compress(cv:array<u32,8>, m0:array<u32,16>, cl:u32, ch:u32, bl:u32, fl:u32)->array<u32,16>{
|
| 46 |
-
var v:array<u32,16>;
|
| 47 |
-
v[0]=cv[0];v[1]=cv[1];v[2]=cv[2];v[3]=cv[3];v[4]=cv[4];v[5]=cv[5];v[6]=cv[6];v[7]=cv[7];
|
| 48 |
-
v[8]=IV[0];v[9]=IV[1];v[10]=IV[2];v[11]=IV[3];v[12]=cl;v[13]=ch;v[14]=bl;v[15]=fl;
|
| 49 |
-
var m:array<u32,16>=m0;
|
| 50 |
-
for(var r:u32=0u;r<7u;r=r+1u){
|
| 51 |
-
v[0]=v[0]+v[4]+m[0];v[12]=rotr(v[12]^v[0],16u);v[8]=v[8]+v[12];v[4]=rotr(v[4]^v[8],12u);v[0]=v[0]+v[4]+m[1];v[12]=rotr(v[12]^v[0],8u);v[8]=v[8]+v[12];v[4]=rotr(v[4]^v[8],7u);
|
| 52 |
-
v[1]=v[1]+v[5]+m[2];v[13]=rotr(v[13]^v[1],16u);v[9]=v[9]+v[13];v[5]=rotr(v[5]^v[9],12u);v[1]=v[1]+v[5]+m[3];v[13]=rotr(v[13]^v[1],8u);v[9]=v[9]+v[13];v[5]=rotr(v[5]^v[9],7u);
|
| 53 |
-
v[2]=v[2]+v[6]+m[4];v[14]=rotr(v[14]^v[2],16u);v[10]=v[10]+v[14];v[6]=rotr(v[6]^v[10],12u);v[2]=v[2]+v[6]+m[5];v[14]=rotr(v[14]^v[2],8u);v[10]=v[10]+v[14];v[6]=rotr(v[6]^v[10],7u);
|
| 54 |
-
v[3]=v[3]+v[7]+m[6];v[15]=rotr(v[15]^v[3],16u);v[11]=v[11]+v[15];v[7]=rotr(v[7]^v[11],12u);v[3]=v[3]+v[7]+m[7];v[15]=rotr(v[15]^v[3],8u);v[11]=v[11]+v[15];v[7]=rotr(v[7]^v[11],7u);
|
| 55 |
-
v[0]=v[0]+v[5]+m[8];v[15]=rotr(v[15]^v[0],16u);v[10]=v[10]+v[15];v[5]=rotr(v[5]^v[10],12u);v[0]=v[0]+v[5]+m[9];v[15]=rotr(v[15]^v[0],8u);v[10]=v[10]+v[15];v[5]=rotr(v[5]^v[10],7u);
|
| 56 |
-
v[1]=v[1]+v[6]+m[10];v[12]=rotr(v[12]^v[1],16u);v[11]=v[11]+v[12];v[6]=rotr(v[6]^v[11],12u);v[1]=v[1]+v[6]+m[11];v[12]=rotr(v[12]^v[1],8u);v[11]=v[11]+v[12];v[6]=rotr(v[6]^v[11],7u);
|
| 57 |
-
v[2]=v[2]+v[7]+m[12];v[13]=rotr(v[13]^v[2],16u);v[8]=v[8]+v[13];v[7]=rotr(v[7]^v[8],12u);v[2]=v[2]+v[7]+m[13];v[13]=rotr(v[13]^v[2],8u);v[8]=v[8]+v[13];v[7]=rotr(v[7]^v[8],7u);
|
| 58 |
-
v[3]=v[3]+v[4]+m[14];v[14]=rotr(v[14]^v[3],16u);v[9]=v[9]+v[14];v[4]=rotr(v[4]^v[9],12u);v[3]=v[3]+v[4]+m[15];v[14]=rotr(v[14]^v[3],8u);v[9]=v[9]+v[14];v[4]=rotr(v[4]^v[9],7u);
|
| 59 |
-
if(r<6u){var perm=array<u32,16>(2u,6u,3u,10u,7u,0u,4u,13u,1u,11u,12u,5u,9u,14u,15u,8u);var p:array<u32,16>;for(var i:u32=0u;i<16u;i=i+1u){p[i]=m[perm[i]];}m=p;}
|
| 60 |
-
}
|
| 61 |
-
var o:array<u32,16>;for(var i:u32=0u;i<8u;i=i+1u){o[i]=v[i]^v[i+8u];o[i+8u]=v[i+8u]^cv[i];}return o;
|
| 62 |
-
}
|
| 63 |
-
fn loadBlock(bo:u32)->array<u32,16>{let base=bo>>2u;var m:array<u32,16>;for(var i:u32=0u;i<16u;i=i+1u){m[i]=inp[base+i];}return m;}
|
| 64 |
-
fn chunkCV(s:u32,l:u32,ct:u32)->array<u32,8>{var cv:array<u32,8>=IV;let nB=max(1u,(l+63u)/64u);var fl:u32=1u;for(var i:u32=0u;i<nB-1u;i=i+1u){let o=compress(cv,loadBlock(s+i*64u),ct,0u,64u,fl);for(var j:u32=0u;j<8u;j=j+1u){cv[j]=o[j];}fl=0u;}let lo=s+(nB-1u)*64u;let ll=l-(nB-1u)*64u;let o2=compress(cv,loadBlock(lo),ct,0u,ll,fl|2u);var out:array<u32,8>;for(var j:u32=0u;j<8u;j=j+1u){out[j]=o2[j];}return out;}
|
| 65 |
-
fn chunkRoot(s:u32,l:u32)->array<u32,8>{var cv:array<u32,8>=IV;let nB=max(1u,(l+63u)/64u);var fl:u32=1u;for(var i:u32=0u;i<nB-1u;i=i+1u){let o=compress(cv,loadBlock(s+i*64u),0u,0u,64u,fl);for(var j:u32=0u;j<8u;j=j+1u){cv[j]=o[j];}fl=0u;}let lo=s+(nB-1u)*64u;let ll=l-(nB-1u)*64u;let o=compress(cv,loadBlock(lo),0u,0u,ll,fl|2u|8u);var out:array<u32,8>;for(var j:u32=0u;j<8u;j=j+1u){out[j]=o[j];}return out;}
|
| 66 |
-
fn parentCV(l:array<u32,8>,r:array<u32,8>,root:bool)->array<u32,8>{var m:array<u32,16>;for(var i:u32=0u;i<8u;i=i+1u){m[i]=l[i];m[i+8u]=r[i];}var fl:u32=4u;if(root){fl=12u;}let o=compress(IV,m,0u,0u,64u,fl);var out:array<u32,8>;for(var i:u32=0u;i<8u;i=i+1u){out[i]=o[i];}return out;}
|
| 67 |
-
fn getCV(i:u32)->array<u32,8>{var c:array<u32,8>;for(var j:u32=0u;j<8u;j=j+1u){c[j]=cvs[i*8u+j];}return c;}
|
| 68 |
-
|
| 69 |
-
// PHASE 1 — one invocation per chunk (parallel)
|
| 70 |
-
@compute @workgroup_size(64)
|
| 71 |
-
fn phase1(@builtin(global_invocation_id) gid:vec3<u32>){
|
| 72 |
-
let i=gid.x; let N=P.y; let len=P.x;
|
| 73 |
-
if(i>=N){return;}
|
| 74 |
-
let off=i*1024u; let ln=min(1024u,len-off);
|
| 75 |
-
var cv:array<u32,8>;
|
| 76 |
-
if(N==1u){cv=chunkRoot(off,ln);}else{cv=chunkCV(off,ln,i);}
|
| 77 |
-
for(var j:u32=0u;j<8u;j=j+1u){cvs[i*8u+j]=cv[j];}
|
| 78 |
-
}
|
| 79 |
-
// PHASE 2 — single invocation: stack tree-merge over the CVs → root
|
| 80 |
-
@compute @workgroup_size(1)
|
| 81 |
-
fn phase2(){
|
| 82 |
-
let N=P.y;
|
| 83 |
-
var root:array<u32,8>;
|
| 84 |
-
if(N==1u){root=getCV(0u);}
|
| 85 |
-
else{
|
| 86 |
-
var stack:array<array<u32,8>,54>; var depth:u32=0u;
|
| 87 |
-
for(var c:u32=0u;c<N-1u;c=c+1u){
|
| 88 |
-
var cv=getCV(c); var t=c+1u;
|
| 89 |
-
loop{ if((t&1u)!=0u){break;} depth=depth-1u; cv=parentCV(stack[depth],cv,false); t=t>>1u; }
|
| 90 |
-
stack[depth]=cv; depth=depth+1u;
|
| 91 |
-
}
|
| 92 |
-
var node=getCV(N-1u);
|
| 93 |
-
for(var i:u32=0u;i<depth;i=i+1u){ let li=depth-1u-i; root=parentCV(stack[li],node,i==depth-1u); node=root; }
|
| 94 |
-
}
|
| 95 |
-
for(var i:u32=0u;i<8u;i=i+1u){outp[i]=root[i];}
|
| 96 |
-
}
|
| 97 |
-
`;
|
| 98 |
-
const $=s=>document.querySelector(s),st_=$("#status"),sp=$("#speed"),tb=$("#tbl tbody"),res=$("#result");
|
| 99 |
-
const hexLE=w=>{let s='';for(let i=0;i<8;i++){const x=w[i]>>>0;s+=(x&255).toString(16).padStart(2,'0')+((x>>>8)&255).toString(16).padStart(2,'0')+((x>>>16)&255).toString(16).padStart(2,'0')+((x>>>24)&255).toString(16).padStart(2,'0');}return s;};
|
| 100 |
-
(async()=>{
|
| 101 |
-
if(!navigator.gpu){st_.textContent="✗ No WebGPU.";st_.classList.add("no");return;}
|
| 102 |
-
const ad=await navigator.gpu.requestAdapter(),dev=await ad.requestDevice();
|
| 103 |
-
const mod=dev.createShaderModule({code:WGSL});const ci=await mod.getCompilationInfo();const er=ci.messages.filter(m=>m.type==="error");
|
| 104 |
-
if(er.length){st_.textContent="✗ WGSL: "+er[0].message+" (line "+er[0].lineNum+")";st_.classList.add("no");return;}
|
| 105 |
-
// explicit shared layout so ONE bind group works for both phases (auto-layout would infer different layouts)
|
| 106 |
-
const bgl=dev.createBindGroupLayout({entries:[
|
| 107 |
-
{binding:0,visibility:GPUShaderStage.COMPUTE,buffer:{type:"read-only-storage"}},
|
| 108 |
-
{binding:1,visibility:GPUShaderStage.COMPUTE,buffer:{type:"storage"}},
|
| 109 |
-
{binding:2,visibility:GPUShaderStage.COMPUTE,buffer:{type:"storage"}},
|
| 110 |
-
{binding:3,visibility:GPUShaderStage.COMPUTE,buffer:{type:"uniform"}}]});
|
| 111 |
-
const pll=dev.createPipelineLayout({bindGroupLayouts:[bgl]});
|
| 112 |
-
const p1=dev.createComputePipeline({layout:pll,compute:{module:mod,entryPoint:"phase1"}});
|
| 113 |
-
const p2=dev.createComputePipeline({layout:pll,compute:{module:mod,entryPoint:"phase2"}});
|
| 114 |
-
async function gpu(bytes){
|
| 115 |
-
const len=bytes.length,N=len===0?1:Math.ceil(len/1024);
|
| 116 |
-
const padU=Math.max(16,(Math.ceil(len/64)||1)*16);const u=new Uint32Array(padU);new Uint8Array(u.buffer).set(bytes);
|
| 117 |
-
const inB=dev.createBuffer({size:u.byteLength,usage:GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_DST});dev.queue.writeBuffer(inB,0,u);
|
| 118 |
-
const cvB=dev.createBuffer({size:Math.max(32,N*32),usage:GPUBufferUsage.STORAGE});
|
| 119 |
-
const oB=dev.createBuffer({size:32,usage:GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_SRC});
|
| 120 |
-
const pB=dev.createBuffer({size:16,usage:GPUBufferUsage.UNIFORM|GPUBufferUsage.COPY_DST});dev.queue.writeBuffer(pB,0,new Uint32Array([len,N,0,0]));
|
| 121 |
-
const sg=dev.createBuffer({size:32,usage:GPUBufferUsage.COPY_DST|GPUBufferUsage.MAP_READ});
|
| 122 |
-
const bg=dev.createBindGroup({layout:bgl,entries:[{binding:0,resource:{buffer:inB}},{binding:1,resource:{buffer:cvB}},{binding:2,resource:{buffer:oB}},{binding:3,resource:{buffer:pB}}]});
|
| 123 |
-
const t0=performance.now();
|
| 124 |
-
const e=dev.createCommandEncoder();
|
| 125 |
-
let pass=e.beginComputePass();pass.setPipeline(p1);pass.setBindGroup(0,bg);pass.dispatchWorkgroups(Math.ceil(N/64));pass.end();
|
| 126 |
-
pass=e.beginComputePass();pass.setPipeline(p2);pass.setBindGroup(0,bg);pass.dispatchWorkgroups(1);pass.end();
|
| 127 |
-
e.copyBufferToBuffer(oB,0,sg,0,32);dev.queue.submit([e.finish()]);
|
| 128 |
-
await sg.mapAsync(GPUMapMode.READ);const w=new Uint32Array(sg.getMappedRange().slice(0));sg.unmap();
|
| 129 |
-
const ms=performance.now()-t0;
|
| 130 |
-
inB.destroy();cvB.destroy();oB.destroy();pB.destroy();sg.destroy();
|
| 131 |
-
return {hex:hexLE(w),ms};
|
| 132 |
-
}
|
| 133 |
-
// correctness
|
| 134 |
-
st_.textContent="checking correctness…";let pass=0;const sizes=[0,1,64,1023,1024,1025,2048,4096,100000,1048576];
|
| 135 |
-
for(const n of sizes){const b=new Uint8Array(n);for(let i=0;i<n;i++)b[i]=i&255;const {hex}=await gpu(b);const ok=hex===b3hex(b);if(ok)pass++;const tr=document.createElement("tr");tr.innerHTML=`<td class="n">${n.toLocaleString()}</td><td class="${ok?'ok':'no'}">${ok?'✓ match':'✗ MISMATCH'}</td>`;tb.appendChild(tr);}
|
| 136 |
-
st_.textContent=`correctness: ${pass}/${sizes.length}`;st_.classList.add(pass===sizes.length?"ok":"no");
|
| 137 |
-
// throughput — 138 MB (the embed block size)
|
| 138 |
-
sp.textContent="timing 138 MB (embed-sized block)…";
|
| 139 |
-
const BIG=138*1024*1024;const big=new Uint8Array(BIG);for(let i=0;i<BIG;i++)big[i]=i&255;
|
| 140 |
-
let best=1e9;for(let k=0;k<3;k++){const {ms}=await gpu(big);best=Math.min(best,ms);}
|
| 141 |
-
const gbps=(BIG/1073741824)/(best/1000);
|
| 142 |
-
sp.innerHTML=`<b>138 MB verified on GPU in ${best.toFixed(0)} ms → ${gbps.toFixed(2)} GB/s</b>`;sp.classList.add(gbps>1?"ok":"");
|
| 143 |
-
res.innerHTML=(pass===sizes.length)?`<div class="big ok">✓ Parallel BLAKE3 on your GPU — byte-exact, and ${gbps.toFixed(1)} GB/s on an embed-sized block.</div><div class="hx">Adapter: ${ad.info?ad.info.vendor+" "+ad.info.architecture:"?"}</div>`:`<div class="big no">✗ correctness failed — see table</div>`;
|
| 144 |
-
})();
|
| 145 |
-
</script></body></html>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
blake3-gpu-test.html
DELETED
|
@@ -1,195 +0,0 @@
|
|
| 1 |
-
<!doctype html><html><head><meta charset=utf8><meta name=viewport content="width=device-width,initial-scale=1">
|
| 2 |
-
<title>BLAKE3 on your GPU — parity test</title>
|
| 3 |
-
<style>
|
| 4 |
-
:root{--bg:#0a0d13;--panel:#111825;--ink:#e8ebf1;--dim:#8b95a7;--ac:#7c5cff;--ok:#48c26c;--no:#f0616d;--line:#1e2836}
|
| 5 |
-
*{box-sizing:border-box}body{margin:0;background:var(--bg);color:var(--ink);font:15px/1.55 -apple-system,Segoe UI,Roboto,monospace;padding:24px;max-width:820px;margin:0 auto}
|
| 6 |
-
h1{font-size:20px;margin:0 0 4px}.sub{color:var(--dim);font-size:13px;margin:0 0 18px}
|
| 7 |
-
#status{font-family:ui-monospace,monospace;padding:10px 14px;border:1px solid var(--line);border-radius:10px;background:var(--panel);margin-bottom:16px}
|
| 8 |
-
table{border-collapse:collapse;width:100%;font-family:ui-monospace,monospace;font-size:12.5px}
|
| 9 |
-
th,td{text-align:left;padding:7px 10px;border-bottom:1px solid var(--line)}
|
| 10 |
-
th{color:var(--dim);text-transform:uppercase;font-size:10.5px;letter-spacing:.08em}
|
| 11 |
-
td.n{text-align:right;font-variant-numeric:tabular-nums}
|
| 12 |
-
.ok{color:var(--ok)}.no{color:var(--no)}
|
| 13 |
-
.big{font-size:17px;font-weight:600;margin:16px 0 4px}
|
| 14 |
-
.hx{color:var(--dim)}
|
| 15 |
-
</style></head><body>
|
| 16 |
-
<h1>BLAKE3 — <span style="color:var(--ac)">on your GPU</span> vs the CPU oracle</h1>
|
| 17 |
-
<p class="sub">WGSL kernel hashes each input entirely on the GPU; compared byte-for-byte to <code>holo-blake3.mjs</code> and the official empty-input vector. Boundary sizes exercise block (64), chunk (1024), and tree merges.</p>
|
| 18 |
-
<div id="status">initializing WebGPU…</div>
|
| 19 |
-
<div id="result"></div>
|
| 20 |
-
<table id="tbl"><thead><tr><th>input</th><th class="n">bytes</th><th>GPU digest (first 32 hex)</th><th>match</th></tr></thead><tbody></tbody></table>
|
| 21 |
-
|
| 22 |
-
<script type="module">
|
| 23 |
-
// ─────────────────── CPU ORACLE (holo-blake3.mjs, inlined verbatim) ───────────────────
|
| 24 |
-
const IV=[0x6a09e667,0xbb67ae85,0x3c6ef372,0xa54ff53a,0x510e527f,0x9b05688c,0x1f83d9ab,0x5be0cd19];
|
| 25 |
-
const MSG=[2,6,3,10,7,0,4,13,1,11,12,5,9,14,15,8];
|
| 26 |
-
const CHUNK_START=1,CHUNK_END=2,PARENT=4,ROOT=8,BLOCK=64,CHUNK=1024;
|
| 27 |
-
const rotr=(x,n)=>((x>>>n)|(x<<(32-n)))>>>0;
|
| 28 |
-
function g(v,a,b,c,d,mx,my){v[a]=(v[a]+v[b]+mx)>>>0;v[d]=rotr(v[d]^v[a],16);v[c]=(v[c]+v[d])>>>0;v[b]=rotr(v[b]^v[c],12);v[a]=(v[a]+v[b]+my)>>>0;v[d]=rotr(v[d]^v[a],8);v[c]=(v[c]+v[d])>>>0;v[b]=rotr(v[b]^v[c],7);}
|
| 29 |
-
function roundFn(v,m){g(v,0,4,8,12,m[0],m[1]);g(v,1,5,9,13,m[2],m[3]);g(v,2,6,10,14,m[4],m[5]);g(v,3,7,11,15,m[6],m[7]);g(v,0,5,10,15,m[8],m[9]);g(v,1,6,11,12,m[10],m[11]);g(v,2,7,8,13,m[12],m[13]);g(v,3,4,9,14,m[14],m[15]);}
|
| 30 |
-
function compress(cv,m0,counter,blockLen,flags){const cl=counter>>>0,ch=Math.floor(counter/4294967296)>>>0;const v=[cv[0],cv[1],cv[2],cv[3],cv[4],cv[5],cv[6],cv[7],IV[0],IV[1],IV[2],IV[3],cl,ch,blockLen>>>0,flags>>>0];let m=m0.slice();for(let r=0;r<7;r++){roundFn(v,m);if(r<6){const p=new Array(16);for(let i=0;i<16;i++)p[i]=m[MSG[i]];m=p;}}const out=new Array(16);for(let i=0;i<8;i++){out[i]=(v[i]^v[i+8])>>>0;out[i+8]=(v[i+8]^cv[i])>>>0;}return out;}
|
| 31 |
-
function words(bytes,off,len){const m=new Array(16).fill(0);for(let i=0;i<len;i++)m[i>>2]|=bytes[off+i]<<((i&3)*8);for(let i=0;i<16;i++)m[i]>>>=0;return m;}
|
| 32 |
-
function nodeCV(o){return compress(o.cv,o.m,o.counter,o.blockLen,o.flags).slice(0,8);}
|
| 33 |
-
function nodeRoot(o){const out=compress(o.cv,o.m,0,o.blockLen,o.flags|ROOT);const b=new Uint8Array(32);for(let i=0;i<8;i++){const w=out[i];b[i*4]=w&255;b[i*4+1]=(w>>>8)&255;b[i*4+2]=(w>>>16)&255;b[i*4+3]=(w>>>24)&255;}return b;}
|
| 34 |
-
function chunkNode(bytes,start,len,counter){let cv=IV.slice();const nB=Math.max(1,Math.ceil(len/BLOCK));let flags=CHUNK_START;for(let i=0;i<nB-1;i++){cv=compress(cv,words(bytes,start+i*BLOCK,BLOCK),counter,BLOCK,flags).slice(0,8);flags=0;}const lo=start+(nB-1)*BLOCK,ll=len-(nB-1)*BLOCK;return{cv,m:words(bytes,lo,ll),counter,blockLen:ll,flags:flags|CHUNK_END};}
|
| 35 |
-
function parentNode(l,r){return{cv:IV.slice(),m:l.concat(r),counter:0,blockLen:BLOCK,flags:PARENT};}
|
| 36 |
-
function subtree(bytes,start,len,counter){if(len<=CHUNK)return chunkNode(bytes,start,len,counter);let left=CHUNK;while(left*2<len)left*=2;const lcv=nodeCV(subtree(bytes,start,left,counter));const rcv=nodeCV(subtree(bytes,start+left,len-left,counter+left/CHUNK));return parentNode(lcv,rcv);}
|
| 37 |
-
function blake3hex(bytes){const d=nodeRoot(subtree(bytes,0,bytes.length,0));let s="";for(let i=0;i<32;i++)s+=d[i].toString(16).padStart(2,"0");return s;}
|
| 38 |
-
|
| 39 |
-
// ─────────────────── WGSL BLAKE3 — runs ENTIRELY on the GPU ───────────────────
|
| 40 |
-
// Single-invocation port of the incremental/stack tree (no recursion). Input is zero-padded to a
|
| 41 |
-
// multiple of 64 bytes as an array<u32> (LE) so each 64-byte block = 16 consecutive u32. blockLen in
|
| 42 |
-
// the compression state carries the true length. Stack of subtree CVs collapses on even chunk counts,
|
| 43 |
-
// exactly like createBlake3(); the final chunk merges down the stack with ROOT — byte-identical to blake3().
|
| 44 |
-
const WGSL = `
|
| 45 |
-
@group(0) @binding(0) var<storage, read> inp : array<u32>;
|
| 46 |
-
@group(0) @binding(1) var<storage, read_write> outp : array<u32>;
|
| 47 |
-
@group(0) @binding(2) var<uniform> P : vec4<u32>; // P.x = byte length
|
| 48 |
-
|
| 49 |
-
const IV = array<u32,8>(0x6a09e667u,0xbb67ae85u,0x3c6ef372u,0xa54ff53au,0x510e527fu,0x9b05688cu,0x1f83d9abu,0x5be0cd19u);
|
| 50 |
-
|
| 51 |
-
fn rotr(x:u32,n:u32)->u32 { return (x >> n) | (x << (32u - n)); }
|
| 52 |
-
|
| 53 |
-
// compress → 16 words. cv[8], m[16], counter lo/hi, blockLen, flags.
|
| 54 |
-
fn compress(cv:array<u32,8>, m0:array<u32,16>, cl:u32, ch:u32, blockLen:u32, flags:u32) -> array<u32,16> {
|
| 55 |
-
var v:array<u32,16>;
|
| 56 |
-
v[0]=cv[0]; v[1]=cv[1]; v[2]=cv[2]; v[3]=cv[3]; v[4]=cv[4]; v[5]=cv[5]; v[6]=cv[6]; v[7]=cv[7];
|
| 57 |
-
v[8]=IV[0]; v[9]=IV[1]; v[10]=IV[2]; v[11]=IV[3]; v[12]=cl; v[13]=ch; v[14]=blockLen; v[15]=flags;
|
| 58 |
-
var m:array<u32,16> = m0;
|
| 59 |
-
for (var r:u32=0u; r<7u; r=r+1u) {
|
| 60 |
-
// roundFn — 8 g() applications, indices are literals
|
| 61 |
-
// g(a,b,c,d,mx,my): a=a+b+mx; d=rotr(d^a,16); c=c+d; b=rotr(b^c,12); a=a+b+my; d=rotr(d^a,8); c=c+d; b=rotr(b^c,7)
|
| 62 |
-
// col 0
|
| 63 |
-
v[0]=v[0]+v[4]+m[0]; v[12]=rotr(v[12]^v[0],16u); v[8]=v[8]+v[12]; v[4]=rotr(v[4]^v[8],12u); v[0]=v[0]+v[4]+m[1]; v[12]=rotr(v[12]^v[0],8u); v[8]=v[8]+v[12]; v[4]=rotr(v[4]^v[8],7u);
|
| 64 |
-
v[1]=v[1]+v[5]+m[2]; v[13]=rotr(v[13]^v[1],16u); v[9]=v[9]+v[13]; v[5]=rotr(v[5]^v[9],12u); v[1]=v[1]+v[5]+m[3]; v[13]=rotr(v[13]^v[1],8u); v[9]=v[9]+v[13]; v[5]=rotr(v[5]^v[9],7u);
|
| 65 |
-
v[2]=v[2]+v[6]+m[4]; v[14]=rotr(v[14]^v[2],16u); v[10]=v[10]+v[14]; v[6]=rotr(v[6]^v[10],12u); v[2]=v[2]+v[6]+m[5]; v[14]=rotr(v[14]^v[2],8u); v[10]=v[10]+v[14]; v[6]=rotr(v[6]^v[10],7u);
|
| 66 |
-
v[3]=v[3]+v[7]+m[6]; v[15]=rotr(v[15]^v[3],16u); v[11]=v[11]+v[15]; v[7]=rotr(v[7]^v[11],12u); v[3]=v[3]+v[7]+m[7]; v[15]=rotr(v[15]^v[3],8u); v[11]=v[11]+v[15]; v[7]=rotr(v[7]^v[11],7u);
|
| 67 |
-
// diag
|
| 68 |
-
v[0]=v[0]+v[5]+m[8]; v[15]=rotr(v[15]^v[0],16u); v[10]=v[10]+v[15]; v[5]=rotr(v[5]^v[10],12u); v[0]=v[0]+v[5]+m[9]; v[15]=rotr(v[15]^v[0],8u); v[10]=v[10]+v[15]; v[5]=rotr(v[5]^v[10],7u);
|
| 69 |
-
v[1]=v[1]+v[6]+m[10]; v[12]=rotr(v[12]^v[1],16u); v[11]=v[11]+v[12]; v[6]=rotr(v[6]^v[11],12u); v[1]=v[1]+v[6]+m[11]; v[12]=rotr(v[12]^v[1],8u); v[11]=v[11]+v[12]; v[6]=rotr(v[6]^v[11],7u);
|
| 70 |
-
v[2]=v[2]+v[7]+m[12]; v[13]=rotr(v[13]^v[2],16u); v[8]=v[8]+v[13]; v[7]=rotr(v[7]^v[8],12u); v[2]=v[2]+v[7]+m[13]; v[13]=rotr(v[13]^v[2],8u); v[8]=v[8]+v[13]; v[7]=rotr(v[7]^v[8],7u);
|
| 71 |
-
v[3]=v[3]+v[4]+m[14]; v[14]=rotr(v[14]^v[3],16u); v[9]=v[9]+v[14]; v[4]=rotr(v[4]^v[9],12u); v[3]=v[3]+v[4]+m[15]; v[14]=rotr(v[14]^v[3],8u); v[9]=v[9]+v[14]; v[4]=rotr(v[4]^v[9],7u);
|
| 72 |
-
if (r < 6u) { var perm=array<u32,16>(2u,6u,3u,10u,7u,0u,4u,13u,1u,11u,12u,5u,9u,14u,15u,8u); var p:array<u32,16>; for (var i:u32=0u;i<16u;i=i+1u){ p[i]=m[perm[i]]; } m=p; }
|
| 73 |
-
}
|
| 74 |
-
var o:array<u32,16>;
|
| 75 |
-
for (var i:u32=0u;i<8u;i=i+1u){ o[i]=v[i]^v[i+8u]; o[i+8u]=v[i+8u]^cv[i]; }
|
| 76 |
-
return o;
|
| 77 |
-
}
|
| 78 |
-
fn loadBlock(byteOff:u32) -> array<u32,16> {
|
| 79 |
-
let base = byteOff >> 2u; var m:array<u32,16>;
|
| 80 |
-
for (var i:u32=0u;i<16u;i=i+1u){ m[i] = inp[base+i]; } // input is zero-padded → direct read
|
| 81 |
-
return m;
|
| 82 |
-
}
|
| 83 |
-
// chunk [start,start+len) at index ctr → chaining value (8) ; final=false path
|
| 84 |
-
fn chunkCV(start:u32, len:u32, ctr:u32) -> array<u32,8> {
|
| 85 |
-
var cv:array<u32,8> = IV;
|
| 86 |
-
let nB = max(1u, (len + 63u) / 64u);
|
| 87 |
-
var flags:u32 = 1u; // CHUNK_START
|
| 88 |
-
for (var i:u32=0u; i<nB-1u; i=i+1u) {
|
| 89 |
-
let o = compress(cv, loadBlock(start + i*64u), ctr, 0u, 64u, flags);
|
| 90 |
-
for (var j:u32=0u;j<8u;j=j+1u){ cv[j]=o[j]; }
|
| 91 |
-
flags = 0u;
|
| 92 |
-
}
|
| 93 |
-
let lastOff = start + (nB-1u)*64u;
|
| 94 |
-
let lastLen = len - (nB-1u)*64u;
|
| 95 |
-
let o2 = compress(cv, loadBlock(lastOff), ctr, 0u, lastLen, flags | 2u); // CHUNK_END
|
| 96 |
-
var out:array<u32,8>; for (var j:u32=0u;j<8u;j=j+1u){ out[j]=o2[j]; } return out;
|
| 97 |
-
}
|
| 98 |
-
// final chunk → 32-byte root (returns 8 words). merged with a running CV from the stack side is handled in main.
|
| 99 |
-
// parent(left8,right8) → cv8 (or root8 if isRoot)
|
| 100 |
-
fn parentCV(l:array<u32,8>, r:array<u32,8>, isRoot:bool) -> array<u32,8> {
|
| 101 |
-
var m:array<u32,16>; for (var i:u32=0u;i<8u;i=i+1u){ m[i]=l[i]; m[i+8u]=r[i]; }
|
| 102 |
-
var flags:u32 = 4u; if (isRoot) { flags = 4u | 8u; }
|
| 103 |
-
let o = compress(IV, m, 0u, 0u, 64u, flags);
|
| 104 |
-
var out:array<u32,8>; for (var i:u32=0u;i<8u;i=i+1u){ out[i]=o[i]; } return out;
|
| 105 |
-
}
|
| 106 |
-
// final chunk root (when there are no parents): compress final block with ROOT, counter 0
|
| 107 |
-
fn chunkRoot(start:u32, len:u32, ctr:u32) -> array<u32,8> {
|
| 108 |
-
var cv:array<u32,8> = IV;
|
| 109 |
-
let nB = max(1u, (len + 63u) / 64u);
|
| 110 |
-
var flags:u32 = 1u;
|
| 111 |
-
for (var i:u32=0u; i<nB-1u; i=i+1u){ let o=compress(cv,loadBlock(start+i*64u),ctr,0u,64u,flags); for(var j:u32=0u;j<8u;j=j+1u){cv[j]=o[j];} flags=0u; }
|
| 112 |
-
let lastOff=start+(nB-1u)*64u; let lastLen=len-(nB-1u)*64u;
|
| 113 |
-
let o=compress(cv, loadBlock(lastOff), 0u,0u, lastLen, flags | 2u | 8u); // CHUNK_END|ROOT, counter 0
|
| 114 |
-
var out:array<u32,8>; for(var j:u32=0u;j<8u;j=j+1u){out[j]=o[j];} return out;
|
| 115 |
-
}
|
| 116 |
-
|
| 117 |
-
@compute @workgroup_size(1)
|
| 118 |
-
fn main() {
|
| 119 |
-
let len = P.x;
|
| 120 |
-
var stack: array<array<u32,8>, 54>;
|
| 121 |
-
var depth:u32 = 0u;
|
| 122 |
-
let totalChunks = select((len + 1023u) / 1024u, 1u, len == 0u);
|
| 123 |
-
let flushed = totalChunks - 1u; // chunks 0..flushed-1 go to the stack; last is final
|
| 124 |
-
// build the stack from the flushed chunks
|
| 125 |
-
for (var c:u32=0u; c<flushed; c=c+1u) {
|
| 126 |
-
var cv = chunkCV(c*1024u, 1024u, c);
|
| 127 |
-
var t = c + 1u; // add(cv, totalChunks=c+1)
|
| 128 |
-
loop { if ((t & 1u) != 0u) { break; } depth=depth-1u; cv = parentCV(stack[depth], cv, false); t = t >> 1u; }
|
| 129 |
-
stack[depth] = cv; depth = depth + 1u;
|
| 130 |
-
}
|
| 131 |
-
let finalOff = flushed * 1024u;
|
| 132 |
-
let finalLen = len - finalOff; // 0..1024
|
| 133 |
-
var root: array<u32,8>;
|
| 134 |
-
if (depth == 0u) {
|
| 135 |
-
root = chunkRoot(finalOff, finalLen, flushed); // single chunk → its own root
|
| 136 |
-
} else {
|
| 137 |
-
// final chunk's CV, then merge down the stack; the LAST merge is ROOT
|
| 138 |
-
var node = chunkCV(finalOff, finalLen, flushed);
|
| 139 |
-
for (var i:u32=0u; i<depth; i=i+1u) {
|
| 140 |
-
let li = depth - 1u - i;
|
| 141 |
-
root = parentCV(stack[li], node, i == depth-1u); // top merge = ROOT
|
| 142 |
-
node = root;
|
| 143 |
-
}
|
| 144 |
-
}
|
| 145 |
-
for (var i:u32=0u;i<8u;i=i+1u){ outp[i] = root[i]; }
|
| 146 |
-
}
|
| 147 |
-
`;
|
| 148 |
-
|
| 149 |
-
// ─────────────────── WebGPU harness ───────────────────
|
| 150 |
-
const $=s=>document.querySelector(s), st=$("#status"), tb=$("#tbl tbody"), res=$("#result");
|
| 151 |
-
function hexLE(words){let s="";for(let i=0;i<8;i++){const w=words[i]>>>0;s+=(w&255).toString(16).padStart(2,"0")+((w>>>8)&255).toString(16).padStart(2,"0")+((w>>>16)&255).toString(16).padStart(2,"0")+((w>>>24)&255).toString(16).padStart(2,"0");}return s;}
|
| 152 |
-
|
| 153 |
-
(async()=>{
|
| 154 |
-
if(!navigator.gpu){st.textContent="✗ No WebGPU in this browser. Open in Chrome/Edge (or enable WebGPU).";st.classList.add("no");return;}
|
| 155 |
-
let adapter,device;
|
| 156 |
-
try{adapter=await navigator.gpu.requestAdapter();device=await adapter.requestDevice();}
|
| 157 |
-
catch(e){st.textContent="✗ WebGPU device error: "+e.message;return;}
|
| 158 |
-
let module;
|
| 159 |
-
try{ module=device.createShaderModule({code:WGSL}); const info=await module.getCompilationInfo(); const errs=info.messages.filter(m=>m.type==="error"); if(errs.length){st.textContent="✗ WGSL compile error: "+errs[0].message+" (line "+errs[0].lineNum+")";st.classList.add("no");return;} }
|
| 160 |
-
catch(e){st.textContent="✗ shader error: "+e.message;return;}
|
| 161 |
-
const pipeline=device.createComputePipeline({layout:"auto",compute:{module,entryPoint:"main"}});
|
| 162 |
-
|
| 163 |
-
async function gpuHash(bytes){
|
| 164 |
-
const len=bytes.length, padded=Math.max(16,(Math.ceil(len/64)||1)*16); // u32 count, ≥1 block
|
| 165 |
-
const u=new Uint32Array(padded); const b=new Uint8Array(u.buffer); b.set(bytes);
|
| 166 |
-
const inBuf=device.createBuffer({size:u.byteLength,usage:GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_DST}); device.queue.writeBuffer(inBuf,0,u);
|
| 167 |
-
const outBuf=device.createBuffer({size:32,usage:GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_SRC});
|
| 168 |
-
const parBuf=device.createBuffer({size:16,usage:GPUBufferUsage.UNIFORM|GPUBufferUsage.COPY_DST}); device.queue.writeBuffer(parBuf,0,new Uint32Array([len,0,0,0]));
|
| 169 |
-
const stg=device.createBuffer({size:32,usage:GPUBufferUsage.COPY_DST|GPUBufferUsage.MAP_READ});
|
| 170 |
-
const bg=device.createBindGroup({layout:pipeline.getBindGroupLayout(0),entries:[{binding:0,resource:{buffer:inBuf}},{binding:1,resource:{buffer:outBuf}},{binding:2,resource:{buffer:parBuf}}]});
|
| 171 |
-
const enc=device.createCommandEncoder(); const p=enc.beginComputePass(); p.setPipeline(pipeline); p.setBindGroup(0,bg); p.dispatchWorkgroups(1); p.end();
|
| 172 |
-
enc.copyBufferToBuffer(outBuf,0,stg,0,32); device.queue.submit([enc.finish()]);
|
| 173 |
-
await stg.mapAsync(GPUMapMode.READ); const w=new Uint32Array(stg.getMappedRange().slice(0)); stg.unmap();
|
| 174 |
-
inBuf.destroy();outBuf.destroy();parBuf.destroy();stg.destroy();
|
| 175 |
-
return hexLE(w);
|
| 176 |
-
}
|
| 177 |
-
|
| 178 |
-
const sizes=[0,1,63,64,65,127,128,129,1023,1024,1025,2047,2048,2049,4096,10000,65536,100000];
|
| 179 |
-
st.textContent="running "+sizes.length+" boundary cases on your GPU…";
|
| 180 |
-
let pass=0;
|
| 181 |
-
// official empty-input vector, an extra ground-truth anchor
|
| 182 |
-
const OFFICIAL_EMPTY="af1349b9f5f9a1a6a0404dea36dcc9499bcb25c9adc112b7cc9a93cae41f3262";
|
| 183 |
-
for(const n of sizes){
|
| 184 |
-
const bytes=new Uint8Array(n); for(let i=0;i<n;i++)bytes[i]=i&255;
|
| 185 |
-
const cpu=blake3hex(bytes); const gpu=await gpuHash(bytes);
|
| 186 |
-
const ok=gpu===cpu && (n!==0 || cpu===OFFICIAL_EMPTY); if(ok)pass++;
|
| 187 |
-
const tr=document.createElement("tr");
|
| 188 |
-
tr.innerHTML=`<td>${n===0?"empty":"pattern"}${n===0?" (official vector)":""}</td><td class="n">${n.toLocaleString()}</td><td class="hx">${gpu.slice(0,32)}…</td><td class="${ok?'ok':'no'}">${ok?'✓ match':'✗ MISMATCH'}</td>`;
|
| 189 |
-
tb.appendChild(tr);
|
| 190 |
-
if(!ok){res.innerHTML=`<div class="big no">✗ MISMATCH at ${n} bytes</div><div class="hx">gpu ${gpu}<br>cpu ${cpu}</div>`;}
|
| 191 |
-
}
|
| 192 |
-
st.textContent=`done · ${pass}/${sizes.length} cases`; st.classList.add(pass===sizes.length?"ok":"no");
|
| 193 |
-
if(pass===sizes.length){res.innerHTML=`<div class="big ok">✓ BLAKE3 runs entirely on your GPU — byte-identical to the CPU oracle across every block, chunk, and tree boundary.</div><div class="hx" style="margin-top:6px">Adapter: ${adapter.info?adapter.info.vendor+" "+adapter.info.architecture:"(info withheld)"}</div>`;}
|
| 194 |
-
})();
|
| 195 |
-
</script></body></html>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
core/agents.js
DELETED
|
@@ -1,74 +0,0 @@
|
|
| 1 |
-
// core/agents.js — AGENTS as κ-objects (LibreChat agent semantics, substrate-native). An agent
|
| 2 |
-
// bundles identity + instructions + model + parameters + armed tools + conversation starters;
|
| 3 |
-
// selecting one shapes every turn (instructions ride the system block, its tool selection arms
|
| 4 |
-
// the MCP loop). Field names follow the LibreChat agent schema so exports interoperate; each
|
| 5 |
-
// version re-seals to a new κ (history is free, Law L3).
|
| 6 |
-
|
| 7 |
-
import { normPreset } from "./schema.js";
|
| 8 |
-
|
| 9 |
-
const LC = { lc: "https://librechat.ai/ns#" };
|
| 10 |
-
|
| 11 |
-
export function normAgent(a = {}) {
|
| 12 |
-
return {
|
| 13 |
-
id: typeof a.id === "string" ? a.id : null,
|
| 14 |
-
name: a.name || "New Agent",
|
| 15 |
-
description: a.description || "",
|
| 16 |
-
instructions: a.instructions || "",
|
| 17 |
-
avatar: a.avatar || null, // { filepath, source } | emoji string | null
|
| 18 |
-
provider: a.provider || "holo-q", // the local substrate engine
|
| 19 |
-
model: a.model || null, // a MODELS[] name
|
| 20 |
-
model_parameters: {
|
| 21 |
-
temperature: +(a.model_parameters?.temperature ?? 0) || 0,
|
| 22 |
-
max_output_tokens: +(a.model_parameters?.max_output_tokens ?? 900) || 900,
|
| 23 |
-
},
|
| 24 |
-
tools: Array.isArray(a.tools) ? a.tools : [], // armed tool names (the MCP loop selection)
|
| 25 |
-
mcpServerNames: Array.isArray(a.mcpServerNames) ? a.mcpServerNames : [],
|
| 26 |
-
conversation_starters: Array.isArray(a.conversation_starters) ? a.conversation_starters.filter(Boolean).slice(0, 4) : [],
|
| 27 |
-
artifacts: a.artifacts || "default", // 'default' | 'custom' (custom: instructions own the format)
|
| 28 |
-
recursion_limit: +(a.recursion_limit ?? 4) || 4, // max tool rounds
|
| 29 |
-
category: a.category || "general",
|
| 30 |
-
version: +(a.version ?? 1) || 1,
|
| 31 |
-
};
|
| 32 |
-
}
|
| 33 |
-
|
| 34 |
-
export function makeAgents(chatStore) {
|
| 35 |
-
const { store, getIndex, newId } = chatStore;
|
| 36 |
-
const putIndex = async (idx) => { const b = new TextEncoder().encode(JSON.stringify(idx)); return store.backend.putRaw ? store.backend.putRaw("index:org.hologram.HoloQ", b) : store.backend.put("index:org.hologram.HoloQ", b); };
|
| 37 |
-
|
| 38 |
-
async function save(a) {
|
| 39 |
-
const norm = normAgent(a);
|
| 40 |
-
if (!norm.id) norm.id = newId("agent");
|
| 41 |
-
const prevPtr = ((await getIndex()).agents || []).find((x) => x.id === norm.id);
|
| 42 |
-
if (prevPtr) norm.version = (prevPtr.version || 1) + 1;
|
| 43 |
-
const obj = await store.makeObject({
|
| 44 |
-
type: ["schema:SoftwareAgent", "prov:Entity"], context: [LC],
|
| 45 |
-
"schema:identifier": norm.id, "schema:name": norm.name, "schema:description": norm.description,
|
| 46 |
-
"lc:agent": norm,
|
| 47 |
-
...(prevPtr ? { links: [{ ...store.contentLink("prov:wasRevisionOf", prevPtr.kappa, "schema:SoftwareAgent"), "schema:name": "previousVersion" }] } : {}),
|
| 48 |
-
});
|
| 49 |
-
const idx = await getIndex();
|
| 50 |
-
idx.agents = idx.agents || [];
|
| 51 |
-
const ptr = { id: norm.id, kappa: obj.id, name: norm.name, avatar: norm.avatar, category: norm.category, version: norm.version };
|
| 52 |
-
const i = idx.agents.findIndex((x) => x.id === norm.id);
|
| 53 |
-
if (i >= 0) idx.agents[i] = ptr; else idx.agents.push(ptr);
|
| 54 |
-
await putIndex(idx);
|
| 55 |
-
return { ...norm, kappa: obj.id };
|
| 56 |
-
}
|
| 57 |
-
|
| 58 |
-
async function list() { return (await getIndex()).agents || []; }
|
| 59 |
-
|
| 60 |
-
async function get(id) {
|
| 61 |
-
const ptr = (await list()).find((x) => x.id === id); if (!ptr) return null;
|
| 62 |
-
const obj = await store.getObj(ptr.kappa); if (!obj) return null;
|
| 63 |
-
if (!(await store.verify(obj))) return null; // Law L5: a tampered agent is refused
|
| 64 |
-
return { ...normAgent(obj["lc:agent"]), kappa: ptr.kappa };
|
| 65 |
-
}
|
| 66 |
-
|
| 67 |
-
async function remove(id) {
|
| 68 |
-
const idx = await getIndex();
|
| 69 |
-
idx.agents = (idx.agents || []).filter((x) => x.id !== id);
|
| 70 |
-
await putIndex(idx);
|
| 71 |
-
}
|
| 72 |
-
|
| 73 |
-
return { save, list, get, remove, normAgent };
|
| 74 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
core/backend.js
DELETED
|
@@ -1,23 +0,0 @@
|
|
| 1 |
-
// core/backend.js — the durable byte backend for the κ-object store: IndexedDB via the OS
|
| 2 |
-
// runtime's _shared/holo-store.js (Law L3: the store is the memory; RAM is a bounded cache).
|
| 3 |
-
// Keys are κ hex strings (plus the one well-known boot-index pointer key). Same async shape
|
| 4 |
-
// as the witness's Map backend, so the L5 contract exercises identical code in both runtimes.
|
| 5 |
-
|
| 6 |
-
import { idbBackend } from "../_shared/holo-store.js";
|
| 7 |
-
|
| 8 |
-
export function kappaBackend() {
|
| 9 |
-
const idb = idbBackend({ db: "holo-q", store: "kappa" });
|
| 10 |
-
return {
|
| 11 |
-
get: async (hex) => (await idb.get(hex)) || undefined,
|
| 12 |
-
put: (hex, bytes) => idb.set(hex, bytes),
|
| 13 |
-
getRaw: async (k) => (await idb.get(k)) || undefined,
|
| 14 |
-
putRaw: (k, bytes) => idb.set(k, bytes),
|
| 15 |
-
has: (k) => idb.has(k),
|
| 16 |
-
};
|
| 17 |
-
}
|
| 18 |
-
|
| 19 |
-
// Storage usage for the Settings → Data tab (W3C StorageManager).
|
| 20 |
-
export async function storageEstimate() {
|
| 21 |
-
try { const e = await navigator.storage.estimate(); return { usage: e.usage || 0, quota: e.quota || 0 }; }
|
| 22 |
-
catch { return { usage: 0, quota: 0 }; }
|
| 23 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
core/code-tools.js
DELETED
|
@@ -1,130 +0,0 @@
|
|
| 1 |
-
// core/code-tools.js — NATIVE agentic-coding tools for Holo Q's Code mode. Self-contained over the
|
| 2 |
-
// browser's OPFS (navigator.storage.getDirectory) — no external VFS, works in any browser, 100%
|
| 3 |
-
// serverless. Each tool returns Holo Q's agent-tool shape { def, serverName:"code", call }, so the
|
| 4 |
-
// Coder-7B brain drives real file work through the existing agentic loop + conscience gate + receipts.
|
| 5 |
-
// The workspace is an OPFS "workspace" dir = the agent's writable home; every result is content-addressable.
|
| 6 |
-
//
|
| 7 |
-
// C2 semantic skin (extended to all κ-objects): build_app emits a schema:SoftwareApplication node and
|
| 8 |
-
// verify_object returns the file's schema.org @type — so every object the agent makes is W3C-legible.
|
| 9 |
-
|
| 10 |
-
import { appAsSoftware, fileAsDocument } from "./semantic.js";
|
| 11 |
-
|
| 12 |
-
const te = new TextEncoder(), td = new TextDecoder();
|
| 13 |
-
const sha256Hex = async (bytes) => [...new Uint8Array(await crypto.subtle.digest("SHA-256", bytes))].map((b) => b.toString(16).padStart(2, "0")).join("");
|
| 14 |
-
const norm = (p) => ("/" + String(p || "").replace(/^\/+/, "").replace(/\/+/g, "/")).replace(/\/$/, "") || "/";
|
| 15 |
-
|
| 16 |
-
async function root() { const d = await navigator.storage.getDirectory(); return d.getDirectoryHandle("workspace", { create: true }); }
|
| 17 |
-
async function dirOf(parts, create = false) {
|
| 18 |
-
let h = await root();
|
| 19 |
-
for (const p of parts) if (p) h = await h.getDirectoryHandle(p, { create });
|
| 20 |
-
return h;
|
| 21 |
-
}
|
| 22 |
-
function split(path) { const parts = norm(path).split("/").filter(Boolean); const name = parts.pop(); return { parts, name }; }
|
| 23 |
-
|
| 24 |
-
async function readFile(path) {
|
| 25 |
-
const { parts, name } = split(path);
|
| 26 |
-
const dir = await dirOf(parts);
|
| 27 |
-
const fh = await dir.getFileHandle(name);
|
| 28 |
-
const f = await fh.getFile();
|
| 29 |
-
return new Uint8Array(await f.arrayBuffer());
|
| 30 |
-
}
|
| 31 |
-
async function writeFile(path, content) {
|
| 32 |
-
const { parts, name } = split(path);
|
| 33 |
-
const dir = await dirOf(parts, true);
|
| 34 |
-
const fh = await dir.getFileHandle(name, { create: true });
|
| 35 |
-
const w = await fh.createWritable(); await w.write(te.encode(String(content ?? ""))); await w.close();
|
| 36 |
-
}
|
| 37 |
-
async function walk(dirHandle, prefix, out) {
|
| 38 |
-
for await (const [n, h] of dirHandle.entries()) {
|
| 39 |
-
const p = prefix + "/" + n;
|
| 40 |
-
if (h.kind === "file") out.push(p);
|
| 41 |
-
else await walk(h, p, out);
|
| 42 |
-
}
|
| 43 |
-
}
|
| 44 |
-
async function listAll() { const out = []; await walk(await root(), "", out); return out.sort(); }
|
| 45 |
-
|
| 46 |
-
function globToRe(glob) {
|
| 47 |
-
let re = "^";
|
| 48 |
-
for (let i = 0; i < glob.length; i++) { const c = glob[i];
|
| 49 |
-
if (c === "*") { if (glob[i + 1] === "*") { re += ".*"; i++; if (glob[i + 1] === "/") i++; } else re += "[^/]*"; }
|
| 50 |
-
else if (c === "?") re += "[^/]"; else if ("\\^$.|+()[]{}".includes(c)) re += "\\" + c; else re += c; }
|
| 51 |
-
return new RegExp(re + "$");
|
| 52 |
-
}
|
| 53 |
-
|
| 54 |
-
export function codeTools() {
|
| 55 |
-
return [
|
| 56 |
-
{
|
| 57 |
-
def: { name: "read_file", description: "Read a text file from the workspace. Returns its content.", inputSchema: { type: "object", properties: { path: { type: "string" } }, required: ["path"] } },
|
| 58 |
-
serverName: "code", call: async ({ path }) => {
|
| 59 |
-
try { const bytes = await readFile(path); return { text: td.decode(bytes), isError: false }; }
|
| 60 |
-
catch (e) { return { text: "read error: " + (e.message || e), isError: true }; }
|
| 61 |
-
},
|
| 62 |
-
},
|
| 63 |
-
{
|
| 64 |
-
def: { name: "write_file", description: "Create or overwrite a file in the workspace with the given content.", inputSchema: { type: "object", properties: { path: { type: "string" }, content: { type: "string" } }, required: ["path", "content"] } },
|
| 65 |
-
serverName: "code", call: async ({ path, content }) => {
|
| 66 |
-
try { await writeFile(path, content); const k = await sha256Hex(te.encode(String(content ?? ""))); return { text: `wrote ${norm(path)} (${String(content ?? "").length} bytes) · did:holo:sha256:${k.slice(0, 16)}`, isError: false }; }
|
| 67 |
-
catch (e) { return { text: "write error: " + (e.message || e), isError: true }; }
|
| 68 |
-
},
|
| 69 |
-
},
|
| 70 |
-
{
|
| 71 |
-
def: { name: "edit_file", description: "Replace the first exact occurrence of old_text with new_text in a file.", inputSchema: { type: "object", properties: { path: { type: "string" }, old_text: { type: "string" }, new_text: { type: "string" } }, required: ["path", "old_text", "new_text"] } },
|
| 72 |
-
serverName: "code", call: async ({ path, old_text, new_text }) => {
|
| 73 |
-
try { const cur = td.decode(await readFile(path)); if (!cur.includes(old_text)) return { text: "edit error: old_text not found", isError: true };
|
| 74 |
-
const next = cur.replace(old_text, new_text); await writeFile(path, next); return { text: `edited ${norm(path)} (${cur.length}→${next.length} bytes)`, isError: false }; }
|
| 75 |
-
catch (e) { return { text: "edit error: " + (e.message || e), isError: true }; }
|
| 76 |
-
},
|
| 77 |
-
},
|
| 78 |
-
{
|
| 79 |
-
def: { name: "list_files", description: "List all files in the workspace, optionally filtered by a glob (e.g. **/*.py).", inputSchema: { type: "object", properties: { glob: { type: "string" } }, required: [] } },
|
| 80 |
-
serverName: "code", call: async ({ glob }) => {
|
| 81 |
-
try { let files = await listAll(); if (glob) { const re = globToRe(glob); files = files.filter((f) => re.test(f)); }
|
| 82 |
-
return { text: files.length ? files.join("\n") : "(workspace empty)", isError: false }; }
|
| 83 |
-
catch (e) { return { text: "list error: " + (e.message || e), isError: true }; }
|
| 84 |
-
},
|
| 85 |
-
},
|
| 86 |
-
{
|
| 87 |
-
def: { name: "grep", description: "Search file contents for a regular expression. Returns matching path:line: text.", inputSchema: { type: "object", properties: { pattern: { type: "string" }, glob: { type: "string" } }, required: ["pattern"] } },
|
| 88 |
-
serverName: "code", call: async ({ pattern, glob }) => {
|
| 89 |
-
try { const re = new RegExp(pattern); let files = await listAll(); if (glob) { const g = globToRe(glob); files = files.filter((f) => g.test(f)); }
|
| 90 |
-
const hits = [];
|
| 91 |
-
for (const f of files) { let text; try { text = td.decode(await readFile(f)); } catch { continue; }
|
| 92 |
-
text.split("\n").forEach((ln, i) => { if (re.test(ln) && hits.length < 100) hits.push(`${f}:${i + 1}: ${ln.trim().slice(0, 160)}`); }); }
|
| 93 |
-
return { text: hits.length ? hits.join("\n") : "(no matches)", isError: false }; }
|
| 94 |
-
catch (e) { return { text: "grep error: " + (e.message || e), isError: true }; }
|
| 95 |
-
},
|
| 96 |
-
},
|
| 97 |
-
// ── κ-NATIVE: build a RENDERABLE app object, sealed (the neural-computer flagship: an agent
|
| 98 |
-
// produces a real app, content-addressed, that renders + re-derives — serverless, verifiable) ──
|
| 99 |
-
{
|
| 100 |
-
def: { name: "build_app", description: "Build a complete self-contained HTML app (one file, inline CSS/JS) and seal it as a content-addressed object. Returns its did:holo κ; the app renders live in the preview pane. Use for 'build me an app/page/tool' requests.", inputSchema: { type: "object", properties: { name: { type: "string" }, html: { type: "string" } }, required: ["name", "html"] } },
|
| 101 |
-
serverName: "code", call: async ({ name, html }) => {
|
| 102 |
-
try {
|
| 103 |
-
const path = (name.endsWith(".html") ? name : name + ".html");
|
| 104 |
-
await writeFile(path, html);
|
| 105 |
-
const bytes = te.encode(String(html));
|
| 106 |
-
const kappa = "did:holo:sha256:" + await sha256Hex(bytes);
|
| 107 |
-
// semantic skin: the app object carries a W3C @type (schema:SoftwareApplication + prov:Entity),
|
| 108 |
-
// written as a sidecar .jsonld so the object is self-describing to any agent / RDF tool.
|
| 109 |
-
const ld = appAsSoftware({ name: path, kappa, bytes: bytes.length });
|
| 110 |
-
await writeFile(path.replace(/\.html$/i, "") + ".app.jsonld", JSON.stringify(ld, null, 1));
|
| 111 |
-
return { text: `built app "${path}" (${html.length} bytes), sealed ${kappa.slice(0, 30)}… · typed ${ld["@type"].join("+")} — renders in the preview, re-derivable (Law L5).`, isError: false, render: { kind: "app", path: norm(path), kappa, html: String(html), ld } };
|
| 112 |
-
} catch (e) { return { text: "build_app error: " + (e.message || e), isError: true }; }
|
| 113 |
-
},
|
| 114 |
-
},
|
| 115 |
-
{
|
| 116 |
-
def: { name: "verify_object", description: "Re-derive a workspace file's content address and confirm it matches (Law L5 — verify every byte). Returns whether the bytes are authentic.", inputSchema: { type: "object", properties: { path: { type: "string" }, kappa: { type: "string" } }, required: ["path"] } },
|
| 117 |
-
serverName: "code", call: async ({ path, kappa }) => {
|
| 118 |
-
try {
|
| 119 |
-
const bytes = await readFile(path);
|
| 120 |
-
const got = "did:holo:sha256:" + await sha256Hex(bytes);
|
| 121 |
-
const ok = !kappa || got === kappa || got.startsWith(kappa) || kappa.startsWith(got.slice(0, kappa.length));
|
| 122 |
-
// verify = re-derive κ (Law L5) AND emit the W3C @type — apps are SoftwareApplication, all else DigitalDocument.
|
| 123 |
-
const ld = /\.html$/i.test(path) ? appAsSoftware({ name: norm(path), kappa: got, bytes: bytes.length })
|
| 124 |
-
: fileAsDocument({ path: norm(path), kappa: got, bytes: bytes.length });
|
| 125 |
-
return { text: `${norm(path)} re-derives to ${got.slice(0, 34)}…${kappa ? (ok ? " ✓ matches (authentic)" : " ✗ MISMATCH") : ""} · @type ${ld["@type"].join("+")}`, isError: false, render: { kind: "linked-data", ld } };
|
| 126 |
-
} catch (e) { return { text: "verify error: " + (e.message || e), isError: true }; }
|
| 127 |
-
},
|
| 128 |
-
},
|
| 129 |
-
];
|
| 130 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
core/keymap.js
DELETED
|
@@ -1,29 +0,0 @@
|
|
| 1 |
-
// core/keymap.js — the keyboard map as a data table (LibreChat's native set): Alt+N new chat,
|
| 2 |
-
// Alt+S toggle sidebar, Alt+W focus the input, Ctrl/⌘+Enter send, Esc stop generation,
|
| 3 |
-
// Ctrl/⌘+K model menu. `/` and `@` composer menus live in ui/composer.js (they are caret-local).
|
| 4 |
-
|
| 5 |
-
export const BINDINGS = [
|
| 6 |
-
{ combo: "Alt+N", action: "newChat", label: "New chat" },
|
| 7 |
-
{ combo: "Alt+S", action: "toggleNav", label: "Toggle sidebar" },
|
| 8 |
-
{ combo: "Alt+W", action: "focusInput", label: "Focus message box" },
|
| 9 |
-
{ combo: "Ctrl+Enter", action: "send", label: "Send message" },
|
| 10 |
-
{ combo: "Escape", action: "stop", label: "Stop generating" },
|
| 11 |
-
{ combo: "Ctrl+K", action: "modelMenu", label: "Choose model" },
|
| 12 |
-
];
|
| 13 |
-
|
| 14 |
-
export function installKeymap(handlers) {
|
| 15 |
-
const onKey = (e) => {
|
| 16 |
-
const ctrl = e.ctrlKey || e.metaKey;
|
| 17 |
-
const fire = (name) => { const fn = handlers[name]; if (fn) { e.preventDefault(); fn(e); } };
|
| 18 |
-
if (e.altKey && !ctrl && !e.shiftKey) {
|
| 19 |
-
if (e.code === "KeyN") return fire("newChat");
|
| 20 |
-
if (e.code === "KeyS") return fire("toggleNav");
|
| 21 |
-
if (e.code === "KeyW") return fire("focusInput");
|
| 22 |
-
}
|
| 23 |
-
if (ctrl && e.key === "Enter") return fire("send");
|
| 24 |
-
if (ctrl && !e.shiftKey && e.code === "KeyK") return fire("modelMenu");
|
| 25 |
-
if (e.key === "Escape") { const fn = handlers.stop; if (fn) fn(e); } // don't preventDefault — Esc also closes menus
|
| 26 |
-
};
|
| 27 |
-
document.addEventListener("keydown", onKey);
|
| 28 |
-
return () => document.removeEventListener("keydown", onKey);
|
| 29 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
core/mcp.js
DELETED
|
@@ -1,144 +0,0 @@
|
|
| 1 |
-
// core/mcp.js — a lean, browser-native Model Context Protocol CLIENT. Speaks the open MCP
|
| 2 |
-
// spec (JSON-RPC 2.0) over the Streamable HTTP transport (single endpoint, POST per message,
|
| 3 |
-
// optional SSE response bodies, Mcp-Session-Id header) with a fallback for plain HTTP+SSE
|
| 4 |
-
// servers. No SDK, no build step — the protocol is small and this is all of it that a chat
|
| 5 |
-
// client needs: initialize → tools/list → tools/call.
|
| 6 |
-
//
|
| 7 |
-
// Substrate twist: every connected server and every tool call is content-addressable — the
|
| 8 |
-
// caller can seal a PROV-O receipt around call inputs/outputs (see core/tools.js), so tool
|
| 9 |
-
// use gets the same verifiability as inference (Law L5).
|
| 10 |
-
|
| 11 |
-
const PROTOCOL_VERSION = "2025-03-26";
|
| 12 |
-
const CLIENT_INFO = { name: "holo-q", version: "2.0.0" };
|
| 13 |
-
|
| 14 |
-
let _seq = 0;
|
| 15 |
-
const nextId = () => ++_seq;
|
| 16 |
-
|
| 17 |
-
// Parse an SSE stream body and resolve with the JSON-RPC response matching `id`.
|
| 18 |
-
async function readSse(res, id) {
|
| 19 |
-
const reader = res.body.getReader(); const dec = new TextDecoder();
|
| 20 |
-
let buf = "", result = null;
|
| 21 |
-
for (;;) {
|
| 22 |
-
const { done, value } = await reader.read(); if (done) break;
|
| 23 |
-
buf += dec.decode(value, { stream: true });
|
| 24 |
-
let i;
|
| 25 |
-
while ((i = buf.indexOf("\n\n")) >= 0) {
|
| 26 |
-
const frame = buf.slice(0, i); buf = buf.slice(i + 2);
|
| 27 |
-
const data = frame.split("\n").filter((l) => l.startsWith("data:")).map((l) => l.slice(5).trim()).join("");
|
| 28 |
-
if (!data) continue;
|
| 29 |
-
try { const msg = JSON.parse(data); if (msg.id === id) { result = msg; reader.cancel().catch(() => {}); return result; } } catch {}
|
| 30 |
-
}
|
| 31 |
-
}
|
| 32 |
-
return result;
|
| 33 |
-
}
|
| 34 |
-
|
| 35 |
-
export function makeMcpClient({ url, headers = {}, name = "", timeoutMs = 30000 } = {}) {
|
| 36 |
-
let sessionId = null, initialized = null, serverInfo = null, toolCache = null;
|
| 37 |
-
|
| 38 |
-
async function rpc(method, params, { notification = false } = {}) {
|
| 39 |
-
const body = notification
|
| 40 |
-
? { jsonrpc: "2.0", method, ...(params ? { params } : {}) }
|
| 41 |
-
: { jsonrpc: "2.0", id: nextId(), method, ...(params ? { params } : {}) };
|
| 42 |
-
const ctrl = new AbortController();
|
| 43 |
-
const t = setTimeout(() => ctrl.abort(), timeoutMs);
|
| 44 |
-
try {
|
| 45 |
-
const res = await fetch(url, {
|
| 46 |
-
method: "POST", signal: ctrl.signal,
|
| 47 |
-
headers: { "content-type": "application/json", accept: "application/json, text/event-stream", ...(sessionId ? { "mcp-session-id": sessionId } : {}), ...headers },
|
| 48 |
-
body: JSON.stringify(body),
|
| 49 |
-
});
|
| 50 |
-
const sid = res.headers.get("mcp-session-id"); if (sid) sessionId = sid;
|
| 51 |
-
if (notification) return null;
|
| 52 |
-
if (res.status === 202) return null;
|
| 53 |
-
if (!res.ok) throw new Error(`MCP ${res.status} ${res.statusText}`);
|
| 54 |
-
const ctype = res.headers.get("content-type") || "";
|
| 55 |
-
const msg = ctype.includes("text/event-stream") ? await readSse(res, body.id) : await res.json();
|
| 56 |
-
if (!msg) throw new Error("MCP: no response for request " + body.id);
|
| 57 |
-
if (msg.error) throw new Error(`MCP ${method}: ${msg.error.message || JSON.stringify(msg.error)}`);
|
| 58 |
-
return msg.result;
|
| 59 |
-
} finally { clearTimeout(t); }
|
| 60 |
-
}
|
| 61 |
-
|
| 62 |
-
async function initialize() {
|
| 63 |
-
if (initialized) return initialized;
|
| 64 |
-
initialized = (async () => {
|
| 65 |
-
const r = await rpc("initialize", { protocolVersion: PROTOCOL_VERSION, capabilities: {}, clientInfo: CLIENT_INFO });
|
| 66 |
-
serverInfo = r && r.serverInfo || { name: name || url };
|
| 67 |
-
await rpc("notifications/initialized", undefined, { notification: true }).catch(() => {});
|
| 68 |
-
return r;
|
| 69 |
-
})();
|
| 70 |
-
return initialized;
|
| 71 |
-
}
|
| 72 |
-
|
| 73 |
-
// tools/list (cached; paginated per spec)
|
| 74 |
-
async function listTools(force = false) {
|
| 75 |
-
if (toolCache && !force) return toolCache;
|
| 76 |
-
await initialize();
|
| 77 |
-
const out = []; let cursor;
|
| 78 |
-
do {
|
| 79 |
-
const r = await rpc("tools/list", cursor ? { cursor } : {});
|
| 80 |
-
out.push(...(r.tools || [])); cursor = r.nextCursor;
|
| 81 |
-
} while (cursor);
|
| 82 |
-
toolCache = out;
|
| 83 |
-
return out;
|
| 84 |
-
}
|
| 85 |
-
|
| 86 |
-
// tools/call → { content: [{type:"text",text}|{type:"image",...}], isError }
|
| 87 |
-
async function callTool(toolName, args) {
|
| 88 |
-
await initialize();
|
| 89 |
-
const r = await rpc("tools/call", { name: toolName, arguments: args || {} });
|
| 90 |
-
return r;
|
| 91 |
-
}
|
| 92 |
-
|
| 93 |
-
return {
|
| 94 |
-
url, name: () => (serverInfo && serverInfo.name) || name || url,
|
| 95 |
-
initialize, listTools, callTool,
|
| 96 |
-
get serverInfo() { return serverInfo; },
|
| 97 |
-
};
|
| 98 |
-
}
|
| 99 |
-
|
| 100 |
-
// Flatten a tools/call result's content into plain text for the model's <tool_response>.
|
| 101 |
-
export function contentToText(result) {
|
| 102 |
-
if (!result) return "";
|
| 103 |
-
if (result.isError) return "ERROR: " + (result.content || []).map((c) => c.text || "").join("\n");
|
| 104 |
-
return (result.content || []).map((c) => c.type === "text" ? c.text : c.type === "image" ? "[image]" : c.type === "resource" ? JSON.stringify(c.resource).slice(0, 2000) : "").join("\n");
|
| 105 |
-
}
|
| 106 |
-
|
| 107 |
-
// ── DISCOVERY — the MCP universe, three rings out from the substrate ─────────────────────
|
| 108 |
-
// 1. the SUBSTRATE's own roster (/.well-known/mcp.json — same-origin, verifiable)
|
| 109 |
-
// 2. user-added servers (κ-pinned config in the boot index)
|
| 110 |
-
// 3. the OFFICIAL public MCP registry (registry.modelcontextprotocol.io — the open index
|
| 111 |
-
// of the ecosystem; remote/streamable-http entries are directly connectable from a tab)
|
| 112 |
-
export async function discoverSubstrate() {
|
| 113 |
-
try {
|
| 114 |
-
const r = await fetch("/.well-known/mcp.json"); if (!r.ok) return [];
|
| 115 |
-
const j = await r.json();
|
| 116 |
-
const servers = j.servers || j.mcpServers || (Array.isArray(j) ? j : [j]);
|
| 117 |
-
return (Array.isArray(servers) ? servers : Object.entries(servers).map(([k, v]) => ({ name: k, ...v })))
|
| 118 |
-
.map((s) => ({ source: "substrate", name: s.name || "hologram", url: s.url || s.endpoint || null, description: s.description || "", tools: s.tools || null, raw: s }))
|
| 119 |
-
.filter((s) => s.url || s.tools);
|
| 120 |
-
} catch { return []; }
|
| 121 |
-
}
|
| 122 |
-
|
| 123 |
-
export async function searchRegistry(query, { limit = 20 } = {}) {
|
| 124 |
-
// The official registry's public REST API (v0). Remote servers carry `remotes[]` with
|
| 125 |
-
// streamable-http/sse URLs — those are connectable straight from the browser.
|
| 126 |
-
const base = "https://registry.modelcontextprotocol.io/v0/servers";
|
| 127 |
-
const u = base + "?limit=" + limit + (query ? "&search=" + encodeURIComponent(query) : "");
|
| 128 |
-
const r = await fetch(u, { headers: { accept: "application/json" } });
|
| 129 |
-
if (!r.ok) throw new Error("registry " + r.status);
|
| 130 |
-
const j = await r.json();
|
| 131 |
-
return (j.servers || []).map((s) => {
|
| 132 |
-
const remote = (s.remotes || []).find((x) => /streamable|http/.test(x.type || "")) || (s.remotes || [])[0] || null;
|
| 133 |
-
return { source: "registry", name: s.name, description: s.description || "", version: s.version,
|
| 134 |
-
url: remote ? remote.url : null, transport: remote ? remote.type : (s.packages?.length ? "package (host-run)" : "unknown"), raw: s };
|
| 135 |
-
});
|
| 136 |
-
}
|
| 137 |
-
|
| 138 |
-
export async function discoverAgents() {
|
| 139 |
-
// The substrate's agent doors: NANDA AgentFacts + A2A AgentCards + Skills — one entry point.
|
| 140 |
-
try {
|
| 141 |
-
const r = await fetch("/.well-known/agents.json"); if (!r.ok) return null;
|
| 142 |
-
return await r.json();
|
| 143 |
-
} catch { return null; }
|
| 144 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
core/mcphub.js
DELETED
|
@@ -1,107 +0,0 @@
|
|
| 1 |
-
// core/mcphub.js — the MCP connection HUB: manages servers (substrate roster · user-added ·
|
| 2 |
-
// registry finds), their lifecycles and tool lists, and exposes the flat enabled-tool set the
|
| 3 |
-
// generation loop consumes. Server configs persist in the boot index (substrate-local).
|
| 4 |
-
//
|
| 5 |
-
// Discovery rings: (1) the substrate's own /.well-known/mcp.json (the OS's 30 verified tools),
|
| 6 |
-
// (2) servers the user added by URL, (3) the official public MCP registry. One hub, the
|
| 7 |
-
// whole universe reachable from a tab.
|
| 8 |
-
|
| 9 |
-
import { makeMcpClient, contentToText, discoverSubstrate, searchRegistry, discoverAgents } from "./mcp.js";
|
| 10 |
-
|
| 11 |
-
export function makeMcpHub({ chatStore, bus }) {
|
| 12 |
-
const servers = new Map(); // id → { id, name, url, headers, enabled, client, tools, status, error }
|
| 13 |
-
let loaded = false;
|
| 14 |
-
|
| 15 |
-
const INDEX_KEY = "index:org.hologram.HoloQ";
|
| 16 |
-
async function persist() {
|
| 17 |
-
const idx = await chatStore.getIndex();
|
| 18 |
-
idx.mcpServers = [...servers.values()].map((s) => ({ id: s.id, name: s.name, url: s.url, headers: s.headers || {}, enabled: !!s.enabled, source: s.source || "user" }));
|
| 19 |
-
const b = new TextEncoder().encode(JSON.stringify(idx));
|
| 20 |
-
await (chatStore.store.backend.putRaw ? chatStore.store.backend.putRaw(INDEX_KEY, b) : chatStore.store.backend.put(INDEX_KEY, b));
|
| 21 |
-
}
|
| 22 |
-
|
| 23 |
-
async function restore() {
|
| 24 |
-
if (loaded) return; loaded = true;
|
| 25 |
-
const idx = await chatStore.getIndex();
|
| 26 |
-
for (const s of idx.mcpServers || []) if (!servers.has(s.id)) servers.set(s.id, { ...s, client: null, tools: [], status: "saved" }); // never clobber a live entry
|
| 27 |
-
bus.emit("mcp-changed");
|
| 28 |
-
}
|
| 29 |
-
|
| 30 |
-
const norm = (url) => { try { return new URL(url, location.origin).href; } catch { return url; } };
|
| 31 |
-
const idOf = (url) => "mcp-" + norm(url).replace(/[^\w]+/g, "-").slice(0, 60);
|
| 32 |
-
|
| 33 |
-
async function add({ name, url, headers = {}, source = "user", enabled = true }) {
|
| 34 |
-
const u = norm(url); const id = idOf(u);
|
| 35 |
-
if (!servers.has(id)) servers.set(id, { id, name: name || u, url: u, headers, source, enabled, client: null, tools: [], status: "saved" });
|
| 36 |
-
const s = servers.get(id);
|
| 37 |
-
s.enabled = enabled; // adding is an explicit intent — (re)enable even if a saved entry existed
|
| 38 |
-
await connect(s.id).catch(() => {});
|
| 39 |
-
await persist();
|
| 40 |
-
bus.emit("mcp-changed");
|
| 41 |
-
return s;
|
| 42 |
-
}
|
| 43 |
-
|
| 44 |
-
async function connect(id) {
|
| 45 |
-
const s = servers.get(id); if (!s) return null;
|
| 46 |
-
s.status = "connecting"; s.error = null; bus.emit("mcp-changed");
|
| 47 |
-
try {
|
| 48 |
-
s.client = makeMcpClient({ url: s.url, headers: s.headers, name: s.name });
|
| 49 |
-
await s.client.initialize();
|
| 50 |
-
s.tools = await s.client.listTools();
|
| 51 |
-
s.name = s.client.name() || s.name;
|
| 52 |
-
s.status = "connected";
|
| 53 |
-
} catch (e) {
|
| 54 |
-
s.client = null; s.tools = []; s.status = "error"; s.error = String(e && e.message || e);
|
| 55 |
-
}
|
| 56 |
-
bus.emit("mcp-changed");
|
| 57 |
-
return s;
|
| 58 |
-
}
|
| 59 |
-
|
| 60 |
-
async function remove(id) { servers.delete(id); await persist(); bus.emit("mcp-changed"); }
|
| 61 |
-
async function setEnabled(id, on) { const s = servers.get(id); if (s) { s.enabled = !!on; if (on && !s.client) await connect(id); await persist(); bus.emit("mcp-changed"); } }
|
| 62 |
-
|
| 63 |
-
// The flat ARMED tool set the loop consumes: [{ def, serverName, call(args)→{text,isError} }].
|
| 64 |
-
// Per-server `toolFilter` (a name allowlist, set from the UI) selects which tools arm; without
|
| 65 |
-
// one, the first DEFAULT_ARM tools arm. A hard ceiling keeps the system block inside the model's
|
| 66 |
-
// context (a local model can't juggle 30 schemas — pick, like LibreChat's per-server selection).
|
| 67 |
-
const DEFAULT_ARM = 4, MAX_ARM = 8;
|
| 68 |
-
// the substrate's own roster is curated to its core verbs by default
|
| 69 |
-
const SUBSTRATE_DEFAULT = ["search_web", "resolve_object", "answer", "verify_object"];
|
| 70 |
-
function enabledTools() {
|
| 71 |
-
const out = [];
|
| 72 |
-
for (const s of servers.values()) {
|
| 73 |
-
if (!s.enabled || s.status !== "connected") continue;
|
| 74 |
-
const names = s.toolFilter && s.toolFilter.length ? s.toolFilter
|
| 75 |
-
: (s.source === "substrate" ? SUBSTRATE_DEFAULT.filter((n) => s.tools.some((t) => t.name === n)) : s.tools.slice(0, DEFAULT_ARM).map((t) => t.name));
|
| 76 |
-
for (const t of s.tools) {
|
| 77 |
-
if (!names.includes(t.name)) continue;
|
| 78 |
-
out.push({
|
| 79 |
-
def: { name: t.name, description: t.description, inputSchema: t.inputSchema },
|
| 80 |
-
serverName: s.name, serverId: s.id,
|
| 81 |
-
call: async (args) => { const r = await s.client.callTool(t.name, args); return { text: contentToText(r), isError: !!(r && r.isError) }; },
|
| 82 |
-
});
|
| 83 |
-
}
|
| 84 |
-
}
|
| 85 |
-
// de-dup by tool name (first server wins) — small models address tools by bare name
|
| 86 |
-
const seen = new Set();
|
| 87 |
-
return out.filter((t) => (seen.has(t.def.name) ? false : (seen.add(t.def.name), true))).slice(0, MAX_ARM);
|
| 88 |
-
}
|
| 89 |
-
async function setToolFilter(id, names) { const s = servers.get(id); if (s) { s.toolFilter = names && names.length ? names : null; bus.emit("mcp-changed"); } }
|
| 90 |
-
|
| 91 |
-
// Substrate ring: read the OS roster; candidate endpoints = declared, same-origin /mcp, the
|
| 92 |
-
// conventional localhost MCP port. First reachable wins.
|
| 93 |
-
async function discoverSubstrateServers() {
|
| 94 |
-
const entries = await discoverSubstrate();
|
| 95 |
-
const candidates = new Set();
|
| 96 |
-
for (const e of entries) if (e.url) candidates.add(norm(e.url));
|
| 97 |
-
candidates.add(norm("/mcp"));
|
| 98 |
-
candidates.add("http://127.0.0.1:8787/mcp");
|
| 99 |
-
return { entries, candidates: [...candidates] };
|
| 100 |
-
}
|
| 101 |
-
|
| 102 |
-
return {
|
| 103 |
-
restore, add, connect, remove, setEnabled, setToolFilter, enabledTools, persist,
|
| 104 |
-
list: () => [...servers.values()],
|
| 105 |
-
discoverSubstrateServers, searchRegistry, discoverAgents,
|
| 106 |
-
};
|
| 107 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
core/memory.js
DELETED
|
@@ -1,69 +0,0 @@
|
|
| 1 |
-
// core/memory.js — persistent USER MEMORY (LibreChat memory semantics, substrate-native).
|
| 2 |
-
// Memories are key/value facts (key validated /^[a-z_]+$/, value a complete sentence) stored as
|
| 3 |
-
// κ-objects with pointers in the boot index. They inject into the system block as the
|
| 4 |
-
// "# Existing memory:" section, and the model maintains them itself through two LOCAL tools
|
| 5 |
-
// (set_memory / delete_memory) armed alongside MCP tools in the agentic loop — no server,
|
| 6 |
-
// every write content-addressed and verifiable.
|
| 7 |
-
|
| 8 |
-
const LC = { lc: "https://librechat.ai/ns#" };
|
| 9 |
-
export const KEY_RE = /^[a-z_]+$/;
|
| 10 |
-
|
| 11 |
-
export function makeMemory(chatStore, { tokenLimit = 2000 } = {}) {
|
| 12 |
-
const { store, getIndex, newId } = chatStore;
|
| 13 |
-
const putIndex = async (idx) => { const b = new TextEncoder().encode(JSON.stringify(idx)); return store.backend.putRaw ? store.backend.putRaw("index:org.hologram.HoloQ", b) : store.backend.put("index:org.hologram.HoloQ", b); };
|
| 14 |
-
const tokensOf = (s) => Math.ceil((s || "").length / 4); // a fair, dependency-free estimate
|
| 15 |
-
|
| 16 |
-
async function list() { return (await getIndex()).memories || []; }
|
| 17 |
-
|
| 18 |
-
async function set(key, value) {
|
| 19 |
-
key = String(key || "").toLowerCase().trim();
|
| 20 |
-
if (!KEY_RE.test(key)) return { ok: false, error: "invalid key — use lowercase letters and underscores" };
|
| 21 |
-
const idx = await getIndex();
|
| 22 |
-
idx.memories = idx.memories || [];
|
| 23 |
-
const used = idx.memories.filter((m) => m.key !== key).reduce((n, m) => n + (m.tokenCount || 0), 0);
|
| 24 |
-
const tc = tokensOf(value);
|
| 25 |
-
if (used + tc > tokenLimit) return { ok: false, error: `memory full: ${used}+${tc} > ${tokenLimit} tokens` };
|
| 26 |
-
const obj = await store.makeObject({
|
| 27 |
-
type: ["schema:Statement", "prov:Entity"], context: [LC],
|
| 28 |
-
"lc:key": key, "lc:value": String(value || ""), "lc:tokenCount": tc,
|
| 29 |
-
"schema:dateModified": new Date().toISOString(),
|
| 30 |
-
});
|
| 31 |
-
const i = idx.memories.findIndex((m) => m.key === key);
|
| 32 |
-
const ptr = { key, kappa: obj.id, value: String(value || ""), tokenCount: tc, updated_at: new Date().toISOString() };
|
| 33 |
-
if (i >= 0) idx.memories[i] = ptr; else idx.memories.push(ptr);
|
| 34 |
-
await putIndex(idx);
|
| 35 |
-
return { ok: true, key, kappa: obj.id };
|
| 36 |
-
}
|
| 37 |
-
|
| 38 |
-
async function remove(key) {
|
| 39 |
-
const idx = await getIndex();
|
| 40 |
-
idx.memories = (idx.memories || []).filter((m) => m.key !== key);
|
| 41 |
-
await putIndex(idx);
|
| 42 |
-
return { ok: true };
|
| 43 |
-
}
|
| 44 |
-
|
| 45 |
-
// The system-block injection ("# Existing memory:" — the LibreChat convention).
|
| 46 |
-
async function injection() {
|
| 47 |
-
const mems = await list();
|
| 48 |
-
if (!mems.length) return "";
|
| 49 |
-
const used = mems.reduce((n, m) => n + (m.tokenCount || 0), 0);
|
| 50 |
-
return `# Memory Status:\nCurrent memory usage: ${used} tokens\nToken limit: ${tokenLimit} tokens\nRemaining capacity: ${Math.max(0, tokenLimit - used)} tokens\n\n# Existing memory:\n` +
|
| 51 |
-
mems.map((m) => `- ${m.key}: ${m.value}`).join("\n");
|
| 52 |
-
}
|
| 53 |
-
|
| 54 |
-
// The two LOCAL tools the agentic loop arms (same shape as MCP hub tools).
|
| 55 |
-
function localTools() {
|
| 56 |
-
return [
|
| 57 |
-
{
|
| 58 |
-
def: { name: "set_memory", description: "Remember a fact about the user across conversations. Only when the user explicitly asks (\"remember that…\"). key: lowercase_with_underscores; value: one complete sentence.", inputSchema: { type: "object", properties: { key: { type: "string" }, value: { type: "string" } }, required: ["key", "value"] } },
|
| 59 |
-
serverName: "memory", call: async (a) => { const r = await set(a.key, a.value); return { text: JSON.stringify(r), isError: !r.ok }; },
|
| 60 |
-
},
|
| 61 |
-
{
|
| 62 |
-
def: { name: "delete_memory", description: "Forget a remembered fact, by key. Only when the user explicitly asks.", inputSchema: { type: "object", properties: { key: { type: "string" } }, required: ["key"] } },
|
| 63 |
-
serverName: "memory", call: async (a) => { const r = await remove(a.key); return { text: JSON.stringify(r), isError: false }; },
|
| 64 |
-
},
|
| 65 |
-
];
|
| 66 |
-
}
|
| 67 |
-
|
| 68 |
-
return { list, set, remove, injection, localTools, tokenLimit };
|
| 69 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
core/presets.js
DELETED
|
@@ -1,54 +0,0 @@
|
|
| 1 |
-
// core/presets.js — preset CRUD (the saved parameter bundles a conversation spreads, LibreChat's
|
| 2 |
-
// conversationPreset semantics). Each preset is a κ-object in the store; the boot index keeps
|
| 3 |
-
// pointer rows { presetId, kappa, title } so listing is O(1) without a scan.
|
| 4 |
-
|
| 5 |
-
import { normPreset } from "./schema.js";
|
| 6 |
-
|
| 7 |
-
const LC = { lc: "https://librechat.ai/ns#" };
|
| 8 |
-
|
| 9 |
-
export function makePresets(chatStore) {
|
| 10 |
-
const { store, getIndex, newId } = chatStore;
|
| 11 |
-
const putIndex = async (idx) => { const b = new TextEncoder().encode(JSON.stringify(idx)); return store.backend.putRaw ? store.backend.putRaw("index:org.hologram.HoloQ", b) : store.backend.put("index:org.hologram.HoloQ", b); };
|
| 12 |
-
|
| 13 |
-
async function save(p) {
|
| 14 |
-
const norm = normPreset(p);
|
| 15 |
-
if (!norm.presetId) norm.presetId = newId("preset");
|
| 16 |
-
const obj = await store.makeObject({ type: ["schema:PropertyValue", "prov:Entity"], context: [LC], "schema:identifier": norm.presetId, "schema:name": norm.title, "lc:preset": norm });
|
| 17 |
-
const idx = await getIndex();
|
| 18 |
-
idx.presets = idx.presets || [];
|
| 19 |
-
const i = idx.presets.findIndex((x) => x.presetId === norm.presetId);
|
| 20 |
-
const ptr = { presetId: norm.presetId, kappa: obj.id, title: norm.title, defaultPreset: !!p.defaultPreset };
|
| 21 |
-
if (i >= 0) idx.presets[i] = ptr; else idx.presets.push(ptr);
|
| 22 |
-
if (p.defaultPreset) idx.presets.forEach((x) => { if (x.presetId !== norm.presetId) x.defaultPreset = false; });
|
| 23 |
-
await putIndex(idx);
|
| 24 |
-
return { ...norm, kappa: obj.id };
|
| 25 |
-
}
|
| 26 |
-
|
| 27 |
-
async function list() { return (await getIndex()).presets || []; }
|
| 28 |
-
|
| 29 |
-
async function get(presetId) {
|
| 30 |
-
const ptr = (await list()).find((x) => x.presetId === presetId); if (!ptr) return null;
|
| 31 |
-
const obj = await store.getObj(ptr.kappa); if (!obj) return null;
|
| 32 |
-
if (!(await store.verify(obj))) return null; // Law L5: a tampered preset is refused
|
| 33 |
-
return { ...normPreset(obj["lc:preset"]), kappa: ptr.kappa };
|
| 34 |
-
}
|
| 35 |
-
|
| 36 |
-
async function getDefault() {
|
| 37 |
-
const ptrs = await list(); const d = ptrs.find((x) => x.defaultPreset) || null;
|
| 38 |
-
return d ? get(d.presetId) : null;
|
| 39 |
-
}
|
| 40 |
-
|
| 41 |
-
async function remove(presetId) {
|
| 42 |
-
const idx = await getIndex();
|
| 43 |
-
idx.presets = (idx.presets || []).filter((x) => x.presetId !== presetId);
|
| 44 |
-
await putIndex(idx); // the κ-object stays content-addressed in the store (dedup'd, unreferenced)
|
| 45 |
-
}
|
| 46 |
-
|
| 47 |
-
return { save, list, get, getDefault, remove };
|
| 48 |
-
}
|
| 49 |
-
|
| 50 |
-
// The factory default — mirrors the engine's real capabilities (greedy decode is the
|
| 51 |
-
// deterministic, verifiable path; temperature stays 0 unless a model declares one).
|
| 52 |
-
export const DEFAULT_PRESET = () => normPreset({
|
| 53 |
-
title: "Default", temperature: 0, maxOutputTokens: 900, promptPrefix: "",
|
| 54 |
-
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
core/promptsLib.js
DELETED
|
@@ -1,109 +0,0 @@
|
|
| 1 |
-
// core/promptsLib.js — the PROMPTS LIBRARY as κ-objects (LibreChat prompt-group semantics).
|
| 2 |
-
// A group carries name · category · oneliner · command (the /slash trigger) and points at its
|
| 3 |
-
// PRODUCTION version; every version is its own immutable κ-object (text or chat type). The
|
| 4 |
-
// {{variable}} syntax matches LibreChat: {{name}}, {{name:opt1|opt2}} dropdowns, and the
|
| 5 |
-
// special variables {{current_date}} {{current_datetime}} {{iso_datetime}} {{current_user}}.
|
| 6 |
-
|
| 7 |
-
const LC = { lc: "https://librechat.ai/ns#" };
|
| 8 |
-
export const COMMAND_RE = /^[a-z0-9-]{1,56}$/;
|
| 9 |
-
export const VAR_RE = /{{([^{}]+?)}}/g;
|
| 10 |
-
const SPECIAL = ["current_date", "current_datetime", "iso_datetime", "current_user"];
|
| 11 |
-
|
| 12 |
-
export function detectVariables(text) {
|
| 13 |
-
const out = []; const seen = new Set(); let m;
|
| 14 |
-
VAR_RE.lastIndex = 0;
|
| 15 |
-
while ((m = VAR_RE.exec(text || ""))) {
|
| 16 |
-
const raw = m[1].trim();
|
| 17 |
-
const name = raw.split(":")[0].trim();
|
| 18 |
-
if (SPECIAL.includes(name.toLowerCase()) || seen.has(name)) continue;
|
| 19 |
-
seen.add(name);
|
| 20 |
-
const opts = raw.includes(":") ? raw.slice(raw.indexOf(":") + 1).split("|").map((s) => s.trim()).filter(Boolean) : null;
|
| 21 |
-
out.push({ name, raw, options: opts });
|
| 22 |
-
}
|
| 23 |
-
return out;
|
| 24 |
-
}
|
| 25 |
-
|
| 26 |
-
export function replaceSpecialVars(text, { userName = "operator" } = {}) {
|
| 27 |
-
const d = new Date();
|
| 28 |
-
const pad = (n) => String(n).padStart(2, "0");
|
| 29 |
-
const day = ["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"][d.getDay()];
|
| 30 |
-
const date = `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}`;
|
| 31 |
-
const time = `${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`;
|
| 32 |
-
const off = -d.getTimezoneOffset(); const tz = (off >= 0 ? "+" : "-") + pad(Math.floor(Math.abs(off) / 60)) + ":" + pad(Math.abs(off) % 60);
|
| 33 |
-
const sub = (t, name, v) => t.replace(new RegExp(`{{\\s*${name}\\s*}}`, "gi"), v);
|
| 34 |
-
let out = text || "";
|
| 35 |
-
out = sub(out, "current_date", `${date} (${day})`);
|
| 36 |
-
out = sub(out, "current_datetime", `${date} ${time} ${tz} (${day})`);
|
| 37 |
-
out = sub(out, "iso_datetime", d.toISOString());
|
| 38 |
-
out = sub(out, "current_user", userName);
|
| 39 |
-
return out;
|
| 40 |
-
}
|
| 41 |
-
|
| 42 |
-
export function fillVariables(text, values) {
|
| 43 |
-
let out = text || "";
|
| 44 |
-
for (const [raw, v] of Object.entries(values || {})) {
|
| 45 |
-
const esc = raw.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
| 46 |
-
out = out.replace(new RegExp(`{{\\s*${esc}\\s*}}`, "g"), v);
|
| 47 |
-
}
|
| 48 |
-
return out;
|
| 49 |
-
}
|
| 50 |
-
|
| 51 |
-
export function makePromptsLib(chatStore) {
|
| 52 |
-
const { store, getIndex, newId } = chatStore;
|
| 53 |
-
const putIndex = async (idx) => { const b = new TextEncoder().encode(JSON.stringify(idx)); return store.backend.putRaw ? store.backend.putRaw("index:org.hologram.HoloQ", b) : store.backend.put("index:org.hologram.HoloQ", b); };
|
| 54 |
-
|
| 55 |
-
// Save a version into a group (creates the group when groupId is null); production by default.
|
| 56 |
-
async function save({ groupId, name, category = "", oneliner = "", command = "", prompt, type = "text", production = true }) {
|
| 57 |
-
command = (command || "").toLowerCase();
|
| 58 |
-
if (command && !COMMAND_RE.test(command)) throw new Error("command must be a-z 0-9 dash, ≤56 chars");
|
| 59 |
-
const versionObj = await store.makeObject({
|
| 60 |
-
type: ["schema:CreativeWork", "prov:Entity"], context: [LC],
|
| 61 |
-
"lc:prompt": prompt || "", "lc:type": type === "chat" ? "chat" : "text",
|
| 62 |
-
"schema:dateCreated": new Date().toISOString(),
|
| 63 |
-
});
|
| 64 |
-
const idx = await getIndex();
|
| 65 |
-
idx.promptGroups = idx.promptGroups || [];
|
| 66 |
-
let g = groupId ? idx.promptGroups.find((x) => x.groupId === groupId) : null;
|
| 67 |
-
if (!g) {
|
| 68 |
-
g = { groupId: newId("pgroup"), name: name || "New Prompt", category, oneliner, command, versions: [], productionKappa: null, numberOfGenerations: 0 };
|
| 69 |
-
idx.promptGroups.push(g);
|
| 70 |
-
}
|
| 71 |
-
if (name) g.name = name;
|
| 72 |
-
if (category !== undefined) g.category = category;
|
| 73 |
-
if (oneliner !== undefined) g.oneliner = oneliner;
|
| 74 |
-
if (command !== undefined) g.command = command;
|
| 75 |
-
g.versions.push({ kappa: versionObj.id, createdAt: new Date().toISOString() });
|
| 76 |
-
if (production || !g.productionKappa) g.productionKappa = versionObj.id;
|
| 77 |
-
await putIndex(idx);
|
| 78 |
-
return { ...g };
|
| 79 |
-
}
|
| 80 |
-
|
| 81 |
-
async function list() { return (await getIndex()).promptGroups || []; }
|
| 82 |
-
|
| 83 |
-
async function production(groupId) {
|
| 84 |
-
const g = (await list()).find((x) => x.groupId === groupId); if (!g || !g.productionKappa) return null;
|
| 85 |
-
const obj = await store.getObj(g.productionKappa); if (!obj) return null;
|
| 86 |
-
if (!(await store.verify(obj))) return null; // Law L5
|
| 87 |
-
return { group: g, prompt: obj["lc:prompt"], type: obj["lc:type"], kappa: g.productionKappa };
|
| 88 |
-
}
|
| 89 |
-
|
| 90 |
-
async function setProduction(groupId, kappa) {
|
| 91 |
-
const idx = await getIndex();
|
| 92 |
-
const g = (idx.promptGroups || []).find((x) => x.groupId === groupId);
|
| 93 |
-
if (g && g.versions.some((v) => v.kappa === kappa)) { g.productionKappa = kappa; await putIndex(idx); }
|
| 94 |
-
}
|
| 95 |
-
|
| 96 |
-
async function recordUse(groupId) {
|
| 97 |
-
const idx = await getIndex();
|
| 98 |
-
const g = (idx.promptGroups || []).find((x) => x.groupId === groupId);
|
| 99 |
-
if (g) { g.numberOfGenerations = (g.numberOfGenerations || 0) + 1; await putIndex(idx); }
|
| 100 |
-
}
|
| 101 |
-
|
| 102 |
-
async function remove(groupId) {
|
| 103 |
-
const idx = await getIndex();
|
| 104 |
-
idx.promptGroups = (idx.promptGroups || []).filter((x) => x.groupId !== groupId);
|
| 105 |
-
await putIndex(idx);
|
| 106 |
-
}
|
| 107 |
-
|
| 108 |
-
return { save, list, production, setProduction, recordUse, remove };
|
| 109 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
core/q-brain-fast.mjs
DELETED
|
@@ -1,153 +0,0 @@
|
|
| 1 |
-
// core/q-brain-fast.mjs — Q's FAST on-device brain, drop-in for createHoloModelBrain.
|
| 2 |
-
//
|
| 3 |
-
// Same provider shape (load · generate → text-delta async-iterator · chat · info · setSkill), so
|
| 4 |
-
// holo-q-contact.mjs's makeQResponder / makeQGroupResponder bind it with ZERO changes: Q rides the exact
|
| 5 |
-
// stream→finalize pipeline, it just sources its bytes from THIS engine (core/loader + core/engine — the
|
| 6 |
-
// native-ternary κ-object path with the fixed incremental-detok decode) instead of the qwen holo-brain.
|
| 7 |
-
//
|
| 8 |
-
// Why this exists: the messenger's default brain (qwen2.5-0.5b via holo-brain-engine) is heavy and its
|
| 9 |
-
// app-path decode was O(n²). This one loads a native-ternary BitNet κ-object (0.69 GB, verified per-block),
|
| 10 |
-
// decodes at ~70 tok/s warm, and streams byte-identical incremental text. It is 100% on-device at decode
|
| 11 |
-
// time — only a ONE-TIME tokenizer-header Range fetch touches the source host at load (the weights are the
|
| 12 |
-
// local, L5-verified b/<κ>.gz blocks). Bundle the header to make load fully egress-free (follow-up).
|
| 13 |
-
//
|
| 14 |
-
// URL discipline (the one gotcha): core/loader's MODELS use a PAGE-relative kappaUrl ("./models/<name>"),
|
| 15 |
-
// which is wrong from any page other than /apps/q/. loadKappaObject resolves the manifest, blocks AND the
|
| 16 |
-
// bundled tokenizer relative to its baseUrl, so we override kappaUrl to an ABSOLUTE mount ("/apps/q/models/
|
| 17 |
-
// <name>") — that makes the whole load self-contained from the messenger (or anywhere).
|
| 18 |
-
|
| 19 |
-
import { ready, loadModel, MODELS } from "./loader.js";
|
| 20 |
-
import { createEngine } from "./engine.js";
|
| 21 |
-
import { selfFacts, selfPersona, selfIntro } from "./q-self.mjs"; // Q's live, grounded self-knowledge (M0)
|
| 22 |
-
|
| 23 |
-
// The default fast brain = BitNet-2B (native ternary, llama3 template, coherent — Falcon-E degenerates).
|
| 24 |
-
// Override via opts.family / opts.modelName. kappaBase is the absolute mount core/loader's models live under.
|
| 25 |
-
const DEFAULTS = { family: "BitNet", kappaBase: "/apps/q/models", maxTokens: 512 };
|
| 26 |
-
|
| 27 |
-
function pickModel(cfg) {
|
| 28 |
-
const want = String(cfg.modelName || cfg.family || "BitNet").toLowerCase();
|
| 29 |
-
const m = MODELS.find((x) => (x.fam || "").toLowerCase() === want || (x.name || "").toLowerCase().includes(want)) || MODELS.find((x) => (x.fam || "").toLowerCase() === "bitnet");
|
| 30 |
-
if (!m) throw new Error("q-brain-fast: no model matches " + want);
|
| 31 |
-
// An ABSOLUTE host URL (the κ-object now lives on HF, not on this machine) is already page-independent —
|
| 32 |
-
// use it verbatim. Only a page-relative "./models/<name>" needs absolute-mounting so it resolves from ANY page.
|
| 33 |
-
if (/^https?:\/\//.test(m.kappaUrl || "")) return { ...m };
|
| 34 |
-
const rel = String(m.kappaUrl || "").replace(/^\.?\//, "").replace(/^models\//, ""); // "./models/bitnet-2b" → "bitnet-2b"
|
| 35 |
-
return { ...m, kappaUrl: String(cfg.kappaBase).replace(/\/+$/, "") + "/" + rel };
|
| 36 |
-
}
|
| 37 |
-
|
| 38 |
-
// Render an [{role,content}] history (system + turns, as makeQResponder builds it) to the model's chat
|
| 39 |
-
// template — the multi-turn generalization of core/engine's single-turn frameTurn. Covers the templates
|
| 40 |
-
// the engine knows; unknown families fall back to a persona-led last-user Q/A frame.
|
| 41 |
-
function frameHistory(M, history) {
|
| 42 |
-
const list = Array.isArray(history) ? history : [];
|
| 43 |
-
// merge ALL system turns into one system block (persona + any injected context, e.g. M1 grounded retrieval) —
|
| 44 |
-
// taking only the first would silently DROP injected context and the model would fall back to a generic refusal.
|
| 45 |
-
const persona = list.filter((x) => x && x.role === "system" && x.content).map((x) => x.content).join("\n\n");
|
| 46 |
-
const turns = list.filter((x) => x && x.role !== "system" && (x.content || "").length);
|
| 47 |
-
|
| 48 |
-
if (M.llama3) {
|
| 49 |
-
let s = persona ? `<|start_header_id|>system<|end_header_id|>\n\n${persona}<|eot_id|>` : "";
|
| 50 |
-
for (const t of turns) s += `<|start_header_id|>${t.role === "assistant" ? "assistant" : "user"}<|end_header_id|>\n\n${t.content}<|eot_id|>`;
|
| 51 |
-
return s + `<|start_header_id|>assistant<|end_header_id|>\n\n`;
|
| 52 |
-
}
|
| 53 |
-
if (M.qwen) {
|
| 54 |
-
const noThink = M.qwen3 ? "<think>\n\n</think>\n\n" : "";
|
| 55 |
-
let s = persona ? `<|im_start|>system\n${persona}<|im_end|>\n` : "";
|
| 56 |
-
for (const t of turns) s += `<|im_start|>${t.role === "assistant" ? "assistant" : "user"}\n${t.content}<|im_end|>\n`;
|
| 57 |
-
return s + `<|im_start|>assistant\n` + noThink;
|
| 58 |
-
}
|
| 59 |
-
if (M.olmo) {
|
| 60 |
-
let s = persona ? `<|system|>\n${persona}\n` : "";
|
| 61 |
-
for (const t of turns) s += t.role === "assistant" ? `<|assistant|>\n${t.content}\n` : `<|user|>\n${t.content}\n`;
|
| 62 |
-
return s + `<|assistant|>\n`;
|
| 63 |
-
}
|
| 64 |
-
// word-frame / plain: only the last user turn carries (base models have no multi-turn template)
|
| 65 |
-
const lastUser = [...turns].reverse().find((t) => t.role !== "assistant");
|
| 66 |
-
const q = (lastUser && lastUser.content) || "";
|
| 67 |
-
if (M.userWord) return (persona ? persona + "\n" : "") + "User: " + q + "\nFalcon:";
|
| 68 |
-
return (persona ? persona + "\n" : "") + "Question: " + q + "\nAnswer:";
|
| 69 |
-
}
|
| 70 |
-
|
| 71 |
-
export function createFastQBrain(opts = {}) {
|
| 72 |
-
const cfg = Object.assign({}, DEFAULTS, opts);
|
| 73 |
-
const M = pickModel(cfg);
|
| 74 |
-
let engine = null, loadingP = null;
|
| 75 |
-
let info = { ready: false, model: M.name, device: null, resident: false };
|
| 76 |
-
|
| 77 |
-
// encode a history to the running token ids (framed + optional bos), ready for engine.generate
|
| 78 |
-
function idsFor(history) {
|
| 79 |
-
let ids = engine.tokenize(frameHistory(M, history));
|
| 80 |
-
if (M.bos && engine.bosId != null) ids = [engine.bosId, ...ids];
|
| 81 |
-
return ids;
|
| 82 |
-
}
|
| 83 |
-
|
| 84 |
-
async function load(onProgress) {
|
| 85 |
-
if (engine) return info;
|
| 86 |
-
if (loadingP) return loadingP;
|
| 87 |
-
loadingP = (async () => {
|
| 88 |
-
if (!(typeof navigator !== "undefined" && navigator.gpu)) throw new Error("no WebGPU on this device");
|
| 89 |
-
await ready(); // wasm tokenizer init (shared instance)
|
| 90 |
-
const loaded = await loadModel(M, {
|
| 91 |
-
onStatus: () => {},
|
| 92 |
-
onProgress: (d, t, w) => { try { onProgress && onProgress({ done: d, total: t, phase: w, model: M.name }); } catch (e) {} },
|
| 93 |
-
});
|
| 94 |
-
if (!loaded || !loaded.gpu) throw new Error("q-brain-fast: model load failed (" + M.name + ")");
|
| 95 |
-
engine = await createEngine(M, loaded);
|
| 96 |
-
// MEASURED from the real engine, not asserted: device/resident reflect an engine that actually uploaded
|
| 97 |
-
// weights to the GPU (dims + gpuBytes are live signals) — so info() can never claim residency it lacks.
|
| 98 |
-
info = { ready: true, model: M.name, device: (engine && engine.dims ? "webgpu" : null), resident: !!(engine && (engine.gpuBytes || engine.dims)) };
|
| 99 |
-
return info;
|
| 100 |
-
})().catch((e) => { loadingP = null; throw e; });
|
| 101 |
-
return loadingP;
|
| 102 |
-
}
|
| 103 |
-
async function ensure(onProgress) { if (!engine) await load(onProgress); return engine; }
|
| 104 |
-
|
| 105 |
-
// generate(history, { signal, onProgress }) → async-iterator of TEXT DELTAS (exactly what makeQResponder
|
| 106 |
-
// accumulates + paints via onDelta). engine.generate reports CUMULATIVE text per step; we diff to deltas
|
| 107 |
-
// and pump them through a small queue so this stays a clean generator (and honors the abort signal).
|
| 108 |
-
async function* generate(history, o = {}) {
|
| 109 |
-
await ensure(o.onProgress);
|
| 110 |
-
if (!engine) return;
|
| 111 |
-
const signal = o.signal || null;
|
| 112 |
-
const ids = idsFor(history);
|
| 113 |
-
const cap = o.maxTokens || cfg.maxTokens || M.cap || 256;
|
| 114 |
-
|
| 115 |
-
const queue = []; let done = false, wake = null, prev = "";
|
| 116 |
-
const kick = () => { if (wake) { const w = wake; wake = null; w(); } };
|
| 117 |
-
const run = engine.generate(ids, {
|
| 118 |
-
maxNew: cap, signal,
|
| 119 |
-
onToken: ({ text }) => { const d = (text || "").slice(prev.length); if (d) { prev = text; queue.push(d); kick(); } },
|
| 120 |
-
}).then(() => { done = true; kick(); }).catch(() => { done = true; kick(); });
|
| 121 |
-
|
| 122 |
-
while (true) {
|
| 123 |
-
if (queue.length) { yield queue.shift(); continue; }
|
| 124 |
-
if (done) break;
|
| 125 |
-
if (signal && signal.aborted) break;
|
| 126 |
-
await new Promise((r) => (wake = r));
|
| 127 |
-
}
|
| 128 |
-
try { await run; } catch (e) {}
|
| 129 |
-
}
|
| 130 |
-
|
| 131 |
-
// chat(history, opts) → full string (used by window.HoloQ.generate + light background features)
|
| 132 |
-
async function chat(history, o = {}) {
|
| 133 |
-
await ensure(o.onProgress);
|
| 134 |
-
if (!engine) return "";
|
| 135 |
-
const res = await engine.generate(idsFor(history), { maxNew: o.maxTokens || cfg.maxTokens || M.cap || 256, signal: o.signal || null });
|
| 136 |
-
return ((res && res.text) || "").trim();
|
| 137 |
-
}
|
| 138 |
-
|
| 139 |
-
// native-ternary κ-object → no LoRA adapters; skill routing is a no-op (the base is the specialist).
|
| 140 |
-
const setSkill = async () => ({ adapter: false, unsupported: true });
|
| 141 |
-
const setAdapter = async () => ({ adapter: false, unsupported: true });
|
| 142 |
-
|
| 143 |
-
// Q's LIVE self-knowledge (M0), derived from THIS instance's real model + engine κ — the grounded truth
|
| 144 |
-
// every surface leads with so Q is honestly self-aware and never confabulates a cloud identity. Available
|
| 145 |
-
// even before load (identity is in M); the κ fills in once the engine is resident.
|
| 146 |
-
const facts = () => selfFacts({ model: M, engine });
|
| 147 |
-
const persona = () => selfPersona({ model: M, engine });
|
| 148 |
-
const intro = () => selfIntro({ model: M, engine });
|
| 149 |
-
|
| 150 |
-
return { id: "q-brain-fast-" + (M.fam || M.name), load, generate, chat, setSkill, setAdapter, info: () => info, facts, persona, intro };
|
| 151 |
-
}
|
| 152 |
-
|
| 153 |
-
export default createFastQBrain;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
core/schema.js
DELETED
|
@@ -1,85 +0,0 @@
|
|
| 1 |
-
// core/schema.js — lean, dependency-free validators ("zod-lite") for the LibreChat-faithful
|
| 2 |
-
// Conversation / Message / Preset shapes. Field names mirror LibreChat's data model (packages/
|
| 3 |
-
// data-schemas) so an exported conversation interoperates; values are validated/normalised, not
|
| 4 |
-
// hashed (the κ-object store is what makes them content-addressed). No build step, no dep.
|
| 5 |
-
|
| 6 |
-
const isStr = (v) => typeof v === "string";
|
| 7 |
-
const isBool = (v) => typeof v === "boolean";
|
| 8 |
-
const isNum = (v) => typeof v === "number" && !Number.isNaN(v);
|
| 9 |
-
const arr = (v) => (Array.isArray(v) ? v : []);
|
| 10 |
-
const clampNum = (v, lo, hi, d) => (isNum(v) ? Math.min(hi, Math.max(lo, v)) : d);
|
| 11 |
-
|
| 12 |
-
// A chat message (one node in the parentMessageId tree).
|
| 13 |
-
export function normMessage(m = {}) {
|
| 14 |
-
return {
|
| 15 |
-
messageId: isStr(m.messageId) ? m.messageId : null,
|
| 16 |
-
conversationId: isStr(m.conversationId) ? m.conversationId : null,
|
| 17 |
-
parentMessageId: isStr(m.parentMessageId) ? m.parentMessageId : null, // tree edge
|
| 18 |
-
sender: isStr(m.sender) ? m.sender : (m.isCreatedByUser ? "User" : "Assistant"),
|
| 19 |
-
isCreatedByUser: isBool(m.isCreatedByUser) ? m.isCreatedByUser : false,
|
| 20 |
-
model: isStr(m.model) ? m.model : null,
|
| 21 |
-
text: isStr(m.text) ? m.text : "",
|
| 22 |
-
content: arr(m.content), // structured parts (text/tool/image/reasoning)
|
| 23 |
-
tokenCount: isNum(m.tokenCount) ? m.tokenCount : null,
|
| 24 |
-
error: m.error || null,
|
| 25 |
-
unfinished: !!m.unfinished,
|
| 26 |
-
feedback: m.feedback && isStr(m.feedback.rating || "") ? { rating: m.feedback.rating, tag: m.feedback.tag || null, text: m.feedback.text || "" } : (m.feedback || null),
|
| 27 |
-
files: arr(m.files),
|
| 28 |
-
createdAt: isStr(m.createdAt) ? m.createdAt : null,
|
| 29 |
-
receiptKappa: isStr(m.receiptKappa) ? m.receiptKappa : null, // links the PROV-O inference receipt
|
| 30 |
-
};
|
| 31 |
-
}
|
| 32 |
-
|
| 33 |
-
// A conversation (spreads the active preset, like LibreChat's conversationPreset).
|
| 34 |
-
export function normConversation(c = {}) {
|
| 35 |
-
return {
|
| 36 |
-
conversationId: isStr(c.conversationId) ? c.conversationId : null,
|
| 37 |
-
title: isStr(c.title) ? c.title : "New Chat",
|
| 38 |
-
tags: arr(c.tags),
|
| 39 |
-
files: arr(c.files),
|
| 40 |
-
folder: isStr(c.folder) ? c.folder : null,
|
| 41 |
-
archived: !!c.archived,
|
| 42 |
-
favorite: !!c.favorite,
|
| 43 |
-
isTemporary: !!c.isTemporary,
|
| 44 |
-
preset: c.preset ? normPreset(c.preset) : null,
|
| 45 |
-
createdAt: isStr(c.createdAt) ? c.createdAt : null,
|
| 46 |
-
updatedAt: isStr(c.updatedAt) ? c.updatedAt : null,
|
| 47 |
-
};
|
| 48 |
-
}
|
| 49 |
-
|
| 50 |
-
// A preset / parameter bundle. Bounds mirror common LLM ranges; model is the local κ-object id.
|
| 51 |
-
export function normPreset(p = {}) {
|
| 52 |
-
return {
|
| 53 |
-
presetId: isStr(p.presetId) ? p.presetId : null,
|
| 54 |
-
title: isStr(p.title) ? p.title : "Preset",
|
| 55 |
-
model: isStr(p.model) ? p.model : null,
|
| 56 |
-
temperature: clampNum(p.temperature, 0, 2, 0),
|
| 57 |
-
top_p: clampNum(p.top_p, 0, 1, 1),
|
| 58 |
-
top_k: clampNum(p.top_k, 0, 200, 40),
|
| 59 |
-
maxOutputTokens: clampNum(p.maxOutputTokens, 1, 8192, 1024),
|
| 60 |
-
promptPrefix: isStr(p.promptPrefix) ? p.promptPrefix : "",
|
| 61 |
-
presence_penalty: clampNum(p.presence_penalty, -2, 2, 0),
|
| 62 |
-
frequency_penalty: clampNum(p.frequency_penalty, -2, 2, 0),
|
| 63 |
-
stop: arr(p.stop),
|
| 64 |
-
iconURL: isStr(p.iconURL) ? p.iconURL : null,
|
| 65 |
-
isArchived: !!p.isArchived,
|
| 66 |
-
};
|
| 67 |
-
}
|
| 68 |
-
|
| 69 |
-
// Walk a flat message list into the LibreChat parentMessageId tree → {roots, byId, childrenOf}.
|
| 70 |
-
export function buildTree(messages) {
|
| 71 |
-
const byId = new Map(); const childrenOf = new Map(); const roots = [];
|
| 72 |
-
for (const m of messages) { byId.set(m.messageId, m); childrenOf.set(m.messageId, []); }
|
| 73 |
-
for (const m of messages) {
|
| 74 |
-
if (m.parentMessageId && childrenOf.has(m.parentMessageId)) childrenOf.get(m.parentMessageId).push(m.messageId);
|
| 75 |
-
else roots.push(m.messageId);
|
| 76 |
-
}
|
| 77 |
-
return { roots, byId, childrenOf };
|
| 78 |
-
}
|
| 79 |
-
|
| 80 |
-
// The active thread = follow the chosen child at each branch from a root to a leaf.
|
| 81 |
-
export function threadOf(tree, { choose = (siblings) => siblings[siblings.length - 1] } = {}) {
|
| 82 |
-
const out = []; let cur = tree.roots.length ? choose(tree.roots) : null;
|
| 83 |
-
while (cur) { out.push(tree.byId.get(cur)); const kids = tree.childrenOf.get(cur) || []; cur = kids.length ? choose(kids) : null; }
|
| 84 |
-
return out;
|
| 85 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
core/semantic-witness.mjs
DELETED
|
@@ -1,33 +0,0 @@
|
|
| 1 |
-
// core/semantic-witness.mjs — the W3C SEMANTIC-TYPING gate (C2, extended to all κ-objects).
|
| 2 |
-
// Proves EVERY substrate object kind yields a node that is simultaneously: content-addressed (Law L1,
|
| 3 |
-
// @id = did:holo), schema.org-typed (WHAT it is), and PROV-O-bearing (HOW it came to be) — i.e. legible
|
| 4 |
-
// to any W3C JSON-LD/RDF tool. Pure + node-runnable: `node core/semantic-witness.mjs`. Exit 1 on any fail.
|
| 5 |
-
|
| 6 |
-
import { linkedDataFor, skillAsHowTo, fileAsDocument, appAsSoftware, modelAsSource, asLinkedData, verifySemantic, HOLO_CONTEXT } from "./semantic.js";
|
| 7 |
-
|
| 8 |
-
const K = (h) => "did:holo:sha256:" + h.padEnd(64, "0").slice(0, 64);
|
| 9 |
-
const cases = [
|
| 10 |
-
{ kind: "skill", node: skillAsHowTo({ name: "Format JSON", description: "When asked to pretty-print JSON", instructions: "1. Parse the text\n2. Re-serialize with 2-space indent", kappa: K("a1"), prov: [{ v: 1, kappa: K("a0") }, { v: 2, kappa: K("a1") }] }), want: "schema:HowTo" },
|
| 11 |
-
{ kind: "file", node: fileAsDocument({ path: "/src/main.py", kappa: K("b1"), bytes: 412 }), want: "schema:DigitalDocument" },
|
| 12 |
-
{ kind: "app", node: appAsSoftware({ name: "counter.html", kappa: K("c1"), bytes: 980 }), want: "schema:SoftwareApplication" },
|
| 13 |
-
{ kind: "model", node: modelAsSource({ name: "BitNet-2B-4T", family: "BitNet", params: "0.69 GB", format: "t2 1.58-bit κ", kappa: K("19e16e6d") }), want: "schema:SoftwareSourceCode" },
|
| 14 |
-
{ kind: "conversation", node: linkedDataFor("conversation", { kappa: K("d1"), props: { "schema:name": "chat #1" } }), want: "schema:Conversation" },
|
| 15 |
-
{ kind: "receipt", node: asLinkedData({ kind: "receipt", kappa: K("e1"), props: { "schema:name": "tool-call receipt" } }), want: "schema:CreativeWork" },
|
| 16 |
-
];
|
| 17 |
-
|
| 18 |
-
let pass = 0;
|
| 19 |
-
console.log("W3C semantic-typing gate — every κ-object is schema.org-typed + PROV-O + content-addressed\n");
|
| 20 |
-
for (const c of cases) {
|
| 21 |
-
const v = verifySemantic(c.node);
|
| 22 |
-
const types = [].concat(c.node["@type"]);
|
| 23 |
-
const hasWant = types.includes(c.want);
|
| 24 |
-
const ctxOk = c.node["@context"] === HOLO_CONTEXT || !!c.node["@context"];
|
| 25 |
-
// JSON round-trip must be lossless (it's just data)
|
| 26 |
-
let rt = false; try { rt = JSON.stringify(JSON.parse(JSON.stringify(c.node))) === JSON.stringify(c.node); } catch {}
|
| 27 |
-
const ok = v.ok && hasWant && ctxOk && rt;
|
| 28 |
-
if (ok) pass++;
|
| 29 |
-
console.log(`${ok ? "✓" : "✗"} ${c.kind.padEnd(13)} @id=${String(c.node["@id"]).slice(0, 24)}… @type=[${types.join(", ")}] schema:${v.hasSchema?"y":"n"} prov:${v.hasProv?"y":"n"} id:${v.hasId?"y":"n"} ctx:${ctxOk?"y":"n"} rt:${rt?"y":"n"}`);
|
| 30 |
-
}
|
| 31 |
-
const all = pass === cases.length;
|
| 32 |
-
console.log(`\n${all ? "PASS" : "FAIL"} — ${pass}/${cases.length} κ-object kinds carry a valid W3C @type`);
|
| 33 |
-
process.exit(all ? 0 : 1);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
core/skills.js
DELETED
|
@@ -1,92 +0,0 @@
|
|
| 1 |
-
// core/skills.js — SELF-EVOLVING AGENT SKILLS, 100% agentskills.io-compatible, enhanced by the UOR
|
| 2 |
-
// substrate. A skill is a folder with SKILL.md (name + description frontmatter + instructions) —
|
| 3 |
-
// the open Anthropic/agentskills.io format, portable to any skills-compatible agent. The Hermes
|
| 4 |
-
// idea (the agent autonomously CREATES and IMPROVES skills from experience) is realized as the
|
| 5 |
-
// agent's own tools. The substrate enhancement: every skill version is content-addressed (κ) and
|
| 6 |
-
// chained — a tamper-proof PROVENANCE of how the skill evolved (Holo Prov). Skills live in OPFS
|
| 7 |
-
// "/skills/<name>/SKILL.md"; the chain log in "/skills/<name>/.prov" (one κ per version).
|
| 8 |
-
//
|
| 9 |
-
// Progressive disclosure (the spec): DISCOVERY (names+descriptions injected at session start) →
|
| 10 |
-
// ACTIVATION (read_skill loads full SKILL.md) → EXECUTION (the agent follows it, using file tools).
|
| 11 |
-
|
| 12 |
-
import { skillAsHowTo, verifySemantic } from "./semantic.js";
|
| 13 |
-
|
| 14 |
-
const te = new TextEncoder(), td = new TextDecoder();
|
| 15 |
-
const sha = async (s) => "did:holo:sha256:" + [...new Uint8Array(await crypto.subtle.digest("SHA-256", te.encode(s)))].map((b) => b.toString(16).padStart(2, "0")).join("");
|
| 16 |
-
const slug = (s) => String(s || "").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 48) || "skill";
|
| 17 |
-
|
| 18 |
-
async function skillsRoot() { const d = await navigator.storage.getDirectory(); const a = await d.getDirectoryHandle("skills", { create: true }); return a; }
|
| 19 |
-
async function skillDir(name, create = false) { return (await skillsRoot()).getDirectoryHandle(slug(name), { create }); }
|
| 20 |
-
async function readText(dir, file) { const fh = await dir.getFileHandle(file); return td.decode(await (await fh.getFile()).arrayBuffer()); }
|
| 21 |
-
async function writeText(dir, file, text) { const fh = await dir.getFileHandle(file, { create: true }); const w = await fh.createWritable(); await w.write(te.encode(text)); await w.close(); }
|
| 22 |
-
|
| 23 |
-
// SKILL.md = YAML-ish frontmatter (name, description) + markdown instructions (verbatim spec form).
|
| 24 |
-
function buildSkillMd({ name, description, instructions }) {
|
| 25 |
-
return `---\nname: ${name}\ndescription: ${description}\n---\n\n${instructions || ""}\n`;
|
| 26 |
-
}
|
| 27 |
-
function parseSkillMd(text) {
|
| 28 |
-
const m = text.match(/^---\s*\n([\s\S]*?)\n---\s*\n?([\s\S]*)$/);
|
| 29 |
-
const meta = {}; if (m) for (const line of m[1].split("\n")) { const i = line.indexOf(":"); if (i > 0) meta[line.slice(0, i).trim()] = line.slice(i + 1).trim(); }
|
| 30 |
-
return { name: meta.name || "", description: meta.description || "", instructions: m ? m[2].trim() : text.trim() };
|
| 31 |
-
}
|
| 32 |
-
|
| 33 |
-
export async function listSkills() {
|
| 34 |
-
const out = [];
|
| 35 |
-
try { const root = await skillsRoot();
|
| 36 |
-
for await (const [n, h] of root.entries()) { if (h.kind !== "directory") continue;
|
| 37 |
-
try { const sk = parseSkillMd(await readText(h, "SKILL.md")); out.push({ slug: n, name: sk.name || n, description: sk.description }); } catch {} } }
|
| 38 |
-
catch {}
|
| 39 |
-
return out.sort((a, b) => a.name.localeCompare(b.name));
|
| 40 |
-
}
|
| 41 |
-
|
| 42 |
-
export async function readSkill(name) { const dir = await skillDir(name); return parseSkillMd(await readText(dir, "SKILL.md")); }
|
| 43 |
-
|
| 44 |
-
// save = create OR improve (self-evolution). Appends a κ to the provenance chain — every version
|
| 45 |
-
// sealed + chained to its parent, so the skill's evolution is verifiable (Holo Prov shape).
|
| 46 |
-
export async function saveSkill({ name, description, instructions }) {
|
| 47 |
-
const dir = await skillDir(name, true);
|
| 48 |
-
const md = buildSkillMd({ name, description, instructions });
|
| 49 |
-
const kappa = await sha(md);
|
| 50 |
-
let prov = []; try { prov = JSON.parse(await readText(dir, ".prov")); } catch {}
|
| 51 |
-
const parent = prov.length ? prov[prov.length - 1].kappa : null;
|
| 52 |
-
prov.push({ v: prov.length + 1, kappa, parent, descKappa: await sha(description || "") });
|
| 53 |
-
await writeText(dir, "SKILL.md", md);
|
| 54 |
-
await writeText(dir, ".prov", JSON.stringify(prov, null, 1));
|
| 55 |
-
// semantic skin (C2): emit the W3C linked-data view — schema:HowTo + PROV-O revision chain.
|
| 56 |
-
const ld = skillAsHowTo({ name, description, instructions, kappa, prov });
|
| 57 |
-
await writeText(dir, "skill.jsonld", JSON.stringify(ld, null, 1));
|
| 58 |
-
return { slug: slug(name), kappa, version: prov.length, parent, semantic: verifySemantic(ld).ok };
|
| 59 |
-
}
|
| 60 |
-
|
| 61 |
-
// the W3C linked-data view of a skill (schema:HowTo + PROV-O), for export / validation / agents.
|
| 62 |
-
export async function skillLinkedData(name) { try { return JSON.parse(await readText(await skillDir(name), "skill.jsonld")); } catch { return null; } }
|
| 63 |
-
|
| 64 |
-
export async function skillProvenance(name) { try { return JSON.parse(await readText(await skillDir(name), ".prov")); } catch { return []; } }
|
| 65 |
-
|
| 66 |
-
// DISCOVERY block (progressive disclosure stage 1): names + descriptions only — small footprint.
|
| 67 |
-
export async function skillsDiscoveryPrompt() {
|
| 68 |
-
const sk = await listSkills();
|
| 69 |
-
if (!sk.length) return "";
|
| 70 |
-
return "You have these learned SKILLS available (each a verifiable, content-addressed capability you built from experience). " +
|
| 71 |
-
"When a task matches a skill's description, call read_skill to load its full instructions, then follow them. " +
|
| 72 |
-
"When you complete a non-trivial task worth reusing, call save_skill to capture it (or improve an existing one):\n" +
|
| 73 |
-
sk.map((s) => `- ${s.name}: ${s.description}`).join("\n");
|
| 74 |
-
}
|
| 75 |
-
|
| 76 |
-
// the agent's skill tools (Hermes self-evolution: the agent creates/improves/uses skills itself)
|
| 77 |
-
export function skillTools() {
|
| 78 |
-
return [
|
| 79 |
-
{
|
| 80 |
-
def: { name: "list_skills", description: "List the learned skills available (name + description).", inputSchema: { type: "object", properties: {}, required: [] } },
|
| 81 |
-
serverName: "skills", call: async () => { const s = await listSkills(); return { text: s.length ? s.map((x) => `${x.name}: ${x.description}`).join("\n") : "(no skills yet)", isError: false }; },
|
| 82 |
-
},
|
| 83 |
-
{
|
| 84 |
-
def: { name: "read_skill", description: "Load a skill's full instructions by name, to follow them for the current task.", inputSchema: { type: "object", properties: { name: { type: "string" } }, required: ["name"] } },
|
| 85 |
-
serverName: "skills", call: async ({ name }) => { try { const s = await readSkill(name); return { text: `# ${s.name}\n${s.description}\n\n${s.instructions}`, isError: false }; } catch (e) { return { text: "no such skill: " + name, isError: true }; } },
|
| 86 |
-
},
|
| 87 |
-
{
|
| 88 |
-
def: { name: "save_skill", description: "Capture a reusable skill from what you just did (or improve an existing one). name: short; description: one line of WHEN to use it; instructions: the step-by-step procedure. Each save is sealed + chained (verifiable provenance).", inputSchema: { type: "object", properties: { name: { type: "string" }, description: { type: "string" }, instructions: { type: "string" } }, required: ["name", "description", "instructions"] } },
|
| 89 |
-
serverName: "skills", call: async ({ name, description, instructions }) => { try { const r = await saveSkill({ name, description, instructions }); return { text: `skill "${name}" saved (v${r.version}, ${r.kappa.slice(0, 28)}…${r.parent ? `, evolved from ${r.parent.slice(0, 20)}…` : ", first version"})`, isError: false }; } catch (e) { return { text: "save error: " + (e.message || e), isError: true }; } },
|
| 90 |
-
},
|
| 91 |
-
];
|
| 92 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
core/state.js
DELETED
|
@@ -1,39 +0,0 @@
|
|
| 1 |
-
// core/state.js — a tiny event bus + the app's shared mutable state. No framework: modules
|
| 2 |
-
// subscribe to named events and re-render their own DOM islands. Deliberately minimal — the
|
| 3 |
-
// durable truth lives in the κ-object store (Law L3); this is only the session's working set.
|
| 4 |
-
|
| 5 |
-
export function makeBus() {
|
| 6 |
-
const subs = new Map();
|
| 7 |
-
return {
|
| 8 |
-
on(ev, fn) { if (!subs.has(ev)) subs.set(ev, new Set()); subs.get(ev).add(fn); return () => subs.get(ev)?.delete(fn); },
|
| 9 |
-
emit(ev, data) { for (const fn of subs.get(ev) || []) { try { fn(data); } catch (e) { console.error(`[bus:${ev}]`, e); } } },
|
| 10 |
-
};
|
| 11 |
-
}
|
| 12 |
-
|
| 13 |
-
export function makeState() {
|
| 14 |
-
return {
|
| 15 |
-
// model / engine
|
| 16 |
-
engine: null, // core/engine.js instance (null until a model loads)
|
| 17 |
-
modelIndex: 0, // index into loader.MODELS
|
| 18 |
-
loading: false,
|
| 19 |
-
gateOk: false, // conscience constitution self-verify
|
| 20 |
-
|
| 21 |
-
// conversation working set
|
| 22 |
-
convId: null, // active conversation id
|
| 23 |
-
convKappa: null, // its latest sealed κ
|
| 24 |
-
title: "New Chat",
|
| 25 |
-
messages: [], // [{ obj (sealed κ-object), id, kappa, ...norm fields, tokenIds }]
|
| 26 |
-
chosenChild: new Map(), // parent messageId → chosen child messageId (sibling switch)
|
| 27 |
-
streaming: false,
|
| 28 |
-
abort: null, // AbortController while generating
|
| 29 |
-
|
| 30 |
-
// ui
|
| 31 |
-
navOpen: window.innerWidth > 900,
|
| 32 |
-
panelOpen: false, // the chat is the product — the panel opens on demand
|
| 33 |
-
panelTab: "params",
|
| 34 |
-
settingsOpen: false,
|
| 35 |
-
|
| 36 |
-
// preset (the active parameter bundle — LibreChat conversationPreset semantics)
|
| 37 |
-
preset: null,
|
| 38 |
-
};
|
| 39 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
core/store.js
DELETED
|
@@ -1,185 +0,0 @@
|
|
| 1 |
-
// core/store.js — the κ-object CONVERSATION STORE. Conversations are NOT database rows; each
|
| 2 |
-
// Message and each Conversation is a self-verifying UOR object (a canonical JSON-LD document whose
|
| 3 |
-
// identity is did:holo:sha256 of its own content), and the LibreChat parentMessageId tree is
|
| 4 |
-
// expressed as content-addressed LINKS so a child's κ commits to its parent's κ — a Merkle-DAG
|
| 5 |
-
// (Law L3 dedup, Law L5 verify-on-resolve). The object envelope mirrors _shared/holo-object.mjs
|
| 6 |
-
// byte-for-byte (same UOR_CONTEXT, same address() = H(canonical without id), same link digests),
|
| 7 |
-
// but is implemented here over Web Crypto (core/kappa.js) so it runs in the browser AND in the
|
| 8 |
-
// pure-Node witness. Bytes are persisted through a pluggable BACKEND: holo-kstore (IndexedDB) in
|
| 9 |
-
// the app, a Map in the witness. No server; everything is content-addressed and re-derivable.
|
| 10 |
-
|
| 11 |
-
import { jcs, sha256hex, didHolo } from "./kappa.js";
|
| 12 |
-
|
| 13 |
-
const _enc = new TextEncoder();
|
| 14 |
-
const _dec = new TextDecoder();
|
| 15 |
-
|
| 16 |
-
// The base envelope @context — identical to _shared/holo-object.mjs.
|
| 17 |
-
export const UOR_CONTEXT = [
|
| 18 |
-
"https://www.w3.org/ns/did/v1",
|
| 19 |
-
"https://w3id.org/security/data-integrity/v2",
|
| 20 |
-
{ schema: "https://schema.org/", prov: "http://www.w3.org/ns/prov#", dcterms: "http://purl.org/dc/terms/",
|
| 21 |
-
rel: "schema:additionalType", links: { "@id": "schema:hasPart", "@container": "@set" } },
|
| 22 |
-
];
|
| 23 |
-
|
| 24 |
-
const hexOf = (did) => String(did).split(":").pop();
|
| 25 |
-
const _b64 = (u8) => { let s = ""; for (let i = 0; i < u8.length; i++) s += String.fromCharCode(u8[i]); return btoa(s); };
|
| 26 |
-
const _b64url = (u8) => _b64(u8).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
| 27 |
-
async function sha256raw(u8) { return new Uint8Array(await crypto.subtle.digest("SHA-256", u8)); }
|
| 28 |
-
async function sriOf(u8) { return "sha256-" + _b64(await sha256raw(u8)); }
|
| 29 |
-
async function mbOf(u8) { const d = await sha256raw(u8); const mh = new Uint8Array(d.length + 2); mh[0] = 0x12; mh[1] = 0x20; mh.set(d, 2); return "u" + _b64url(mh); }
|
| 30 |
-
|
| 31 |
-
// address(obj): self-verifying identity = did:holo:sha256:H(canonical content with id/alsoKnownAs removed).
|
| 32 |
-
const stripId = (obj) => { const { id, alsoKnownAs, ...content } = obj; return content; };
|
| 33 |
-
const addressOf = (obj) => didHolo(stripId(obj));
|
| 34 |
-
const canonStored = (sealed) => _enc.encode(jcs(sealed)); // the EXACT bytes a link's digest commits to (sealed, WITH id — mirrors holo-object put())
|
| 35 |
-
|
| 36 |
-
// contentLink(rel, kappa): a LEAF edge to raw content already addressed by its κ (no bytes needed) —
|
| 37 |
-
// identical to holo-object.contentLink. Verified at resolve by re-hashing the raw bytes to the address.
|
| 38 |
-
export function contentLink(rel, kappa, type = "schema:MediaObject") {
|
| 39 |
-
const hex = String(kappa).split(":").pop();
|
| 40 |
-
const digest = Uint8Array.from(hex.match(/.{2}/g).map((h) => parseInt(h, 16)));
|
| 41 |
-
const mh = new Uint8Array(digest.length + 2); mh[0] = 0x12; mh[1] = 0x20; mh.set(digest, 2);
|
| 42 |
-
return { id: `did:holo:sha256:${hex}`, rel, "@type": type, leaf: true, digestSRI: "sha256-" + _b64(digest), digestMultibase: "u" + _b64url(mh) };
|
| 43 |
-
}
|
| 44 |
-
|
| 45 |
-
// makeStore(backend) — backend = { get(hex)→Promise<Uint8Array|undefined>, put(hex,bytes)→Promise,
|
| 46 |
-
// putRaw?(key,bytes)/getRaw?(key) for the boot-index pointer record }.
|
| 47 |
-
export function makeStore(backend) {
|
| 48 |
-
const mem = new Map(); // hex → Uint8Array (session cache; sync verifyDeep operates over it)
|
| 49 |
-
|
| 50 |
-
async function putObj(obj) {
|
| 51 |
-
const sealed = { ...obj, id: await addressOf(obj) };
|
| 52 |
-
const bytes = canonStored(sealed);
|
| 53 |
-
const hex = hexOf(sealed.id);
|
| 54 |
-
mem.set(hex, bytes);
|
| 55 |
-
await backend.put(hex, bytes);
|
| 56 |
-
return sealed;
|
| 57 |
-
}
|
| 58 |
-
async function getBytes(did) { const hex = hexOf(did); let b = mem.get(hex); if (b) return b; b = await backend.get(hex); if (b) mem.set(hex, b); return b; }
|
| 59 |
-
async function getObj(did) { const b = await getBytes(did); return b ? JSON.parse(_dec.decode(b)) : null; }
|
| 60 |
-
|
| 61 |
-
// a typed Merkle edge to a sealed UOR child (the digest commits to the child's stored bytes).
|
| 62 |
-
async function uorLink(rel, child) {
|
| 63 |
-
const bytes = mem.get(hexOf(child.id)) || await backend.get(hexOf(child.id));
|
| 64 |
-
return { id: child.id, rel, "@type": child["@type"], digestSRI: await sriOf(bytes), digestMultibase: await mbOf(bytes) };
|
| 65 |
-
}
|
| 66 |
-
|
| 67 |
-
// build + seal + store a UOR object. `links` already built (children stored first → Merkle).
|
| 68 |
-
async function makeObject({ type, context = [], links = [], ...props }) {
|
| 69 |
-
const obj = { "@context": [...UOR_CONTEXT, ...context], "@type": type, ...props, ...(links.length ? { links } : {}) };
|
| 70 |
-
return putObj(obj);
|
| 71 |
-
}
|
| 72 |
-
|
| 73 |
-
const verify = async (obj) => obj.id === await addressOf(obj);
|
| 74 |
-
|
| 75 |
-
// verifyDeep — re-derive + verify the WHOLE DAG (Law L5 at every level). Pre-loads links into mem.
|
| 76 |
-
async function verifyDeep(obj, depth = 0) {
|
| 77 |
-
if (!(await verify(obj))) return { ok: false, at: obj.id, why: "id does not re-derive", depth };
|
| 78 |
-
let maxDepth = depth;
|
| 79 |
-
for (const link of obj.links || []) {
|
| 80 |
-
const bytes = await getBytes(link.id);
|
| 81 |
-
if (!bytes) return { ok: false, at: link.id, why: "unresolved link", depth };
|
| 82 |
-
if ((await sriOf(bytes)) !== link.digestSRI) return { ok: false, at: link.id, why: "link digest mismatch", depth };
|
| 83 |
-
if (link.leaf) { if ((await sha256hex(bytes)) !== hexOf(link.id)) return { ok: false, at: link.id, why: "content hash mismatch", depth }; maxDepth = Math.max(maxDepth, depth + 1); continue; }
|
| 84 |
-
const child = JSON.parse(_dec.decode(bytes));
|
| 85 |
-
if (child.id !== link.id) return { ok: false, at: link.id, why: "id/link mismatch", depth };
|
| 86 |
-
const r = await verifyDeep(child, depth + 1); if (!r.ok) return r; maxDepth = Math.max(maxDepth, r.depth);
|
| 87 |
-
}
|
| 88 |
-
return { ok: true, depth: maxDepth };
|
| 89 |
-
}
|
| 90 |
-
|
| 91 |
-
// tamper helper (for the witness): overwrite a stored object's bytes with a flipped field.
|
| 92 |
-
async function _corrupt(did, mutate) { const o = await getObj(did); mutate(o); const bytes = canonStored(o); mem.set(hexOf(did), bytes); await backend.put(hexOf(did), bytes); }
|
| 93 |
-
|
| 94 |
-
return { makeObject, putObj, getObj, getBytes, uorLink, contentLink, verify, verifyDeep, mem, backend, _corrupt };
|
| 95 |
-
}
|
| 96 |
-
|
| 97 |
-
// ── high-level chat API over a κ-object store ───────────────────────────────────────────────
|
| 98 |
-
// LibreChat-faithful field names. parentMessageId → an "lc:parentMessage" content-addressed link.
|
| 99 |
-
const LC = { lc: "https://librechat.ai/ns#" };
|
| 100 |
-
const rid = (p) => p + "-" + Math.random().toString(36).slice(2, 12); // id is a human handle; substrate identity is the κ
|
| 101 |
-
|
| 102 |
-
export function makeChatStore(backend, { now = () => new Date().toISOString() } = {}) {
|
| 103 |
-
const S = makeStore(backend);
|
| 104 |
-
const INDEX_KEY = "index:org.hologram.HoloQ";
|
| 105 |
-
const getIndex = async () => { try { const b = await (backend.getRaw ? backend.getRaw(INDEX_KEY) : backend.get(INDEX_KEY)); return b ? JSON.parse(_dec.decode(b)) : { conversations: [], presets: [] }; } catch { return { conversations: [], presets: [] }; } };
|
| 106 |
-
const putIndex = async (idx) => { const b = _enc.encode(JSON.stringify(idx)); return backend.putRaw ? backend.putRaw(INDEX_KEY, b) : backend.put(INDEX_KEY, b); };
|
| 107 |
-
|
| 108 |
-
// Persist a PROV-O inference receipt as raw content addressed by its own κ (rec.id = H(jcs(body))),
|
| 109 |
-
// so a message's contentLink("lc:receipt", rec.id) leaf-verifies (sha256(stored bytes) === κ).
|
| 110 |
-
async function saveReceipt(rec) {
|
| 111 |
-
const bytes = _enc.encode(jcs(rec.body));
|
| 112 |
-
const hex = String(rec.id).split(":").pop();
|
| 113 |
-
S.mem.set(hex, bytes); await backend.put(hex, bytes);
|
| 114 |
-
return rec.id;
|
| 115 |
-
}
|
| 116 |
-
|
| 117 |
-
// Save one message as a κ-object. `parent` (a sealed message object) is a Merkle uorLink — the
|
| 118 |
-
// branch tree, so the child's κ commits to the parent's κ. receiptKappa is a leaf content-link.
|
| 119 |
-
// tokenIds (the turn's exact token sequence) makes context reconstruction and Law-L5
|
| 120 |
-
// re-derivation possible after a cold reload.
|
| 121 |
-
async function saveMessage({ messageId, conversationId, sender, isCreatedByUser, model, text, content, tokenCount, tokenIds, error, unfinished, feedback, fromMemo, toolTrace, createdAt, parent, receiptKappa, toolReceiptKappas = [], files = [] }) {
|
| 122 |
-
const links = [];
|
| 123 |
-
if (parent) links.push({ ...(await S.uorLink("lc:parentMessage", parent)), "schema:name": "parentMessageId" });
|
| 124 |
-
if (receiptKappa) links.push({ ...S.contentLink("lc:receipt", receiptKappa, "prov:Activity"), "schema:name": "receipt" });
|
| 125 |
-
for (const k of toolReceiptKappas) links.push({ ...S.contentLink("lc:toolReceipt", k, "prov:Activity"), "schema:name": "toolReceipt" });
|
| 126 |
-
for (const f of files) links.push({ ...S.contentLink("schema:associatedMedia", f.kappa, "schema:MediaObject"), "schema:name": f.name });
|
| 127 |
-
return S.makeObject({
|
| 128 |
-
type: ["schema:Message", "prov:Entity"], context: [LC],
|
| 129 |
-
"schema:identifier": messageId || rid("msg"), "lc:conversationId": conversationId,
|
| 130 |
-
"lc:parentMessageId": (parent && parent["schema:identifier"]) || null, // the human handle of the parent (for export/tree rebuild)
|
| 131 |
-
"lc:sender": sender, "lc:isCreatedByUser": !!isCreatedByUser, "lc:model": model || null,
|
| 132 |
-
"schema:text": text || "", "lc:content": content || null, "lc:tokenCount": tokenCount ?? null,
|
| 133 |
-
"lc:tokenIds": tokenIds || null, "lc:fromMemo": !!fromMemo,
|
| 134 |
-
"lc:toolTrace": toolTrace || null,
|
| 135 |
-
"lc:error": error || null, "lc:unfinished": !!unfinished, "lc:feedback": feedback || null,
|
| 136 |
-
"schema:dateCreated": createdAt || now(),
|
| 137 |
-
links,
|
| 138 |
-
});
|
| 139 |
-
}
|
| 140 |
-
|
| 141 |
-
// Save the conversation κ-object → links to the head (active leaf) + branch tips. Re-sealed each turn.
|
| 142 |
-
async function saveConversation({ conversationId, title, tags, files, preset, createdAt, headMessage, branchTips = [] }) {
|
| 143 |
-
const links = [];
|
| 144 |
-
if (headMessage) links.push({ ...(await S.uorLink("lc:head", headMessage)), "schema:name": "head" });
|
| 145 |
-
for (const t of branchTips) links.push({ ...(await S.uorLink("lc:branchTip", t)), "schema:name": "tip" });
|
| 146 |
-
const conv = await S.makeObject({
|
| 147 |
-
type: ["schema:Conversation", "prov:Collection"], context: [LC],
|
| 148 |
-
"schema:identifier": conversationId, "schema:name": title || "New Chat",
|
| 149 |
-
"lc:tags": tags || [], "lc:files": files || [], "lc:preset": preset || null,
|
| 150 |
-
"schema:dateCreated": createdAt || now(), "schema:dateModified": now(),
|
| 151 |
-
links,
|
| 152 |
-
});
|
| 153 |
-
// update the boot index (pointer-only; the conversation itself is verifiable)
|
| 154 |
-
const idx = await getIndex();
|
| 155 |
-
const ptr = { conversationId, kappa: conv.id, title: conv["schema:name"], updatedAt: conv["schema:dateModified"], archived: false, favorite: false, folder: null };
|
| 156 |
-
const i = idx.conversations.findIndex((c) => c.conversationId === conversationId);
|
| 157 |
-
if (i >= 0) idx.conversations[i] = { ...idx.conversations[i], ...ptr }; else idx.conversations.unshift(ptr);
|
| 158 |
-
await putIndex(idx);
|
| 159 |
-
return conv;
|
| 160 |
-
}
|
| 161 |
-
|
| 162 |
-
async function listConversations() { return (await getIndex()).conversations; }
|
| 163 |
-
async function updatePointer(conversationId, patch) { const idx = await getIndex(); const i = idx.conversations.findIndex((c) => c.conversationId === conversationId); if (i >= 0) { idx.conversations[i] = { ...idx.conversations[i], ...patch }; await putIndex(idx); } }
|
| 164 |
-
async function loadConversation(conversationId) {
|
| 165 |
-
const idx = await getIndex(); const ptr = idx.conversations.find((c) => c.conversationId === conversationId); if (!ptr) return null;
|
| 166 |
-
const conv = await S.getObj(ptr.kappa); if (!conv) return null;
|
| 167 |
-
const v = await S.verifyDeep(conv);
|
| 168 |
-
return { conv, kappa: ptr.kappa, ok: v.ok, integrity: v };
|
| 169 |
-
}
|
| 170 |
-
const verifyConversation = async (kappa) => { const conv = await S.getObj(kappa); return conv ? S.verifyDeep(conv) : { ok: false, why: "not found" }; };
|
| 171 |
-
|
| 172 |
-
return { store: S, saveReceipt, saveMessage, saveConversation, listConversations, loadConversation, verifyConversation, updatePointer, getIndex, newId: rid };
|
| 173 |
-
}
|
| 174 |
-
|
| 175 |
-
// A simple Map backend (the witness uses this; the app wraps holo-kstore instead).
|
| 176 |
-
export function mapBackend() {
|
| 177 |
-
const m = new Map();
|
| 178 |
-
return {
|
| 179 |
-
get: async (hex) => m.get(hex),
|
| 180 |
-
put: async (hex, bytes) => { m.set(hex, bytes); },
|
| 181 |
-
getRaw: async (k) => m.get(k),
|
| 182 |
-
putRaw: async (k, bytes) => { m.set(k, bytes); },
|
| 183 |
-
_map: m,
|
| 184 |
-
};
|
| 185 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
core/tools.js
DELETED
|
@@ -1,112 +0,0 @@
|
|
| 1 |
-
// core/tools.js — the agentic TOOL-CALLING loop over the local engine + MCP. Qwen2.5's
|
| 2 |
-
// native function-calling convention: the system turn declares tools as JSON schemas inside
|
| 3 |
-
// a <tools>…</tools> block; the model emits <tool_call>{"name":…,"arguments":…}</tool_call>;
|
| 4 |
-
// each result returns as a user-role <tool_response>…</tool_response> turn and generation
|
| 5 |
-
// continues — until a turn arrives with no tool call (the final answer) or maxRounds hits.
|
| 6 |
-
//
|
| 7 |
-
// Substrate-native: EVERY tool call is sealed as its own PROV-O activity (tool ⊕ args ⊕
|
| 8 |
-
// result, each by κ) and the per-call receipts ride the answer's receipt — a verifiable
|
| 9 |
-
// work-trail for agentic answers, the chat-layer analogue of Holo Orchestrate (ADR-045).
|
| 10 |
-
|
| 11 |
-
import { didHolo, kappaText, jcs } from "./kappa.js";
|
| 12 |
-
|
| 13 |
-
// The system turn that arms the model with tools (Qwen2.5 function-calling convention).
|
| 14 |
-
export function toolSystemPrompt(tools, extra = "") {
|
| 15 |
-
const decls = tools.map((t) => JSON.stringify({ type: "function", function: { name: t.name, description: t.description || "", parameters: t.inputSchema || { type: "object", properties: {} } } })).join("\n");
|
| 16 |
-
return (
|
| 17 |
-
(extra ? extra + "\n\n" : "") +
|
| 18 |
-
"# Tools\n\nYou may call one or more functions to assist with the user query.\n\n" +
|
| 19 |
-
"You are provided with function signatures within <tools></tools> XML tags:\n<tools>\n" + decls + "\n</tools>\n\n" +
|
| 20 |
-
"For each function call, return a json object with function name and arguments within <tool_call></tool_call> XML tags:\n" +
|
| 21 |
-
'<tool_call>\n{"name": <function-name>, "arguments": <args-json-object>}\n</tool_call>'
|
| 22 |
-
);
|
| 23 |
-
}
|
| 24 |
-
|
| 25 |
-
// Parse <tool_call> blocks out of a model turn. Tolerant: bare JSON with name+arguments
|
| 26 |
-
// also counts (small models sometimes drop the tags).
|
| 27 |
-
export function parseToolCalls(text) {
|
| 28 |
-
const calls = [];
|
| 29 |
-
const re = /<tool_call>\s*([\s\S]*?)\s*<\/tool_call>/g;
|
| 30 |
-
let m;
|
| 31 |
-
while ((m = re.exec(text))) { try { const j = JSON.parse(m[1]); if (j && j.name) calls.push({ name: j.name, arguments: j.arguments || {} }); } catch {} }
|
| 32 |
-
// ```json fenced code blocks with {name, arguments} (Qwen2.5-Coder often emits this form)
|
| 33 |
-
if (!calls.length) {
|
| 34 |
-
const cb = /```(?:json|tool_call)?\s*(\{[\s\S]*?\})\s*```/g;
|
| 35 |
-
while ((m = cb.exec(text))) { try { const j = JSON.parse(m[1]); if (j && j.name && ("arguments" in j)) calls.push({ name: j.name, arguments: j.arguments || {} }); } catch {} }
|
| 36 |
-
}
|
| 37 |
-
if (!calls.length) {
|
| 38 |
-
const t = text.trim();
|
| 39 |
-
if (t.startsWith("{") && t.includes('"name"')) { try { const j = JSON.parse(t); if (j.name && ("arguments" in j)) calls.push({ name: j.name, arguments: j.arguments || {} }); } catch {} }
|
| 40 |
-
}
|
| 41 |
-
return calls;
|
| 42 |
-
}
|
| 43 |
-
|
| 44 |
-
export const stripToolCalls = (text) => text
|
| 45 |
-
.replace(/<tool_call>[\s\S]*?<\/tool_call>/g, "")
|
| 46 |
-
.replace(/```(?:json|tool_call)?\s*\{[\s\S]*?"name"[\s\S]*?\}\s*```/g, "") // strip fenced tool calls too
|
| 47 |
-
.trim();
|
| 48 |
-
|
| 49 |
-
// Seal one tool call as a PROV-O activity (its own did:holo — tamper → refuse).
|
| 50 |
-
export async function sealToolReceipt({ server, tool, args, resultText, ok }) {
|
| 51 |
-
const body = {
|
| 52 |
-
"@context": ["http://www.w3.org/ns/prov#", { holo: "https://hologram.os/ns/q#" }],
|
| 53 |
-
"@type": "prov:Activity", "holo:kind": "tool-call",
|
| 54 |
-
"prov:used": { "holo:server": server, "holo:tool": tool, "holo:args": await didHolo(JSON.parse(jcs(args || {}))), "holo:argsJson": jcs(args || {}) },
|
| 55 |
-
"prov:generated": { "holo:result": await kappaText(resultText), "holo:ok": !!ok },
|
| 56 |
-
};
|
| 57 |
-
return { id: await didHolo(body), body };
|
| 58 |
-
}
|
| 59 |
-
|
| 60 |
-
// Frame the agentic first turn: system(tools) + user prompt (ChatML, by hand so the system
|
| 61 |
-
// block rides this turn). Exposed so the caller can persist the EXACT ids that run (the
|
| 62 |
-
// ctx-concat invariant of the message tree).
|
| 63 |
-
export function frameAgenticTurn({ tools, promptText, hasHistory, extraSystem = "" }) {
|
| 64 |
-
const sys = toolSystemPrompt(tools.map((t) => t.def), extraSystem);
|
| 65 |
-
return (hasHistory ? "<|im_end|>\n" : "") +
|
| 66 |
-
`<|im_start|>system\n${sys}<|im_end|>\n<|im_start|>user\n${promptText}<|im_end|>\n<|im_start|>assistant\n`;
|
| 67 |
-
}
|
| 68 |
-
|
| 69 |
-
// The loop. `callbacks.onToolCall({name,args})` / `onToolResult({name,text,receipt})` let the
|
| 70 |
-
// UI stream the work-trail live. Returns { text, rounds, toolReceipts, trace, ids }.
|
| 71 |
-
export async function runToolLoop({ engine, tools, promptText, ctxIds, firstFramed, signal, onToken, onToolCall, onToolResult, maxRounds = 4 }) {
|
| 72 |
-
const toolReceipts = []; const trace = [];
|
| 73 |
-
let framed = firstFramed || frameAgenticTurn({ tools, promptText, hasHistory: ctxIds.length > 0 });
|
| 74 |
-
let ids = ctxIds.slice();
|
| 75 |
-
let finalText = "", rounds = 0;
|
| 76 |
-
|
| 77 |
-
for (; rounds < maxRounds; rounds++) {
|
| 78 |
-
const turnIds = engine.tokenize(framed);
|
| 79 |
-
ids = ids.concat(turnIds);
|
| 80 |
-
// tight per-round budget: a tool_call is ~50 tokens, a final answer ~300 — snappy rounds,
|
| 81 |
-
// the same total work, far lower latency than letting one round run the whole cap.
|
| 82 |
-
// generous budget: tool calls that carry CODE (write_file, build_app) need room — a truncated
|
| 83 |
-
// call is invalid JSON and silently fails. 1536 fits a small app; plain rounds end early on EOS.
|
| 84 |
-
const res = await engine.generate(ids, { signal, onToken, maxNew: 1536 });
|
| 85 |
-
ids = res.ids;
|
| 86 |
-
const calls = parseToolCalls(res.text);
|
| 87 |
-
if (!calls.length || (signal && signal.aborted)) { finalText = stripToolCalls(res.text); break; }
|
| 88 |
-
|
| 89 |
-
// execute every call this round through MCP, seal a receipt each
|
| 90 |
-
const responses = [];
|
| 91 |
-
for (const c of calls) {
|
| 92 |
-
if (signal && signal.aborted) break;
|
| 93 |
-
const t = tools.find((x) => x.def.name === c.name);
|
| 94 |
-
onToolCall && onToolCall({ name: c.name, args: c.arguments, server: t ? t.serverName : "?" });
|
| 95 |
-
let text, ok = true, render = null;
|
| 96 |
-
try {
|
| 97 |
-
if (!t) throw new Error("unknown tool: " + c.name);
|
| 98 |
-
const r = await t.call(c.arguments);
|
| 99 |
-
text = r.text; ok = !r.isError; render = r.render || null; // build_app → live preview payload
|
| 100 |
-
} catch (e) { text = "ERROR: " + (e && e.message || e); ok = false; }
|
| 101 |
-
const receipt = await sealToolReceipt({ server: t ? t.serverName : "?", tool: c.name, args: c.arguments, resultText: text, ok });
|
| 102 |
-
toolReceipts.push(receipt);
|
| 103 |
-
trace.push({ name: c.name, args: c.arguments, text: text.slice(0, 4000), ok, receiptId: receipt.id, server: t ? t.serverName : "?" });
|
| 104 |
-
onToolResult && onToolResult({ name: c.name, text, ok, receipt, render });
|
| 105 |
-
responses.push(`<tool_response>\n${text.slice(0, 2400)}\n</tool_response>`); // bounded — tool output must fit the local context window
|
| 106 |
-
}
|
| 107 |
-
// feed results back (user role per the Qwen convention) and continue
|
| 108 |
-
framed = `<|im_end|>\n<|im_start|>user\n${responses.join("\n")}<|im_end|>\n<|im_start|>assistant\n`;
|
| 109 |
-
finalText = stripToolCalls(res.text); // best-so-far, replaced if a later round answers
|
| 110 |
-
}
|
| 111 |
-
return { text: finalText, rounds: rounds + 1, toolReceipts, trace, ids };
|
| 112 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
gemv-bench.html
DELETED
|
@@ -1,183 +0,0 @@
|
|
| 1 |
-
<!doctype html><html><head><meta charset=utf8><meta name=viewport content="width=device-width,initial-scale=1">
|
| 2 |
-
<title>Ternary GEMV kernel lab — your GPU</title>
|
| 3 |
-
<style>
|
| 4 |
-
:root{--bg:#0a0d13;--panel:#111825;--ink:#e8ebf1;--dim:#8b95a7;--ac:#7c5cff;--ok:#48c26c;--no:#f0616d;--warn:#e0a94a;--line:#1e2836}
|
| 5 |
-
*{box-sizing:border-box}body{margin:0;background:var(--bg);color:var(--ink);font:15px/1.6 -apple-system,Segoe UI,Roboto,monospace;padding:24px;max-width:820px;margin:0 auto}
|
| 6 |
-
h1{font-size:20px;margin:0 0 4px}.sub{color:var(--dim);font-size:13px;margin:0 0 18px}
|
| 7 |
-
.card{font-family:ui-monospace,monospace;padding:14px 16px;border:1px solid var(--line);border-radius:10px;background:var(--panel);margin-bottom:12px;word-break:break-word}
|
| 8 |
-
.big{font-size:22px;font-weight:700}.k{color:var(--dim)}
|
| 9 |
-
.ok{color:var(--ok)}.no{color:var(--no)}.warn{color:var(--warn)}
|
| 10 |
-
table{width:100%;border-collapse:collapse;font-family:ui-monospace,monospace;font-size:13px}
|
| 11 |
-
td,th{padding:6px 8px;border-bottom:1px solid var(--line);text-align:left}th{color:var(--dim);font-weight:600}
|
| 12 |
-
td.n{text-align:right;font-variant-numeric:tabular-nums}
|
| 13 |
-
.verdict{font-size:16px;font-weight:600;margin-top:14px}
|
| 14 |
-
</style></head><body>
|
| 15 |
-
<h1>Ternary GEMV <span style="color:var(--ac)">kernel lab</span> — your GPU</h1>
|
| 16 |
-
<p class="sub">Runs the batch-1 BitNet decode kernel (weights read once/token) in several variants against a ~0.69 GB ternary matrix on your real GPU, and reports the decode rate each reaches vs the 152 GB/s roofline (220 tok/s).</p>
|
| 17 |
-
<div id="status" class="card">starting…</div>
|
| 18 |
-
<div id="out"></div>
|
| 19 |
-
|
| 20 |
-
<script type="module">
|
| 21 |
-
const $=s=>document.querySelector(s), st=$("#status"), out=$("#out");
|
| 22 |
-
const MODEL_GB = 0.69, ROOF_TOK = 220, CUR_TOK = 54;
|
| 23 |
-
const say=(t,cls)=>{ st.textContent=t; st.className="card"+(cls?" "+cls:""); };
|
| 24 |
-
window.addEventListener("unhandledrejection",e=>say("✗ unhandled: "+(e.reason&&(e.reason.message||e.reason)),"no"));
|
| 25 |
-
|
| 26 |
-
// ---- kernel body templates (KX=1 decode). Same reduction structure; vary the inner math. ----
|
| 27 |
-
// V0: scalar u32 load, per-code f32 convert + (-1) bias (the current engine kernel)
|
| 28 |
-
const DOT_V0 = `
|
| 29 |
-
fn dot16(word:u32, v:u32) -> f32 {
|
| 30 |
-
var s4=vec4<f32>(0.0);
|
| 31 |
-
s4=s4+x[v] *(vec4<f32>(f32(word&3u),f32((word>>2u)&3u),f32((word>>4u)&3u),f32((word>>6u)&3u))-vec4<f32>(1.0));
|
| 32 |
-
s4=s4+x[v+1u]*(vec4<f32>(f32((word>>8u)&3u),f32((word>>10u)&3u),f32((word>>12u)&3u),f32((word>>14u)&3u))-vec4<f32>(1.0));
|
| 33 |
-
s4=s4+x[v+2u]*(vec4<f32>(f32((word>>16u)&3u),f32((word>>18u)&3u),f32((word>>20u)&3u),f32((word>>22u)&3u))-vec4<f32>(1.0));
|
| 34 |
-
s4=s4+x[v+3u]*(vec4<f32>(f32((word>>24u)&3u),f32((word>>26u)&3u),f32((word>>28u)&3u),f32((word>>30u)&3u))-vec4<f32>(1.0));
|
| 35 |
-
return s4.x+s4.y+s4.z+s4.w;
|
| 36 |
-
}`;
|
| 37 |
-
const BODY_V0 = `let word=qw[rowW+w]; acc=acc+dot16(word, w<<2u);`;
|
| 38 |
-
const LOOP_V0 = { arrQw:"array<u32>", stride:"64u", nwExpr:"nw", asum:false };
|
| 39 |
-
|
| 40 |
-
// V1: wide vec4<u32> load (64 codes / 16 bytes per load), same math as V0
|
| 41 |
-
const BODY_V1 = `let qv=qw[rowW+w]; let v=(w<<2u)<<2u;
|
| 42 |
-
acc=acc+dot16(qv.x,v)+dot16(qv.y,v+4u)+dot16(qv.z,v+8u)+dot16(qv.w,v+12u);`;
|
| 43 |
-
const LOOP_V1 = { arrQw:"array<vec4<u32>>", stride:"64u", nwExpr:"(nw>>2u)", asum:false };
|
| 44 |
-
|
| 45 |
-
// V2: wide load + BIAS FOLD — drop the 16 per-code (-1); subtract sum(activations) once per row
|
| 46 |
-
const DOT_V2 = `
|
| 47 |
-
fn dot16(word:u32, v:u32) -> f32 {
|
| 48 |
-
var s4=vec4<f32>(0.0);
|
| 49 |
-
s4=s4+x[v] *vec4<f32>(f32(word&3u),f32((word>>2u)&3u),f32((word>>4u)&3u),f32((word>>6u)&3u));
|
| 50 |
-
s4=s4+x[v+1u]*vec4<f32>(f32((word>>8u)&3u),f32((word>>10u)&3u),f32((word>>12u)&3u),f32((word>>14u)&3u));
|
| 51 |
-
s4=s4+x[v+2u]*vec4<f32>(f32((word>>16u)&3u),f32((word>>18u)&3u),f32((word>>20u)&3u),f32((word>>22u)&3u));
|
| 52 |
-
s4=s4+x[v+3u]*vec4<f32>(f32((word>>24u)&3u),f32((word>>26u)&3u),f32((word>>28u)&3u),f32((word>>30u)&3u));
|
| 53 |
-
return s4.x+s4.y+s4.z+s4.w;
|
| 54 |
-
}`;
|
| 55 |
-
const BODY_V2 = `let qv=qw[rowW+w]; let v=(w<<2u)<<2u;
|
| 56 |
-
acc=acc+dot16(qv.x,v)+dot16(qv.y,v+4u)+dot16(qv.z,v+8u)+dot16(qv.w,v+12u);`;
|
| 57 |
-
const LOOP_V2 = { arrQw:"array<vec4<u32>>", stride:"64u", nwExpr:"(nw>>2u)", asum:true };
|
| 58 |
-
|
| 59 |
-
function kernelWGSL(dot, body, L){
|
| 60 |
-
return `
|
| 61 |
-
@group(0) @binding(0) var<storage,read> x: array<vec4<f32>>;
|
| 62 |
-
@group(0) @binding(1) var<storage,read> qw: ${L.arrQw};
|
| 63 |
-
@group(0) @binding(2) var<storage,read_write> o: array<f32>;
|
| 64 |
-
@group(0) @binding(3) var<uniform> P: vec4<u32>; // x=K, y=N, z=0, w=asumBits
|
| 65 |
-
var<workgroup> red: array<f32, 256>;
|
| 66 |
-
${dot}
|
| 67 |
-
@compute @workgroup_size(256)
|
| 68 |
-
fn main(@builtin(workgroup_id) wg:vec3<u32>, @builtin(local_invocation_id) lid:vec3<u32>){
|
| 69 |
-
let K=P.x; let nw=K>>4u; let rr=lid.x/64u; let t=lid.x%64u;
|
| 70 |
-
let n0=(wg.y*65535u+wg.x)*4u+rr; let n=min(n0, P.y-1u);
|
| 71 |
-
let rowW=n*${L.nwExpr}; var acc=0.0;
|
| 72 |
-
var w=t;
|
| 73 |
-
loop{ if(w>=${L.nwExpr}){break;}
|
| 74 |
-
${body}
|
| 75 |
-
w=w+${L.stride}; }
|
| 76 |
-
${L.asum? "acc = acc - bitcast<f32>(P.w);" : ""}
|
| 77 |
-
red[lid.x]=acc; workgroupBarrier();
|
| 78 |
-
var s=32u; loop{ if(s==0u){break;} if(t<s){ red[rr*64u+t]=red[rr*64u+t]+red[rr*64u+t+s]; } workgroupBarrier(); s=s/2u; }
|
| 79 |
-
if(t==0u && n0<P.y){ o[n0]=red[rr*64u]; }
|
| 80 |
-
}`;
|
| 81 |
-
}
|
| 82 |
-
|
| 83 |
-
// pseudo-random 2-bit codes fill (non-zero, non-compressible)
|
| 84 |
-
const FILL = `
|
| 85 |
-
@group(0) @binding(0) var<storage,read_write> d: array<u32>;
|
| 86 |
-
@group(0) @binding(1) var<uniform> P: vec4<u32>; // x=#u32, y=stride
|
| 87 |
-
@compute @workgroup_size(256)
|
| 88 |
-
fn main(@builtin(global_invocation_id) gid:vec3<u32>){
|
| 89 |
-
let n=P.x; var i=gid.x;
|
| 90 |
-
loop{ if(i>=n){break;} d[i]=(i*2654435761u+1u)^((i<<7u)*40503u); i=i+P.y; }
|
| 91 |
-
}`;
|
| 92 |
-
|
| 93 |
-
const VARIANTS=[
|
| 94 |
-
{id:"V0", name:"V0 baseline (scalar load, per-code −1)", dot:DOT_V0, body:BODY_V0, L:LOOP_V0},
|
| 95 |
-
{id:"V1", name:"V1 wide vec4 load", dot:DOT_V0, body:BODY_V1, L:LOOP_V1},
|
| 96 |
-
{id:"V2", name:"V2 wide load + bias-fold", dot:DOT_V2, body:BODY_V2, L:LOOP_V2},
|
| 97 |
-
];
|
| 98 |
-
|
| 99 |
-
(async()=>{
|
| 100 |
-
try{
|
| 101 |
-
if(!navigator.gpu){ say("✗ No WebGPU. Open in Chrome/Edge.","no"); return; }
|
| 102 |
-
say("requesting adapter…");
|
| 103 |
-
let ad=await navigator.gpu.requestAdapter({powerPreference:"high-performance"}); if(!ad) ad=await navigator.gpu.requestAdapter();
|
| 104 |
-
if(!ad){ say("✗ no GPU adapter.","no"); return; }
|
| 105 |
-
const L=ad.limits, info=ad.info||{};
|
| 106 |
-
const dev=await ad.requestDevice({requiredLimits:{
|
| 107 |
-
maxStorageBufferBindingSize:L.maxStorageBufferBindingSize, maxBufferSize:L.maxBufferSize,
|
| 108 |
-
maxComputeWorkgroupsPerDimension:L.maxComputeWorkgroupsPerDimension }});
|
| 109 |
-
dev.lost.then(i=>say("✗ device lost: "+(i&&i.message||i.reason||""),"no"));
|
| 110 |
-
|
| 111 |
-
const K=4096, nw=K/16; // 256 words/row
|
| 112 |
-
// weight bytes: as close to 0.69 GB as the device allows
|
| 113 |
-
const wantBytes=Math.min(L.maxStorageBufferBindingSize, L.maxBufferSize, Math.round(MODEL_GB*1e9));
|
| 114 |
-
const N=Math.floor((wantBytes/4)/nw); // whole rows
|
| 115 |
-
const qU32=N*nw, wBytes=qU32*4;
|
| 116 |
-
say("allocating "+(wBytes/1048576).toFixed(0)+" MB ternary weights ("+N.toLocaleString()+" rows × "+K+")…");
|
| 117 |
-
|
| 118 |
-
const qw=dev.createBuffer({size:wBytes, usage:GPUBufferUsage.STORAGE});
|
| 119 |
-
const xbuf=dev.createBuffer({size:K*4, usage:GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_DST});
|
| 120 |
-
const obuf=dev.createBuffer({size:N*4, usage:GPUBufferUsage.STORAGE});
|
| 121 |
-
const P=dev.createBuffer({size:16, usage:GPUBufferUsage.UNIFORM|GPUBufferUsage.COPY_DST});
|
| 122 |
-
|
| 123 |
-
// activations: random f32, and their sum (for V2 bias fold)
|
| 124 |
-
const xa=new Float32Array(K); let asum=0; for(let i=0;i<K;i++){ xa[i]=(Math.sin(i*12.9898)*43758.5453)%1; asum+=xa[i]; }
|
| 125 |
-
dev.queue.writeBuffer(xbuf,0,xa);
|
| 126 |
-
const asumBits=new Uint32Array(new Float32Array([asum]).buffer)[0];
|
| 127 |
-
dev.queue.writeBuffer(P,0,new Uint32Array([K, N, 0, asumBits]));
|
| 128 |
-
|
| 129 |
-
// fill weights (non-zero) so reads hit real VRAM
|
| 130 |
-
say("filling weights…");
|
| 131 |
-
{ const fmod=dev.createShaderModule({code:FILL});
|
| 132 |
-
const fpipe=dev.createComputePipeline({layout:"auto",compute:{module:fmod,entryPoint:"main"}});
|
| 133 |
-
const fp=dev.createBuffer({size:16,usage:GPUBufferUsage.UNIFORM|GPUBufferUsage.COPY_DST});
|
| 134 |
-
const wgF=Math.min(L.maxComputeWorkgroupsPerDimension,65535);
|
| 135 |
-
dev.queue.writeBuffer(fp,0,new Uint32Array([qU32, wgF*256, 0, 0]));
|
| 136 |
-
const fbg=dev.createBindGroup({layout:fpipe.getBindGroupLayout(0),entries:[{binding:0,resource:{buffer:qw}},{binding:1,resource:{buffer:fp}}]});
|
| 137 |
-
const e=dev.createCommandEncoder(); const p=e.beginComputePass(); p.setPipeline(fpipe); p.setBindGroup(0,fbg); p.dispatchWorkgroups(wgF); p.end();
|
| 138 |
-
dev.queue.submit([e.finish()]); await dev.queue.onSubmittedWorkDone(); }
|
| 139 |
-
|
| 140 |
-
// dispatch dims: workgroup = 4 rows
|
| 141 |
-
const groups=Math.ceil(N/4), wgx=Math.min(groups,65535), wgy=Math.ceil(groups/65535);
|
| 142 |
-
const PASSES=8, ITERS=5;
|
| 143 |
-
|
| 144 |
-
const rows=[];
|
| 145 |
-
for(const V of VARIANTS){
|
| 146 |
-
say("compiling "+V.id+"…");
|
| 147 |
-
dev.pushErrorScope("validation");
|
| 148 |
-
const mod=dev.createShaderModule({code:kernelWGSL(V.dot,V.body,V.L)});
|
| 149 |
-
const ci=await mod.getCompilationInfo(); const er=ci.messages.filter(m=>m.type==="error");
|
| 150 |
-
if(er.length){ await dev.popErrorScope(); rows.push({V, err:er[0].message}); continue; }
|
| 151 |
-
const pipe=dev.createComputePipeline({layout:"auto",compute:{module:mod,entryPoint:"main"}});
|
| 152 |
-
const bg=dev.createBindGroup({layout:pipe.getBindGroupLayout(0),entries:[
|
| 153 |
-
{binding:0,resource:{buffer:xbuf}},{binding:1,resource:{buffer:qw}},{binding:2,resource:{buffer:obuf}},{binding:3,resource:{buffer:P}}]});
|
| 154 |
-
const scopeErr=await dev.popErrorScope();
|
| 155 |
-
if(scopeErr){ rows.push({V, err:scopeErr.message}); continue; }
|
| 156 |
-
async function run(passes){
|
| 157 |
-
const e=dev.createCommandEncoder();
|
| 158 |
-
for(let k=0;k<passes;k++){ const p=e.beginComputePass(); p.setPipeline(pipe); p.setBindGroup(0,bg); p.dispatchWorkgroups(wgx,wgy); p.end(); }
|
| 159 |
-
const t0=performance.now(); dev.queue.submit([e.finish()]); await dev.queue.onSubmittedWorkDone(); return performance.now()-t0;
|
| 160 |
-
}
|
| 161 |
-
say("timing "+V.id+"…");
|
| 162 |
-
await run(2); // warm
|
| 163 |
-
let best=1e9; for(let k=0;k<ITERS;k++){ best=Math.min(best, await run(PASSES)); }
|
| 164 |
-
const msPer=best/PASSES, gbps=(wBytes/1073741824)/(msPer/1000), tok=gbps/MODEL_GB, pct=100*tok/ROOF_TOK;
|
| 165 |
-
rows.push({V, msPer, gbps, tok, pct});
|
| 166 |
-
}
|
| 167 |
-
|
| 168 |
-
say("done · adapter: "+((info.vendor||"?")+" "+(info.architecture||"")+" "+(info.device||"")).trim(),"ok");
|
| 169 |
-
const best=rows.filter(r=>r.tok).sort((a,b)=>b.tok-a.tok)[0];
|
| 170 |
-
out.innerHTML=`
|
| 171 |
-
<div class="card"><span class="k">Test matrix</span><br><span class="big">${N.toLocaleString()} × ${K}</span> · ${(wBytes/1073741824).toFixed(2)} GB ternary weights · roofline ${ROOF_TOK} tok/s</div>
|
| 172 |
-
<div class="card"><table>
|
| 173 |
-
<tr><th>variant</th><th class="n">ms/token</th><th class="n">GB/s</th><th class="n">tok/s</th><th class="n">% roofline</th></tr>
|
| 174 |
-
${rows.map(r=>r.err
|
| 175 |
-
? `<tr><td>${r.V.name}</td><td colspan=4 class="no">compile/validate failed: ${r.err.slice(0,60)}</td></tr>`
|
| 176 |
-
: `<tr><td>${r.V.name}</td><td class="n">${r.msPer.toFixed(2)}</td><td class="n">${r.gbps.toFixed(0)}</td><td class="n ${r===best?'ok':''}"><b>${r.tok.toFixed(0)}</b></td><td class="n ${r.pct<30?'no':r.pct<70?'warn':'ok'}">${r.pct.toFixed(0)}%</td></tr>`).join("")}
|
| 177 |
-
<tr><td class="k">current shipped kernel (measured)</td><td class="n k">18.4</td><td class="n k">38</td><td class="n k">${CUR_TOK}</td><td class="n k">${(100*CUR_TOK/ROOF_TOK).toFixed(0)}%</td></tr>
|
| 178 |
-
</table></div>
|
| 179 |
-
${best? `<div class="verdict ok">✓ Best: <b>${best.V.name}</b> → ${best.tok.toFixed(0)} tok/s (${best.pct.toFixed(0)}% of roofline, ${(best.tok/CUR_TOK).toFixed(1)}× the shipped kernel). ${best.pct>=70? "Bandwidth-bound — ship it." : "Still ALU-bound; next lever = integer dot / fewer converts."}</div>`
|
| 180 |
-
: `<div class="verdict no">All variants failed to run.</div>`}`;
|
| 181 |
-
}catch(e){ say("✗ "+(e&&(e.message||e)), "no"); }
|
| 182 |
-
})();
|
| 183 |
-
</script></body></html>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
gpu-blake3.mjs
DELETED
|
@@ -1,107 +0,0 @@
|
|
| 1 |
-
// gpu-blake3.mjs — BLAKE3 verification that runs ENTIRELY on the GPU (WGSL). The parallel two-phase kernel
|
| 2 |
-
// (phase1: one GPU thread per 1024-byte chunk; phase2: stack tree-merge → root) — PROVEN byte-exact vs the CPU
|
| 3 |
-
// oracle (holo-blake3.mjs) and measured at 2.74 GB/s on AMD RDNA-3 (138 MB in 49 ms). Used by holo-load2bit's
|
| 4 |
-
// getBlock to re-derive each streamed weight block's κ on the GPU (Law L5) — no CPU hash on the hot path.
|
| 5 |
-
// Lazy: the device + pipelines are created once on first use; gpuBlake3Hex throws if WebGPU is absent so the
|
| 6 |
-
// caller can fall back to the CPU verifier.
|
| 7 |
-
import { getDevice } from "./holo-gpu-device.mjs"; // the ONE shared device (max limits) — never a second one
|
| 8 |
-
|
| 9 |
-
const WGSL = `
|
| 10 |
-
@group(0) @binding(0) var<storage, read> inp : array<u32>;
|
| 11 |
-
@group(0) @binding(1) var<storage, read_write> cvs : array<u32>;
|
| 12 |
-
@group(0) @binding(2) var<storage, read_write> outp : array<u32>;
|
| 13 |
-
@group(0) @binding(3) var<uniform> P : vec4<u32>;
|
| 14 |
-
const IV = array<u32,8>(0x6a09e667u,0xbb67ae85u,0x3c6ef372u,0xa54ff53au,0x510e527fu,0x9b05688cu,0x1f83d9abu,0x5be0cd19u);
|
| 15 |
-
fn rotr(x:u32,n:u32)->u32 { return (x>>n)|(x<<(32u-n)); }
|
| 16 |
-
fn compress(cv:array<u32,8>, m0:array<u32,16>, cl:u32, ch:u32, bl:u32, fl:u32)->array<u32,16>{
|
| 17 |
-
var v:array<u32,16>;
|
| 18 |
-
v[0]=cv[0];v[1]=cv[1];v[2]=cv[2];v[3]=cv[3];v[4]=cv[4];v[5]=cv[5];v[6]=cv[6];v[7]=cv[7];
|
| 19 |
-
v[8]=IV[0];v[9]=IV[1];v[10]=IV[2];v[11]=IV[3];v[12]=cl;v[13]=ch;v[14]=bl;v[15]=fl;
|
| 20 |
-
var m:array<u32,16>=m0;
|
| 21 |
-
for(var r:u32=0u;r<7u;r=r+1u){
|
| 22 |
-
v[0]=v[0]+v[4]+m[0];v[12]=rotr(v[12]^v[0],16u);v[8]=v[8]+v[12];v[4]=rotr(v[4]^v[8],12u);v[0]=v[0]+v[4]+m[1];v[12]=rotr(v[12]^v[0],8u);v[8]=v[8]+v[12];v[4]=rotr(v[4]^v[8],7u);
|
| 23 |
-
v[1]=v[1]+v[5]+m[2];v[13]=rotr(v[13]^v[1],16u);v[9]=v[9]+v[13];v[5]=rotr(v[5]^v[9],12u);v[1]=v[1]+v[5]+m[3];v[13]=rotr(v[13]^v[1],8u);v[9]=v[9]+v[13];v[5]=rotr(v[5]^v[9],7u);
|
| 24 |
-
v[2]=v[2]+v[6]+m[4];v[14]=rotr(v[14]^v[2],16u);v[10]=v[10]+v[14];v[6]=rotr(v[6]^v[10],12u);v[2]=v[2]+v[6]+m[5];v[14]=rotr(v[14]^v[2],8u);v[10]=v[10]+v[14];v[6]=rotr(v[6]^v[10],7u);
|
| 25 |
-
v[3]=v[3]+v[7]+m[6];v[15]=rotr(v[15]^v[3],16u);v[11]=v[11]+v[15];v[7]=rotr(v[7]^v[11],12u);v[3]=v[3]+v[7]+m[7];v[15]=rotr(v[15]^v[3],8u);v[11]=v[11]+v[15];v[7]=rotr(v[7]^v[11],7u);
|
| 26 |
-
v[0]=v[0]+v[5]+m[8];v[15]=rotr(v[15]^v[0],16u);v[10]=v[10]+v[15];v[5]=rotr(v[5]^v[10],12u);v[0]=v[0]+v[5]+m[9];v[15]=rotr(v[15]^v[0],8u);v[10]=v[10]+v[15];v[5]=rotr(v[5]^v[10],7u);
|
| 27 |
-
v[1]=v[1]+v[6]+m[10];v[12]=rotr(v[12]^v[1],16u);v[11]=v[11]+v[12];v[6]=rotr(v[6]^v[11],12u);v[1]=v[1]+v[6]+m[11];v[12]=rotr(v[12]^v[1],8u);v[11]=v[11]+v[12];v[6]=rotr(v[6]^v[11],7u);
|
| 28 |
-
v[2]=v[2]+v[7]+m[12];v[13]=rotr(v[13]^v[2],16u);v[8]=v[8]+v[13];v[7]=rotr(v[7]^v[8],12u);v[2]=v[2]+v[7]+m[13];v[13]=rotr(v[13]^v[2],8u);v[8]=v[8]+v[13];v[7]=rotr(v[7]^v[8],7u);
|
| 29 |
-
v[3]=v[3]+v[4]+m[14];v[14]=rotr(v[14]^v[3],16u);v[9]=v[9]+v[14];v[4]=rotr(v[4]^v[9],12u);v[3]=v[3]+v[4]+m[15];v[14]=rotr(v[14]^v[3],8u);v[9]=v[9]+v[14];v[4]=rotr(v[4]^v[9],7u);
|
| 30 |
-
if(r<6u){var perm=array<u32,16>(2u,6u,3u,10u,7u,0u,4u,13u,1u,11u,12u,5u,9u,14u,15u,8u);var p:array<u32,16>;for(var i:u32=0u;i<16u;i=i+1u){p[i]=m[perm[i]];}m=p;}
|
| 31 |
-
}
|
| 32 |
-
var o:array<u32,16>;for(var i:u32=0u;i<8u;i=i+1u){o[i]=v[i]^v[i+8u];o[i+8u]=v[i+8u]^cv[i];}return o;
|
| 33 |
-
}
|
| 34 |
-
fn loadBlock(bo:u32)->array<u32,16>{let base=bo>>2u;var m:array<u32,16>;for(var i:u32=0u;i<16u;i=i+1u){m[i]=inp[base+i];}return m;}
|
| 35 |
-
fn chunkCV(s:u32,l:u32,ct:u32)->array<u32,8>{var cv:array<u32,8>=IV;let nB=max(1u,(l+63u)/64u);var fl:u32=1u;for(var i:u32=0u;i<nB-1u;i=i+1u){let o=compress(cv,loadBlock(s+i*64u),ct,0u,64u,fl);for(var j:u32=0u;j<8u;j=j+1u){cv[j]=o[j];}fl=0u;}let lo=s+(nB-1u)*64u;let ll=l-(nB-1u)*64u;let o2=compress(cv,loadBlock(lo),ct,0u,ll,fl|2u);var out:array<u32,8>;for(var j:u32=0u;j<8u;j=j+1u){out[j]=o2[j];}return out;}
|
| 36 |
-
fn chunkRoot(s:u32,l:u32)->array<u32,8>{var cv:array<u32,8>=IV;let nB=max(1u,(l+63u)/64u);var fl:u32=1u;for(var i:u32=0u;i<nB-1u;i=i+1u){let o=compress(cv,loadBlock(s+i*64u),0u,0u,64u,fl);for(var j:u32=0u;j<8u;j=j+1u){cv[j]=o[j];}fl=0u;}let lo=s+(nB-1u)*64u;let ll=l-(nB-1u)*64u;let o=compress(cv,loadBlock(lo),0u,0u,ll,fl|2u|8u);var out:array<u32,8>;for(var j:u32=0u;j<8u;j=j+1u){out[j]=o[j];}return out;}
|
| 37 |
-
fn parentCV(l:array<u32,8>,r:array<u32,8>,root:bool)->array<u32,8>{var m:array<u32,16>;for(var i:u32=0u;i<8u;i=i+1u){m[i]=l[i];m[i+8u]=r[i];}var fl:u32=4u;if(root){fl=12u;}let o=compress(IV,m,0u,0u,64u,fl);var out:array<u32,8>;for(var i:u32=0u;i<8u;i=i+1u){out[i]=o[i];}return out;}
|
| 38 |
-
fn getCV(i:u32)->array<u32,8>{var c:array<u32,8>;for(var j:u32=0u;j<8u;j=j+1u){c[j]=cvs[i*8u+j];}return c;}
|
| 39 |
-
@compute @workgroup_size(64)
|
| 40 |
-
fn phase1(@builtin(global_invocation_id) gid:vec3<u32>){
|
| 41 |
-
let i=gid.x; let N=P.y; let len=P.x; if(i>=N){return;}
|
| 42 |
-
let off=i*1024u; let ln=min(1024u,len-off);
|
| 43 |
-
var cv:array<u32,8>; if(N==1u){cv=chunkRoot(off,ln);}else{cv=chunkCV(off,ln,i);}
|
| 44 |
-
for(var j:u32=0u;j<8u;j=j+1u){cvs[i*8u+j]=cv[j];}
|
| 45 |
-
}
|
| 46 |
-
@compute @workgroup_size(1)
|
| 47 |
-
fn phase2(){
|
| 48 |
-
let N=P.y; var root:array<u32,8>;
|
| 49 |
-
if(N==1u){root=getCV(0u);}
|
| 50 |
-
else{
|
| 51 |
-
var stack:array<array<u32,8>,54>; var depth:u32=0u;
|
| 52 |
-
for(var c:u32=0u;c<N-1u;c=c+1u){var cv=getCV(c);var t=c+1u;loop{if((t&1u)!=0u){break;}depth=depth-1u;cv=parentCV(stack[depth],cv,false);t=t>>1u;}stack[depth]=cv;depth=depth+1u;}
|
| 53 |
-
var node=getCV(N-1u);for(var i:u32=0u;i<depth;i=i+1u){let li=depth-1u-i;root=parentCV(stack[li],node,i==depth-1u);node=root;}
|
| 54 |
-
}
|
| 55 |
-
for(var i:u32=0u;i<8u;i=i+1u){outp[i]=root[i];}
|
| 56 |
-
}
|
| 57 |
-
`;
|
| 58 |
-
|
| 59 |
-
let _dev = null, _p1 = null, _p2 = null, _bgl = null, _initP = null;
|
| 60 |
-
async function ensure() {
|
| 61 |
-
if (_dev) return true;
|
| 62 |
-
if (_initP) return _initP;
|
| 63 |
-
_initP = (async () => {
|
| 64 |
-
_dev = (await getDevice()).dev; // SHARE the engine's device (max limits) — no second device
|
| 65 |
-
const mod = _dev.createShaderModule({ code: WGSL });
|
| 66 |
-
const ci = await mod.getCompilationInfo();
|
| 67 |
-
const er = ci.messages.filter((m) => m.type === "error");
|
| 68 |
-
if (er.length) throw new Error("gpu-blake3 WGSL: " + er[0].message);
|
| 69 |
-
_bgl = _dev.createBindGroupLayout({ entries: [
|
| 70 |
-
{ binding: 0, visibility: GPUShaderStage.COMPUTE, buffer: { type: "read-only-storage" } },
|
| 71 |
-
{ binding: 1, visibility: GPUShaderStage.COMPUTE, buffer: { type: "storage" } },
|
| 72 |
-
{ binding: 2, visibility: GPUShaderStage.COMPUTE, buffer: { type: "storage" } },
|
| 73 |
-
{ binding: 3, visibility: GPUShaderStage.COMPUTE, buffer: { type: "uniform" } }] });
|
| 74 |
-
const pll = _dev.createPipelineLayout({ bindGroupLayouts: [_bgl] });
|
| 75 |
-
_p1 = _dev.createComputePipeline({ layout: pll, compute: { module: mod, entryPoint: "phase1" } });
|
| 76 |
-
_p2 = _dev.createComputePipeline({ layout: pll, compute: { module: mod, entryPoint: "phase2" } });
|
| 77 |
-
return true;
|
| 78 |
-
})().catch((e) => { _initP = null; throw e; });
|
| 79 |
-
return _initP;
|
| 80 |
-
}
|
| 81 |
-
|
| 82 |
-
// gpuBlake3Hex(bytes) → 64-hex BLAKE3 of `bytes`, computed entirely on the GPU. Throws if WebGPU is unavailable.
|
| 83 |
-
export async function gpuBlake3Hex(bytes) {
|
| 84 |
-
await ensure();
|
| 85 |
-
const dev = _dev;
|
| 86 |
-
const len = bytes.length, N = len === 0 ? 1 : Math.ceil(len / 1024);
|
| 87 |
-
const padU = Math.max(16, (Math.ceil(len / 64) || 1) * 16);
|
| 88 |
-
const u = new Uint32Array(padU); new Uint8Array(u.buffer).set(bytes);
|
| 89 |
-
const inB = dev.createBuffer({ size: u.byteLength, usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST }); dev.queue.writeBuffer(inB, 0, u);
|
| 90 |
-
const cvB = dev.createBuffer({ size: Math.max(32, N * 32), usage: GPUBufferUsage.STORAGE });
|
| 91 |
-
const oB = dev.createBuffer({ size: 32, usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_SRC });
|
| 92 |
-
const pB = dev.createBuffer({ size: 16, usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST }); dev.queue.writeBuffer(pB, 0, new Uint32Array([len, N, 0, 0]));
|
| 93 |
-
const sg = dev.createBuffer({ size: 32, usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.MAP_READ });
|
| 94 |
-
const bg = dev.createBindGroup({ layout: _bgl, entries: [{ binding: 0, resource: { buffer: inB } }, { binding: 1, resource: { buffer: cvB } }, { binding: 2, resource: { buffer: oB } }, { binding: 3, resource: { buffer: pB } }] });
|
| 95 |
-
const e = dev.createCommandEncoder();
|
| 96 |
-
let pass = e.beginComputePass(); pass.setPipeline(_p1); pass.setBindGroup(0, bg); pass.dispatchWorkgroups(Math.ceil(N / 64)); pass.end();
|
| 97 |
-
pass = e.beginComputePass(); pass.setPipeline(_p2); pass.setBindGroup(0, bg); pass.dispatchWorkgroups(1); pass.end();
|
| 98 |
-
e.copyBufferToBuffer(oB, 0, sg, 0, 32); dev.queue.submit([e.finish()]);
|
| 99 |
-
await sg.mapAsync(GPUMapMode.READ);
|
| 100 |
-
const w = new Uint32Array(sg.getMappedRange().slice(0)); sg.unmap();
|
| 101 |
-
inB.destroy(); cvB.destroy(); oB.destroy(); pB.destroy(); sg.destroy();
|
| 102 |
-
let s = "";
|
| 103 |
-
for (let i = 0; i < 8; i++) { const x = w[i] >>> 0; s += (x & 255).toString(16).padStart(2, "0") + ((x >>> 8) & 255).toString(16).padStart(2, "0") + ((x >>> 16) & 255).toString(16).padStart(2, "0") + ((x >>> 24) & 255).toString(16).padStart(2, "0"); }
|
| 104 |
-
return s;
|
| 105 |
-
}
|
| 106 |
-
export function gpuBlake3Available() { return typeof navigator !== "undefined" && !!navigator.gpu; }
|
| 107 |
-
export default { gpuBlake3Hex, gpuBlake3Available };
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
holo-blake3.mjs
DELETED
|
@@ -1,128 +0,0 @@
|
|
| 1 |
-
// holo-blake3.mjs — BLAKE3 (the hologram substrate's σ-axis, ADR-052), pure JS, lean +
|
| 2 |
-
// browser-native. This is the convergence seam made real: the substrate addresses content as
|
| 3 |
-
// `blake3:<hex>` = standard BLAKE3 of the canonical bytes (proven: its own differential test
|
| 4 |
-
// asserts the blake3 axis == the reference `blake3` crate). Reproducing standard BLAKE3 here —
|
| 5 |
-
// witnessed byte-identical against the substrate's own `kappa()` wasm across chunk boundaries —
|
| 6 |
-
// makes OS2 κ byte-identical to upstream WITHOUT a 6.5 MB wasm or restating substrate internals
|
| 7 |
-
// (BLAKE3 is the public standard the substrate itself consumes). Implements the official spec:
|
| 8 |
-
// 1024-byte chunks, 64-byte blocks, 7-round compression, binary tree of chaining values.
|
| 9 |
-
|
| 10 |
-
const IV = [0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19];
|
| 11 |
-
const MSG = [2, 6, 3, 10, 7, 0, 4, 13, 1, 11, 12, 5, 9, 14, 15, 8];
|
| 12 |
-
const CHUNK_START = 1, CHUNK_END = 2, PARENT = 4, ROOT = 8, BLOCK = 64, CHUNK = 1024;
|
| 13 |
-
const rotr = (x, n) => ((x >>> n) | (x << (32 - n))) >>> 0;
|
| 14 |
-
|
| 15 |
-
function g(v, a, b, c, d, mx, my) {
|
| 16 |
-
v[a] = (v[a] + v[b] + mx) >>> 0; v[d] = rotr(v[d] ^ v[a], 16);
|
| 17 |
-
v[c] = (v[c] + v[d]) >>> 0; v[b] = rotr(v[b] ^ v[c], 12);
|
| 18 |
-
v[a] = (v[a] + v[b] + my) >>> 0; v[d] = rotr(v[d] ^ v[a], 8);
|
| 19 |
-
v[c] = (v[c] + v[d]) >>> 0; v[b] = rotr(v[b] ^ v[c], 7);
|
| 20 |
-
}
|
| 21 |
-
function roundFn(v, m) {
|
| 22 |
-
g(v, 0, 4, 8, 12, m[0], m[1]); g(v, 1, 5, 9, 13, m[2], m[3]);
|
| 23 |
-
g(v, 2, 6, 10, 14, m[4], m[5]); g(v, 3, 7, 11, 15, m[6], m[7]);
|
| 24 |
-
g(v, 0, 5, 10, 15, m[8], m[9]); g(v, 1, 6, 11, 12, m[10], m[11]);
|
| 25 |
-
g(v, 2, 7, 8, 13, m[12], m[13]); g(v, 3, 4, 9, 14, m[14], m[15]);
|
| 26 |
-
}
|
| 27 |
-
// compress(cv[8], m[16], counter, blockLen, flags) → out[16]
|
| 28 |
-
function compress(cv, m0, counter, blockLen, flags) {
|
| 29 |
-
const cl = counter >>> 0, ch = Math.floor(counter / 4294967296) >>> 0;
|
| 30 |
-
const v = [cv[0], cv[1], cv[2], cv[3], cv[4], cv[5], cv[6], cv[7],
|
| 31 |
-
IV[0], IV[1], IV[2], IV[3], cl, ch, blockLen >>> 0, flags >>> 0];
|
| 32 |
-
let m = m0.slice();
|
| 33 |
-
for (let r = 0; r < 7; r++) { roundFn(v, m); if (r < 6) { const p = new Array(16); for (let i = 0; i < 16; i++) p[i] = m[MSG[i]]; m = p; } }
|
| 34 |
-
const out = new Array(16);
|
| 35 |
-
for (let i = 0; i < 8; i++) { out[i] = (v[i] ^ v[i + 8]) >>> 0; out[i + 8] = (v[i + 8] ^ cv[i]) >>> 0; }
|
| 36 |
-
return out;
|
| 37 |
-
}
|
| 38 |
-
// read a 64-byte block (zero-padded) into 16 LE u32 words
|
| 39 |
-
function words(bytes, off, len) {
|
| 40 |
-
const m = new Array(16).fill(0);
|
| 41 |
-
for (let i = 0; i < len; i++) m[i >> 2] |= bytes[off + i] << ((i & 3) * 8);
|
| 42 |
-
for (let i = 0; i < 16; i++) m[i] >>>= 0;
|
| 43 |
-
return m;
|
| 44 |
-
}
|
| 45 |
-
|
| 46 |
-
// An "output" node: its chaining value, or (with ROOT) its 32-byte hash.
|
| 47 |
-
function nodeChainingValue(o) { return compress(o.cv, o.m, o.counter, o.blockLen, o.flags).slice(0, 8); }
|
| 48 |
-
function nodeRootBytes(o) {
|
| 49 |
-
const out = compress(o.cv, o.m, 0, o.blockLen, o.flags | ROOT); // output block counter 0 → first 32 bytes
|
| 50 |
-
const b = new Uint8Array(32);
|
| 51 |
-
for (let i = 0; i < 8; i++) { const w = out[i]; b[i * 4] = w & 255; b[i * 4 + 1] = (w >>> 8) & 255; b[i * 4 + 2] = (w >>> 16) & 255; b[i * 4 + 3] = (w >>> 24) & 255; }
|
| 52 |
-
return b;
|
| 53 |
-
}
|
| 54 |
-
|
| 55 |
-
// One chunk (≤1024 bytes) → its output node (its last block carries CHUNK_END).
|
| 56 |
-
function chunkNode(bytes, start, len, counter) {
|
| 57 |
-
let cv = IV.slice();
|
| 58 |
-
const nBlocks = Math.max(1, Math.ceil(len / BLOCK));
|
| 59 |
-
let flags = CHUNK_START;
|
| 60 |
-
for (let i = 0; i < nBlocks - 1; i++) { cv = compress(cv, words(bytes, start + i * BLOCK, BLOCK), counter, BLOCK, flags).slice(0, 8); flags = 0; }
|
| 61 |
-
const lastOff = start + (nBlocks - 1) * BLOCK;
|
| 62 |
-
const lastLen = len - (nBlocks - 1) * BLOCK; // 0..64 (0 only when len==0)
|
| 63 |
-
return { cv, m: words(bytes, lastOff, lastLen), counter, blockLen: lastLen, flags: flags | CHUNK_END };
|
| 64 |
-
}
|
| 65 |
-
function parentNode(leftCV, rightCV) {
|
| 66 |
-
const m = leftCV.concat(rightCV);
|
| 67 |
-
return { cv: IV.slice(), m, counter: 0, blockLen: BLOCK, flags: PARENT };
|
| 68 |
-
}
|
| 69 |
-
// Hash a subtree [start, start+len) starting at chunk index `counter` → an output node.
|
| 70 |
-
function subtree(bytes, start, len, counter) {
|
| 71 |
-
if (len <= CHUNK) return chunkNode(bytes, start, len, counter);
|
| 72 |
-
let left = CHUNK; while (left * 2 < len) left *= 2; // largest power-of-two chunk span < len
|
| 73 |
-
const leftCV = nodeChainingValue(subtree(bytes, start, left, counter));
|
| 74 |
-
const rightCV = nodeChainingValue(subtree(bytes, start + left, len - left, counter + left / CHUNK));
|
| 75 |
-
return parentNode(leftCV, rightCV);
|
| 76 |
-
}
|
| 77 |
-
|
| 78 |
-
export function blake3(bytes) {
|
| 79 |
-
const b = bytes instanceof Uint8Array ? bytes : new Uint8Array(bytes);
|
| 80 |
-
return nodeRootBytes(subtree(b, 0, b.length, 0));
|
| 81 |
-
}
|
| 82 |
-
export function blake3hex(bytes) {
|
| 83 |
-
const d = blake3(bytes); let s = "";
|
| 84 |
-
for (let i = 0; i < 32; i++) s += d[i].toString(16).padStart(2, "0");
|
| 85 |
-
return s;
|
| 86 |
-
}
|
| 87 |
-
// the hologram σ-axis κ-label: "blake3:" + 64 hex (the 71-byte ContentLabel).
|
| 88 |
-
export function kappaBlake3(bytes) { return "blake3:" + blake3hex(bytes); }
|
| 89 |
-
|
| 90 |
-
// ── tree internals (the σ-axis Merkle structure) ─────────────────────────────────────
|
| 91 |
-
// Exposed so holo-bao.mjs can build verified-streaming (Bao) proofs against the SAME ONE
|
| 92 |
-
// implementation (Law L2 — no second hash). These are the standard BLAKE3 tree pieces:
|
| 93 |
-
// a chunk → output node, two CVs → parent node, a node → its chaining value or ROOT bytes.
|
| 94 |
-
export const CHUNK_BYTES = CHUNK;
|
| 95 |
-
export { chunkNode, parentNode, nodeChainingValue, nodeRootBytes, subtree };
|
| 96 |
-
|
| 97 |
-
// ── incremental hasher (stream-mint without a tail pass) ─────────────────────────────
|
| 98 |
-
// BLAKE3's streaming Hasher: feed chunks via update(), get the SAME digest as one-shot blake3() at
|
| 99 |
-
// digest()/hex(). Buffers at most ONE 1024-byte chunk; each completed chunk's CV folds into a stack via the
|
| 100 |
-
// standard add-chunk-cv merge (collapse while the chunk count is even), and digest() merges the final chunk
|
| 101 |
-
// down the stack with the ROOT flag — the canonical left-balanced tree, byte-identical to blake3() across
|
| 102 |
-
// chunk boundaries. Lets the streaming seam mint a κ AS bytes arrive instead of re-hashing the whole buffer.
|
| 103 |
-
export function createBlake3() {
|
| 104 |
-
const stack = []; // CVs of completed left subtrees (bottom = leftmost)
|
| 105 |
-
let counter = 0; // index of the current chunk
|
| 106 |
-
const buf = new Uint8Array(CHUNK);
|
| 107 |
-
let bufLen = 0;
|
| 108 |
-
const add = (cv, totalChunks) => { let t = totalChunks, c = cv; while ((t & 1) === 0) { c = nodeChainingValue(parentNode(stack.pop(), c)); t >>= 1; } stack.push(c); };
|
| 109 |
-
const api = {
|
| 110 |
-
update(bytes) {
|
| 111 |
-
const b = bytes instanceof Uint8Array ? bytes : new Uint8Array(bytes);
|
| 112 |
-
let off = 0; const n = b.length;
|
| 113 |
-
while (off < n) {
|
| 114 |
-
if (bufLen === CHUNK) { const cv = nodeChainingValue(chunkNode(buf, 0, CHUNK, counter)); counter++; add(cv, counter); bufLen = 0; }
|
| 115 |
-
const take = Math.min(CHUNK - bufLen, n - off);
|
| 116 |
-
buf.set(b.subarray(off, off + take), bufLen); bufLen += take; off += take;
|
| 117 |
-
}
|
| 118 |
-
return api;
|
| 119 |
-
},
|
| 120 |
-
digest() {
|
| 121 |
-
let node = chunkNode(buf, 0, bufLen, counter);
|
| 122 |
-
for (let i = stack.length - 1; i >= 0; i--) node = parentNode(stack[i], nodeChainingValue(node));
|
| 123 |
-
return nodeRootBytes(node);
|
| 124 |
-
},
|
| 125 |
-
hex() { const d = api.digest(); let s = ""; for (let i = 0; i < 32; i++) s += d[i].toString(16).padStart(2, "0"); return s; },
|
| 126 |
-
};
|
| 127 |
-
return api;
|
| 128 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
holo-gpu-device.mjs
DELETED
|
@@ -1,27 +0,0 @@
|
|
| 1 |
-
// holo-gpu-device.mjs — ONE WebGPU device for the whole app (the engine, BLAKE3 verify, Bao streaming).
|
| 2 |
-
// Memoized: exactly one requestDevice() per page. Created with the ADAPTER'S MAX limits so a 145 MB block
|
| 3 |
-
// binds as a storage buffer (the default cap is 128 MB — a separate default-limits device silently produced a
|
| 4 |
-
// wrong hash on the embed → κ mismatch → the two-device clash). timestamp-query is requested when available.
|
| 5 |
-
// Sharing ONE device is what lets fetch→verify→decode coexist without contending.
|
| 6 |
-
let _p = null;
|
| 7 |
-
export function getDevice() {
|
| 8 |
-
if (_p) return _p;
|
| 9 |
-
_p = (async () => {
|
| 10 |
-
if (typeof navigator === "undefined" || !navigator.gpu) throw new Error("no WebGPU");
|
| 11 |
-
const adapter = await navigator.gpu.requestAdapter();
|
| 12 |
-
const L = adapter.limits;
|
| 13 |
-
const canTs = adapter.features.has("timestamp-query");
|
| 14 |
-
const dev = await adapter.requestDevice({
|
| 15 |
-
requiredFeatures: canTs ? ["timestamp-query"] : [],
|
| 16 |
-
requiredLimits: {
|
| 17 |
-
maxStorageBufferBindingSize: L.maxStorageBufferBindingSize,
|
| 18 |
-
maxBufferSize: L.maxBufferSize,
|
| 19 |
-
maxComputeWorkgroupsPerDimension: L.maxComputeWorkgroupsPerDimension,
|
| 20 |
-
},
|
| 21 |
-
});
|
| 22 |
-
try { if (typeof globalThis !== "undefined") globalThis.__gpuDeviceCount = (globalThis.__gpuDeviceCount || 0) + 1; } catch (e) {}
|
| 23 |
-
return { dev, adapter, canTs };
|
| 24 |
-
})().catch((e) => { _p = null; throw e; });
|
| 25 |
-
return _p;
|
| 26 |
-
}
|
| 27 |
-
export default { getDevice };
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
kernel-profile.html
DELETED
|
@@ -1,147 +0,0 @@
|
|
| 1 |
-
<!doctype html><html><head><meta charset=utf8><meta name=viewport content="width=device-width,initial-scale=1">
|
| 2 |
-
<title>Ternary GEMV limiter profile — your GPU</title>
|
| 3 |
-
<style>
|
| 4 |
-
:root{--bg:#0a0d13;--panel:#111825;--ink:#e8ebf1;--dim:#8b95a7;--ac:#7c5cff;--ok:#48c26c;--no:#f0616d;--warn:#e0a94a;--line:#1e2836}
|
| 5 |
-
*{box-sizing:border-box}body{margin:0;background:var(--bg);color:var(--ink);font:15px/1.6 -apple-system,Segoe UI,Roboto,monospace;padding:24px;max-width:860px;margin:0 auto}
|
| 6 |
-
h1{font-size:20px;margin:0 0 4px}.sub{color:var(--dim);font-size:13px;margin:0 0 18px}
|
| 7 |
-
.card{font-family:ui-monospace,monospace;padding:14px 16px;border:1px solid var(--line);border-radius:10px;background:var(--panel);margin-bottom:12px;word-break:break-word}
|
| 8 |
-
table{width:100%;border-collapse:collapse;font-family:ui-monospace,monospace;font-size:13px}
|
| 9 |
-
td,th{padding:6px 8px;border-bottom:1px solid var(--line);text-align:left}th{color:var(--dim);font-weight:600}
|
| 10 |
-
td.n{text-align:right;font-variant-numeric:tabular-nums}
|
| 11 |
-
.verdict{font-size:16px;font-weight:600;margin-top:14px}.bar{display:inline-block;height:9px;background:var(--ac);border-radius:2px;vertical-align:middle}
|
| 12 |
-
</style></head><body>
|
| 13 |
-
<h1>Ternary GEMV <span style="color:var(--ac)">limiter profile</span> — your GPU</h1>
|
| 14 |
-
<p class="sub">Same GEMV access pattern, one knob at a time. <b>read-only</b> = the reads with zero math (memory ceiling at this pattern). <b>dot ×1</b> = the real kernel. <b>×2/×4</b> = same reads, more ALU. If read-only ≫ dot, the ALU is the wall (int8 is the lever); if read-only ≈ dot, the memory access pattern is the wall (int8 won't help).</p>
|
| 15 |
-
<div id="status" class="card">starting…</div>
|
| 16 |
-
<div id="out"></div>
|
| 17 |
-
|
| 18 |
-
<script type="module">
|
| 19 |
-
const $=s=>document.querySelector(s), st=$("#status"), out=$("#out");
|
| 20 |
-
const MODEL_GB=0.69;
|
| 21 |
-
const say=(t,cls)=>{ st.textContent=t; st.className="card"+(cls?" "+cls:""); };
|
| 22 |
-
window.addEventListener("unhandledrejection",e=>say("✗ unhandled: "+(e.reason&&(e.reason.message||e.reason)),"no"));
|
| 23 |
-
|
| 24 |
-
const DOT16=`
|
| 25 |
-
fn dot16(word:u32, v:u32) -> f32 {
|
| 26 |
-
var s4=vec4<f32>(0.0);
|
| 27 |
-
s4=s4+x[v] *(vec4<f32>(f32(word&3u),f32((word>>2u)&3u),f32((word>>4u)&3u),f32((word>>6u)&3u))-vec4<f32>(1.0));
|
| 28 |
-
s4=s4+x[v+1u]*(vec4<f32>(f32((word>>8u)&3u),f32((word>>10u)&3u),f32((word>>12u)&3u),f32((word>>14u)&3u))-vec4<f32>(1.0));
|
| 29 |
-
s4=s4+x[v+2u]*(vec4<f32>(f32((word>>16u)&3u),f32((word>>18u)&3u),f32((word>>20u)&3u),f32((word>>22u)&3u))-vec4<f32>(1.0));
|
| 30 |
-
s4=s4+x[v+3u]*(vec4<f32>(f32((word>>24u)&3u),f32((word>>26u)&3u),f32((word>>28u)&3u),f32((word>>30u)&3u))-vec4<f32>(1.0));
|
| 31 |
-
return s4.x+s4.y+s4.z+s4.w;
|
| 32 |
-
}`;
|
| 33 |
-
// mode: 'read' (xor raw, no unpack) | 'dot' (R× dot16, same reads). T threads/row, ROWS rows/wg (T*ROWS=256).
|
| 34 |
-
const kern=(mode,R,T,ROWS)=>`
|
| 35 |
-
@group(0) @binding(0) var<storage,read> x: array<vec4<f32>>;
|
| 36 |
-
@group(0) @binding(1) var<storage,read> qw: array<u32>;
|
| 37 |
-
@group(0) @binding(2) var<storage,read_write> o: array<f32>;
|
| 38 |
-
@group(0) @binding(3) var<uniform> P: vec4<u32>; // K, N
|
| 39 |
-
var<workgroup> red: array<f32, 256>;
|
| 40 |
-
${mode==='dot'?DOT16:''}
|
| 41 |
-
@compute @workgroup_size(256)
|
| 42 |
-
fn main(@builtin(workgroup_id) wg:vec3<u32>, @builtin(local_invocation_id) lid:vec3<u32>){
|
| 43 |
-
let K=P.x; let nw=K>>4u;
|
| 44 |
-
let rr=lid.x/${T}u; let t=lid.x%${T}u;
|
| 45 |
-
let n0=(wg.y*65535u+wg.x)*${ROWS}u+rr; let n=min(n0, P.y-1u);
|
| 46 |
-
let rowW=n*nw;
|
| 47 |
-
${mode==='read'?'var accu=0u;':'var acc=0.0;'}
|
| 48 |
-
var w=t;
|
| 49 |
-
loop{ if(w>=nw){break;}
|
| 50 |
-
let word=qw[rowW+w]; let v=w<<2u;
|
| 51 |
-
${mode==='read'?'accu=accu^word;':Array.from({length:R},(_,r)=>`acc=acc+dot16(word^${r}u, v);`).join(' ')}
|
| 52 |
-
w=w+${T}u; }
|
| 53 |
-
${mode==='read'?'var acc=f32(accu&1u); if(accu==0xffffffffu){ acc=acc+x[0].x; }':''}
|
| 54 |
-
red[lid.x]=acc; workgroupBarrier();
|
| 55 |
-
var s=${T>>1}u; loop{ if(s==0u){break;} if(t<s){ red[rr*${T}u+t]=red[rr*${T}u+t]+red[rr*${T}u+t+s]; } workgroupBarrier(); s=s/2u; }
|
| 56 |
-
if(t==0u && n0<P.y){ o[n0]=red[rr*${T}u]; }
|
| 57 |
-
}`;
|
| 58 |
-
|
| 59 |
-
const FILL=`
|
| 60 |
-
@group(0) @binding(0) var<storage,read_write> d: array<u32>;
|
| 61 |
-
@group(0) @binding(1) var<uniform> P: vec4<u32>;
|
| 62 |
-
@compute @workgroup_size(256)
|
| 63 |
-
fn main(@builtin(global_invocation_id) gid:vec3<u32>){ let n=P.x; var i=gid.x; loop{ if(i>=n){break;} d[i]=(i*2654435761u+1u)^((i<<7u)*40503u); i=i+P.y; } }`;
|
| 64 |
-
|
| 65 |
-
const VARIANTS=[
|
| 66 |
-
{id:"read", name:"read-only (no ALU) — memory ceiling", mode:"read", R:0, T:64, ROWS:4, grp:"alu"},
|
| 67 |
-
{id:"dot1", name:"dot ×1 (the real kernel)", mode:"dot", R:1, T:64, ROWS:4, grp:"alu"},
|
| 68 |
-
{id:"dot2", name:"dot ×2 (2× ALU, same reads)", mode:"dot", R:2, T:64, ROWS:4, grp:"alu"},
|
| 69 |
-
{id:"dot4", name:"dot ×4 (4× ALU, same reads)", mode:"dot", R:4, T:64, ROWS:4, grp:"alu"},
|
| 70 |
-
{id:"dot8", name:"dot ×8 (KX=8 batched-verify proxy)", mode:"dot", R:8, T:64, ROWS:4, grp:"alu"},
|
| 71 |
-
{id:"occ32", name:"dot ×1 · 32 thr/row · 8 rows/wg", mode:"dot", R:1, T:32, ROWS:8, grp:"occ"},
|
| 72 |
-
{id:"occ128",name:"dot ×1 · 128 thr/row · 2 rows/wg", mode:"dot", R:1, T:128,ROWS:2, grp:"occ"},
|
| 73 |
-
];
|
| 74 |
-
|
| 75 |
-
(async()=>{
|
| 76 |
-
try{
|
| 77 |
-
if(!navigator.gpu){ say("✗ No WebGPU. Open in Chrome/Edge.","no"); return; }
|
| 78 |
-
say("requesting adapter…");
|
| 79 |
-
let ad=await navigator.gpu.requestAdapter({powerPreference:"high-performance"}); if(!ad) ad=await navigator.gpu.requestAdapter();
|
| 80 |
-
if(!ad){ say("✗ no GPU adapter.","no"); return; }
|
| 81 |
-
const L=ad.limits, info=ad.info||{};
|
| 82 |
-
const dev=await ad.requestDevice({requiredLimits:{maxStorageBufferBindingSize:L.maxStorageBufferBindingSize, maxBufferSize:L.maxBufferSize, maxComputeWorkgroupsPerDimension:L.maxComputeWorkgroupsPerDimension}});
|
| 83 |
-
dev.lost.then(i=>say("✗ device lost: "+(i&&i.message||i.reason||""),"no"));
|
| 84 |
-
|
| 85 |
-
const K=4096, nw=K/16;
|
| 86 |
-
const wantBytes=Math.min(L.maxStorageBufferBindingSize, L.maxBufferSize, Math.round(MODEL_GB*1e9));
|
| 87 |
-
const N=Math.floor((wantBytes/4)/nw), qU32=N*nw, wBytes=qU32*4;
|
| 88 |
-
say("allocating "+(wBytes/1048576).toFixed(0)+" MB…");
|
| 89 |
-
const qw=dev.createBuffer({size:wBytes, usage:GPUBufferUsage.STORAGE});
|
| 90 |
-
const xbuf=dev.createBuffer({size:K*4, usage:GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_DST});
|
| 91 |
-
const obuf=dev.createBuffer({size:N*4, usage:GPUBufferUsage.STORAGE});
|
| 92 |
-
const P=dev.createBuffer({size:16, usage:GPUBufferUsage.UNIFORM|GPUBufferUsage.COPY_DST});
|
| 93 |
-
const xa=new Float32Array(K); for(let i=0;i<K;i++) xa[i]=(Math.sin(i*12.9898)*43758.5453)%1;
|
| 94 |
-
dev.queue.writeBuffer(xbuf,0,xa); dev.queue.writeBuffer(P,0,new Uint32Array([K,N,0,0]));
|
| 95 |
-
|
| 96 |
-
say("filling weights…");
|
| 97 |
-
{ const fmod=dev.createShaderModule({code:FILL}); const fpipe=dev.createComputePipeline({layout:"auto",compute:{module:fmod,entryPoint:"main"}});
|
| 98 |
-
const fp=dev.createBuffer({size:16,usage:GPUBufferUsage.UNIFORM|GPUBufferUsage.COPY_DST}); const wgF=Math.min(L.maxComputeWorkgroupsPerDimension,65535);
|
| 99 |
-
dev.queue.writeBuffer(fp,0,new Uint32Array([qU32, wgF*256, 0, 0]));
|
| 100 |
-
const fbg=dev.createBindGroup({layout:fpipe.getBindGroupLayout(0),entries:[{binding:0,resource:{buffer:qw}},{binding:1,resource:{buffer:fp}}]});
|
| 101 |
-
const e=dev.createCommandEncoder(); const p=e.beginComputePass(); p.setPipeline(fpipe); p.setBindGroup(0,fbg); p.dispatchWorkgroups(wgF); p.end();
|
| 102 |
-
dev.queue.submit([e.finish()]); await dev.queue.onSubmittedWorkDone(); }
|
| 103 |
-
|
| 104 |
-
const PASSES=10, ITERS=5;
|
| 105 |
-
const rows=[];
|
| 106 |
-
for(const V of VARIANTS){
|
| 107 |
-
say("compiling "+V.id+"…");
|
| 108 |
-
dev.pushErrorScope("validation");
|
| 109 |
-
const mod=dev.createShaderModule({code:kern(V.mode,V.R,V.T,V.ROWS)});
|
| 110 |
-
const ci=await mod.getCompilationInfo(); const er=ci.messages.filter(m=>m.type==="error");
|
| 111 |
-
if(er.length){ await dev.popErrorScope(); rows.push({V,err:er[0].message}); continue; }
|
| 112 |
-
const pipe=dev.createComputePipeline({layout:"auto",compute:{module:mod,entryPoint:"main"}});
|
| 113 |
-
const bg=dev.createBindGroup({layout:pipe.getBindGroupLayout(0),entries:[{binding:0,resource:{buffer:xbuf}},{binding:1,resource:{buffer:qw}},{binding:2,resource:{buffer:obuf}},{binding:3,resource:{buffer:P}}]});
|
| 114 |
-
const se=await dev.popErrorScope(); if(se){ rows.push({V,err:se.message}); continue; }
|
| 115 |
-
const groups=Math.ceil(N/V.ROWS), wgx=Math.min(groups,65535), wgy=Math.ceil(groups/65535);
|
| 116 |
-
async function run(passes){ const e=dev.createCommandEncoder(); for(let k=0;k<passes;k++){ const p=e.beginComputePass(); p.setPipeline(pipe); p.setBindGroup(0,bg); p.dispatchWorkgroups(wgx,wgy); p.end(); } const t0=performance.now(); dev.queue.submit([e.finish()]); await dev.queue.onSubmittedWorkDone(); return performance.now()-t0; }
|
| 117 |
-
say("timing "+V.id+"…");
|
| 118 |
-
await run(2); let best=1e9; for(let k=0;k<ITERS;k++){ best=Math.min(best, await run(PASSES)); }
|
| 119 |
-
const gbps=(wBytes/1073741824)/((best/PASSES)/1000);
|
| 120 |
-
rows.push({V, gbps, tok:gbps/MODEL_GB});
|
| 121 |
-
}
|
| 122 |
-
|
| 123 |
-
say("done · adapter: "+((info.vendor||"?")+" "+(info.architecture||"")+" "+(info.device||"")).trim(),"ok");
|
| 124 |
-
const g=id=>rows.find(r=>r.V.id===id&&r.gbps); const rd=g("read"), d1=g("dot1"), d4=g("dot4"), d8=g("dot8"), o32=g("occ32"), o128=g("occ128");
|
| 125 |
-
const maxG=Math.max(...rows.filter(r=>r.gbps).map(r=>r.gbps));
|
| 126 |
-
const readVsDot = (rd&&d1)? rd.gbps/d1.gbps : 0; // >~1.5 ⇒ reads themselves fly, ALU is the wall
|
| 127 |
-
const aluSlope = (d1&&d4)? d1.gbps/d4.gbps : 0; // >~1.5 ⇒ time scales with ALU ⇒ ALU-throughput bound
|
| 128 |
-
const occGain = d1? Math.max(o32?o32.gbps/d1.gbps:0, o128?o128.gbps/d1.gbps:0) : 0;
|
| 129 |
-
const batchFree = (d1&&d8)? d8.gbps/d1.gbps : 0; // ~1 ⇒ 8-wide batched verify costs ~same as 1 ⇒ spec-decode is ~free
|
| 130 |
-
const readCeil = rd? rd.gbps : 0;
|
| 131 |
-
let verdict, cls;
|
| 132 |
-
const specNote = d8 ? ` Batched verify (dot ×8, the KX=8 spec proxy) runs at ${d8.gbps.toFixed(0)} GB/s = ${batchFree.toFixed(2)}× the single kernel — so verifying 8 tokens costs ≈ 1 token. Spec-decode IS ~free here; its earlier 1.0× was per-window JS/fence OVERHEAD, not GPU cost. Killing that overhead unlocks the ~4× the 93%-acceptance implies.` : "";
|
| 133 |
-
if(aluSlope>=1.6 && readVsDot>=1.6){ verdict = `ALU-BOUND. reads fly (${readCeil.toFixed(0)} GB/s) but dot ×1 only ${d1.gbps.toFixed(0)}, and ×1→×4 scales ${aluSlope.toFixed(1)}×. Fewer instructions per weight (int8 dot4I8Packed) is the lever.` + specNote; cls="warn"; }
|
| 134 |
-
else{ verdict = `MEMORY-LATENCY-BOUND — NOT ALU-bound. Quadrupling the ALU (×1→×4) costs only ${aluSlope.toFixed(1)}× → the f32 unpack is hidden; int8 will NOT help. read-only tops ${readCeil.toFixed(0)} GB/s at this pattern vs 152 pure-stream, and dot ×1 sits at ${d1?d1.gbps.toFixed(0):"?"} (${readCeil?(100*d1.gbps/readCeil).toFixed(0):"?"}% of even the read ceiling). Lever = more memory-level parallelism (register-block several output rows per thread so more loads are in flight before the barrier), NOT int8/occupancy.` + specNote; cls=(occGain>=1.15?"warn":"no"); }
|
| 135 |
-
|
| 136 |
-
out.innerHTML=`
|
| 137 |
-
<div class="card"><span class="k" style="color:var(--dim)">Test matrix</span> ${N.toLocaleString()} × ${K} · ${(wBytes/1073741824).toFixed(2)} GB · pure-stream roofline 152 GB/s (220 tok/s)</div>
|
| 138 |
-
<div class="card"><table>
|
| 139 |
-
<tr><th>variant</th><th class="n">GB/s</th><th class="n">tok/s-equiv</th><th></th></tr>
|
| 140 |
-
${rows.map(r=>r.err
|
| 141 |
-
? `<tr><td>${r.V.name}</td><td colspan=3 class="no">failed: ${r.err.slice(0,54)}</td></tr>`
|
| 142 |
-
: `<tr><td>${r.V.name}</td><td class="n">${r.gbps.toFixed(0)}</td><td class="n">${r.tok.toFixed(0)}</td><td><span class="bar" style="width:${Math.round(140*r.gbps/maxG)}px"></span></td></tr>`).join("")}
|
| 143 |
-
</table></div>
|
| 144 |
-
<div class="verdict ${cls}">${verdict}</div>`;
|
| 145 |
-
}catch(e){ say("✗ "+(e&&(e.message||e)), "no"); }
|
| 146 |
-
})();
|
| 147 |
-
</script></body></html>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
q-chat.html
DELETED
|
@@ -1,120 +0,0 @@
|
|
| 1 |
-
<!doctype html><html><head><meta charset=utf8><meta name=viewport content="width=device-width,initial-scale=1">
|
| 2 |
-
<title>Q · local chat</title>
|
| 3 |
-
<style>
|
| 4 |
-
:root{--bg:#0b0e14;--panel:#141922;--ink:#e6e9ef;--dim:#8a94a6;--q:#7c5cff;--u:#1f6feb;--line:#1e2531}
|
| 5 |
-
*{box-sizing:border-box}html,body{height:100%}
|
| 6 |
-
body{margin:0;font:15px/1.55 -apple-system,Segoe UI,Roboto,monospace;background:var(--bg);color:var(--ink);display:flex;flex-direction:column}
|
| 7 |
-
header{padding:10px 16px;border-bottom:1px solid var(--line);display:flex;align-items:center;gap:10px;flex:0 0 auto}
|
| 8 |
-
header b{font-weight:600}header .s{color:var(--dim);font-size:12px}
|
| 9 |
-
#log{flex:1;overflow:auto;padding:16px;display:flex;flex-direction:column;gap:12px}
|
| 10 |
-
.msg{max-width:82%;padding:9px 13px;border-radius:12px;white-space:pre-wrap;word-wrap:break-word}
|
| 11 |
-
.u{align-self:flex-end;background:var(--u)}.a{align-self:flex-start;background:var(--panel);border:1px solid #232b3a}
|
| 12 |
-
.a.think{color:var(--dim);font-style:italic}
|
| 13 |
-
footer{padding:12px 16px;border-top:1px solid var(--line);display:flex;gap:8px;flex:0 0 auto}
|
| 14 |
-
#in{flex:1;background:var(--panel);border:1px solid #232b3a;color:var(--ink);border-radius:10px;padding:10px 12px;font:inherit;resize:none;max-height:140px}
|
| 15 |
-
button{background:var(--q);color:#fff;border:0;border-radius:10px;padding:0 18px;font:inherit;cursor:pointer}button:disabled{opacity:.4;cursor:default}
|
| 16 |
-
.stat{color:var(--dim);font-size:11px;margin-top:3px}
|
| 17 |
-
</style></head><body>
|
| 18 |
-
<header><b>Q</b> <span class=s id=st>booting…</span></header>
|
| 19 |
-
<div id=log></div>
|
| 20 |
-
<footer><textarea id=in rows=1 placeholder="Message Q…" disabled></textarea><button id=send disabled>Send</button></footer>
|
| 21 |
-
<script type=module>
|
| 22 |
-
import { ready, loadModel, MODELS, defaultModelIndex } from "./core/loader.js";
|
| 23 |
-
import { createEngine } from "./core/engine.js";
|
| 24 |
-
import { selfPersona } from "./core/q-self.mjs"; // ONE grounded self-knowledge, shared with the messenger + voice
|
| 25 |
-
const $ = (s) => document.querySelector(s);
|
| 26 |
-
const log = $("#log"), input = $("#in"), send = $("#send"), st = $("#st");
|
| 27 |
-
const bubble = (cls, text = "") => { const d = document.createElement("div"); d.className = "msg " + cls; d.textContent = text; log.appendChild(d); log.scrollTop = log.scrollHeight; return d; };
|
| 28 |
-
const params = new URLSearchParams(location.search);
|
| 29 |
-
const pick = params.get("m");
|
| 30 |
-
let m = pick ? (MODELS.find((x) => new RegExp(pick, "i").test(x.name)) || MODELS[0]) : MODELS[defaultModelIndex()];
|
| 31 |
-
// STREAM FROM ANYWHERE: ?hf=<org/repo> (a HuggingFace κ-object) or ?kappa=<absolute-url> loads the weights from
|
| 32 |
-
// that host's CDN instead of the local mount. The κ-object is content-addressed + pinned, so the host is an
|
| 33 |
-
// UNTRUSTED CDN — every block is re-derived (Law L5); a bad byte is rejected. Blocks cache locally after first
|
| 34 |
-
// load (0-network on return). Uses BitNet's engine flags (the HOLOGRAMTECH model is BitNet-2B).
|
| 35 |
-
{
|
| 36 |
-
const hf = params.get("hf"), kappa = params.get("kappa");
|
| 37 |
-
if (hf || kappa) {
|
| 38 |
-
const base = (kappa || `https://huggingface.co/${hf}/resolve/main`).replace(/\/+$/, "");
|
| 39 |
-
const bit = MODELS.find((x) => (x.fam || "").toLowerCase() === "bitnet") || m;
|
| 40 |
-
m = { ...bit, kappaUrl: base, name: bit.name + " · via " + (hf || new URL(base).host) };
|
| 41 |
-
}
|
| 42 |
-
}
|
| 43 |
-
|
| 44 |
-
// GROUND the model as on-device Q (a base/instruct model has NO self-knowledge — without this it confabulates
|
| 45 |
-
// a generic "I run on OpenAI/AWS cloud servers" identity, which is false). Injected as the SYSTEM turn.
|
| 46 |
-
// the system block for the model's own chat template (matches core/engine frameTurn's per-family markers).
|
| 47 |
-
// PERSONA is Q's LIVE grounded self-knowledge (q-self), computed at call time so it names the real resident
|
| 48 |
-
// model + κ — without it a base model confabulates a cloud/OpenAI/AWS identity.
|
| 49 |
-
function frameSystem() {
|
| 50 |
-
const PERSONA = selfPersona({ model: m, engine });
|
| 51 |
-
if (m.llama3) return `<|start_header_id|>system<|end_header_id|>\n\n${PERSONA}<|eot_id|>`;
|
| 52 |
-
if (m.qwen) return `<|im_start|>system\n${PERSONA}<|im_end|>\n`;
|
| 53 |
-
if (m.olmo) return `<|system|>\n${PERSONA}\n`;
|
| 54 |
-
return PERSONA + "\n\n"; // word/plain frame: prepend as leading context
|
| 55 |
-
}
|
| 56 |
-
|
| 57 |
-
let engine = null, convIds = [], busy = false, armed = false, pending = null;
|
| 58 |
-
// type immediately — don't wait for the model. The first message is queued and auto-sent the instant Q is ready.
|
| 59 |
-
input.disabled = send.disabled = false; input.placeholder = "Message Q… (model loading — will send the moment it's ready)"; input.focus();
|
| 60 |
-
|
| 61 |
-
async function generate(text, skipUser) {
|
| 62 |
-
busy = true; input.disabled = send.disabled = true;
|
| 63 |
-
if (!skipUser) bubble("u", text);
|
| 64 |
-
const a = bubble("a think", "…"); let first = true;
|
| 65 |
-
const stat = document.createElement("div"); stat.className = "stat";
|
| 66 |
-
const t0 = performance.now();
|
| 67 |
-
try {
|
| 68 |
-
let framed = engine.frameTurn(text, convIds.length > 0);
|
| 69 |
-
if (convIds.length === 0) framed = frameSystem() + framed; // first turn → lead with the on-device Q persona
|
| 70 |
-
let turnIds = engine.tokenize(framed);
|
| 71 |
-
if (m.bos && engine.bosId != null && convIds.length === 0) turnIds = [engine.bosId, ...turnIds];
|
| 72 |
-
const res = await engine.generate(convIds.concat(turnIds), { maxNew: m.cap || 256, onToken: ({ text: t, stats }) => {
|
| 73 |
-
if (first && t) { a.classList.remove("think"); a.textContent = ""; first = false; }
|
| 74 |
-
a.textContent = t; log.scrollTop = log.scrollHeight;
|
| 75 |
-
if (stats) stat.textContent = `${stats.tokps ? stats.tokps.toFixed(0) + " tok/s" : ""}${stats.ttft ? " · TTFT " + Math.round(stats.ttft) + "ms" : ""}`;
|
| 76 |
-
} });
|
| 77 |
-
if (first) { a.classList.remove("think"); a.textContent = res.text || "(no output)"; }
|
| 78 |
-
convIds = res.ids; a.after(stat);
|
| 79 |
-
} catch (e) { a.classList.remove("think"); a.textContent = "⚠ " + e.message; }
|
| 80 |
-
busy = false; input.disabled = send.disabled = false; input.focus();
|
| 81 |
-
}
|
| 82 |
-
// Q reaches out first — a real, model-GENERATED opening (not a canned line), so it feels alive.
|
| 83 |
-
// This also warms the GPU pipelines. It is standalone (not added to convIds) so the real chat starts fresh.
|
| 84 |
-
async function proactiveGreeting() {
|
| 85 |
-
busy = true; input.disabled = send.disabled = true;
|
| 86 |
-
const a = bubble("a think", "…"); let first = true;
|
| 87 |
-
const stat = document.createElement("div"); stat.className = "stat";
|
| 88 |
-
const FALLBACK = "Hey — I'm Q, running entirely on your device, no server, my weights verified by re-derivation. What can I help you with?";
|
| 89 |
-
try {
|
| 90 |
-
const P = "This is the very first thing you say to the person who just opened you. You are Q — a private AI running entirely on their device with no server, your weights verified by re-derivation. Greet them warmly in one or two sentences and invite them to ask you anything.";
|
| 91 |
-
let ids = engine.tokenize(engine.frameTurn(P, false));
|
| 92 |
-
if (m.bos && engine.bosId != null) ids = [engine.bosId, ...ids];
|
| 93 |
-
await engine.generate(ids, { maxNew: 64, onToken: ({ text: t, stats }) => { if (first && t) { a.classList.remove("think"); a.textContent = ""; first = false; } a.textContent = t; log.scrollTop = log.scrollHeight; if (stats && stats.tokps) stat.textContent = `${stats.tokps.toFixed(0)} tok/s`; } });
|
| 94 |
-
if (first || a.textContent.trim().length < 4) { a.classList.remove("think"); a.textContent = FALLBACK; } else a.after(stat);
|
| 95 |
-
} catch (e) { a.classList.remove("think"); a.textContent = FALLBACK; }
|
| 96 |
-
busy = false; input.disabled = send.disabled = false; input.focus();
|
| 97 |
-
}
|
| 98 |
-
function onSend() {
|
| 99 |
-
const text = input.value.trim(); if (!text || busy) return;
|
| 100 |
-
input.value = ""; input.style.height = "auto";
|
| 101 |
-
if (!armed) { pending = text; bubble("u", text); const w = bubble("a think", "…starting the model, one moment…"); w.dataset.pending = "1"; return; }
|
| 102 |
-
generate(text);
|
| 103 |
-
}
|
| 104 |
-
send.onclick = onSend;
|
| 105 |
-
input.onkeydown = (e) => { if (e.key === "Enter" && !e.shiftKey) { e.preventDefault(); onSend(); } };
|
| 106 |
-
input.oninput = () => { input.style.height = "auto"; input.style.height = Math.min(140, input.scrollHeight) + "px"; };
|
| 107 |
-
|
| 108 |
-
try {
|
| 109 |
-
if (!navigator.gpu) throw new Error("This browser has no WebGPU. Use Chrome/Edge.");
|
| 110 |
-
st.textContent = `loading ${m.name} (${m.size})…`;
|
| 111 |
-
const loaded = await loadModel(m, { onStatus: (s) => { if (s) st.textContent = `${m.name}: ${s}`; }, onProgress: (d, t, w) => { st.textContent = `${m.name}: ${w} ${t ? Math.round(100 * d / t) : 0}%`; } });
|
| 112 |
-
if (!loaded || !loaded.gpu) throw new Error("model load failed");
|
| 113 |
-
engine = await createEngine(m, loaded);
|
| 114 |
-
armed = true;
|
| 115 |
-
st.textContent = `${m.name} · ${m.size} · resident on your GPU · ready`;
|
| 116 |
-
input.placeholder = "Message Q…";
|
| 117 |
-
if (pending) { const w = [...log.querySelectorAll(".a")].reverse().find((x) => x.dataset.pending); if (w) w.remove(); const p = pending; pending = null; generate(p, true); } // you jumped in first → answer that
|
| 118 |
-
else await proactiveGreeting(); // otherwise Q reaches out the moment it's ready (also warms the GPU)
|
| 119 |
-
} catch (e) { st.textContent = "⚠ " + e.message; bubble("a", "Could not start: " + e.message); }
|
| 120 |
-
</script></body></html>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
rb-bench.html
DELETED
|
@@ -1,130 +0,0 @@
|
|
| 1 |
-
<!doctype html><html><head><meta charset=utf8><meta name=viewport content="width=device-width,initial-scale=1">
|
| 2 |
-
<title>Register-blocked GEMV — your GPU</title>
|
| 3 |
-
<style>
|
| 4 |
-
:root{--bg:#0a0d13;--panel:#111825;--ink:#e8ebf1;--dim:#8b95a7;--ac:#7c5cff;--ok:#48c26c;--no:#f0616d;--warn:#e0a94a;--line:#1e2836}
|
| 5 |
-
*{box-sizing:border-box}body{margin:0;background:var(--bg);color:var(--ink);font:15px/1.6 -apple-system,Segoe UI,Roboto,monospace;padding:24px;max-width:860px;margin:0 auto}
|
| 6 |
-
h1{font-size:20px;margin:0 0 4px}.sub{color:var(--dim);font-size:13px;margin:0 0 18px}
|
| 7 |
-
.card{font-family:ui-monospace,monospace;padding:14px 16px;border:1px solid var(--line);border-radius:10px;background:var(--panel);margin-bottom:12px;word-break:break-word}
|
| 8 |
-
table{width:100%;border-collapse:collapse;font-family:ui-monospace,monospace;font-size:13px}
|
| 9 |
-
td,th{padding:6px 8px;border-bottom:1px solid var(--line);text-align:left}th{color:var(--dim);font-weight:600}
|
| 10 |
-
td.n{text-align:right;font-variant-numeric:tabular-nums}
|
| 11 |
-
.verdict{font-size:16px;font-weight:600;margin-top:14px}.bar{display:inline-block;height:9px;background:var(--ac);border-radius:2px;vertical-align:middle}
|
| 12 |
-
</style></head><body>
|
| 13 |
-
<h1>Register-blocked <span style="color:var(--ac)">ternary GEMV</span> — your GPU</h1>
|
| 14 |
-
<p class="sub">The kernel is memory-latency-bound (45 of 152 GB/s). Each 64-thread group here computes <b>B output rows</b>, so each thread issues B independent weight loads per step (more loads in flight to hide latency) while sharing the activation reads. B=1 is today's kernel. Higher B should climb toward the 152 GB/s roofline — until register pressure drops occupancy.</p>
|
| 15 |
-
<div id="status" class="card">starting…</div>
|
| 16 |
-
<div id="out"></div>
|
| 17 |
-
|
| 18 |
-
<script type="module">
|
| 19 |
-
const $=s=>document.querySelector(s), st=$("#status"), out=$("#out");
|
| 20 |
-
const MODEL_GB=0.69, ROOF=152, ROOF_TOK=220;
|
| 21 |
-
const say=(t,cls)=>{ st.textContent=t; st.className="card"+(cls?" "+cls:""); };
|
| 22 |
-
window.addEventListener("unhandledrejection",e=>say("✗ unhandled: "+(e.reason&&(e.reason.message||e.reason)),"no"));
|
| 23 |
-
|
| 24 |
-
const DOT16=`
|
| 25 |
-
fn dot16(word:u32, v:u32) -> f32 {
|
| 26 |
-
var s4=vec4<f32>(0.0);
|
| 27 |
-
s4=s4+x[v] *(vec4<f32>(f32(word&3u),f32((word>>2u)&3u),f32((word>>4u)&3u),f32((word>>6u)&3u))-vec4<f32>(1.0));
|
| 28 |
-
s4=s4+x[v+1u]*(vec4<f32>(f32((word>>8u)&3u),f32((word>>10u)&3u),f32((word>>12u)&3u),f32((word>>14u)&3u))-vec4<f32>(1.0));
|
| 29 |
-
s4=s4+x[v+2u]*(vec4<f32>(f32((word>>16u)&3u),f32((word>>18u)&3u),f32((word>>20u)&3u),f32((word>>22u)&3u))-vec4<f32>(1.0));
|
| 30 |
-
s4=s4+x[v+3u]*(vec4<f32>(f32((word>>24u)&3u),f32((word>>26u)&3u),f32((word>>28u)&3u),f32((word>>30u)&3u))-vec4<f32>(1.0));
|
| 31 |
-
return s4.x+s4.y+s4.z+s4.w;
|
| 32 |
-
}`;
|
| 33 |
-
// B output rows per 64-thread group, 4 groups per workgroup (256 threads). Each thread does B independent
|
| 34 |
-
// weight loads/iteration (memory-level parallelism), reusing the shared activation reads.
|
| 35 |
-
const kern=(B)=>`
|
| 36 |
-
@group(0) @binding(0) var<storage,read> x: array<vec4<f32>>;
|
| 37 |
-
@group(0) @binding(1) var<storage,read> qw: array<u32>;
|
| 38 |
-
@group(0) @binding(2) var<storage,read_write> o: array<f32>;
|
| 39 |
-
@group(0) @binding(3) var<uniform> P: vec4<u32>; // K, N
|
| 40 |
-
var<workgroup> red: array<f32, 256>;
|
| 41 |
-
${DOT16}
|
| 42 |
-
@compute @workgroup_size(256)
|
| 43 |
-
fn main(@builtin(workgroup_id) wg:vec3<u32>, @builtin(local_invocation_id) lid:vec3<u32>){
|
| 44 |
-
let K=P.x; let nw=K>>4u; let grp=lid.x/64u; let t=lid.x%64u;
|
| 45 |
-
let base=((wg.y*65535u+wg.x)*4u + grp)*${B}u;
|
| 46 |
-
var acc: array<f32, ${B}>;
|
| 47 |
-
${Array.from({length:B},(_,b)=>`acc[${b}]=0.0;`).join(" ")}
|
| 48 |
-
var w=t;
|
| 49 |
-
loop{ if(w>=nw){break;} let v=w<<2u;
|
| 50 |
-
${Array.from({length:B},(_,b)=>`let word${b}=qw[min(base+${b}u,P.y-1u)*nw + w];`).join("\n ")}
|
| 51 |
-
${Array.from({length:B},(_,b)=>`acc[${b}]=acc[${b}]+dot16(word${b}, v);`).join("\n ")}
|
| 52 |
-
w=w+64u; }
|
| 53 |
-
${Array.from({length:B},(_,b)=>`
|
| 54 |
-
red[lid.x]=acc[${b}]; workgroupBarrier();
|
| 55 |
-
{ var s=32u; loop{ if(s==0u){break;} if(t<s){ red[grp*64u+t]=red[grp*64u+t]+red[grp*64u+t+s]; } workgroupBarrier(); s=s/2u; } }
|
| 56 |
-
if(t==0u){ let n=base+${b}u; if(n<P.y){ o[n]=red[grp*64u]; } }
|
| 57 |
-
workgroupBarrier();`).join("")}
|
| 58 |
-
}`;
|
| 59 |
-
|
| 60 |
-
const FILL=`
|
| 61 |
-
@group(0) @binding(0) var<storage,read_write> d: array<u32>;
|
| 62 |
-
@group(0) @binding(1) var<uniform> P: vec4<u32>;
|
| 63 |
-
@compute @workgroup_size(256)
|
| 64 |
-
fn main(@builtin(global_invocation_id) gid:vec3<u32>){ let n=P.x; var i=gid.x; loop{ if(i>=n){break;} d[i]=(i*2654435761u+1u)^((i<<7u)*40503u); i=i+P.y; } }`;
|
| 65 |
-
|
| 66 |
-
const BS=[1,2,4,8,16];
|
| 67 |
-
|
| 68 |
-
(async()=>{
|
| 69 |
-
try{
|
| 70 |
-
if(!navigator.gpu){ say("✗ No WebGPU. Open in Chrome/Edge.","no"); return; }
|
| 71 |
-
say("requesting adapter…");
|
| 72 |
-
let ad=await navigator.gpu.requestAdapter({powerPreference:"high-performance"}); if(!ad) ad=await navigator.gpu.requestAdapter();
|
| 73 |
-
if(!ad){ say("✗ no GPU adapter.","no"); return; }
|
| 74 |
-
const L=ad.limits, info=ad.info||{};
|
| 75 |
-
const dev=await ad.requestDevice({requiredLimits:{maxStorageBufferBindingSize:L.maxStorageBufferBindingSize, maxBufferSize:L.maxBufferSize, maxComputeWorkgroupsPerDimension:L.maxComputeWorkgroupsPerDimension}});
|
| 76 |
-
dev.lost.then(i=>say("✗ device lost: "+(i&&i.message||i.reason||""),"no"));
|
| 77 |
-
|
| 78 |
-
const K=4096, nw=K/16;
|
| 79 |
-
const wantBytes=Math.min(L.maxStorageBufferBindingSize, L.maxBufferSize, Math.round(MODEL_GB*1e9));
|
| 80 |
-
const N=Math.floor((wantBytes/4)/nw), qU32=N*nw, wBytes=qU32*4;
|
| 81 |
-
say("allocating "+(wBytes/1048576).toFixed(0)+" MB…");
|
| 82 |
-
const qw=dev.createBuffer({size:wBytes, usage:GPUBufferUsage.STORAGE});
|
| 83 |
-
const xbuf=dev.createBuffer({size:K*4, usage:GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_DST});
|
| 84 |
-
const obuf=dev.createBuffer({size:N*4, usage:GPUBufferUsage.STORAGE});
|
| 85 |
-
const P=dev.createBuffer({size:16, usage:GPUBufferUsage.UNIFORM|GPUBufferUsage.COPY_DST});
|
| 86 |
-
const xa=new Float32Array(K); for(let i=0;i<K;i++) xa[i]=(Math.sin(i*12.9898)*43758.5453)%1;
|
| 87 |
-
dev.queue.writeBuffer(xbuf,0,xa); dev.queue.writeBuffer(P,0,new Uint32Array([K,N,0,0]));
|
| 88 |
-
|
| 89 |
-
say("filling weights…");
|
| 90 |
-
{ const fmod=dev.createShaderModule({code:FILL}); const fpipe=dev.createComputePipeline({layout:"auto",compute:{module:fmod,entryPoint:"main"}});
|
| 91 |
-
const fp=dev.createBuffer({size:16,usage:GPUBufferUsage.UNIFORM|GPUBufferUsage.COPY_DST}); const wgF=Math.min(L.maxComputeWorkgroupsPerDimension,65535);
|
| 92 |
-
dev.queue.writeBuffer(fp,0,new Uint32Array([qU32, wgF*256, 0, 0]));
|
| 93 |
-
const fbg=dev.createBindGroup({layout:fpipe.getBindGroupLayout(0),entries:[{binding:0,resource:{buffer:qw}},{binding:1,resource:{buffer:fp}}]});
|
| 94 |
-
const e=dev.createCommandEncoder(); const p=e.beginComputePass(); p.setPipeline(fpipe); p.setBindGroup(0,fbg); p.dispatchWorkgroups(wgF); p.end();
|
| 95 |
-
dev.queue.submit([e.finish()]); await dev.queue.onSubmittedWorkDone(); }
|
| 96 |
-
|
| 97 |
-
const PASSES=10, ITERS=5, rows=[];
|
| 98 |
-
for(const B of BS){
|
| 99 |
-
say("compiling B="+B+"…");
|
| 100 |
-
dev.pushErrorScope("validation");
|
| 101 |
-
const mod=dev.createShaderModule({code:kern(B)});
|
| 102 |
-
const ci=await mod.getCompilationInfo(); const er=ci.messages.filter(m=>m.type==="error");
|
| 103 |
-
if(er.length){ await dev.popErrorScope(); rows.push({B,err:er[0].message}); continue; }
|
| 104 |
-
const pipe=dev.createComputePipeline({layout:"auto",compute:{module:mod,entryPoint:"main"}});
|
| 105 |
-
const bg=dev.createBindGroup({layout:pipe.getBindGroupLayout(0),entries:[{binding:0,resource:{buffer:xbuf}},{binding:1,resource:{buffer:qw}},{binding:2,resource:{buffer:obuf}},{binding:3,resource:{buffer:P}}]});
|
| 106 |
-
const se=await dev.popErrorScope(); if(se){ rows.push({B,err:se.message}); continue; }
|
| 107 |
-
const groups=Math.ceil(N/(4*B)), wgx=Math.min(groups,65535), wgy=Math.ceil(groups/65535);
|
| 108 |
-
async function run(passes){ const e=dev.createCommandEncoder(); for(let k=0;k<passes;k++){ const p=e.beginComputePass(); p.setPipeline(pipe); p.setBindGroup(0,bg); p.dispatchWorkgroups(wgx,wgy); p.end(); } const t0=performance.now(); dev.queue.submit([e.finish()]); await dev.queue.onSubmittedWorkDone(); return performance.now()-t0; }
|
| 109 |
-
say("timing B="+B+"…");
|
| 110 |
-
await run(2); let best=1e9; for(let k=0;k<ITERS;k++){ best=Math.min(best, await run(PASSES)); }
|
| 111 |
-
const gbps=(wBytes/1073741824)/((best/PASSES)/1000);
|
| 112 |
-
rows.push({B, gbps, tok:gbps/MODEL_GB, pct:100*gbps/ROOF});
|
| 113 |
-
}
|
| 114 |
-
|
| 115 |
-
say("done · adapter: "+((info.vendor||"?")+" "+(info.architecture||"")+" "+(info.device||"")).trim(),"ok");
|
| 116 |
-
const ok=rows.filter(r=>r.gbps); const best=ok.slice().sort((a,b)=>b.gbps-a.gbps)[0]; const b1=ok.find(r=>r.B===1);
|
| 117 |
-
const maxG=Math.max(...ok.map(r=>r.gbps));
|
| 118 |
-
const gain=(best&&b1)? best.gbps/b1.gbps : 0;
|
| 119 |
-
out.innerHTML=`
|
| 120 |
-
<div class="card"><span style="color:var(--dim)">Test matrix</span> ${N.toLocaleString()} × ${K} · ${(wBytes/1073741824).toFixed(2)} GB · roofline ${ROOF} GB/s (${ROOF_TOK} tok/s)</div>
|
| 121 |
-
<div class="card"><table>
|
| 122 |
-
<tr><th>rows/group (B)</th><th class="n">GB/s</th><th class="n">tok/s-equiv</th><th class="n">% roofline</th><th></th></tr>
|
| 123 |
-
${rows.map(r=>r.err
|
| 124 |
-
? `<tr><td>B=${r.B}</td><td colspan=4 class="no">failed: ${r.err.slice(0,50)}</td></tr>`
|
| 125 |
-
: `<tr><td>B=${r.B}${r.B===1?" (today)":""}</td><td class="n">${r.gbps.toFixed(0)}</td><td class="n ${r===best?'ok':''}"><b>${r.tok.toFixed(0)}</b></td><td class="n ${r.pct<40?'no':r.pct<70?'warn':'ok'}">${r.pct.toFixed(0)}%</td><td><span class="bar" style="width:${Math.round(150*r.gbps/maxG)}px"></span></td></tr>`).join("")}
|
| 126 |
-
</table></div>
|
| 127 |
-
${best? `<div class="verdict ${best.pct>=70?'ok':best.pct>=45?'warn':'no'}">Best: <b>B=${best.B}</b> → ${best.gbps.toFixed(0)} GB/s (${best.tok.toFixed(0)} tok/s, ${best.pct.toFixed(0)}% of roofline, <b>${gain.toFixed(2)}×</b> today's kernel). ${best.pct>=70?"Memory-latency hidden — port B="+best.B+" into mmT2/mmT2KK.":best.pct>=45?"Real gain; register pressure likely caps it here — port B="+best.B+".":"Little movement — the pattern needs a deeper restructure (shared-mem staging / wider loads)."}</div>` : `<div class="verdict no">All variants failed.</div>`}`;
|
| 128 |
-
}catch(e){ say("✗ "+(e&&(e.message||e)), "no"); }
|
| 129 |
-
})();
|
| 130 |
-
</script></body></html>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
roofline.html
DELETED
|
@@ -1,126 +0,0 @@
|
|
| 1 |
-
<!doctype html><html><head><meta charset=utf8><meta name=viewport content="width=device-width,initial-scale=1">
|
| 2 |
-
<title>Decode roofline — your GPU</title>
|
| 3 |
-
<style>
|
| 4 |
-
:root{--bg:#0a0d13;--panel:#111825;--ink:#e8ebf1;--dim:#8b95a7;--ac:#7c5cff;--ok:#48c26c;--no:#f0616d;--warn:#e0a94a;--line:#1e2836}
|
| 5 |
-
*{box-sizing:border-box}body{margin:0;background:var(--bg);color:var(--ink);font:15px/1.6 -apple-system,Segoe UI,Roboto,monospace;padding:24px;max-width:760px;margin:0 auto}
|
| 6 |
-
h1{font-size:20px;margin:0 0 4px}.sub{color:var(--dim);font-size:13px;margin:0 0 18px}
|
| 7 |
-
.card{font-family:ui-monospace,monospace;padding:14px 16px;border:1px solid var(--line);border-radius:10px;background:var(--panel);margin-bottom:12px;word-break:break-word}
|
| 8 |
-
.big{font-size:22px;font-weight:700}.k{color:var(--dim)}.v{font-weight:600}
|
| 9 |
-
.ok{color:var(--ok)}.no{color:var(--no)}.warn{color:var(--warn)}
|
| 10 |
-
table{width:100%;border-collapse:collapse;font-family:ui-monospace,monospace;font-size:13px}
|
| 11 |
-
td{padding:5px 8px;border-bottom:1px solid var(--line)}td.n{text-align:right;font-variant-numeric:tabular-nums}
|
| 12 |
-
.verdict{font-size:17px;font-weight:600;margin-top:14px}
|
| 13 |
-
</style></head><body>
|
| 14 |
-
<h1>BitNet decode <span style="color:var(--ac)">roofline</span> on your GPU</h1>
|
| 15 |
-
<p class="sub">Measures your GPU's achievable VRAM read bandwidth, then computes the batch-1 decode ceiling for BitNet-2B (0.69 GB read per token) and how far the current kernel is from it.</p>
|
| 16 |
-
<div id="status" class="card">starting…</div>
|
| 17 |
-
<div id="out"></div>
|
| 18 |
-
|
| 19 |
-
<script type="module">
|
| 20 |
-
const $=s=>document.querySelector(s), st=$("#status"), out=$("#out");
|
| 21 |
-
const MODEL_GB = 0.69, CUR_MS = 18.4;
|
| 22 |
-
const say = (t,cls) => { st.textContent = t; st.className = "card" + (cls?" "+cls:""); };
|
| 23 |
-
window.addEventListener("unhandledrejection", e => say("✗ unhandled: " + (e.reason && (e.reason.message||e.reason)), "no"));
|
| 24 |
-
|
| 25 |
-
// FILL: write a non-zero, non-compressible pattern so the read pass hits real VRAM
|
| 26 |
-
// (an uninitialised STORAGE buffer is zero/DCC-compressed → the memory controller
|
| 27 |
-
// never fetches it → measured bandwidth is fictional). Must run before timing.
|
| 28 |
-
const FILL = `
|
| 29 |
-
@group(0) @binding(0) var<storage, read_write> d : array<vec4<u32>>;
|
| 30 |
-
@group(0) @binding(1) var<uniform> P : vec4<u32>; // x=#vec4 elems, y=total threads
|
| 31 |
-
@compute @workgroup_size(256)
|
| 32 |
-
fn main(@builtin(global_invocation_id) gid:vec3<u32>){
|
| 33 |
-
let n=P.x; let stride=P.y; var i=gid.x;
|
| 34 |
-
loop { if(i>=n){break;}
|
| 35 |
-
d[i]=vec4<u32>(i*2654435761u+1u, i*40503u+7u, i^0x9e3779b9u, i*2246822519u+3u);
|
| 36 |
-
i=i+stride; }
|
| 37 |
-
}`;
|
| 38 |
-
const WGSL = `
|
| 39 |
-
@group(0) @binding(0) var<storage, read> data : array<vec4<u32>>;
|
| 40 |
-
@group(0) @binding(1) var<storage, read_write> sink : array<u32>;
|
| 41 |
-
@group(0) @binding(2) var<uniform> P : vec4<u32>; // x=#vec4 elems, y=total threads, z=repeat
|
| 42 |
-
@compute @workgroup_size(256)
|
| 43 |
-
fn main(@builtin(global_invocation_id) gid:vec3<u32>){
|
| 44 |
-
let n=P.x; let stride=P.y; let R=P.z; var acc=vec4<u32>(0u);
|
| 45 |
-
for(var rep=0u; rep<R; rep=rep+1u){ var i=gid.x; loop { if(i>=n){break;} acc=acc ^ data[i]; i=i+stride; } }
|
| 46 |
-
sink[gid.x]=acc.x ^ acc.y ^ acc.z ^ acc.w;
|
| 47 |
-
}`;
|
| 48 |
-
|
| 49 |
-
(async()=>{
|
| 50 |
-
try{
|
| 51 |
-
if(!navigator.gpu){ say("✗ No WebGPU. Open in Chrome/Edge or a recent browser.","no"); return; }
|
| 52 |
-
say("requesting adapter…");
|
| 53 |
-
let ad = await navigator.gpu.requestAdapter({powerPreference:"high-performance"});
|
| 54 |
-
if(!ad) ad = await navigator.gpu.requestAdapter();
|
| 55 |
-
if(!ad){ say("✗ no GPU adapter available.","no"); return; }
|
| 56 |
-
const L=ad.limits;
|
| 57 |
-
say("requesting device…");
|
| 58 |
-
const dev=await ad.requestDevice({requiredLimits:{
|
| 59 |
-
maxStorageBufferBindingSize:L.maxStorageBufferBindingSize, maxBufferSize:L.maxBufferSize,
|
| 60 |
-
maxComputeWorkgroupsPerDimension:L.maxComputeWorkgroupsPerDimension }});
|
| 61 |
-
dev.lost.then(i => say("✗ device lost: "+(i&&i.message||i.reason||""),"no"));
|
| 62 |
-
dev.pushErrorScope("validation");
|
| 63 |
-
const info=ad.info||{};
|
| 64 |
-
// Buffer must exceed the GPU's last-level cache (RDNA-3 Infinity Cache is up to 96 MB)
|
| 65 |
-
// so re-reads actually miss to VRAM. Aim as large as the device allows, floor 512 MB.
|
| 66 |
-
const wantBytes=Math.min(L.maxStorageBufferBindingSize, L.maxBufferSize, 1024*1024*1024);
|
| 67 |
-
const bytes=Math.floor(Math.max(512*1024*1024, wantBytes)/16)*16;
|
| 68 |
-
const nVec=bytes/16;
|
| 69 |
-
say("allocating "+(bytes/1048576).toFixed(0)+" MB…");
|
| 70 |
-
const buf=dev.createBuffer({size:bytes, usage:GPUBufferUsage.STORAGE});
|
| 71 |
-
const wg=Math.min(L.maxComputeWorkgroupsPerDimension,65535), TOTAL=wg*256, R=1;
|
| 72 |
-
const sink=dev.createBuffer({size:TOTAL*4, usage:GPUBufferUsage.STORAGE});
|
| 73 |
-
const P=dev.createBuffer({size:16, usage:GPUBufferUsage.UNIFORM|GPUBufferUsage.COPY_DST});
|
| 74 |
-
dev.queue.writeBuffer(P,0,new Uint32Array([nVec, TOTAL, R, 0]));
|
| 75 |
-
// fill pipeline (non-zero pattern) — its own bind group, same buffer
|
| 76 |
-
const fmod=dev.createShaderModule({code:FILL});
|
| 77 |
-
const fci=await fmod.getCompilationInfo(); const fer=fci.messages.filter(m=>m.type==="error");
|
| 78 |
-
if(fer.length){ say("✗ FILL WGSL: "+fer[0].message,"no"); return; }
|
| 79 |
-
const fpipe=dev.createComputePipeline({layout:"auto",compute:{module:fmod,entryPoint:"main"}});
|
| 80 |
-
const fbg=dev.createBindGroup({layout:fpipe.getBindGroupLayout(0),entries:[{binding:0,resource:{buffer:buf}},{binding:1,resource:{buffer:P}}]});
|
| 81 |
-
const mod=dev.createShaderModule({code:WGSL});
|
| 82 |
-
const ci=await mod.getCompilationInfo(); const er=ci.messages.filter(m=>m.type==="error");
|
| 83 |
-
if(er.length){ say("✗ WGSL: "+er[0].message,"no"); return; }
|
| 84 |
-
const pipe=dev.createComputePipeline({layout:"auto",compute:{module:mod,entryPoint:"main"}});
|
| 85 |
-
const bg=dev.createBindGroup({layout:pipe.getBindGroupLayout(0),entries:[{binding:0,resource:{buffer:buf}},{binding:1,resource:{buffer:sink}},{binding:2,resource:{buffer:P}}]});
|
| 86 |
-
const rb=dev.createBuffer({size:4, usage:GPUBufferUsage.COPY_DST|GPUBufferUsage.MAP_READ});
|
| 87 |
-
const scopeErr = await dev.popErrorScope();
|
| 88 |
-
if(scopeErr){ say("✗ GPU validation: "+scopeErr.message,"no"); return; }
|
| 89 |
-
// write the non-zero pattern into VRAM before we time reads
|
| 90 |
-
say("filling "+(bytes/1048576).toFixed(0)+" MB with non-zero pattern…");
|
| 91 |
-
{ const e=dev.createCommandEncoder(); const p=e.beginComputePass(); p.setPipeline(fpipe); p.setBindGroup(0,fbg); p.dispatchWorkgroups(wg); p.end(); dev.queue.submit([e.finish()]); await dev.queue.onSubmittedWorkDone(); }
|
| 92 |
-
// Batch many read passes into ONE command buffer, submit once, sync with
|
| 93 |
-
// onSubmittedWorkDone. Moving tens of GB makes GPU time >> fixed submit/map
|
| 94 |
-
// latency, so wall-clock reflects real VRAM bandwidth (not the ~0.1 ms overhead floor).
|
| 95 |
-
const PASSES=64; // 64 × buffer ≈ 32–64 GB of reads
|
| 96 |
-
async function timeReads(passes){
|
| 97 |
-
const e=dev.createCommandEncoder();
|
| 98 |
-
for(let k=0;k<passes;k++){ const p=e.beginComputePass(); p.setPipeline(pipe); p.setBindGroup(0,bg); p.dispatchWorkgroups(wg); p.end(); }
|
| 99 |
-
const t0=performance.now();
|
| 100 |
-
dev.queue.submit([e.finish()]);
|
| 101 |
-
await dev.queue.onSubmittedWorkDone();
|
| 102 |
-
return performance.now()-t0;
|
| 103 |
-
}
|
| 104 |
-
say("timing "+(bytes/1048576*PASSES/1024).toFixed(1)+" GB of reads…");
|
| 105 |
-
await timeReads(4); // warm up (compile, clocks up)
|
| 106 |
-
let best=1e9; for(let k=0;k<5;k++){ best=Math.min(best, await timeReads(PASSES)); }
|
| 107 |
-
const gbps = (bytes*PASSES/1073741824)/(best/1000);
|
| 108 |
-
const roofTok=gbps/MODEL_GB, curTok=1000/CUR_MS, curGBps=MODEL_GB/(CUR_MS/1000);
|
| 109 |
-
const pct=100*curGBps/gbps, gap=gbps/curGBps, reachable=roofTok>=1000, specNeeded=Math.max(1,1000/roofTok);
|
| 110 |
-
say("done · adapter: "+((info.vendor||"?")+" "+(info.architecture||"")+" "+(info.device||"")).trim(),"ok");
|
| 111 |
-
out.innerHTML=`
|
| 112 |
-
<div class="card"><span class="k">Achievable VRAM read bandwidth</span><br><span class="big">${gbps.toFixed(0)} GB/s</span></div>
|
| 113 |
-
<div class="card"><table>
|
| 114 |
-
<tr><td>Weights read / token (BitNet-2B t2)</td><td class="n">${MODEL_GB} GB</td></tr>
|
| 115 |
-
<tr><td><b>Bandwidth roofline (batch-1 ceiling)</b></td><td class="n"><b>${roofTok.toFixed(0)} tok/s</b></td></tr>
|
| 116 |
-
<tr><td>Current kernel</td><td class="n">${curTok.toFixed(0)} tok/s · ${curGBps.toFixed(0)} GB/s</td></tr>
|
| 117 |
-
<tr><td>Current kernel reaches</td><td class="n ${pct<20?'no':pct<60?'warn':'ok'}">${pct.toFixed(1)}% of roofline</td></tr>
|
| 118 |
-
<tr><td>Kernel headroom to roofline</td><td class="n">${gap.toFixed(1)}×</td></tr>
|
| 119 |
-
</table></div>
|
| 120 |
-
<div class="verdict ${reachable?'ok':'warn'}">${reachable
|
| 121 |
-
? '✓ >1000 tok/s is within your GPU roofline ('+roofTok.toFixed(0)+') — a bandwidth-optimal ternary kernel ('+gap.toFixed(1)+'× headroom) gets there; spec-decode is margin.'
|
| 122 |
-
: '⚠ Your GPU batch-1 roofline is '+roofTok.toFixed(0)+' tok/s. A perfect kernel reaches ~'+roofTok.toFixed(0)+'; >1000 needs speculative decode accepting ≥'+specNeeded.toFixed(1)+' tokens/pass, or a card with more bandwidth.'}
|
| 123 |
-
</div>`;
|
| 124 |
-
}catch(e){ say("✗ "+(e && (e.message||e)), "no"); }
|
| 125 |
-
})();
|
| 126 |
-
</script></body></html>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
style.css
DELETED
|
@@ -1,28 +0,0 @@
|
|
| 1 |
-
body {
|
| 2 |
-
padding: 2rem;
|
| 3 |
-
font-family: -apple-system, BlinkMacSystemFont, "Arial", sans-serif;
|
| 4 |
-
}
|
| 5 |
-
|
| 6 |
-
h1 {
|
| 7 |
-
font-size: 16px;
|
| 8 |
-
margin-top: 0;
|
| 9 |
-
}
|
| 10 |
-
|
| 11 |
-
p {
|
| 12 |
-
color: rgb(107, 114, 128);
|
| 13 |
-
font-size: 15px;
|
| 14 |
-
margin-bottom: 10px;
|
| 15 |
-
margin-top: 5px;
|
| 16 |
-
}
|
| 17 |
-
|
| 18 |
-
.card {
|
| 19 |
-
max-width: 620px;
|
| 20 |
-
margin: 0 auto;
|
| 21 |
-
padding: 16px;
|
| 22 |
-
border: 1px solid lightgray;
|
| 23 |
-
border-radius: 16px;
|
| 24 |
-
}
|
| 25 |
-
|
| 26 |
-
.card p:last-child {
|
| 27 |
-
margin-bottom: 0;
|
| 28 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|