AksharaLP-demo / js /engine_host.mjs
sourojitdotcom's picture
Fix load hang in private Space: forward __sign to worker/module/SW requests, main-thread fallback, timeouts, size checks
6bfdc94 verified
Raw History Blame Contribute Delete
3.3 kB
// AksharaLP Space — engine host, shared by the Web Worker (js/worker.js) and the main-thread fallback in js/app.js.
// Two-tier load: the small core (model + reranker) first, so typing works immediately; then the dictionary pack
// (lexicon + sentence LM), read from the Cache API after the first visit. If the pack cannot be fetched, or does not
// arrive within DICT_TIMEOUT_MS, the engine keeps running core-only.
//
// `search` is the page's query string. On a private Hugging Face Space the page is opened as index.html?__sign=<JWT>
// inside a cross-site iframe, where the SameSite=Lax auth cookie is not sent; same-origin requests are authorised only
// through their Referer. A worker's (and a module's) requests carry the worker's / module's own URL as Referer, so
// the query is forwarded onto those URLs. Cross-origin requests (the CDN) only ever see the origin.
const DICT_TIMEOUT_MS = 180000;
export function startHost(post, search = "") {
let engine = null;
const q = new URLSearchParams(search);
const fwd = new URLSearchParams(search); fwd.delete("dict"); const qs = fwd.toString() ? "?" + fwd.toString() : "";
const ready = (async () => {
try {
const { default: BhashaCore } = await import("./aksharalp.mjs" + qs);
const base = new URL("../model/", import.meta.url).href;
engine = await BhashaCore.load(base, { dict: false, onProgress: (file) => post({ type: "progress", file }) });
const t0 = performance.now();
engine.transliterate("main ghar ja raha hoon", { k: 5 });
post({ type: "ready", info: engine.info(), loadMs: engine.loadMs, warmMs: performance.now() - t0, note: engine.manifest && engine.manifest.note, name: engine.manifest && engine.manifest.name });
if (q.get("dict") === "0") { post({ type: "dict", ok: false, skipped: true, info: engine.info() }); return; }
const t1 = performance.now(); let src = null, timer;
const timeout = new Promise((res) => { timer = setTimeout(() => res("timeout"), DICT_TIMEOUT_MS); });
const ok = await Promise.race([engine.loadDict({ onProgress: (file, from) => { src = from; post({ type: "progress", file: "dictionary/" + file, from }); } }), timeout]);
clearTimeout(timer);
if (ok === "timeout") { post({ type: "dict", ok: false, error: `dictionary pack did not arrive within ${DICT_TIMEOUT_MS / 1000} s; running core-only`, info: engine.info() }); return; }
if (ok) engine.transliterate("main ghar ja raha hoon", { k: 5 });
post({ type: "dict", ok, from: src, ms: performance.now() - t1, error: engine.dictError, info: engine.info() });
} catch (e) {
post({ type: "error", message: String(e && e.message || e) });
}
})();
return (data) => {
if (!engine) return post({ type: "error", id: data.id, message: "model not loaded" });
try {
if (data.type === "run") {
const res = engine.transliterate(data.text, data.opts || {});
post({ type: "result", id: data.id, tag: data.tag, res });
} else if (data.type === "batch") {
const out = data.items.map((it) => engine.transliterate(it.text, it.opts || {}));
post({ type: "batch", id: data.id, out });
}
} catch (e) {
post({ type: "error", id: data.id, message: String(e && e.message || e) });
}
};
}