// core/engine.js — the inference ENGINE adapter (the only module that touches the wasm // tokenizer + the WebGPU `gpu` object). DOM-free. Wraps a model that core/loader.js has // already loaded onto the GPU and exposes a clean, UI-agnostic API: // // const engine = await createEngine(modelEntry, { gpu, info, imageKappa }); // const { text, outIds } = await engine.generate(ids, { onToken, signal }); // const rec = await engine.buildReceipt({ ... }); // PROV-O, re-derivable (Law L5) // // The token loop, framing, memo and receipt logic are lifted byte-for-byte from the // original index.html think()/run()/sealReceipt — only the DOM writes are replaced by an // onToken callback and the running/handedOff flags by an AbortSignal, so output (and the // receipt κ) is identical to the original app. import { qvac_tokenize, qvac_continue, kappa } from "../pkg/holospaces_web.js"; import { clean, didHolo, kappaTokens, sealReceipt, verifyIntegrity, idBytes, kappaBytes } from "./kappa.js"; const _perf = () => (typeof performance !== "undefined" ? performance.now() : 0); const _sleep = (ms) => new Promise((r) => setTimeout(r, ms)); // The engine is itself a content-addressed object — hash the wasm once (lazy). let _engineK = null; export async function engineKappa() { if (_engineK) return _engineK; try { const b = new Uint8Array(await (await fetch(new URL("../pkg/holospaces_web_bg.wasm", import.meta.url))).arrayBuffer()); _engineK = await kappaBytes(b); } catch { _engineK = "did:holo:sha256:(engine unavailable)"; } return _engineK; } export async function createEngine(modelEntry, loaded) { const { gpu, info, imageKappa } = loaded; const m = modelEntry; // W2 (Q-WAFER): KV-commons blobs SEALED IN THE WAFER (minted once on a real GPU at forge time) — // a fresh device restores the persona prefill instead of computing it. Matched by (layout, nIds, // idsSha) so a blob only restores on the exact kv layout + token ids it was minted for. const sealedKvc = (loaded.sealed && loaded.sealed.kvc) || null; const idsSha = async (ids) => { try { const b = new TextEncoder().encode(ids.join(",")); return [...new Uint8Array(await crypto.subtle.digest("SHA-256", b))].map((x) => x.toString(16).padStart(2, "0")).join(""); } catch { return null; } }; const engineReady = engineKappa(); // model κ: the κ-disk's VERIFIED image_kappa when present (a real content address of the // weights, every sector re-derived); else the model's declared identity. const modelKappa = imageKappa ? "did:holo:sha256:" + String(imageKappa).replace(/^(did:holo:)?sha256:/, "") : await didHolo({ "@type": "schema:SoftwareSourceCode", name: m.name, size: m.size, fmt: m.fmt || "", family: m.fam || "" }); const memo = new Map(); let _drafter = null; // learned speculative drafter (fn(seq,max)=>ids); null → standard decode. Set via setDrafter(). // D1 (Q-DERIVED-MIND) SPECULATIVE SELF: the spec path also arms by CATALOG opt-in (m.spec) with no // learned drafter — gpu.setDrafter(null) keeps the gpu's internal n-gram drafter (prompt-lookup: // drafts cost ~0 JS, greedy batched verify keeps output BYTE-IDENTICAL, SP.cold self-throttles on // miss streaks). ?spec=0 kills, ?spec=1 forces — both for honest A/B on the ?stats ledger. const _specQ = (() => { try { return typeof location !== "undefined" ? new URLSearchParams(location.search).get("spec") : null; } catch { return null; } })(); let _pinLen = 0; // KV-COMMONS prefix pin: length of the pinned shared prefix (0 = none). See pinPrefix/usePin below. const tokenize = (text) => { try { return JSON.parse(qvac_tokenize(text)).ids || []; } catch { return []; } }; const detokenize = (ids) => { try { return clean(JSON.parse(qvac_continue(JSON.stringify(ids), 0, 0, 0, ids.length)).text || ""); } catch { return ""; } }; const fingerprint = (ids) => kappa(idBytes(ids)); // live mind κ (blake3, from wasm) // Frame one user turn. Qwen2/3 use ChatML (its <|im_*|> markers are atomic BPE tokens); // other instruction models use a plain Q/A frame. (Verbatim from the original run().) function frameTurn(prompt, hasHistory) { if (m.qwen) { const noThink = m.qwen3 ? "\n\n\n\n" : ""; // Qwen3: skip the thinking block for fast direct answers return (hasHistory ? "<|im_end|>\n" : "") + `<|im_start|>user\n${prompt}<|im_end|>\n<|im_start|>assistant\n` + noThink; } if (m.llama3) // LLaMA-3 header template (BitNet b1.58 etc.) return (hasHistory ? "<|eot_id|>" : "") + `<|start_header_id|>user<|end_header_id|>\n\n${prompt}<|eot_id|><|start_header_id|>assistant<|end_header_id|>\n\n`; if (m.olmo) // OLMo/OLMoE: <|user|>/<|assistant|> role tags (each turn self-delimited; leading bos via m.bos) return `<|user|>\n${prompt}\n<|assistant|>\n`; if (m.userWord) // word-frame (Falcon-E: ChatML stalls EMPIRICALLY; "User:/