webml-kit 0.1.0

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/README.md ADDED
@@ -0,0 +1,205 @@
1
+ # webml-kit
2
+
3
+ > Framework-agnostic utilities for loading and running ML models in the browser via WebGPU/WASM.
4
+
5
+ If you've ever built a browser-ML demo, you know the drill: copy 150 lines of Web Worker boilerplate from the last project, wire up `postMessage`, add progress reporting, handle the GPU vanishing mid-inference, and pray the model is cached so your user doesn't wait 3 minutes. Every. Single. Time.
6
+
7
+ This library does that part for you. It wraps [`@huggingface/transformers`](https://huggingface.co/docs/transformers.js) with a sane API and handles the ugly bits: device detection, model caching, token streaming, KV-cache management, and GPU recovery.
8
+
9
+ ## Install
10
+
11
+ ```bash
12
+ npm install webml-kit @huggingface/transformers
13
+ ```
14
+
15
+ ## Quick start
16
+
17
+ ```ts
18
+ import { ModelClient } from 'webml-kit';
19
+
20
+ // Point to the worker file
21
+ const client = new ModelClient(
22
+ new URL('webml-kit/worker', import.meta.url)
23
+ );
24
+
25
+ // What can this machine do?
26
+ const device = await client.detect();
27
+ console.log(device.backend); // 'webgpu' or 'wasm' or 'cpu'
28
+ console.log(device.gpu?.vendor); // 'apple'
29
+ console.log(device.recommendedDtype); // 'q4'
30
+
31
+ // Load a model
32
+ await client.load({
33
+ task: 'text-generation',
34
+ modelId: 'onnx-community/Bonsai-1.7B-ONNX',
35
+ dtype: 'q4',
36
+ onProgress: ({ percent }) => console.log(`Loading: ${percent}%`),
37
+ });
38
+
39
+ // Stream tokens as they're generated
40
+ for await (const { token, tps } of client.stream('Tell me a joke')) {
41
+ process.stdout.write(token);
42
+ }
43
+ ```
44
+
45
+ ## What's in here
46
+
47
+ ### Device detection
48
+
49
+ Figures out what your user's machine can handle and picks a reasonable quantization level:
50
+
51
+ ```ts
52
+ import { detectDevice, canRun } from 'webml-kit';
53
+
54
+ const info = await detectDevice();
55
+ // { backend: 'webgpu', gpu: { vendor: 'apple', vram: 8589934592, vramFormatted: '8.0 GB' }, recommendedDtype: 'fp16' }
56
+
57
+ const { ok, reason } = await canRun('4GB'); // human-readable
58
+ const same = await canRun(4_000_000_000); // raw bytes — same result
59
+ ```
60
+
61
+ ### Cache visibility
62
+
63
+ The worst UX in browser ML is showing "downloading 2GB..." to someone who already has the model. Now you can check:
64
+
65
+ ```ts
66
+ import { isCached, listCachedModels, getCacheSize, clearCache } from 'webml-kit';
67
+
68
+ if (await isCached('onnx-community/Bonsai-1.7B-ONNX')) {
69
+ // Skip the progress bar entirely
70
+ }
71
+
72
+ const models = await listCachedModels();
73
+ // [{ modelId: 'onnx-community/Bonsai-1.7B-ONNX', size: '412.0 MB', sizeBytes: 432013312 }]
74
+
75
+ await clearCache('onnx-community/Bonsai-1.7B-ONNX'); // Free storage on mobile
76
+ ```
77
+
78
+ ### Token streaming
79
+
80
+ A proper `AsyncIterable` instead of raw `postMessage` callbacks. Tracks tokens-per-second and time-to-first-token:
81
+
82
+ ```ts
83
+ for await (const event of client.stream('Hello!')) {
84
+ console.log(event);
85
+ // { token: 'World', tps: 38.5, numTokens: 12, timeToFirstToken: 145 }
86
+ }
87
+
88
+ // Or grab everything at once:
89
+ const { text, tps, numTokens } = await client.generate('Hello!');
90
+ ```
91
+
92
+ ### GPU recovery
93
+
94
+ GPUs disappear. It happens: TDR resets, VRAM pressure, mobile browsers reclaiming resources. Without handling this, the user has to reload the page. This recovers automatically with backoff:
95
+
96
+ ```ts
97
+ import { GPURecovery } from 'webml-kit';
98
+
99
+ const recovery = new GPURecovery({ maxRetries: 3, baseDelayMs: 1000 });
100
+ recovery.on('lost', ({ reason }) => showBanner('GPU lost: ' + reason));
101
+ recovery.on('recovered', ({ adapter }) => console.log('Back online'));
102
+ recovery.on('failed', () => showFallbackMessage());
103
+ ```
104
+
105
+ ### All pipeline tasks
106
+
107
+ Not just text generation. Every task `@huggingface/transformers` supports works through the same API:
108
+
109
+ ```ts
110
+ // Classify an image
111
+ const labels = await client.run('image-classification', imageUrl);
112
+ // [{ label: 'tabby cat', score: 0.98 }]
113
+
114
+ // Transcribe audio
115
+ const { text } = await client.run('automatic-speech-recognition', audioBlob);
116
+
117
+ // Get embeddings
118
+ const vectors = await client.run('feature-extraction', 'Hello world');
119
+
120
+ // Detect objects
121
+ const objects = await client.run('object-detection', imageBlob);
122
+
123
+ // Translate, summarize, caption images, answer questions,
124
+ // classify text, extract entities, estimate depth, segment images
125
+ ```
126
+
127
+ ## API
128
+
129
+ ### ModelClient
130
+
131
+ | Method | What it does |
132
+ |---|---|
133
+ | `detect()` | Returns device capabilities and recommended dtype |
134
+ | `load(options)` | Downloads and initializes a model pipeline |
135
+ | `stream(input, options?)` | Returns an async iterator of tokens |
136
+ | `generate(input, options?)` | Generates text, waits for completion |
137
+ | `run(task, input, options?)` | Runs any pipeline task |
138
+ | `interrupt()` | Stops an in-progress generation |
139
+ | `reset()` | Clears the KV cache (new conversation) |
140
+ | `dispose(modelKey?)` | Frees model memory |
141
+ | `isLoaded(task, modelId)` | Checks if a specific model is ready |
142
+ | `terminate()` | Kills the worker entirely |
143
+ | `on(event, listener)` | Listens for progress, ready, error, device-lost, device-recovered |
144
+
145
+ ### Standalone functions
146
+
147
+ These work without a ModelClient — useful for pre-flight checks:
148
+
149
+ | Function | What it does |
150
+ |---|---|
151
+ | `detectDevice()` | Backend detection + GPU info + dtype recommendation |
152
+ | `checkWebGPU()` | Boolean: is WebGPU available? |
153
+ | `canRun(bytes)` | Can a model of this size fit in VRAM? |
154
+ | `isCached(modelId)` | Is this model already downloaded? |
155
+ | `listCachedModels()` | What's in the cache? |
156
+ | `clearCache(modelId?)` | Delete cached model files |
157
+ | `getCacheSize()` | Total bytes used by cached models |
158
+ | `parseSize(input)` | Convert '4GB' / '512MB' to bytes |
159
+ | `formatSize(bytes)` | Convert bytes to '4.0 GB' / '512.0 MB' |
160
+
161
+ ## Supported tasks
162
+
163
+ | Task | Streaming | Default model |
164
+ |---|---|---|
165
+ | `text-generation` | yes | `onnx-community/Llama-3.2-1B-Instruct-ONNX` |
166
+ | `text-classification` | no | `Xenova/distilbert-base-uncased-finetuned-sst-2-english` |
167
+ | `image-classification` | no | `Xenova/vit-base-patch16-224` |
168
+ | `object-detection` | no | `Xenova/detr-resnet-50` |
169
+ | `automatic-speech-recognition` | no | `onnx-community/whisper-tiny.en` |
170
+ | `text-to-speech` | no | `Xenova/speecht5_tts` |
171
+ | `translation` | no | `Xenova/nllb-200-distilled-600M` |
172
+ | `summarization` | no | `Xenova/distilbart-cnn-6-6` |
173
+ | `feature-extraction` | no | `Xenova/all-MiniLM-L6-v2` |
174
+ | `image-to-text` | no | `Xenova/vit-gpt2-image-captioning` |
175
+ | `zero-shot-classification` | no | `Xenova/mobilebert-uncased-mnli` |
176
+ | `fill-mask` | no | `Xenova/bert-base-uncased` |
177
+ | `question-answering` | no | `Xenova/distilbert-base-uncased-distilled-squad` |
178
+ | `token-classification` | no | `Xenova/bert-base-NER` |
179
+ | `depth-estimation` | no | `Xenova/depth-anything-small-hf` |
180
+ | `image-segmentation` | no | `Xenova/detr-resnet-50-panoptic` |
181
+
182
+ ## How it works
183
+
184
+ ```
185
+ Your App (main thread) Web Worker
186
+ -------------------------- ---------------------------
187
+ ModelClient model-worker.ts
188
+ .load() --- postMessage --> pipeline() from @hf/transformers
189
+ .stream() <-- tokens -------- TextStreamer + KV cache
190
+ .run() <-- result -------- One-shot inference
191
+ .interrupt() -- signal ------> InterruptableStoppingCriteria
192
+
193
+ TokenStream (AsyncIterable) Singleton pipeline cache
194
+ GPURecovery (auto-reconnect) WebGPU device management
195
+ ```
196
+
197
+ ## Requirements
198
+
199
+ - Chrome 113+, Edge 113+, or Safari 18+ (falls back to WASM on older browsers)
200
+ - Node.js 18+ (WASM only, no WebGPU)
201
+ - `@huggingface/transformers` >= 4.0.0 as a peer dependency
202
+
203
+ ## License
204
+
205
+ MIT — [Hemanth HM](https://h3manth.com)
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Model cache visibility utilities.
3
+ *
4
+ * Transformers.js uses the Cache API internally to persist downloaded model
5
+ * files. This module gives users visibility into what's cached, letting them
6
+ * skip "downloading…" UX for returning users or clear storage on mobile.
7
+ */
8
+ import type { CachedModel, CacheBackend } from './types.js';
9
+ /**
10
+ * Detect which cache backend is available.
11
+ * Priority: Cache API → OPFS → IndexedDB
12
+ */
13
+ export declare function getCacheBackend(): Promise<CacheBackend>;
14
+ /**
15
+ * Check if a specific model is already cached locally.
16
+ *
17
+ * ```ts
18
+ * if (await isCached('onnx-community/Bonsai-1.7B-ONNX')) {
19
+ * // Skip "downloading..." UI
20
+ * }
21
+ * ```
22
+ */
23
+ export declare function isCached(modelId: string): Promise<boolean>;
24
+ /**
25
+ * Get total cache size in bytes used by downloaded models.
26
+ */
27
+ export declare function getCacheSize(): Promise<number>;
28
+ /**
29
+ * List all cached models with metadata.
30
+ */
31
+ export declare function listCachedModels(): Promise<CachedModel[]>;
32
+ /**
33
+ * Clear cached model files.
34
+ *
35
+ * @param modelId - Specific model to clear, or omit to clear all.
36
+ *
37
+ * ```ts
38
+ * // Clear a specific model
39
+ * await clearCache('onnx-community/Bonsai-1.7B-ONNX');
40
+ *
41
+ * // Clear all cached models
42
+ * await clearCache();
43
+ * ```
44
+ */
45
+ export declare function clearCache(modelId?: string): Promise<void>;
46
+ //# sourceMappingURL=cache.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../src/cache.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAM5D;;;GAGG;AACH,wBAAsB,eAAe,IAAI,OAAO,CAAC,YAAY,CAAC,CAmB7D;AAED;;;;;;;;GAQG;AACH,wBAAsB,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAoBhE;AAED;;GAEG;AACH,wBAAsB,YAAY,IAAI,OAAO,CAAC,MAAM,CAAC,CASpD;AAED;;GAEG;AACH,wBAAsB,gBAAgB,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC,CA4C/D;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,UAAU,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CA4BhE"}
package/dist/cache.js ADDED
@@ -0,0 +1,160 @@
1
+ /**
2
+ * Model cache visibility utilities.
3
+ *
4
+ * Transformers.js uses the Cache API internally to persist downloaded model
5
+ * files. This module gives users visibility into what's cached, letting them
6
+ * skip "downloading…" UX for returning users or clear storage on mobile.
7
+ */
8
+ import { formatSize } from './device.js';
9
+ /** HuggingFace Transformers.js cache name prefix */
10
+ const HF_CACHE_PREFIX = 'transformers-cache';
11
+ /**
12
+ * Detect which cache backend is available.
13
+ * Priority: Cache API → OPFS → IndexedDB
14
+ */
15
+ export async function getCacheBackend() {
16
+ if (typeof caches !== 'undefined') {
17
+ return 'cache-api';
18
+ }
19
+ if (typeof navigator !== 'undefined' && 'storage' in navigator) {
20
+ try {
21
+ const root = await navigator.storage.getDirectory();
22
+ if (root)
23
+ return 'opfs';
24
+ }
25
+ catch {
26
+ // OPFS not available
27
+ }
28
+ }
29
+ if (typeof indexedDB !== 'undefined') {
30
+ return 'indexeddb';
31
+ }
32
+ return 'cache-api'; // fallback, will fail gracefully
33
+ }
34
+ /**
35
+ * Check if a specific model is already cached locally.
36
+ *
37
+ * ```ts
38
+ * if (await isCached('onnx-community/Bonsai-1.7B-ONNX')) {
39
+ * // Skip "downloading..." UI
40
+ * }
41
+ * ```
42
+ */
43
+ export async function isCached(modelId) {
44
+ if (typeof caches === 'undefined')
45
+ return false;
46
+ try {
47
+ const keys = await caches.keys();
48
+ const hfCaches = keys.filter(k => k.startsWith(HF_CACHE_PREFIX));
49
+ for (const cacheName of hfCaches) {
50
+ const cache = await caches.open(cacheName);
51
+ const cacheKeys = await cache.keys();
52
+ const hasModel = cacheKeys.some(req => req.url.includes(encodeURIComponent(modelId)) || req.url.includes(modelId));
53
+ if (hasModel)
54
+ return true;
55
+ }
56
+ }
57
+ catch {
58
+ // Cache API not available or permission denied
59
+ }
60
+ return false;
61
+ }
62
+ /**
63
+ * Get total cache size in bytes used by downloaded models.
64
+ */
65
+ export async function getCacheSize() {
66
+ if (typeof navigator === 'undefined' || !('storage' in navigator))
67
+ return 0;
68
+ try {
69
+ const estimate = await navigator.storage.estimate();
70
+ return estimate.usage ?? 0;
71
+ }
72
+ catch {
73
+ return 0;
74
+ }
75
+ }
76
+ /**
77
+ * List all cached models with metadata.
78
+ */
79
+ export async function listCachedModels() {
80
+ if (typeof caches === 'undefined')
81
+ return [];
82
+ const models = [];
83
+ try {
84
+ const keys = await caches.keys();
85
+ const hfCaches = keys.filter(k => k.startsWith(HF_CACHE_PREFIX));
86
+ for (const cacheName of hfCaches) {
87
+ const cache = await caches.open(cacheName);
88
+ const cacheKeys = await cache.keys();
89
+ // Group by model ID (extract from URL pattern)
90
+ const modelUrls = new Map();
91
+ for (const request of cacheKeys) {
92
+ const url = request.url;
93
+ // HF URLs: https://huggingface.co/{org}/{model}/resolve/{rev}/{file}
94
+ const match = url.match(/huggingface\.co\/([^/]+\/[^/]+)\//);
95
+ if (match) {
96
+ const id = match[1];
97
+ const response = await cache.match(request);
98
+ const size = response
99
+ ? Number(response.headers.get('content-length') ?? 0)
100
+ : 0;
101
+ modelUrls.set(id, (modelUrls.get(id) ?? 0) + size);
102
+ }
103
+ }
104
+ for (const [modelId, sizeBytes] of modelUrls) {
105
+ models.push({
106
+ modelId,
107
+ sizeBytes,
108
+ size: formatSize(sizeBytes),
109
+ lastAccessed: new Date(), // Cache API doesn't track this
110
+ });
111
+ }
112
+ }
113
+ }
114
+ catch {
115
+ // Cache API not available
116
+ }
117
+ return models;
118
+ }
119
+ /**
120
+ * Clear cached model files.
121
+ *
122
+ * @param modelId - Specific model to clear, or omit to clear all.
123
+ *
124
+ * ```ts
125
+ * // Clear a specific model
126
+ * await clearCache('onnx-community/Bonsai-1.7B-ONNX');
127
+ *
128
+ * // Clear all cached models
129
+ * await clearCache();
130
+ * ```
131
+ */
132
+ export async function clearCache(modelId) {
133
+ if (typeof caches === 'undefined')
134
+ return;
135
+ try {
136
+ const keys = await caches.keys();
137
+ const hfCaches = keys.filter(k => k.startsWith(HF_CACHE_PREFIX));
138
+ for (const cacheName of hfCaches) {
139
+ if (!modelId) {
140
+ // Clear entire cache
141
+ await caches.delete(cacheName);
142
+ }
143
+ else {
144
+ // Clear specific model
145
+ const cache = await caches.open(cacheName);
146
+ const cacheKeys = await cache.keys();
147
+ for (const request of cacheKeys) {
148
+ if (request.url.includes(encodeURIComponent(modelId)) ||
149
+ request.url.includes(modelId)) {
150
+ await cache.delete(request);
151
+ }
152
+ }
153
+ }
154
+ }
155
+ }
156
+ catch {
157
+ // Cache API not available
158
+ }
159
+ }
160
+ //# sourceMappingURL=cache.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cache.js","sourceRoot":"","sources":["../src/cache.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,86 @@
1
+ /**
2
+ * WebGPU / WASM device detection and capability probing.
3
+ *
4
+ * Detects the best available backend, extracts GPU adapter info,
5
+ * and recommends a quantization level based on estimated VRAM.
6
+ */
7
+ import type { DeviceBackend, DeviceInfo, GPUInfo, QuantizationType } from './types.js';
8
+ /**
9
+ * Check if WebGPU is available and request an adapter.
10
+ * Returns `null` if WebGPU is not supported.
11
+ */
12
+ export declare function getGPUAdapter(): Promise<GPUAdapter | null>;
13
+ /**
14
+ * Extract GPU info from an adapter.
15
+ */
16
+ export declare function getGPUInfo(adapter: GPUAdapter): Promise<GPUInfo>;
17
+ /**
18
+ * Recommend a quantization dtype based on available VRAM.
19
+ */
20
+ export declare function recommendDtype(vram: number): QuantizationType;
21
+ /**
22
+ * Check if WebGPU is available.
23
+ */
24
+ export declare function checkWebGPU(): Promise<boolean>;
25
+ /**
26
+ * Check if WebAssembly is available.
27
+ */
28
+ export declare function checkWASM(): boolean;
29
+ /**
30
+ * Detect the best available compute backend and return device info.
31
+ *
32
+ * Priority: WebGPU → WASM → CPU
33
+ *
34
+ * ```ts
35
+ * const info = await detectDevice();
36
+ * console.log(info.backend); // 'webgpu'
37
+ * console.log(info.recommendedDtype); // 'q4'
38
+ * console.log(info.gpu?.vendor); // 'apple'
39
+ * ```
40
+ */
41
+ export declare function detectDevice(): Promise<DeviceInfo>;
42
+ /**
43
+ * Parse a human-readable size string into bytes.
44
+ *
45
+ * Accepts formats like '4GB', '512 MB', '1.5gb', '256mb'.
46
+ * Also accepts raw numbers (passed through as-is).
47
+ *
48
+ * ```ts
49
+ * parseSize('4GB') // 4294967296
50
+ * parseSize('512MB') // 536870912
51
+ * parseSize('1.5gb') // 1610612736
52
+ * parseSize(1024) // 1024
53
+ * ```
54
+ */
55
+ export declare function parseSize(input: string | number): number;
56
+ /**
57
+ * Format bytes into a human-readable string.
58
+ *
59
+ * ```ts
60
+ * formatSize(4294967296) // '4 GB'
61
+ * formatSize(536870912) // '512 MB'
62
+ * formatSize(1536) // '1.5 KB'
63
+ * ```
64
+ */
65
+ export declare function formatSize(bytes: number): string;
66
+ /**
67
+ * Check whether a model of a given size can likely run
68
+ * on the current device without OOM.
69
+ *
70
+ * Accepts human-readable strings or raw byte counts:
71
+ *
72
+ * ```ts
73
+ * await canRun('4GB');
74
+ * await canRun('512MB');
75
+ * await canRun(4_000_000_000);
76
+ * ```
77
+ *
78
+ * This is a heuristic — real limits depend on browser, OS, and
79
+ * other tabs consuming VRAM.
80
+ */
81
+ export declare function canRun(estimatedSize: string | number): Promise<{
82
+ ok: boolean;
83
+ backend: DeviceBackend;
84
+ reason?: string;
85
+ }>;
86
+ //# sourceMappingURL=device.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"device.d.ts","sourceRoot":"","sources":["../src/device.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,UAAU,EAAE,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAWvF;;;GAGG;AACH,wBAAsB,aAAa,IAAI,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CAYhE;AAED;;GAEG;AACH,wBAAsB,UAAU,CAAC,OAAO,EAAE,UAAU,GAAG,OAAO,CAAC,OAAO,CAAC,CAetE;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,gBAAgB,CAK7D;AAED;;GAEG;AACH,wBAAsB,WAAW,IAAI,OAAO,CAAC,OAAO,CAAC,CAGpD;AAED;;GAEG;AACH,wBAAgB,SAAS,IAAI,OAAO,CAEnC;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,YAAY,IAAI,OAAO,CAAC,UAAU,CAAC,CA2BxD;AAYD;;;;;;;;;;;;GAYG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAaxD;AAED;;;;;;;;GAQG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAMhD;AAID;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,MAAM,CAAC,aAAa,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;IACpE,EAAE,EAAE,OAAO,CAAC;IACZ,OAAO,EAAE,aAAa,CAAC;IACvB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC,CAiBD"}