q / index.html
Humuhumu33's picture
Upload index.html with huggingface_hub
1185393 verified
Raw
History Blame
11.4 kB
<!doctype html><html><head><meta charset=utf8><meta name=viewport content="width=device-width,initial-scale=1,viewport-fit=cover">
<title>Q — private AI, in your browser</title>
<style>
:root{--bg:#0b0e14;--panel:#141922;--ink:#e6e9ef;--dim:#8a94a6;--q:#7c5cff;--u:#1f6feb;--line:#1e2531}
*{box-sizing:border-box}html,body{height:100%}
body{margin:0;font:15px/1.55 -apple-system,Segoe UI,Roboto,system-ui,monospace;background:var(--bg);color:var(--ink);display:flex;flex-direction:column;overscroll-behavior:none}
header{padding:10px 16px;border-bottom:1px solid var(--line);display:flex;align-items:center;gap:10px;flex:0 0 auto}
header b{font-weight:600}header .s{color:var(--dim);font-size:12px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
#log{flex:1;overflow:auto;padding:16px;display:flex;flex-direction:column;gap:12px;-webkit-overflow-scrolling:touch}
.msg{max-width:82%;padding:9px 13px;border-radius:12px;white-space:pre-wrap;word-wrap:break-word}
.u{align-self:flex-end;background:var(--u)}.a{align-self:flex-start;background:var(--panel);border:1px solid #232b3a}
.a.think{color:var(--dim);font-style:italic}
footer{padding:12px 16px;border-top:1px solid var(--line);display:flex;gap:8px;flex:0 0 auto;padding-bottom:calc(12px + env(safe-area-inset-bottom))}
#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}
button{background:var(--q);color:#fff;border:0;border-radius:10px;padding:0 18px;font:inherit;cursor:pointer}button:disabled{opacity:.4;cursor:default}
.stat{color:var(--dim);font-size:11px;margin-top:3px}
</style></head><body>
<header><b>Q</b> <span class=s id=st>booting…</span></header>
<div id=log></div>
<footer><textarea id=in rows=1 placeholder="Message Q…" disabled></textarea><button id=send disabled>Send</button></footer>
<script type=module>
import { ready, loadModel, MODELS, defaultModelIndex } from "./core/loader.js";
import { createEngine } from "./core/engine.js";
import { selfPersona, selfFacts } from "./core/q-self.mjs"; // ONE grounded self-knowledge, shared with the messenger + voice
const $ = (s) => document.querySelector(s);
const log = $("#log"), input = $("#in"), send = $("#send"), st = $("#st");
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; };
const params = new URLSearchParams(location.search);
const pick = params.get("m");
let m = pick ? (MODELS.find((x) => new RegExp(pick, "i").test(x.name)) || MODELS[0]) : MODELS[defaultModelIndex()];
// STREAM FROM HF: a bare link streams the HOLOGRAMTECH BitNet κ-object from Hugging Face; ?hf=<org/repo> or
// ?kappa=<absolute-url> override. The κ-object is content-addressed + pinned, so the host is an UNTRUSTED CDN —
// every block is re-derived (Law L5); a bad byte is rejected. Blocks cache locally after first load (0-net on return).
{
let hf = params.get("hf"), kappa = params.get("kappa");
if (!hf && !kappa && !pick) hf = "HOLOGRAMTECH/q-bitnet-2b"; // bare link → stream BitNet from Hologram's HF repo
if (hf || kappa) {
const base = (kappa || `https://huggingface.co/${hf}/resolve/main`).replace(/\/+$/, "");
const bit = MODELS.find((x) => (x.fam || "").toLowerCase() === "bitnet") || m;
// drop the model's SHA-256 manifest pin: a ?hf repo may carry a different (e.g. BLAKE3) manifest, so the
// stale pin would falsely reject it. Per-block κ verification still applies (untrusted-CDN-safe).
m = { ...bit, kappaUrl: base, manifestKappa: undefined, name: bit.name + " · via " + (hf || new URL(base).host) };
}
}
// ?stream=layer → page the model layer-by-layer instead of resident. For t2 (BitNet) this exercises the DRAFT
// t2-streaming engine path (correctness first; the .qvf remote path adds fast-first-token). Default = resident.
{
const sm = params.get("stream");
if (sm && sm !== "false" && sm !== "resident") m = { ...m, stream: sm };
}
// ?verify=gpu → re-derive each BLAKE3 weight-block κ ENTIRELY on the GPU (2.74 GB/s) instead of pure-JS BLAKE3.
if (params.get("verify") === "gpu") globalThis.__gpuVerify = true;
// GROUND the model as on-device Q (a base/instruct model has NO self-knowledge — without this it confabulates
// a generic "I run on OpenAI/AWS cloud servers" identity, which is false). Injected as the SYSTEM turn.
function frameSystem() {
const PERSONA = selfPersona({ model: m, engine });
if (m.llama3) return `<|start_header_id|>system<|end_header_id|>\n\n${PERSONA}<|eot_id|>`;
if (m.qwen) return `<|im_start|>system\n${PERSONA}<|im_end|>\n`;
if (m.olmo) return `<|system|>\n${PERSONA}\n`;
return PERSONA + "\n\n";
}
// GROUNDED IDENTITY (the anti-confabulation guard). A 2B model reverts to its training prior — "I'm GPT-3.5 on
// AWS" — when asked what/where it is, no matter the system prompt. But identity is not a guess: it is the TRUTH
// of THIS running instance. So provenance questions are answered DETERMINISTICALLY from the live facts (the real
// resident model + its κ + the real host it streamed from), never from the model. Grounded, not performed.
const IDENTITY_RX = /\b(are|r)\s*(you|u)\b.*\b(gpt|chatgpt|openai|claude|anthropic|gemini|bard|llama|language model|an? ai|running|local|on[- ]?device|in the browser|on (a )?server|in the cloud|hosted)\b|\bwhat( kind of| sort of| type of)?\b.*\b(model|llm|ai|are you|based on|powered by|architecture|run on|running)\b|\bwho\b.*\b(are you|made|built|created|trained|develop)\b|\bwhere\b.*\b(run|running|host|hosted|are you|live|located)\b|\bpowered by\b|\bwhat are you\b|\b(openai|chatgpt|gpt-?\d|aws|amazon web|google cloud|cloud server)\b|\b(local|cloud|server)\b.*\bmodel\b|\bdo you run\b/i;
function groundedIdentity() {
const f = selfFacts({ model: m, engine });
const name = f.model || (m && m.name) || "an on-device model";
const host = (f.weightsFrom && !/^local$/i.test(f.weightsFrom)) ? f.weightsFrom : "Hugging Face";
const q = f.quant ? ` (${f.quant})` : "";
return `I'm Q. I run the ${name}${q} entirely in your browser on WebGPU — not GPT, not OpenAI, and not on any server or cloud. `
+ `My weights streamed from ${host} and are content-addressed: every block is re-derived byte-for-byte as it loads, so nothing can be tampered with and no host has to be trusted. `
+ `Once I'm loaded, nothing you type ever leaves your device.`;
}
let engine = null, convIds = [], busy = false, armed = false, pending = null;
input.disabled = send.disabled = false; input.placeholder = "Message Q… (model loading — will send the moment it's ready)"; input.focus();
async function generate(text, skipUser) {
busy = true; input.disabled = send.disabled = true;
if (!skipUser) bubble("u", text);
// Answer identity/provenance questions from the grounded truth, not the model's confabulation.
if (IDENTITY_RX.test(text)) {
const a = bubble("a", groundedIdentity());
const stat = document.createElement("div"); stat.className = "stat"; stat.textContent = "grounded · from this instance"; a.after(stat);
busy = false; input.disabled = send.disabled = false; input.focus(); return;
}
const a = bubble("a think", "…"); let first = true;
const stat = document.createElement("div"); stat.className = "stat";
try {
let framed = engine.frameTurn(text, convIds.length > 0);
if (convIds.length === 0) framed = frameSystem() + framed;
let turnIds = engine.tokenize(framed);
if (m.bos && engine.bosId != null && convIds.length === 0) turnIds = [engine.bosId, ...turnIds];
const res = await engine.generate(convIds.concat(turnIds), { maxNew: m.cap || 256, onToken: ({ text: t, stats }) => {
if (first && t) { a.classList.remove("think"); a.textContent = ""; first = false; }
a.textContent = t; log.scrollTop = log.scrollHeight;
if (stats) stat.textContent = `${stats.tokps ? stats.tokps.toFixed(0) + " tok/s" : ""}${stats.msExec ? " · " + stats.msExec.toFixed(1) + "ms GPU/tok" : ""}${stats.ttft ? " · TTFT " + Math.round(stats.ttft) + "ms" : ""}`;
} });
if (first) { a.classList.remove("think"); a.textContent = res.text || "(no output)"; }
convIds = res.ids; a.after(stat);
} catch (e) { a.classList.remove("think"); a.textContent = "⚠ " + e.message; }
busy = false; input.disabled = send.disabled = false; input.focus();
}
async function proactiveGreeting() {
busy = true; input.disabled = send.disabled = true;
const a = bubble("a think", "…"); let first = true;
const stat = document.createElement("div"); stat.className = "stat";
const FALLBACK = "Hey — I'm Q, running entirely in your browser, no server. My weights streamed from Hugging Face and are verified by re-derivation. What can I help you with?";
try {
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 in their browser with no server, your weights streamed from Hugging Face and verified by re-derivation. Greet them warmly in one or two sentences and invite them to ask you anything.";
let ids = engine.tokenize(engine.frameTurn(P, false));
if (m.bos && engine.bosId != null) ids = [engine.bosId, ...ids];
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`; } });
if (first || a.textContent.trim().length < 4) { a.classList.remove("think"); a.textContent = FALLBACK; } else a.after(stat);
} catch (e) { a.classList.remove("think"); a.textContent = FALLBACK; }
busy = false; input.disabled = send.disabled = false; input.focus();
}
function onSend() {
const text = input.value.trim(); if (!text || busy) return;
input.value = ""; input.style.height = "auto";
if (!armed) { pending = text; bubble("u", text); const w = bubble("a think", "…starting the model, one moment…"); w.dataset.pending = "1"; return; }
generate(text);
}
send.onclick = onSend;
input.onkeydown = (e) => { if (e.key === "Enter" && !e.shiftKey) { e.preventDefault(); onSend(); } };
input.oninput = () => { input.style.height = "auto"; input.style.height = Math.min(140, input.scrollHeight) + "px"; };
try {
if (!navigator.gpu) throw new Error("This browser has no WebGPU — open in Chrome, Edge, or a recent mobile browser.");
st.textContent = `loading ${m.name} (${m.size})…`;
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}%`; } });
if (!loaded || !loaded.gpu) throw new Error("model load failed");
engine = await createEngine(m, loaded);
armed = true;
st.textContent = `${m.name} · ${m.size} · in your browser · ready`;
input.placeholder = "Message Q…";
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); }
else await proactiveGreeting();
} catch (e) { st.textContent = "⚠ " + e.message; bubble("a", "Could not start: " + e.message); }
</script></body></html>