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 +205 -0
- package/dist/cache.d.ts +46 -0
- package/dist/cache.d.ts.map +1 -0
- package/dist/cache.js +160 -0
- package/dist/cache.js.map +1 -0
- package/dist/device.d.ts +86 -0
- package/dist/device.d.ts.map +1 -0
- package/dist/device.js +195 -0
- package/dist/device.js.map +1 -0
- package/dist/gpu-recovery.d.ts +61 -0
- package/dist/gpu-recovery.d.ts.map +1 -0
- package/dist/gpu-recovery.js +105 -0
- package/dist/gpu-recovery.js.map +1 -0
- package/dist/index.d.ts +51 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +53 -0
- package/dist/index.js.map +1 -0
- package/dist/model-client.d.ts +149 -0
- package/dist/model-client.d.ts.map +1 -0
- package/dist/model-client.js +325 -0
- package/dist/model-client.js.map +1 -0
- package/dist/model-worker.d.ts +16 -0
- package/dist/model-worker.d.ts.map +1 -0
- package/dist/model-worker.js +303 -0
- package/dist/model-worker.js.map +1 -0
- package/dist/pipelines/index.d.ts +28 -0
- package/dist/pipelines/index.d.ts.map +1 -0
- package/dist/pipelines/index.js +122 -0
- package/dist/pipelines/index.js.map +1 -0
- package/dist/streaming.d.ts +55 -0
- package/dist/streaming.d.ts.map +1 -0
- package/dist/streaming.js +142 -0
- package/dist/streaming.js.map +1 -0
- package/dist/types.d.ts +186 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -0
- package/package.json +56 -0
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)
|
package/dist/cache.d.ts
ADDED
|
@@ -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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"}
|
package/dist/device.d.ts
ADDED
|
@@ -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"}
|