Spaces:
Running
Running
Upload spike-qwen2b.html with huggingface_hub
Browse files- spike-qwen2b.html +21 -4
spike-qwen2b.html
CHANGED
|
@@ -28,8 +28,13 @@ const log = (m) => { $("log").textContent += m + "\n"; $("log").scrollTop = 1e9;
|
|
| 28 |
const esc = (s) => String(s).replace(/[&<>]/g, (c) => ({ "&":"&","<":"<",">":">" }[c]));
|
| 29 |
// Official web-llm prebuilts (web-llm resolves weights+wasm from its own config).
|
| 30 |
// ?model=2b|4b selects the tier — 4B is the reasoner half of the 0.8B/4B pairing.
|
| 31 |
-
const MODELS = { "2b": "Qwen3.5-2B-q4f16_1-MLC", "4b": "Qwen3.5-4B-q4f16_1-MLC"
|
| 32 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 33 |
document.getElementById("h1").textContent = `${MODEL_ID} — browser WebGPU check`;
|
| 34 |
|
| 35 |
// Qwen3.5 native tool-call format (XML). Parse both the native <function=…> and
|
|
@@ -117,8 +122,18 @@ async function main() {
|
|
| 117 |
const t0 = performance.now();
|
| 118 |
log(`loading ${MODEL_ID} (first run downloads to browser cache: ~1.3GB for 2B, ~2.5GB for 4B)…`);
|
| 119 |
let engine;
|
|
|
|
| 120 |
try {
|
| 121 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 122 |
} catch (e) { $("env").innerHTML = `<span class="bad">✗ load failed:</span> ${esc(e.message)}`; log("LOAD ERROR: " + e.message); return; }
|
| 123 |
const loadMs = performance.now() - t0;
|
| 124 |
$("env").innerHTML = `<span class="ok">✓ model loaded</span> in ${(loadMs/1000).toFixed(1)}s`;
|
|
@@ -129,7 +144,9 @@ async function main() {
|
|
| 129 |
let res, text = "", toolCalls = null;
|
| 130 |
try {
|
| 131 |
const prompt = renderQwen(test.system, test.user, test.withTools);
|
| 132 |
-
|
|
|
|
|
|
|
| 133 |
text = (THINK ? "<think>\n" : "") + (res.choices?.[0]?.text || "");
|
| 134 |
} catch (e) { text = "[ERROR] " + e.message; }
|
| 135 |
const ms = performance.now() - c0;
|
|
|
|
| 28 |
const esc = (s) => String(s).replace(/[&<>]/g, (c) => ({ "&":"&","<":"<",">":">" }[c]));
|
| 29 |
// Official web-llm prebuilts (web-llm resolves weights+wasm from its own config).
|
| 30 |
// ?model=2b|4b selects the tier — 4B is the reasoner half of the 0.8B/4B pairing.
|
| 31 |
+
const MODELS = { "2b": "Qwen3.5-2B-q4f16_1-MLC", "4b": "Qwen3.5-4B-q4f16_1-MLC",
|
| 32 |
+
// khazarai = our OWN q4f16 conversion (weights-only); reuses web-llm's vanilla
|
| 33 |
+
// Qwen3.5-2B wasm (same arch) at load time — see the appConfig branch below.
|
| 34 |
+
"khazarai": "khazarai-2b-q4f16-MLC" };
|
| 35 |
+
const MODEL_KEY = new URLSearchParams(location.search).get("model") || "2b";
|
| 36 |
+
const MODEL_ID = MODELS[MODEL_KEY] || MODELS["2b"];
|
| 37 |
+
const KHAZARAI_WEIGHTS = "https://huggingface.co/tinkersnot/qwen3.5-2b-khazarai-q4f16-MLC/resolve/main";
|
| 38 |
document.getElementById("h1").textContent = `${MODEL_ID} — browser WebGPU check`;
|
| 39 |
|
| 40 |
// Qwen3.5 native tool-call format (XML). Parse both the native <function=…> and
|
|
|
|
| 122 |
const t0 = performance.now();
|
| 123 |
log(`loading ${MODEL_ID} (first run downloads to browser cache: ~1.3GB for 2B, ~2.5GB for 4B)…`);
|
| 124 |
let engine;
|
| 125 |
+
const onprog = (p) => { $("env").innerHTML = `<span class="warn">loading… ${(p.progress*100|0)}%</span> ${esc(p.text||"")}`; };
|
| 126 |
try {
|
| 127 |
+
if (MODEL_KEY === "khazarai") {
|
| 128 |
+
// Custom appConfig: our khazarai weights + web-llm's OWN vanilla Qwen3.5-2B
|
| 129 |
+
// model_lib (wasm) — weight-independent for the identical qwen3_5 arch.
|
| 130 |
+
const vanilla = webllm.prebuiltAppConfig.model_list.find((m) => m.model_id === "Qwen3.5-2B-q4f16_1-MLC");
|
| 131 |
+
if (!vanilla) throw new Error("vanilla Qwen3.5-2B not in web-llm prebuilt config (can't borrow wasm)");
|
| 132 |
+
const appConfig = { model_list: [{ model: KHAZARAI_WEIGHTS, model_id: MODEL_ID, model_lib: vanilla.model_lib }] };
|
| 133 |
+
engine = await webllm.CreateMLCEngine(MODEL_ID, { appConfig, initProgressCallback: onprog });
|
| 134 |
+
} else {
|
| 135 |
+
engine = await webllm.CreateMLCEngine(MODEL_ID, { initProgressCallback: onprog });
|
| 136 |
+
}
|
| 137 |
} catch (e) { $("env").innerHTML = `<span class="bad">✗ load failed:</span> ${esc(e.message)}`; log("LOAD ERROR: " + e.message); return; }
|
| 138 |
const loadMs = performance.now() - t0;
|
| 139 |
$("env").innerHTML = `<span class="ok">✓ model loaded</span> in ${(loadMs/1000).toFixed(1)}s`;
|
|
|
|
| 144 |
let res, text = "", toolCalls = null;
|
| 145 |
try {
|
| 146 |
const prompt = renderQwen(test.system, test.user, test.withTools);
|
| 147 |
+
// khazarai artifact is ctx=1024 — cap gen budget so prompt+gen fits (it converges well under this).
|
| 148 |
+
const maxTok = MODEL_KEY === "khazarai" ? (THINK ? 720 : 480) : (THINK ? 900 : 640);
|
| 149 |
+
res = await engine.completions.create({ prompt, ...SAMP, max_tokens: maxTok, stream: false, stop: ["<|im_end|>"] });
|
| 150 |
text = (THINK ? "<think>\n" : "") + (res.choices?.[0]?.text || "");
|
| 151 |
} catch (e) { text = "[ERROR] " + e.message; }
|
| 152 |
const ms = performance.now() - c0;
|