Automated salvage commit — agent session ended (exit 124). Work in progress, may need continuation.
121 lines
4.1 KiB
JavaScript
121 lines
4.1 KiB
JavaScript
/**
|
|
* edge-worker-client.js — Main-thread proxy for the edge-worker Web Worker.
|
|
*
|
|
* Spawns js/edge-worker.js as a module Worker and exposes:
|
|
* classify(text) → Promise<{
|
|
* complexity: 'trivial'|'moderate'|'complex',
|
|
* score: number,
|
|
* reason: string,
|
|
* localReply?: string // only when complexity === 'trivial'
|
|
* }>
|
|
* sentiment(text) → Promise<{ label:'POSITIVE'|'NEGATIVE'|'NEUTRAL', score }>
|
|
* onReady(fn) → register a callback fired when models finish loading
|
|
* onError(fn) → register a callback fired if the worker fails to boot
|
|
* isReady() → boolean — true once both models are warm
|
|
* warmup() → start the worker early so first classify() is fast
|
|
*
|
|
* Complexity tiers (set by the worker):
|
|
* trivial — greeting/small-talk; answered locally, 0 sats, no server call
|
|
* moderate — simple question; show cost preview, route to server
|
|
* complex — technical/creative/code; always priced, show cost preview
|
|
*
|
|
* If Web Workers are unavailable (SSR / old browser), all calls fall back
|
|
* gracefully: classify → { complexity:'moderate', ... } so the app still works.
|
|
*/
|
|
|
|
let _worker = null;
|
|
let _ready = false;
|
|
let _readyCb = null;
|
|
let _errorCb = null;
|
|
const _pending = new Map(); // id → { resolve, reject }
|
|
let _nextId = 1;
|
|
|
|
function _init() {
|
|
if (_worker) return;
|
|
|
|
try {
|
|
// Use import.meta.url so Vite can resolve the worker URL correctly.
|
|
// type:'module' is required for ESM imports inside the worker.
|
|
_worker = new Worker(new URL('./edge-worker.js', import.meta.url), { type: 'module' });
|
|
|
|
_worker.addEventListener('message', ({ data }) => {
|
|
// Lifecycle events have no id
|
|
if (data?.type === 'ready') {
|
|
_ready = true;
|
|
if (_readyCb) { _readyCb(); _readyCb = null; }
|
|
return;
|
|
}
|
|
if (data?.type === 'error') {
|
|
console.warn('[edge-worker] worker boot error:', data.message);
|
|
if (_errorCb) { _errorCb(data.message); _errorCb = null; }
|
|
// Resolve all pending with fallback values
|
|
for (const [, { resolve }] of _pending) resolve(_fallback(null));
|
|
_pending.clear();
|
|
return;
|
|
}
|
|
|
|
// Regular response: { id, result }
|
|
const { id, result } = data ?? {};
|
|
const entry = _pending.get(id);
|
|
if (entry) {
|
|
_pending.delete(id);
|
|
entry.resolve(result);
|
|
}
|
|
});
|
|
|
|
_worker.addEventListener('error', (err) => {
|
|
console.warn('[edge-worker] worker error:', err.message);
|
|
});
|
|
|
|
} catch (err) {
|
|
console.warn('[edge-worker] Web Workers unavailable — using fallback routing:', err.message);
|
|
_worker = null;
|
|
}
|
|
}
|
|
|
|
function _fallback(type) {
|
|
if (type === 'sentiment') return { label: 'NEUTRAL', score: 0.5 };
|
|
// classify fallback: moderate keeps the UI functional (shows estimate, routes to server)
|
|
return { complexity: 'moderate', score: 0, reason: 'worker-unavailable' };
|
|
}
|
|
|
|
function _send(type, text) {
|
|
if (!_worker) return Promise.resolve(_fallback(type));
|
|
|
|
const id = _nextId++;
|
|
return new Promise((resolve) => {
|
|
_pending.set(id, { resolve, reject: resolve });
|
|
_worker.postMessage({ id, type, text });
|
|
});
|
|
}
|
|
|
|
// ── Public API ────────────────────────────────────────────────────────────────
|
|
|
|
export function classify(text) {
|
|
_init();
|
|
return _send('classify', text);
|
|
}
|
|
|
|
export function sentiment(text) {
|
|
_init();
|
|
return _send('sentiment', text);
|
|
}
|
|
|
|
export function onReady(fn) {
|
|
if (_ready) { fn(); return; }
|
|
_readyCb = fn;
|
|
}
|
|
|
|
/** Register a callback fired if the worker fails to boot (model load error). */
|
|
export function onError(fn) {
|
|
_errorCb = fn;
|
|
}
|
|
|
|
export function isReady() { return _ready; }
|
|
|
|
/**
|
|
* warmup() — start the worker (and model loading) early so classify/sentiment
|
|
* calls on first user interaction don't stall waiting for models.
|
|
*/
|
|
export function warmup() { _init(); }
|