| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| 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)); |
|
|
| |
| 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; |
| |
| |
| |
| 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(); |
|
|
| |
| |
| 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; |
| |
| |
| |
| |
| const _specQ = (() => { try { return typeof location !== "undefined" ? new URLSearchParams(location.search).get("spec") : null; } catch { return null; } })(); |
| let _pinLen = 0; |
|
|
| 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)); |
|
|
| |
| |
| function frameTurn(prompt, hasHistory) { |
| if (m.qwen) { |
| const noThink = m.qwen3 ? "<think>\n\n</think>\n\n" : ""; |
| return (hasHistory ? "<|im_end|>\n" : "") + `<|im_start|>user\n${prompt}<|im_end|>\n<|im_start|>assistant\n` + noThink; |
| } |
| if (m.llama3) |
| 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) |
| return `<|user|>\n${prompt}\n<|assistant|>\n`; |
| if (m.userWord) |
| return (hasHistory ? "\n" : "") + "User: " + prompt + "\n" + (m.asstLabel || "Falcon") + ":"; |
| return "Question: " + prompt + "\nAnswer:"; |
| } |
|
|
| function params() { |
| const temp = m.temp || 0; |
| return { decode: temp > 0 ? "sampled@t=" + temp : "greedy-argmax", maxTokens: m.cap, repetitionPenalty: m.rep ?? 1.05, template: m.qwen ? "chatml" : m.llama3 ? "llama3" : "qa", thinking: !m.qwen3 }; |
| } |
|
|
| |
| |
| async function generate(ids, { onToken, signal, repPenalty, maxNew } = {}) { |
| const rep = repPenalty ?? m.rep ?? 1.3; |
| const newCap = maxNew ?? m.cap ?? 80; |
| const kvCap = (m.ctx || m.cap || 80) + 8; |
| const promptLen = ids.length; |
| const tStart = _perf(); |
| let first = true, decodeStart = 0, decodeTok = 0, ttft = 0, tokps = 0, msExec = 0, err = null, outText = ""; |
| if (promptLen >= kvCap - 1) err = new Error(`context full: ${promptLen} tokens ≥ ${kvCap} KV positions`); |
|
|
| |
| |
| |
| |
| const _specOn = _specQ === "0" ? false : _specQ === "1" ? true : !!(_drafter || m.spec); |
| if (!err && _specOn && gpu.specDecode && gpu.setDrafter) { |
| try { |
| gpu.setDrafter(_drafter); |
| const out = []; const t0 = _perf(); let ttft2 = 0, tokps2 = 0; |
| const seq = await gpu.specDecode(ids.slice(), newCap, rep, (tk) => { |
| if (signal && signal.aborted) return false; |
| out.push(tk); |
| if (!ttft2) ttft2 = _perf() - tStart; |
| const dt = _perf() - t0; if (dt > 0) tokps2 = out.length / (dt / 1000); |
| if (onToken) onToken({ text: detokenize(out), ids: ids.slice(0, promptLen).concat(out), outIds: out.slice(), stats: { ttft: ttft2, tokps: tokps2, msExec: gpu.timing ? gpu.timing.exec : 0, gpuBytes: gpu.gpuBytes, spec: gpu.specStats ? gpu.specStats() : null } }); |
| return out.length < newCap; |
| }); |
| const outIds = seq.slice(promptLen); |
| let text = detokenize(outIds); |
| if (m.stopText) { const ix = text.indexOf(m.stopText); if (ix >= 0) text = text.slice(0, ix); } |
| return { text, outIds, ids: seq, stats: { ttft: ttft2, tokps: tokps2, msExec: gpu.timing ? gpu.timing.exec : 0, spec: gpu.specStats ? gpu.specStats() : null }, error: null }; |
| } catch (e) { try { gpu.setDrafter(null); } catch {} } |
| } |
|
|
| |
| |
| |
| |
| |
| if (!err && gpu.decode && gpu.decodeStreamed) { |
| try { |
| const outAcc = []; |
| const seq = await gpu.decode(ids.slice(), newCap, rep, (got) => { |
| if (signal && signal.aborted) return false; |
| if (first) { ttft = _perf() - tStart; decodeStart = _perf(); first = false; } |
| else { decodeTok += got.length; const dt = _perf() - decodeStart; if (dt > 0) tokps = decodeTok / (dt / 1000); } |
| const nOld = outAcc.length; outAcc.push(...got); |
| |
| { const a = Math.max(0, outAcc.length - (got.length + 8)); const wf = detokenize(outAcc.slice(a)); const wp = a >= nOld ? "" : detokenize(outAcc.slice(a, nOld)); outText += wf.slice(wp.length); } |
| let text = outText, hitStop = false; |
| if (m.stopText) { const ix = text.indexOf(m.stopText); if (ix >= 0) { text = text.slice(0, ix); hitStop = true; } } |
| if (onToken) onToken({ text, ids: ids.slice(0, promptLen).concat(outAcc), outIds: outAcc.slice(), stats: { ttft, tokps, msExec, gpuBytes: gpu.gpuBytes } }); |
| if (hitStop) return false; |
| if (text.length > 80 && /(.)\1{63}$/.test(text)) { err = new Error("degenerate repetition — stopped"); return false; } |
| return true; |
| }); |
| ids = seq; |
| const outIds = ids.slice(promptLen); |
| let text = detokenize(outIds); |
| if (m.stopText) { const ix = text.indexOf(m.stopText); if (ix >= 0) text = text.slice(0, ix); } |
| return { text, outIds, ids, stats: { ttft, tokps, msExec: gpu.timing ? gpu.timing.exec : msExec }, error: err }; |
| } catch (e) { err = null; } |
| } |
|
|
| while (!err && !(signal && signal.aborted) && ids.length - promptLen < newCap && ids.length < kvCap - 1) { |
| const prevLen = ids.length; |
| try { ids = await (gpu.decode || gpu.generate)(ids, first ? 1 : 6, rep); } |
| catch (e) { err = e; break; } |
| const dn = ids.length - prevLen; |
| if (dn > 0) { |
| msExec = gpu.timing ? gpu.timing.exec : msExec; |
| if (first) { ttft = _perf() - tStart; decodeStart = _perf(); first = false; } |
| else { decodeTok += dn; const dt = _perf() - decodeStart; if (dt > 0) tokps = decodeTok / (dt / 1000); } |
| } |
| const di = ids.slice(promptLen); |
| |
| |
| { const a = Math.max(promptLen, ids.length - (dn + 8)); const wf = detokenize(ids.slice(a)); const wp = a >= ids.length - dn ? "" : detokenize(ids.slice(a, ids.length - dn)); outText += wf.slice(wp.length); } |
| let text = outText, hitStop = false; |
| if (m.stopText) { const ix = text.indexOf(m.stopText); if (ix >= 0) { text = text.slice(0, ix); hitStop = true; } } |
| if (onToken) onToken({ text, ids: ids.slice(), outIds: di.slice(), stats: { ttft, tokps, msExec, gpuBytes: gpu.gpuBytes } }); |
| if (hitStop) break; |
| if (ids.length <= prevLen) break; |
| |
| |
| if (text.length > 80 && /(.)\1{63}$/.test(text)) { err = new Error("degenerate repetition — stopped"); break; } |
| await _sleep(0); |
| } |
| const outIds = ids.slice(promptLen); |
| let text = detokenize(outIds); |
| if (m.stopText) { const ix = text.indexOf(m.stopText); if (ix >= 0) text = text.slice(0, ix); } |
| return { text, outIds, ids, stats: { ttft, tokps, msExec }, error: err }; |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| async function diffuse(ids, { genLen, steps, fill, causal, signal, onToken } = {}) { |
| if (!gpu || !gpu.diffuse) throw new Error("this model has no diffusion engine (load a diffusion κ-object)"); |
| const gl = fill ? 0 : (genLen ?? Math.min(m.cap || 64, (m.ctx || 192) - ids.length - 1)); |
| const S = steps ?? m.steps ?? 12; |
| const tStart = _perf(); |
| const seq = await gpu.diffuse(ids, gl, { steps: S, fill: !!fill, causal: !!causal, signal }); |
| |
| const outIds = fill ? seq.slice() : seq.slice(ids.length); |
| let text = detokenize(outIds); |
| if (m.stopText && !fill) { const ix = text.indexOf(m.stopText); if (ix >= 0) text = text.slice(0, ix); } |
| const stats = { ttft: _perf() - tStart, tokps: 0, msExec: gpu.timing ? gpu.timing.exec : 0, steps: S, fill: !!fill, diff: gpu.diffStats ? gpu.diffStats() : null }; |
| if (onToken) onToken({ text, ids: seq.slice(), outIds: outIds.slice(), stats }); |
| return { text, outIds, ids: seq, stats, error: null }; |
| } |
|
|
| |
| const memoKey = async (ctxIds, turnIds, p) => didHolo({ ctx: await kappaTokens(ctxIds.concat(turnIds)), model: modelKappa, params: p || params() }); |
|
|
| async function buildReceipt({ promptText, ctxIds, turnIds, outIds, fromMemo, evaluateText, paramsPatch, extraUsed }) { |
| return sealReceipt({ |
| promptText, ctxIds, turnIds, outIds, text: detokenize(outIds), params: { ...params(), ...(paramsPatch || {}) }, fromMemo, |
| modelKappa, engineKappa: await engineReady, evaluateText, extraUsed, |
| }); |
| } |
|
|
| |
| async function reDerive(rec) { |
| if (!gpu) return { ok: false, reason: "load the model to re-derive" }; |
| if (/sampled/.test(rec.params.decode)) return { ok: false, reason: "sampled decode — only the κ-binding is verifiable, not re-derivation" }; |
| try { |
| let seq = rec.ctxIds.concat(rec.turnIds); const start = seq.length; |
| gpu.reset(); |
| seq = await (gpu.decode || gpu.generate)(seq, rec.outIds.length, rec.params.repetitionPenalty); |
| const got = await kappaTokens(seq.slice(start)), want = rec.body["prov:generated"]["holo:outputTokens"]; |
| return { ok: got === want, got, want }; |
| } finally { try { gpu.reset(); } catch {} } |
| } |
|
|
| return { |
| model: m, dims: gpu.dims, modelKappa, bosId: info?.bos ?? null, get gpuBytes() { return gpu.gpuBytes; }, |
| tokenize, detokenize, fingerprint, frameTurn, params, |
| generate, |
| |
| setDrafter: (fn) => { _drafter = fn || null; try { gpu.setDrafter && gpu.setDrafter(_drafter); } catch (e) {} }, |
| specAvailable: !!(gpu.specDecode && gpu.setDrafter), |
| |
| |
| |
| |
| |
| kvPinAvailable: !!(gpu.truncateTo && gpu.sync), |
| |
| pinPrefix: async (ids) => { if (!gpu.sync || !gpu.truncateTo) return 0; try { gpu.reset(); await gpu.sync(ids.slice()); _pinLen = gpu.cachedLen; return _pinLen; } catch (e) { _pinLen = 0; return 0; } }, |
| |
| pinCurrent: (len) => { if (!gpu.truncateTo) return 0; try { _pinLen = gpu.truncateTo(len); return _pinLen; } catch (e) { return 0; } }, |
| |
| usePin: () => { if (_pinLen > 0 && gpu.truncateTo) { try { return gpu.truncateTo(_pinLen); } catch (e) { return 0; } } return 0; }, |
| pinLen: () => _pinLen, |
| |
| |
| |
| |
| |
| kvCommonsAvailable: !!(gpu.dumpState && gpu.restoreState && gpu.kvLayoutOf && typeof navigator !== "undefined" && navigator.storage && navigator.storage.getDirectory), |
| kvCommonsSave: async (ids) => { |
| try { |
| if (!gpu.dumpState || !ids || gpu.cachedLen < ids.length || ids.length < 2) return false; |
| const blob = await gpu.dumpState(ids.length); |
| if (!blob) return false; |
| const { commonsKey, save } = await import("./kv-commons.mjs"); |
| const key = commonsKey(["kvc1", modelKappa, blob.layout, ids.join(",")]); |
| if (!key) return false; |
| await save(key, blob.bytes, { layout: blob.layout, L: blob.L, nIds: ids.length }); |
| return true; |
| } catch (e) { return false; } |
| }, |
| kvCommonsLoad: async (ids) => { |
| try { |
| if (!gpu.restoreState || !gpu.kvLayoutOf || !ids || ids.length < 2) return 0; |
| const layout = gpu.kvLayoutOf(ids.length); |
| |
| |
| if (sealedKvc) { |
| try { |
| const want = sealedKvc.filter((e) => e.layout === layout && e.nIds === ids.length); |
| if (want.length) { |
| const sha = await idsSha(ids); |
| const hit = sha && want.find((e) => e.idsSha === sha); |
| if (hit) { |
| const bytes = await hit.get(); |
| const resident = await gpu.restoreState(ids.slice(), { layout: hit.layout, L: hit.L, bytes }); |
| if (resident) { |
| await gpu.sync(ids.slice(), true); |
| _pinLen = gpu.cachedLen; |
| return _pinLen; |
| } |
| } |
| } |
| } catch (e) { try { gpu.reset(); } catch {} } |
| } |
| const { commonsKey, load } = await import("./kv-commons.mjs"); |
| const key = commonsKey(["kvc1", modelKappa, layout, ids.join(",")]); |
| if (!key) return 0; |
| const got = await load(key); |
| if (!got || got.meta.layout !== layout) return 0; |
| const resident = await gpu.restoreState(ids.slice(), { layout: got.meta.layout, L: got.meta.L, bytes: got.bytes }); |
| if (!resident) return 0; |
| await gpu.sync(ids.slice(), true); |
| _pinLen = gpu.cachedLen; |
| return _pinLen; |
| } catch (e) { try { gpu.reset(); } catch {} _pinLen = 0; return 0; } |
| }, |
| memoKey, memoGet: (k) => memo.get(k), memoHas: (k) => memo.has(k), memoSet: (k, v) => memo.set(k, v), |
| buildReceipt, verify: verifyIntegrity, reDerive, |
| stats: () => gpu.timing, reset: () => { try { gpu.reset(); } catch {} }, destroy: () => { try { gpu.destroy(); } catch {} }, |
| }; |
| } |
|
|