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.
Files changed (62) hide show
  1. package/package.json +3 -3
  2. package/dist/cache.d.ts +0 -46
  3. package/dist/cache.d.ts.map +0 -1
  4. package/dist/cache.js +0 -161
  5. package/dist/cache.js.map +0 -1
  6. package/dist/decision.d.ts +0 -182
  7. package/dist/decision.d.ts.map +0 -1
  8. package/dist/decision.js +0 -789
  9. package/dist/decision.js.map +0 -1
  10. package/dist/device.d.ts +0 -86
  11. package/dist/device.d.ts.map +0 -1
  12. package/dist/device.js +0 -195
  13. package/dist/device.js.map +0 -1
  14. package/dist/gpu-recovery.d.ts +0 -61
  15. package/dist/gpu-recovery.d.ts.map +0 -1
  16. package/dist/gpu-recovery.js +0 -105
  17. package/dist/gpu-recovery.js.map +0 -1
  18. package/dist/hub.d.ts +0 -97
  19. package/dist/hub.d.ts.map +0 -1
  20. package/dist/hub.js +0 -142
  21. package/dist/hub.js.map +0 -1
  22. package/dist/index.d.ts +0 -63
  23. package/dist/index.d.ts.map +0 -1
  24. package/dist/index.js +0 -65
  25. package/dist/index.js.map +0 -1
  26. package/dist/inputs.d.ts +0 -27
  27. package/dist/inputs.d.ts.map +0 -1
  28. package/dist/inputs.js +0 -155
  29. package/dist/inputs.js.map +0 -1
  30. package/dist/loader.d.ts +0 -100
  31. package/dist/loader.d.ts.map +0 -1
  32. package/dist/loader.js +0 -246
  33. package/dist/loader.js.map +0 -1
  34. package/dist/model-client.d.ts +0 -149
  35. package/dist/model-client.d.ts.map +0 -1
  36. package/dist/model-client.js +0 -318
  37. package/dist/model-client.js.map +0 -1
  38. package/dist/model-worker.d.ts +0 -16
  39. package/dist/model-worker.d.ts.map +0 -1
  40. package/dist/model-worker.js +0 -340
  41. package/dist/model-worker.js.map +0 -1
  42. package/dist/onnx-pipeline.d.ts +0 -20
  43. package/dist/onnx-pipeline.d.ts.map +0 -1
  44. package/dist/onnx-pipeline.js +0 -462
  45. package/dist/onnx-pipeline.js.map +0 -1
  46. package/dist/pipelines/index.d.ts +0 -28
  47. package/dist/pipelines/index.d.ts.map +0 -1
  48. package/dist/pipelines/index.js +0 -140
  49. package/dist/pipelines/index.js.map +0 -1
  50. package/dist/shim-ort.d.ts +0 -6
  51. package/dist/shim-ort.d.ts.map +0 -1
  52. package/dist/shim-ort.js +0 -38
  53. package/dist/shim-ort.js.map +0 -1
  54. package/dist/streaming.d.ts +0 -55
  55. package/dist/streaming.d.ts.map +0 -1
  56. package/dist/streaming.js +0 -142
  57. package/dist/streaming.js.map +0 -1
  58. package/dist/types.d.ts +0 -200
  59. package/dist/types.d.ts.map +0 -1
  60. package/dist/types.js +0 -3
  61. package/dist/types.js.map +0 -1
  62. package/dist/webml-kit.browser.js +0 -2568
@@ -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
@@ -1 +0,0 @@
1
- {"version":3,"file":"model-client.js","sourceRoot":"","sources":["../src/model-client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAeH,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAC5D,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAiBhD,iBAAiB;AAEjB,MAAM,OAAO,WAAW;IACd,MAAM,GAAkB,IAAI,CAAC;IAC7B,SAAS,CAAsB;IAC/B,SAAS,GAAG,IAAI,GAAG,EAAkC,CAAC;IACtD,eAAe,GAAG,IAAI,GAAG,EAO9B,CAAC;IACI,cAAc,GAAG,CAAC,CAAC;IACnB,UAAU,GAAsB,IAAI,CAAC;IACrC,YAAY,GAAG,IAAI,GAAG,EAAU,CAAC;IACjC,gBAAgB,GAA4B,IAAI,CAAC;IACjD,WAAW,CAAc;IAEjC;;;;;OAKG;IACH,YAAY,SAAwB;QAClC,IAAI,CAAC,SAAS,GAAG,SAAS,IAAI,IAAI,CAAC;QACnC,IAAI,CAAC,WAAW,GAAG,IAAI,WAAW,EAAE,CAAC;QAErC,IAAI,CAAC,WAAW,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE;YACzC,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC;QACvC,CAAC,CAAC,CAAC;QACH,IAAI,CAAC,WAAW,CAAC,EAAE,CAAC,WAAW,EAAE,GAAG,EAAE;YACpC,IAAI,CAAC,IAAI,CAAC,kBAAkB,EAAE,EAAE,CAAC,CAAC;QACpC,CAAC,CAAC,CAAC;IACL,CAAC;IAED,2BAA2B;IAEnB,SAAS;QACf,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC;YACjB,MAAM,GAAG,GAAG,IAAI,CAAC,SAAS;mBACrB,IAAI,GAAG,CAAC,mBAAmB,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YAEnD,IAAI,CAAC,MAAM,GAAG,IAAI,MAAM,CAAC,GAAG,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,CAAC;YAElD,IAAI,CAAC,MAAM,CAAC,gBAAgB,CAAC,SAAS,EAAE,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;YACvE,IAAI,CAAC,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,CAAC,CAAC,EAAE,EAAE;gBAC1C,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC;YAC7C,CAAC,CAAC,CAAC;QACL,CAAC;QACD,OAAO,IAAI,CAAC,MAAM,CAAC;IACrB,CAAC;IAEO,IAAI,CAAC,GAAkB;QAC7B,IAAI,CAAC,SAAS,EAAE,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;IACpC,CAAC;IAEO,MAAM;QACZ,OAAO,OAAO,EAAE,IAAI,CAAC,cAAc,IAAI,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC;IACtD,CAAC;IAED,0BAA0B;IAElB,aAAa,CAAC,CAA+B;QACnD,MAAM,GAAG,GAAG,CAAC,CAAC,IAAI,CAAC;QAEnB,QAAQ,GAAG,CAAC,IAAI,EAAE,CAAC;YACjB,KAAK,aAAa,CAAC,CAAC,CAAC;gBACnB,IAAI,CAAC,UAAU,GAAG,GAAG,CAAC,IAAI,CAAC;gBAC3B,MAAM,OAAO,GAAG,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;gBACnD,IAAI,OAAO,EAAE,CAAC;oBACZ,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;oBAC1B,IAAI,CAAC,eAAe,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;gBACxC,CAAC;gBACD,MAAM;YACR,CAAC;YAED,KAAK,UAAU,CAAC,CAAC,CAAC;gBAChB,IAAI,CAAC,gBAAgB,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;gBAClC,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,GAAG,CAAC,IAAI,CAAC,CAAC;gBAChC,MAAM;YACR,CAAC;YAED,KAAK,OAAO,CAAC,CAAC,CAAC;gBACb,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;gBACpC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,QAAQ,EAAE,GAAG,CAAC,QAAQ,EAAE,CAAC,CAAC;gBAC/C,MAAM,OAAO,GAAG,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;gBACjD,IAAI,OAAO,EAAE,CAAC;oBACZ,OAAO,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;oBAC3B,IAAI,CAAC,eAAe,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;gBACtC,CAAC;gBACD,MAAM;YACR,CAAC;YAED,KAAK,OAAO,CAAC,CAAC,CAAC;gBACb,MAAM,OAAO,GAAG,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;gBACjD,IAAI,OAAO,EAAE,MAAM,EAAE,CAAC;oBACpB,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;gBAChC,CAAC;gBACD,MAAM;YACR,CAAC;YAED,KAAK,QAAQ,CAAC,CAAC,CAAC;gBACd,MAAM,OAAO,GAAG,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;gBACjD,IAAI,OAAO,EAAE,CAAC;oBACZ,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;wBACnB,OAAO,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC;oBACvB,CAAC;oBACD,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;oBAC1B,IAAI,CAAC,eAAe,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;gBACtC,CAAC;gBACD,MAAM;YACR,CAAC;YAED,KAAK,OAAO,CAAC,CAAC,CAAC;gBACb,MAAM,OAAO,GAAG,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;gBACjD,IAAI,OAAO,EAAE,CAAC;oBACZ,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;wBACnB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC;oBAC5C,CAAC;oBACD,OAAO,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC;oBACpC,IAAI,CAAC,eAAe,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;gBACtC,CAAC;gBACD,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,OAAO,EAAE,GAAG,CAAC,IAAI,EAAE,EAAE,EAAE,GAAG,CAAC,EAAE,EAAE,CAAC,CAAC;gBACtD,MAAM;YACR,CAAC;YAED,KAAK,aAAa,CAAC,CAAC,CAAC;gBACnB,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC;gBACjD,MAAM;YACR,CAAC;YAED,KAAK,kBAAkB,CAAC,CAAC,CAAC;gBACxB,IAAI,CAAC,IAAI,CAAC,kBAAkB,EAAE,EAAE,CAAC,CAAC;gBAClC,MAAM;YACR,CAAC;QACH,CAAC;IACH,CAAC;IAED,iBAAiB;IAEjB,kCAAkC;IAClC,EAAE,CAAC,KAAsB,EAAE,QAAkB;QAC3C,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;YAC/B,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,EAAE,IAAI,GAAG,EAAE,CAAC,CAAC;QACvC,CAAC;QACD,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,CAAE,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QACzC,OAAO,IAAI,CAAC;IACd,CAAC;IAED,gCAAgC;IAChC,GAAG,CAAC,KAAsB,EAAE,QAAkB;QAC5C,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC;QAC5C,OAAO,IAAI,CAAC;IACd,CAAC;IAEO,IAAI,CAAC,KAAsB,EAAE,IAAa;QAChD,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC;IACrD,CAAC;IAED,qBAAqB;IAErB;;;;;;;;;OASG;IACH,KAAK,CAAC,MAAM;QACV,IAAI,IAAI,CAAC,UAAU;YAAE,OAAO,IAAI,CAAC,UAAU,CAAC;QAE5C,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YACrC,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,QAAQ,EAAE,EAAE,OAAO,EAAE,OAA+B,EAAE,MAAM,EAAE,CAAC,CAAC;YACzF,IAAI,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC;QAC/B,CAAC,CAAC,CAAC;IACL,CAAC;IAED;;;;;;;;;;;OAWG;IACH,KAAK,CAAC,IAAI,CAAC,OAAoB;QAC7B,IAAI,CAAC,gBAAgB,GAAG,OAAO,CAAC,UAAU,IAAI,IAAI,CAAC;QAEnD,MAAM,MAAM,GAAgB;YAC1B,IAAI,EAAE,OAAO,CAAC,IAAI;YAClB,OAAO,EAAE,OAAO,CAAC,OAAO;YACxB,KAAK,EAAE,OAAO,CAAC,KAAK;YACpB,MAAM,EAAE,OAAO,CAAC,MAAM;YACtB,QAAQ,EAAE,OAAO,CAAC,QAAQ;SAC3B,CAAC;QAEF,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YACrC,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,MAAM,EAAE,EAAE,OAAO,EAAE,OAA+B,EAAE,MAAM,EAAE,CAAC,CAAC;YACvF,IAAI,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;QACtC,CAAC,CAAC,CAAC;IACL,CAAC;IAED;;;;;;;;;;;;;OAaG;IACH,KAAK,CAAC,GAAG,CACP,IAAkB,EAClB,KAAc,EACd,OAAiC;QAEjC,MAAM,EAAE,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC;QAEzB,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YACrC,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,EAAE,EAAE,EAAE,OAAO,EAAE,OAA+B,EAAE,MAAM,EAAE,CAAC,CAAC;YACnF,IAAI,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,CAAC;QACvD,CAAC,CAAC,CAAC;IACL,CAAC;IAED;;;;;;;;;;OAUG;IACH,MAAM,CACJ,KAA6B,EAC7B,OAA+B;QAE/B,MAAM,EAAE,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC;QACzB,MAAM,MAAM,GAAG,IAAI,WAAW,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;QAEhD,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,EAAE,EAAE;YAC3B,OAAO,EAAE,GAAG,EAAE,GAAE,CAAC,EAAE,kCAAkC;YACrD,MAAM,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC;YAClC,MAAM;SACP,CAAC,CAAC;QAEH,IAAI,CAAC,IAAI,CAAC;YACR,IAAI,EAAE,KAAK;YACX,EAAE;YACF,IAAI,EAAE,iBAAiB;YACvB,KAAK;YACL,OAAO,EAAE,OAA8C;SACxD,CAAC,CAAC;QAEH,OAAO,MAAM,CAAC;IAChB,CAAC;IAED;;;;;;OAMG;IACH,KAAK,CAAC,QAAQ,CACZ,KAA6B,EAC7B,OAA+B;QAE/B,MAAM,WAAW,GAAG,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;QAChD,MAAM,SAAS,GAAG,MAAM,aAAa,CAAC,WAAW,CAAC,CAAC;QACnD,OAAO;YACL,GAAG,SAAS;YACZ,SAAS,EAAE,CAAC,EAAE,oCAAoC;SACnD,CAAC;IACJ,CAAC;IAED;;OAEG;IACH,SAAS;QACP,IAAI,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,CAAC,CAAC;IACnC,CAAC;IAED;;OAEG;IACH,KAAK;QACH,IAAI,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC;IAC/B,CAAC;IAED;;;;OAIG;IACH,OAAO,CAAC,QAAiB;QACvB,IAAI,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,CAAC,CAAC;QACzC,IAAI,QAAQ,EAAE,CAAC;YACb,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QACrC,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,YAAY,CAAC,KAAK,EAAE,CAAC;QAC5B,CAAC;IACH,CAAC;IAED;;OAEG;IACH,QAAQ,CAAC,IAAkB,EAAE,OAAe;QAC1C,OAAO,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,GAAG,IAAI,KAAK,OAAO,EAAE,CAAC,CAAC;IACtD,CAAC;IAED;;OAEG;IACH,SAAS;QACP,IAAI,CAAC,MAAM,EAAE,SAAS,EAAE,CAAC;QACzB,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC;QACnB,IAAI,CAAC,YAAY,CAAC,KAAK,EAAE,CAAC;QAC1B,IAAI,CAAC,eAAe,CAAC,KAAK,EAAE,CAAC;QAC7B,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC;IACzB,CAAC;CACF"}
@@ -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"}
@@ -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