kumo-tabular-webgpu / worker.js
mrfakename's picture
Initial Space demo
68a88b7 verified
Raw History Blame Contribute Delete
3.24 kB
import * as ort from "https://cdn.jsdelivr.net/npm/onnxruntime-web@1.30.0/dist/ort.webgpu.bundle.min.mjs";
import { predict } from "./predict.js";
const REPO = "https://huggingface.co/mrfakename/Kumo-Tabular-ONNX/resolve/main/onnx";
ort.env.wasm.wasmPaths = "https://cdn.jsdelivr.net/npm/onnxruntime-web@1.30.0/dist/";
ort.env.wasm.numThreads = self.crossOriginIsolated ? Math.min(4, navigator.hardwareConcurrency || 1) : 1;
const sessions = new Map();
const post = (type, extra) => self.postMessage({ type, ...extra });
async function fetchModel(url, label) {
let cache = null;
try { cache = await caches.open("kumo-tabular-onnx-v1"); } catch {}
const hit = cache && (await cache.match(url));
if (hit) return new Uint8Array(await hit.arrayBuffer());
const res = await fetch(url);
if (!res.ok) throw new Error(`Download failed (${res.status}) for ${label}`);
const total = +res.headers.get("content-length") || 0;
const reader = res.body.getReader();
const parts = []; let got = 0;
for (;;) {
const { done, value } = await reader.read();
if (done) break;
parts.push(value); got += value.length;
post("status", { message: `Downloading ${label}… ${(got / 1e6).toFixed(0)}${total ? ` / ${(total / 1e6).toFixed(0)}` : ""} MB`, progress: total ? got / total : null });
}
const buf = new Uint8Array(got); let o = 0;
for (const p of parts) { buf.set(p, o); o += p.length; }
if (cache) { try { await cache.put(url, new Response(buf)); } catch {} }
return buf;
}
async function getSession(size, task, backend) {
const key = `${size}_${task}_${backend}`;
if (sessions.has(key)) return sessions.get(key);
// keep at most one session alive to bound memory
for (const [k, s] of sessions) { try { await s.release(); } catch {} sessions.delete(k); }
const label = `${size} ${task} model`;
const bytes = await fetchModel(`${REPO}/${size}_${task}.onnx`, label);
post("status", { message: `Initializing ${label} on ${backend.toUpperCase()}…`, progress: null });
const providers = backend === "webgpu" ? ["webgpu", "wasm"] : ["wasm"];
const session = await ort.InferenceSession.create(bytes, { executionProviders: providers, graphOptimizationLevel: "all" });
sessions.set(key, session);
return session;
}
self.onmessage = async ({ data }) => {
if (data.type !== "predict") return;
let backend = data.backend;
if (backend === "webgpu" && !(navigator.gpu && (await navigator.gpu.requestAdapter().catch(() => null)))) backend = "wasm";
try {
const t0 = performance.now();
const res = await predict({
ort, ctx: data.ctx, qry: data.qry, target: data.target, task: data.task,
numEstimators: data.numEstimators,
getSession: (task) => getSession(data.size, task, backend),
onProgress: (message, progress) => post("status", { message, progress }),
});
const secs = ((performance.now() - t0) / 1000).toFixed(1);
post("result", {
...res,
summary: `${res.task} · ${data.size} model on ${backend.toUpperCase()} · ${res.nContext} context rows · ${res.nQuery} predicted · ${res.nFeatures} features · ${secs}s`,
});
} catch (e) {
console.error(e);
post("error", { message: `Error: ${e.message || e}` });
}
};