webml-kit 0.4.0 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +3 -3
- package/dist/cache.d.ts +0 -46
- package/dist/cache.d.ts.map +0 -1
- package/dist/cache.js +0 -161
- package/dist/cache.js.map +0 -1
- package/dist/decision.d.ts +0 -182
- package/dist/decision.d.ts.map +0 -1
- package/dist/decision.js +0 -789
- package/dist/decision.js.map +0 -1
- package/dist/device.d.ts +0 -86
- package/dist/device.d.ts.map +0 -1
- package/dist/device.js +0 -195
- package/dist/device.js.map +0 -1
- package/dist/gpu-recovery.d.ts +0 -61
- package/dist/gpu-recovery.d.ts.map +0 -1
- package/dist/gpu-recovery.js +0 -105
- package/dist/gpu-recovery.js.map +0 -1
- package/dist/hub.d.ts +0 -97
- package/dist/hub.d.ts.map +0 -1
- package/dist/hub.js +0 -142
- package/dist/hub.js.map +0 -1
- package/dist/index.d.ts +0 -63
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js +0 -65
- package/dist/index.js.map +0 -1
- package/dist/inputs.d.ts +0 -27
- package/dist/inputs.d.ts.map +0 -1
- package/dist/inputs.js +0 -155
- package/dist/inputs.js.map +0 -1
- package/dist/loader.d.ts +0 -100
- package/dist/loader.d.ts.map +0 -1
- package/dist/loader.js +0 -246
- package/dist/loader.js.map +0 -1
- package/dist/model-client.d.ts +0 -149
- package/dist/model-client.d.ts.map +0 -1
- package/dist/model-client.js +0 -318
- package/dist/model-client.js.map +0 -1
- package/dist/model-worker.d.ts +0 -16
- package/dist/model-worker.d.ts.map +0 -1
- package/dist/model-worker.js +0 -340
- package/dist/model-worker.js.map +0 -1
- package/dist/onnx-pipeline.d.ts +0 -20
- package/dist/onnx-pipeline.d.ts.map +0 -1
- package/dist/onnx-pipeline.js +0 -462
- package/dist/onnx-pipeline.js.map +0 -1
- package/dist/pipelines/index.d.ts +0 -28
- package/dist/pipelines/index.d.ts.map +0 -1
- package/dist/pipelines/index.js +0 -140
- package/dist/pipelines/index.js.map +0 -1
- package/dist/shim-ort.d.ts +0 -6
- package/dist/shim-ort.d.ts.map +0 -1
- package/dist/shim-ort.js +0 -38
- package/dist/shim-ort.js.map +0 -1
- package/dist/streaming.d.ts +0 -55
- package/dist/streaming.d.ts.map +0 -1
- package/dist/streaming.js +0 -142
- package/dist/streaming.js.map +0 -1
- package/dist/types.d.ts +0 -200
- package/dist/types.d.ts.map +0 -1
- package/dist/types.js +0 -3
- package/dist/types.js.map +0 -1
- package/dist/webml-kit.browser.js +0 -2568
package/dist/model-client.js
DELETED
|
@@ -1,318 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Main-thread client for communicating with the ML worker.
|
|
3
|
-
*
|
|
4
|
-
* Wraps the Web Worker's postMessage protocol with a promise-based API,
|
|
5
|
-
* AsyncIterable token streaming, and typed events.
|
|
6
|
-
*
|
|
7
|
-
* ```ts
|
|
8
|
-
* import { ModelClient } from 'webml-kit';
|
|
9
|
-
*
|
|
10
|
-
* const client = new ModelClient();
|
|
11
|
-
*
|
|
12
|
-
* // Check device capabilities
|
|
13
|
-
* const device = await client.detect();
|
|
14
|
-
* console.log(device.backend); // 'webgpu'
|
|
15
|
-
*
|
|
16
|
-
* // Load a model
|
|
17
|
-
* await client.load({
|
|
18
|
-
* task: 'text-generation',
|
|
19
|
-
* modelId: 'onnx-community/Bonsai-1.7B-ONNX',
|
|
20
|
-
* dtype: 'q4',
|
|
21
|
-
* onProgress: ({ percent }) => console.log(`${percent}%`),
|
|
22
|
-
* });
|
|
23
|
-
*
|
|
24
|
-
* // Stream text generation
|
|
25
|
-
* for await (const { token, tps } of client.stream('Hello!')) {
|
|
26
|
-
* process.stdout.write(token);
|
|
27
|
-
* }
|
|
28
|
-
*
|
|
29
|
-
* // One-shot inference
|
|
30
|
-
* const labels = await client.run('image-classification', imageBlob);
|
|
31
|
-
* ```
|
|
32
|
-
*/
|
|
33
|
-
import { TokenStream, collectStream } from './streaming.js';
|
|
34
|
-
import { GPURecovery } from './gpu-recovery.js';
|
|
35
|
-
// ─── Client ───
|
|
36
|
-
export class ModelClient {
|
|
37
|
-
worker = null;
|
|
38
|
-
workerUrl;
|
|
39
|
-
listeners = new Map();
|
|
40
|
-
pendingRequests = new Map();
|
|
41
|
-
requestCounter = 0;
|
|
42
|
-
deviceInfo = null;
|
|
43
|
-
loadedModels = new Set();
|
|
44
|
-
progressCallback = null;
|
|
45
|
-
gpuRecovery;
|
|
46
|
-
/**
|
|
47
|
-
* Create a new ModelClient.
|
|
48
|
-
*
|
|
49
|
-
* @param workerUrl - URL to the model-worker.js file. If omitted,
|
|
50
|
-
* creates a Blob URL from the bundled worker (requires bundler support).
|
|
51
|
-
*/
|
|
52
|
-
constructor(workerUrl) {
|
|
53
|
-
this.workerUrl = workerUrl ?? null;
|
|
54
|
-
this.gpuRecovery = new GPURecovery();
|
|
55
|
-
this.gpuRecovery.on('lost', ({ reason }) => {
|
|
56
|
-
this.emit('device-lost', { reason });
|
|
57
|
-
});
|
|
58
|
-
this.gpuRecovery.on('recovered', () => {
|
|
59
|
-
this.emit('device-recovered', {});
|
|
60
|
-
});
|
|
61
|
-
}
|
|
62
|
-
// ─── Worker Lifecycle ───
|
|
63
|
-
getWorker() {
|
|
64
|
-
if (!this.worker) {
|
|
65
|
-
const url = this.workerUrl
|
|
66
|
-
?? new URL('./model-worker.js', import.meta.url);
|
|
67
|
-
this.worker = new Worker(url, { type: 'module' });
|
|
68
|
-
this.worker.addEventListener('message', this.handleMessage.bind(this));
|
|
69
|
-
this.worker.addEventListener('error', (e) => {
|
|
70
|
-
this.emit('error', { message: e.message });
|
|
71
|
-
});
|
|
72
|
-
}
|
|
73
|
-
return this.worker;
|
|
74
|
-
}
|
|
75
|
-
send(cmd) {
|
|
76
|
-
this.getWorker().postMessage(cmd);
|
|
77
|
-
}
|
|
78
|
-
nextId() {
|
|
79
|
-
return `req_${++this.requestCounter}_${Date.now()}`;
|
|
80
|
-
}
|
|
81
|
-
// ─── Message Handler ───
|
|
82
|
-
handleMessage(e) {
|
|
83
|
-
const msg = e.data;
|
|
84
|
-
switch (msg.type) {
|
|
85
|
-
case 'device-info': {
|
|
86
|
-
this.deviceInfo = msg.data;
|
|
87
|
-
const pending = this.pendingRequests.get('detect');
|
|
88
|
-
if (pending) {
|
|
89
|
-
pending.resolve(msg.data);
|
|
90
|
-
this.pendingRequests.delete('detect');
|
|
91
|
-
}
|
|
92
|
-
break;
|
|
93
|
-
}
|
|
94
|
-
case 'progress': {
|
|
95
|
-
this.progressCallback?.(msg.data);
|
|
96
|
-
this.emit('progress', msg.data);
|
|
97
|
-
break;
|
|
98
|
-
}
|
|
99
|
-
case 'ready': {
|
|
100
|
-
this.loadedModels.add(msg.modelKey);
|
|
101
|
-
this.emit('ready', { modelKey: msg.modelKey });
|
|
102
|
-
const pending = this.pendingRequests.get('load');
|
|
103
|
-
if (pending) {
|
|
104
|
-
pending.resolve(undefined);
|
|
105
|
-
this.pendingRequests.delete('load');
|
|
106
|
-
}
|
|
107
|
-
break;
|
|
108
|
-
}
|
|
109
|
-
case 'token': {
|
|
110
|
-
const pending = this.pendingRequests.get(msg.id);
|
|
111
|
-
if (pending?.stream) {
|
|
112
|
-
pending.stream.push(msg.data);
|
|
113
|
-
}
|
|
114
|
-
break;
|
|
115
|
-
}
|
|
116
|
-
case 'result': {
|
|
117
|
-
const pending = this.pendingRequests.get(msg.id);
|
|
118
|
-
if (pending) {
|
|
119
|
-
if (pending.stream) {
|
|
120
|
-
pending.stream.end();
|
|
121
|
-
}
|
|
122
|
-
pending.resolve(msg.data);
|
|
123
|
-
this.pendingRequests.delete(msg.id);
|
|
124
|
-
}
|
|
125
|
-
break;
|
|
126
|
-
}
|
|
127
|
-
case 'error': {
|
|
128
|
-
const pending = this.pendingRequests.get(msg.id);
|
|
129
|
-
if (pending) {
|
|
130
|
-
if (pending.stream) {
|
|
131
|
-
pending.stream.abort(new Error(msg.data));
|
|
132
|
-
}
|
|
133
|
-
pending.reject(new Error(msg.data));
|
|
134
|
-
this.pendingRequests.delete(msg.id);
|
|
135
|
-
}
|
|
136
|
-
this.emit('error', { message: msg.data, id: msg.id });
|
|
137
|
-
break;
|
|
138
|
-
}
|
|
139
|
-
case 'device-lost': {
|
|
140
|
-
this.emit('device-lost', { reason: msg.reason });
|
|
141
|
-
break;
|
|
142
|
-
}
|
|
143
|
-
case 'device-recovered': {
|
|
144
|
-
this.emit('device-recovered', {});
|
|
145
|
-
break;
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
// ─── Events ───
|
|
150
|
-
/** Register an event listener. */
|
|
151
|
-
on(event, listener) {
|
|
152
|
-
if (!this.listeners.has(event)) {
|
|
153
|
-
this.listeners.set(event, new Set());
|
|
154
|
-
}
|
|
155
|
-
this.listeners.get(event).add(listener);
|
|
156
|
-
return this;
|
|
157
|
-
}
|
|
158
|
-
/** Remove an event listener. */
|
|
159
|
-
off(event, listener) {
|
|
160
|
-
this.listeners.get(event)?.delete(listener);
|
|
161
|
-
return this;
|
|
162
|
-
}
|
|
163
|
-
emit(event, data) {
|
|
164
|
-
this.listeners.get(event)?.forEach(fn => fn(data));
|
|
165
|
-
}
|
|
166
|
-
// ─── Public API ───
|
|
167
|
-
/**
|
|
168
|
-
* Detect the best available compute backend.
|
|
169
|
-
*
|
|
170
|
-
* ```ts
|
|
171
|
-
* const info = await client.detect();
|
|
172
|
-
* console.log(info.backend); // 'webgpu'
|
|
173
|
-
* console.log(info.gpu?.vendor); // 'apple'
|
|
174
|
-
* console.log(info.recommendedDtype); // 'q4'
|
|
175
|
-
* ```
|
|
176
|
-
*/
|
|
177
|
-
async detect() {
|
|
178
|
-
if (this.deviceInfo)
|
|
179
|
-
return this.deviceInfo;
|
|
180
|
-
return new Promise((resolve, reject) => {
|
|
181
|
-
this.pendingRequests.set('detect', { resolve: resolve, reject });
|
|
182
|
-
this.send({ type: 'check' });
|
|
183
|
-
});
|
|
184
|
-
}
|
|
185
|
-
/**
|
|
186
|
-
* Load a model pipeline.
|
|
187
|
-
*
|
|
188
|
-
* ```ts
|
|
189
|
-
* await client.load({
|
|
190
|
-
* task: 'text-generation',
|
|
191
|
-
* modelId: 'onnx-community/Bonsai-1.7B-ONNX',
|
|
192
|
-
* dtype: 'q4',
|
|
193
|
-
* onProgress: ({ percent }) => updateUI(percent),
|
|
194
|
-
* });
|
|
195
|
-
* ```
|
|
196
|
-
*/
|
|
197
|
-
async load(options) {
|
|
198
|
-
this.progressCallback = options.onProgress ?? null;
|
|
199
|
-
const config = {
|
|
200
|
-
task: options.task,
|
|
201
|
-
modelId: options.modelId,
|
|
202
|
-
dtype: options.dtype,
|
|
203
|
-
device: options.device,
|
|
204
|
-
revision: options.revision,
|
|
205
|
-
};
|
|
206
|
-
return new Promise((resolve, reject) => {
|
|
207
|
-
this.pendingRequests.set('load', { resolve: resolve, reject });
|
|
208
|
-
this.send({ type: 'load', config });
|
|
209
|
-
});
|
|
210
|
-
}
|
|
211
|
-
/**
|
|
212
|
-
* Run one-shot inference for any pipeline task.
|
|
213
|
-
*
|
|
214
|
-
* ```ts
|
|
215
|
-
* // Image classification
|
|
216
|
-
* const labels = await client.run('image-classification', imageUrl);
|
|
217
|
-
*
|
|
218
|
-
* // Speech recognition
|
|
219
|
-
* const { text } = await client.run('automatic-speech-recognition', audioBlob);
|
|
220
|
-
*
|
|
221
|
-
* // Embeddings
|
|
222
|
-
* const vectors = await client.run('feature-extraction', 'Hello world');
|
|
223
|
-
* ```
|
|
224
|
-
*/
|
|
225
|
-
async run(task, input, options) {
|
|
226
|
-
const id = this.nextId();
|
|
227
|
-
return new Promise((resolve, reject) => {
|
|
228
|
-
this.pendingRequests.set(id, { resolve: resolve, reject });
|
|
229
|
-
this.send({ type: 'run', id, task, input, options });
|
|
230
|
-
});
|
|
231
|
-
}
|
|
232
|
-
/**
|
|
233
|
-
* Generate text with streaming tokens.
|
|
234
|
-
*
|
|
235
|
-
* Returns an `AsyncIterable<TokenEvent>` that yields tokens as they're generated.
|
|
236
|
-
*
|
|
237
|
-
* ```ts
|
|
238
|
-
* for await (const { token, tps } of client.stream('Tell me a joke')) {
|
|
239
|
-
* process.stdout.write(token);
|
|
240
|
-
* }
|
|
241
|
-
* ```
|
|
242
|
-
*/
|
|
243
|
-
stream(input, options) {
|
|
244
|
-
const id = this.nextId();
|
|
245
|
-
const stream = new TokenStream(options?.signal);
|
|
246
|
-
this.pendingRequests.set(id, {
|
|
247
|
-
resolve: () => { }, // Result comes through the stream
|
|
248
|
-
reject: (err) => stream.abort(err),
|
|
249
|
-
stream,
|
|
250
|
-
});
|
|
251
|
-
this.send({
|
|
252
|
-
type: 'run',
|
|
253
|
-
id,
|
|
254
|
-
task: 'text-generation',
|
|
255
|
-
input,
|
|
256
|
-
options: options,
|
|
257
|
-
});
|
|
258
|
-
return stream;
|
|
259
|
-
}
|
|
260
|
-
/**
|
|
261
|
-
* Generate text and wait for the complete result.
|
|
262
|
-
*
|
|
263
|
-
* ```ts
|
|
264
|
-
* const { text, tps, numTokens } = await client.generate('Hello!');
|
|
265
|
-
* ```
|
|
266
|
-
*/
|
|
267
|
-
async generate(input, options) {
|
|
268
|
-
const tokenStream = this.stream(input, options);
|
|
269
|
-
const collected = await collectStream(tokenStream);
|
|
270
|
-
return {
|
|
271
|
-
...collected,
|
|
272
|
-
totalTime: 0, // Will be filled from worker result
|
|
273
|
-
};
|
|
274
|
-
}
|
|
275
|
-
/**
|
|
276
|
-
* Interrupt an ongoing text generation.
|
|
277
|
-
*/
|
|
278
|
-
interrupt() {
|
|
279
|
-
this.send({ type: 'interrupt' });
|
|
280
|
-
}
|
|
281
|
-
/**
|
|
282
|
-
* Reset the KV cache (start a new conversation).
|
|
283
|
-
*/
|
|
284
|
-
reset() {
|
|
285
|
-
this.send({ type: 'reset' });
|
|
286
|
-
}
|
|
287
|
-
/**
|
|
288
|
-
* Dispose a loaded model and free memory.
|
|
289
|
-
*
|
|
290
|
-
* @param modelKey - Specific model key (task::modelId), or omit to dispose all.
|
|
291
|
-
*/
|
|
292
|
-
dispose(modelKey) {
|
|
293
|
-
this.send({ type: 'dispose', modelKey });
|
|
294
|
-
if (modelKey) {
|
|
295
|
-
this.loadedModels.delete(modelKey);
|
|
296
|
-
}
|
|
297
|
-
else {
|
|
298
|
-
this.loadedModels.clear();
|
|
299
|
-
}
|
|
300
|
-
}
|
|
301
|
-
/**
|
|
302
|
-
* Check if a model is currently loaded.
|
|
303
|
-
*/
|
|
304
|
-
isLoaded(task, modelId) {
|
|
305
|
-
return this.loadedModels.has(`${task}::${modelId}`);
|
|
306
|
-
}
|
|
307
|
-
/**
|
|
308
|
-
* Terminate the worker completely.
|
|
309
|
-
*/
|
|
310
|
-
terminate() {
|
|
311
|
-
this.worker?.terminate();
|
|
312
|
-
this.worker = null;
|
|
313
|
-
this.loadedModels.clear();
|
|
314
|
-
this.pendingRequests.clear();
|
|
315
|
-
this.deviceInfo = null;
|
|
316
|
-
}
|
|
317
|
-
}
|
|
318
|
-
//# sourceMappingURL=model-client.js.map
|
package/dist/model-client.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"model-client.js","sourceRoot":"","sources":["../src/model-client.ts"],"names":[],"mappings":"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"}
|
package/dist/model-worker.d.ts
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Generic Web Worker for running ML pipelines.
|
|
3
|
-
*
|
|
4
|
-
* This file runs in a Web Worker thread. It handles:
|
|
5
|
-
* - WebGPU device detection + WASM fallback
|
|
6
|
-
* - Pipeline instantiation (singleton, keyed by task+model)
|
|
7
|
-
* - Progress reporting during model download
|
|
8
|
-
* - Text generation with streaming + KV cache
|
|
9
|
-
* - One-shot inference for classification, detection, ASR, etc.
|
|
10
|
-
* - GPU device-lost recovery
|
|
11
|
-
* - Memory cleanup
|
|
12
|
-
*
|
|
13
|
-
* Communication is via structured `postMessage` using WorkerCommand/WorkerResponse types.
|
|
14
|
-
*/
|
|
15
|
-
export {};
|
|
16
|
-
//# sourceMappingURL=model-worker.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"model-worker.d.ts","sourceRoot":"","sources":["../src/model-worker.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG"}
|
package/dist/model-worker.js
DELETED
|
@@ -1,340 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Generic Web Worker for running ML pipelines.
|
|
3
|
-
*
|
|
4
|
-
* This file runs in a Web Worker thread. It handles:
|
|
5
|
-
* - WebGPU device detection + WASM fallback
|
|
6
|
-
* - Pipeline instantiation (singleton, keyed by task+model)
|
|
7
|
-
* - Progress reporting during model download
|
|
8
|
-
* - Text generation with streaming + KV cache
|
|
9
|
-
* - One-shot inference for classification, detection, ASR, etc.
|
|
10
|
-
* - GPU device-lost recovery
|
|
11
|
-
* - Memory cleanup
|
|
12
|
-
*
|
|
13
|
-
* Communication is via structured `postMessage` using WorkerCommand/WorkerResponse types.
|
|
14
|
-
*/
|
|
15
|
-
import { pipeline as hfPipeline, TextStreamer, DynamicCache, InterruptableStoppingCriteria, } from '@huggingface/transformers';
|
|
16
|
-
import { createOnnxPipeline } from './onnx-pipeline.js';
|
|
17
|
-
const instances = new Map();
|
|
18
|
-
const kvCaches = new Map();
|
|
19
|
-
const stoppingCriteria = new InterruptableStoppingCriteria();
|
|
20
|
-
function modelKey(task, modelId) {
|
|
21
|
-
return `${task}::${modelId}`;
|
|
22
|
-
}
|
|
23
|
-
// ─── Helpers ───
|
|
24
|
-
function send(msg) {
|
|
25
|
-
self.postMessage(msg);
|
|
26
|
-
}
|
|
27
|
-
// ─── Device Check ───
|
|
28
|
-
async function checkDevice() {
|
|
29
|
-
let backend = 'cpu';
|
|
30
|
-
let gpu = null;
|
|
31
|
-
try {
|
|
32
|
-
if ('gpu' in navigator) {
|
|
33
|
-
const adapter = await navigator.gpu.requestAdapter({
|
|
34
|
-
powerPreference: 'high-performance',
|
|
35
|
-
});
|
|
36
|
-
if (adapter) {
|
|
37
|
-
backend = 'webgpu';
|
|
38
|
-
const info = adapter.info;
|
|
39
|
-
const vram = Number(adapter.limits?.maxBufferSize ?? 0);
|
|
40
|
-
const fmtVram = vram >= 1024 ** 3
|
|
41
|
-
? `${(vram / 1024 ** 3).toFixed(1)} GB`
|
|
42
|
-
: `${(vram / 1024 ** 2).toFixed(1)} MB`;
|
|
43
|
-
gpu = {
|
|
44
|
-
vendor: info?.vendor ?? 'unknown',
|
|
45
|
-
architecture: info?.architecture ?? 'unknown',
|
|
46
|
-
description: info?.description ?? 'unknown',
|
|
47
|
-
vram,
|
|
48
|
-
vramFormatted: fmtVram,
|
|
49
|
-
};
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
catch {
|
|
54
|
-
// WebGPU not available
|
|
55
|
-
}
|
|
56
|
-
if (backend === 'cpu' && typeof WebAssembly !== 'undefined') {
|
|
57
|
-
backend = 'wasm';
|
|
58
|
-
}
|
|
59
|
-
// Simple VRAM-based dtype recommendation
|
|
60
|
-
const vram = gpu?.vram ?? 0;
|
|
61
|
-
const recommendedDtype = vram >= 8e9 ? 'fp16' : vram >= 4e9 ? 'q8' : 'q4';
|
|
62
|
-
send({ type: 'device-info', data: { backend, gpu, recommendedDtype } });
|
|
63
|
-
}
|
|
64
|
-
// ─── Load Pipeline ───
|
|
65
|
-
async function loadPipeline(config) {
|
|
66
|
-
const key = modelKey(config.task, config.modelId);
|
|
67
|
-
// Dispose previous KV cache if switching models for same task
|
|
68
|
-
if (kvCaches.has(key)) {
|
|
69
|
-
kvCaches.get(key)?.dispose?.();
|
|
70
|
-
kvCaches.delete(key);
|
|
71
|
-
}
|
|
72
|
-
send({
|
|
73
|
-
type: 'progress',
|
|
74
|
-
data: { status: 'downloading', loaded: 0, total: 0, percent: 0 },
|
|
75
|
-
});
|
|
76
|
-
// Create singleton pipeline
|
|
77
|
-
if (!instances.has(key)) {
|
|
78
|
-
const isExplicitOnnx = config.modelType === 'onnx' ||
|
|
79
|
-
config.modelId.endsWith('.onnx') ||
|
|
80
|
-
config.task === 'raw-onnx' ||
|
|
81
|
-
config.task === 'custom';
|
|
82
|
-
const onProgress = (info) => {
|
|
83
|
-
const loaded = Number(info.loaded ?? 0);
|
|
84
|
-
const total = Number(info.total ?? 1);
|
|
85
|
-
const percent = info.percent != null
|
|
86
|
-
? Number(info.percent)
|
|
87
|
-
: (total > 0 ? Math.round((loaded / total) * 100) : 0);
|
|
88
|
-
send({
|
|
89
|
-
type: 'progress',
|
|
90
|
-
data: {
|
|
91
|
-
status: info.status ?? 'downloading',
|
|
92
|
-
file: String(info.file ?? ''),
|
|
93
|
-
loaded,
|
|
94
|
-
total,
|
|
95
|
-
percent,
|
|
96
|
-
},
|
|
97
|
-
});
|
|
98
|
-
};
|
|
99
|
-
const loadFn = async () => {
|
|
100
|
-
if (isExplicitOnnx) {
|
|
101
|
-
return createOnnxPipeline(config, onProgress);
|
|
102
|
-
}
|
|
103
|
-
if (config.modelType === 'transformers') {
|
|
104
|
-
return (await hfPipeline(config.task, config.modelId, {
|
|
105
|
-
device: config.device ?? 'webgpu',
|
|
106
|
-
dtype: config.dtype ?? 'q4',
|
|
107
|
-
revision: config.revision,
|
|
108
|
-
progress_callback: onProgress,
|
|
109
|
-
}));
|
|
110
|
-
}
|
|
111
|
-
// 'auto' mode: try Hugging Face pipeline first, fallback to ONNX if config is missing
|
|
112
|
-
try {
|
|
113
|
-
return (await hfPipeline(config.task, config.modelId, {
|
|
114
|
-
device: config.device ?? 'webgpu',
|
|
115
|
-
dtype: config.dtype ?? 'q4',
|
|
116
|
-
revision: config.revision,
|
|
117
|
-
progress_callback: onProgress,
|
|
118
|
-
}));
|
|
119
|
-
}
|
|
120
|
-
catch (hfErr) {
|
|
121
|
-
const errMsg = hfErr instanceof Error ? hfErr.message : String(hfErr);
|
|
122
|
-
if (errMsg.includes('config.json') ||
|
|
123
|
-
errMsg.includes('Could not locate file') ||
|
|
124
|
-
errMsg.includes('Unsupported model type')) {
|
|
125
|
-
return createOnnxPipeline(config, onProgress);
|
|
126
|
-
}
|
|
127
|
-
throw hfErr;
|
|
128
|
-
}
|
|
129
|
-
};
|
|
130
|
-
instances.set(key, loadFn());
|
|
131
|
-
}
|
|
132
|
-
try {
|
|
133
|
-
const generator = await instances.get(key);
|
|
134
|
-
// Warm up with dummy inference (compiles WebGPU shaders)
|
|
135
|
-
send({
|
|
136
|
-
type: 'progress',
|
|
137
|
-
data: { status: 'compiling', loaded: 0, total: 0, percent: 100 },
|
|
138
|
-
});
|
|
139
|
-
if (config.task === 'text-generation' && 'tokenizer' in generator) {
|
|
140
|
-
const tokenizer = generator.tokenizer;
|
|
141
|
-
const model = generator.model;
|
|
142
|
-
if (tokenizer && model && typeof tokenizer === 'function') {
|
|
143
|
-
const inputs = tokenizer('a');
|
|
144
|
-
await model.generate({
|
|
145
|
-
...inputs,
|
|
146
|
-
max_new_tokens: 1,
|
|
147
|
-
});
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
send({ type: 'ready', modelKey: key });
|
|
151
|
-
}
|
|
152
|
-
catch (e) {
|
|
153
|
-
instances.delete(key);
|
|
154
|
-
send({
|
|
155
|
-
type: 'error',
|
|
156
|
-
id: 'load',
|
|
157
|
-
data: e instanceof Error ? e.message : String(e),
|
|
158
|
-
});
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
// ─── Run Inference ───
|
|
162
|
-
async function runInference(id, task, input, options) {
|
|
163
|
-
// Find the loaded pipeline for this task
|
|
164
|
-
let pipelineInstance = null;
|
|
165
|
-
for (const [key, promise] of instances) {
|
|
166
|
-
if (key.startsWith(`${task}::`)) {
|
|
167
|
-
pipelineInstance = await promise;
|
|
168
|
-
break;
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
if (!pipelineInstance) {
|
|
172
|
-
send({ type: 'error', id, data: `No pipeline loaded for task: ${task}` });
|
|
173
|
-
return;
|
|
174
|
-
}
|
|
175
|
-
try {
|
|
176
|
-
if (task === 'text-generation') {
|
|
177
|
-
await runTextGeneration(id, pipelineInstance, input, options);
|
|
178
|
-
}
|
|
179
|
-
else {
|
|
180
|
-
// One-shot inference for all other tasks
|
|
181
|
-
const result = await pipelineInstance(input, options);
|
|
182
|
-
send({ type: 'result', id, data: result });
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
catch (e) {
|
|
186
|
-
send({
|
|
187
|
-
type: 'error',
|
|
188
|
-
id,
|
|
189
|
-
data: e instanceof Error ? e.message : String(e),
|
|
190
|
-
});
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
// ─── Text Generation (Streaming) ───
|
|
194
|
-
async function runTextGeneration(id, generator, messages, options) {
|
|
195
|
-
let startTime = null;
|
|
196
|
-
let numTokens = 0;
|
|
197
|
-
let tps = 0;
|
|
198
|
-
let firstTokenTime = 0;
|
|
199
|
-
const genObj = generator;
|
|
200
|
-
if (typeof genObj !== 'function' && !('tokenizer' in genObj)) {
|
|
201
|
-
send({ type: 'error', id, data: 'Invalid text-generation pipeline' });
|
|
202
|
-
return;
|
|
203
|
-
}
|
|
204
|
-
const streamer = new TextStreamer(genObj.tokenizer, {
|
|
205
|
-
skip_prompt: true,
|
|
206
|
-
skip_special_tokens: true,
|
|
207
|
-
callback_function: (output) => {
|
|
208
|
-
const event = {
|
|
209
|
-
token: output,
|
|
210
|
-
tps,
|
|
211
|
-
numTokens,
|
|
212
|
-
timeToFirstToken: firstTokenTime,
|
|
213
|
-
};
|
|
214
|
-
send({ type: 'token', id, data: event });
|
|
215
|
-
},
|
|
216
|
-
token_callback_function: () => {
|
|
217
|
-
const now = performance.now();
|
|
218
|
-
if (startTime === null) {
|
|
219
|
-
startTime = now;
|
|
220
|
-
firstTokenTime = now; // Will be relative to request start
|
|
221
|
-
}
|
|
222
|
-
numTokens++;
|
|
223
|
-
if (numTokens > 1 && startTime !== null) {
|
|
224
|
-
tps = (numTokens / (now - startTime)) * 1000;
|
|
225
|
-
}
|
|
226
|
-
},
|
|
227
|
-
});
|
|
228
|
-
// Get or create KV cache for this task+model
|
|
229
|
-
const key = [...instances.keys()].find(k => k.startsWith('text-generation::'));
|
|
230
|
-
if (key && !kvCaches.has(key)) {
|
|
231
|
-
kvCaches.set(key, new DynamicCache());
|
|
232
|
-
}
|
|
233
|
-
const pastKeyValues = key ? kvCaches.get(key) : undefined;
|
|
234
|
-
stoppingCriteria.reset();
|
|
235
|
-
const genOptions = {
|
|
236
|
-
max_new_tokens: options?.maxNewTokens ?? 1024,
|
|
237
|
-
do_sample: options?.doSample ?? false,
|
|
238
|
-
temperature: options?.temperature,
|
|
239
|
-
top_p: options?.topP,
|
|
240
|
-
top_k: options?.topK,
|
|
241
|
-
repetition_penalty: options?.repetitionPenalty,
|
|
242
|
-
streamer,
|
|
243
|
-
stopping_criteria: stoppingCriteria,
|
|
244
|
-
...(pastKeyValues ? { past_key_values: pastKeyValues } : {}),
|
|
245
|
-
};
|
|
246
|
-
// Remove undefined keys
|
|
247
|
-
for (const k of Object.keys(genOptions)) {
|
|
248
|
-
if (genOptions[k] === undefined)
|
|
249
|
-
delete genOptions[k];
|
|
250
|
-
}
|
|
251
|
-
try {
|
|
252
|
-
const output = await genObj(messages, genOptions);
|
|
253
|
-
// Extract final text
|
|
254
|
-
let finalText = '';
|
|
255
|
-
if (Array.isArray(output) && output[0]?.generated_text) {
|
|
256
|
-
const genText = output[0].generated_text;
|
|
257
|
-
finalText = Array.isArray(genText)
|
|
258
|
-
? genText[genText.length - 1]?.content ?? ''
|
|
259
|
-
: genText;
|
|
260
|
-
}
|
|
261
|
-
send({
|
|
262
|
-
type: 'result',
|
|
263
|
-
id,
|
|
264
|
-
data: {
|
|
265
|
-
text: finalText,
|
|
266
|
-
numTokens,
|
|
267
|
-
tps,
|
|
268
|
-
timeToFirstToken: firstTokenTime,
|
|
269
|
-
totalTime: startTime ? performance.now() - startTime : 0,
|
|
270
|
-
},
|
|
271
|
-
});
|
|
272
|
-
}
|
|
273
|
-
catch (e) {
|
|
274
|
-
send({
|
|
275
|
-
type: 'error',
|
|
276
|
-
id,
|
|
277
|
-
data: e instanceof Error ? e.message : String(e),
|
|
278
|
-
});
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
// ─── Cleanup ───
|
|
282
|
-
function disposeModel(targetKey) {
|
|
283
|
-
if (targetKey) {
|
|
284
|
-
const p = instances.get(targetKey);
|
|
285
|
-
p?.then(inst => inst?.dispose?.());
|
|
286
|
-
instances.delete(targetKey);
|
|
287
|
-
if (kvCaches.has(targetKey)) {
|
|
288
|
-
kvCaches.get(targetKey)?.dispose?.();
|
|
289
|
-
kvCaches.delete(targetKey);
|
|
290
|
-
}
|
|
291
|
-
}
|
|
292
|
-
else {
|
|
293
|
-
for (const p of instances.values()) {
|
|
294
|
-
p.then(inst => inst?.dispose?.());
|
|
295
|
-
}
|
|
296
|
-
instances.clear();
|
|
297
|
-
for (const cache of kvCaches.values()) {
|
|
298
|
-
cache?.dispose?.();
|
|
299
|
-
}
|
|
300
|
-
kvCaches.clear();
|
|
301
|
-
}
|
|
302
|
-
}
|
|
303
|
-
function resetKVCache() {
|
|
304
|
-
stoppingCriteria.reset();
|
|
305
|
-
for (const cache of kvCaches.values()) {
|
|
306
|
-
cache?.dispose?.();
|
|
307
|
-
}
|
|
308
|
-
kvCaches.clear();
|
|
309
|
-
}
|
|
310
|
-
// ─── Message Handler ───
|
|
311
|
-
self.addEventListener('message', async (e) => {
|
|
312
|
-
const cmd = e.data;
|
|
313
|
-
switch (cmd.type) {
|
|
314
|
-
case 'check':
|
|
315
|
-
await checkDevice();
|
|
316
|
-
break;
|
|
317
|
-
case 'load':
|
|
318
|
-
await loadPipeline(cmd.config);
|
|
319
|
-
break;
|
|
320
|
-
case 'run':
|
|
321
|
-
await runInference(cmd.id, cmd.task, cmd.input, cmd.options);
|
|
322
|
-
break;
|
|
323
|
-
case 'interrupt':
|
|
324
|
-
stoppingCriteria.interrupt();
|
|
325
|
-
break;
|
|
326
|
-
case 'reset':
|
|
327
|
-
resetKVCache();
|
|
328
|
-
break;
|
|
329
|
-
case 'dispose':
|
|
330
|
-
disposeModel(cmd.modelKey);
|
|
331
|
-
break;
|
|
332
|
-
default:
|
|
333
|
-
send({
|
|
334
|
-
type: 'error',
|
|
335
|
-
id: 'unknown',
|
|
336
|
-
data: `Unknown command: ${cmd.type}`,
|
|
337
|
-
});
|
|
338
|
-
}
|
|
339
|
-
});
|
|
340
|
-
//# sourceMappingURL=model-worker.js.map
|