@degreesign/chat 1.2.0 → 1.3.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 +84 -115
- package/dist/browser/chat.min.mjs +1 -1
- package/dist/browser/engine-wllama.min.mjs +1 -1
- package/dist/describe.d.ts +2 -2
- package/dist/enums.d.ts +0 -1
- package/dist/index.d.ts +1 -0
- package/dist/models.d.ts +1 -3
- package/dist/node/chat.node.min.js +1 -1
- package/dist/node/chat.node.min.mjs +1 -1
- package/dist/node/engine-wllama.node.min.js +1 -1
- package/dist/node/engine-wllama.node.min.mjs +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -15,6 +15,7 @@ with auto resume for low network connections.
|
|
|
15
15
|
[](#faq)
|
|
16
16
|
[](#faq)
|
|
17
17
|
[](https://degreesign.github.io/ds_chat/architecture.html)
|
|
18
|
+
[](https://degreesign.github.io/ds_chat/vs_web_llm.html)
|
|
18
19
|
|
|
19
20
|
## Table of contents
|
|
20
21
|
|
|
@@ -46,7 +47,7 @@ with auto resume for low network connections.
|
|
|
46
47
|
- Images: a user turn can carry image URLs, sent only to a vision model and
|
|
47
48
|
capped by the prompt budgets.
|
|
48
49
|
- Audio: a user turn can carry an audio URL, sent only to a model with audio
|
|
49
|
-
support
|
|
50
|
+
support. No model in the catalog has an audio encoder.
|
|
50
51
|
- Reusable: the loaded model stays in memory for the next call.
|
|
51
52
|
- Typed: written in TypeScript with declarations for every export.
|
|
52
53
|
- Portable: importing and calling works in browsers, workers, React and Node.
|
|
@@ -133,38 +134,28 @@ second, measured from the first token so prompt processing is excluded.
|
|
|
133
134
|
Attach images to a user turn with `images: [dataUrl]`. The package sends them
|
|
134
135
|
only to a model with vision support, fits each image to 128 to 136 vision
|
|
135
136
|
tokens, snaps both sides to multiples of 32 px, crops the remainder, and caps
|
|
136
|
-
the number with `limits.maxImages`.
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
`
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
A model without audio drops the clip when the turn also has text; an audio-only
|
|
146
|
-
turn reports `Model does not accept audio`. On a phone or tablet
|
|
147
|
-
`gemma4_e2b_limited` is text only, so audio follows the same rule as images
|
|
148
|
-
there. The package does not transcode, so the host supplies one of those
|
|
149
|
-
formats.
|
|
137
|
+
the number with `limits.maxImages`. `qwen3_vl_2b` is the vision model in the
|
|
138
|
+
catalog, and it runs on the CPU engine. A turn sent to a model without vision
|
|
139
|
+
drops its images when the turn also has text, and an image-only turn reports
|
|
140
|
+
`Model does not accept images`.
|
|
141
|
+
|
|
142
|
+
Attach a recording with `audio: [dataUrl]`. No model in the catalog has an
|
|
143
|
+
audio encoder, so a text turn drops the clip and an audio-only turn reports
|
|
144
|
+
`Model does not accept audio`. `limits.maxAudio` still caps the clips
|
|
145
|
+
(default 1).
|
|
150
146
|
|
|
151
147
|
The package never writes storage, so the host appends the user message before
|
|
152
148
|
the call and appends the assistant message after the last `onResponse`.
|
|
153
149
|
|
|
154
150
|
## Enable WebGPU
|
|
155
151
|
|
|
156
|
-
A request without `model` picks
|
|
157
|
-
and `qwen3_vl_2b` when it does not.
|
|
158
|
-
`
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
or resolved, every status snapshot reports the build that runs in `model`, so a
|
|
164
|
-
host that asks for `gemma4_e2b` on a phone reads `gemma4-e2b-limited` back.
|
|
165
|
-
Hosts that need images on mobile pass `qwen3_vl_2b`; audio is desktop only. The
|
|
166
|
-
GPU engine is far faster on a desktop, so check the browser before blaming the
|
|
167
|
-
package:
|
|
152
|
+
A request without `model` picks `gemma4_e2b_limited` when the browser exposes a
|
|
153
|
+
WebGPU adapter and `qwen3_vl_2b` when it does not. The Gemma build is the 2.0 GB
|
|
154
|
+
text-only `gemma-4-E2B-it-gpu.litertlm`, and it is the only WebGPU model in the
|
|
155
|
+
catalog, so it loads on a desktop and on a phone alike. Every status snapshot
|
|
156
|
+
reports the build that runs in `model`. Hosts that need images pass
|
|
157
|
+
`qwen3_vl_2b`. The GPU engine is far faster on a desktop, so check the browser
|
|
158
|
+
before blaming the package:
|
|
168
159
|
|
|
169
160
|
```js
|
|
170
161
|
// Run in the console of the page that loads the package
|
|
@@ -217,19 +208,14 @@ CPU, not the GPU. Remove it when measuring speed. Headless Chromium hides
|
|
|
217
208
|
`--enable-features=Vulkan,WebGPU` and `--use-angle=vulkan`; check the adapter
|
|
218
209
|
there too, because a missing adapter silently selects the CPU model.
|
|
219
210
|
|
|
220
|
-
### GPU memory
|
|
211
|
+
### GPU memory
|
|
221
212
|
|
|
222
|
-
The WebGPU engine keeps the model
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
the crop is resized to 384 x 384 rather than the full 768 x 768. If the error
|
|
229
|
-
still appears, close other GPU tabs, unload the model, or switch to the CPU
|
|
230
|
-
model. A lost device needs a page or model reload; later calls cannot reuse it.
|
|
231
|
-
A phone or tablet resolves Gemma to the text-only browser build, so it never
|
|
232
|
-
starts the vision or audio encoder.
|
|
213
|
+
The WebGPU engine keeps the model and its KV cache in GPU memory. The Gemma
|
|
214
|
+
build carries no vision or audio encoder, so it never starts one and never
|
|
215
|
+
resizes an image. A card that is already busy can lose the device
|
|
216
|
+
(`VK_ERROR_DEVICE_LOST`) during a long generation. If the error appears, close
|
|
217
|
+
other GPU tabs, unload the model, or switch to the CPU model. A lost device
|
|
218
|
+
needs a page or model reload; later calls cannot reuse it.
|
|
233
219
|
|
|
234
220
|
While a reply streams, the GPU engine rests after every token readback, so the
|
|
235
221
|
card stays near 70% of wall time instead of pegging at 100% and starving the
|
|
@@ -247,16 +233,16 @@ describeImages(input: DescribeImagesInput): Promise<ImageDescription[]>
|
|
|
247
233
|
describeTranslated(input: DescribeTranslatedInput): Promise<string[]>
|
|
248
234
|
describeModelId: ChatModelId
|
|
249
235
|
imageReplyTokens: number
|
|
236
|
+
gpuAdapter(): Promise<boolean>
|
|
250
237
|
clearModelCache(): Promise<void>
|
|
251
238
|
```
|
|
252
239
|
|
|
253
240
|
Call it once per response. A new call aborts the previous stream. When the
|
|
254
|
-
requested model is already loaded, no download or load happens.
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
file from this device.
|
|
241
|
+
requested model is already loaded, no download or load happens. One model stays
|
|
242
|
+
resident on every device, so a new model replaces the old one, including a move
|
|
243
|
+
between the CPU describer and the GPU translator. Files stay on disk, and
|
|
244
|
+
`clearModelCache` releases the loaded model and deletes every cached model file
|
|
245
|
+
from this device.
|
|
260
246
|
|
|
261
247
|
`ChatInput`:
|
|
262
248
|
|
|
@@ -273,10 +259,11 @@ file from this device.
|
|
|
273
259
|
| `onStatus?` | Receives a `ChatStatusSnapshot` on every state change. |
|
|
274
260
|
|
|
275
261
|
Without `model` the package picks the GPU build when the browser provides a
|
|
276
|
-
WebGPU adapter and the CPU build when it does not.
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
262
|
+
WebGPU adapter and the CPU build when it does not. `gpuAdapter` runs the same
|
|
263
|
+
probe, so a host can check the device before it names a model. The budget
|
|
264
|
+
defaults are 250 reply tokens, 128 CPU thinking tokens, 1200 GPU reasoning
|
|
265
|
+
tokens (320 on the CPU), 2 images, 1 audio clip, 1000 characters per message,
|
|
266
|
+
10000 prompt characters and 32 CPU threads.
|
|
280
267
|
|
|
281
268
|
`CancelChat` is `() => void`. Its meaning depends on the current state:
|
|
282
269
|
|
|
@@ -286,10 +273,9 @@ characters and 32 CPU threads.
|
|
|
286
273
|
| Ready | Aborts the reply and unloads the model that answered. |
|
|
287
274
|
| An older call | Nothing, because a newer call owns the runtime. |
|
|
288
275
|
|
|
289
|
-
The model stays loaded between calls until a cancel frees it, and a newer
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
download.
|
|
276
|
+
The model stays loaded between calls until a cancel frees it, and a newer model
|
|
277
|
+
replaces it. One model download serves every caller: a chat that joins a preload
|
|
278
|
+
shares its progress and never starts a second download.
|
|
293
279
|
|
|
294
280
|
### Preload
|
|
295
281
|
|
|
@@ -304,9 +290,8 @@ runtime, and either call joins a load already in flight. A preload owns no cance
|
|
|
304
290
|
function, so a chat that joins its download and is cancelled leaves the download
|
|
305
291
|
running.
|
|
306
292
|
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
its own model, and a load on the same backend still replaces the one in flight.
|
|
293
|
+
A preload of another model replaces the resident one, because one model stays
|
|
294
|
+
in memory at a time. A load of the same model still joins the one in flight.
|
|
310
295
|
|
|
311
296
|
`ChatPreloadInput` is the subset of `ChatInput` that a preload needs:
|
|
312
297
|
|
|
@@ -327,7 +312,7 @@ on failure. A preload owns no cancel function; a new load replaces it.
|
|
|
327
312
|
describeImages(input: DescribeImagesInput): Promise<ImageDescription[]>
|
|
328
313
|
```
|
|
329
314
|
|
|
330
|
-
Describe every image in English with `
|
|
315
|
+
Describe every image in English with `qwen3_vl_2b_instruct`, the Q4_K_M
|
|
331
316
|
build of Unsloth Qwen3-VL 2B Instruct, one image at a time. When a WebGPU
|
|
332
317
|
adapter exists and `targetLanguage` is not English, the English descriptions
|
|
333
318
|
are then translated one by one with `gemma4_e2b_limited`. Without a GPU, when
|
|
@@ -438,7 +423,7 @@ search and currency tools fetch over the network; `calculate` does not.
|
|
|
438
423
|
|
|
439
424
|
| Field | Description |
|
|
440
425
|
| ----- | ----------- |
|
|
441
|
-
| `model?` | The
|
|
426
|
+
| `model?` | The model that runs, or the requested build before it resolves. |
|
|
442
427
|
| `availability` | `ChatStatus.online`, `.away` or `.offline`, derived from `status`. |
|
|
443
428
|
| `status` | `LlmStatus`: `idle`, `downloading`, `ready`, `error` or `unsupported`. |
|
|
444
429
|
| `backend` | `LlmBackend.cpu`, `.gpu` or `.none`. |
|
|
@@ -453,7 +438,7 @@ search and currency tools fetch over the network; `calculate` does not.
|
|
|
453
438
|
|
|
454
439
|
The first snapshot fires synchronously inside `createChat`. A final snapshot
|
|
455
440
|
with `requesting: false` marks the end of the call. `describeImages` reports the
|
|
456
|
-
same snapshot for each model it loads: `
|
|
441
|
+
same snapshot for each model it loads: `qwen3_vl_2b_instruct` while it
|
|
457
442
|
describes, then `gemma4_e2b_limited` while it translates.
|
|
458
443
|
|
|
459
444
|
A stream that never produces its first chunk ends with an error after 5
|
|
@@ -478,43 +463,34 @@ without it goes quiet again.
|
|
|
478
463
|
| `qwen3_vl_2b` | Qwen3-VL 2B | Yes | Yes | No | Yes | No |
|
|
479
464
|
| `qwen3_vl_2b_instruct` | Qwen3-VL 2B Instruct | No | Yes | No | Yes | No |
|
|
480
465
|
| `qwen3_vl_2b_instruct_q3` | Qwen3-VL 2B Instruct Q3 | No | Yes | No | Yes | No |
|
|
481
|
-
| `gemma4_e2b` | Gemma 4 E2B | Yes | Yes | Yes | No | Yes |
|
|
482
466
|
| `gemma4_e2b_limited` | Gemma 4 E2B limited | Yes | No | No | No | Yes |
|
|
483
467
|
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
`gemma4_e2b_limited` loads that same file on a desktop. The build that actually
|
|
487
|
-
runs is always the one in `ChatStatusSnapshot.model`. The limited build has no
|
|
468
|
+
`gemma4_e2b_limited` loads `gemma-4-E2B-it-gpu.litertlm` on every device. The
|
|
469
|
+
build that runs is always the one in `ChatStatusSnapshot.model`. It has no
|
|
488
470
|
vision or audio encoder, so `supportsImages` and `supportsAudio` report `false`
|
|
489
|
-
there. Use `qwen3_vl_2b` for images
|
|
471
|
+
there. Use `qwen3_vl_2b` for images.
|
|
490
472
|
|
|
491
473
|
The first call downloads the model before its engine loads it. Later calls
|
|
492
|
-
reuse the loaded runtime.
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
next attempt, including after a reload.
|
|
474
|
+
reuse the loaded runtime. One model stays resident, and a new model replaces it.
|
|
475
|
+
An interrupted download keeps every part it committed and resumes from the last
|
|
476
|
+
one on the next attempt, including after a reload.
|
|
496
477
|
|
|
497
478
|
Qwen3-VL 2B downloads a vision projector of about 445 MB with the model, so its
|
|
498
479
|
total download is about 1.5 GB. It runs on the CPU engine, with a 4096 token
|
|
499
480
|
context on a phone and 8192 on a desktop. Its projector uses the same CPU thread
|
|
500
|
-
budget as the text pass, so image turns
|
|
501
|
-
Use the GPU model when images need to be fast.
|
|
481
|
+
budget as the text pass, so image turns wait behind the CPU queue.
|
|
502
482
|
|
|
503
|
-
`
|
|
504
|
-
`describeImages` loads for the English pass, its
|
|
483
|
+
`qwen3_vl_2b_instruct` is the Unsloth Qwen3-VL 2B Instruct build that
|
|
484
|
+
`describeImages` loads for the English pass, its Q4_K_M file. Both instruct
|
|
505
485
|
builds use Qwen's Q8_0 projector, about 445 MB, so the describer totals about
|
|
506
|
-
1.
|
|
507
|
-
prompt directly. `
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
Gemma 4 E2B downloads `gemma-4-E2B-it.litertlm`,
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
instead downloads the 2.0 GB `gemma-4-E2B-it-gpu.litertlm`, the precompiled
|
|
515
|
-
GPU_ARTISAN build Google ships for browsers. The smaller file is what keeps a
|
|
516
|
-
mobile tab alive at the end of the model load, and a desktop can ask for it
|
|
517
|
-
too, trading vision and audio for 600 MB less download.
|
|
486
|
+
1.5 GB on the CPU engine. It has no reasoning pass, so it answers an image
|
|
487
|
+
prompt directly. `qwen3_vl_2b_instruct_q3` is the same model in Q3_K_M, about
|
|
488
|
+
160 MB smaller, for a host that prefers the smaller file and names it
|
|
489
|
+
explicitly.
|
|
490
|
+
|
|
491
|
+
Gemma 4 E2B downloads the 2.0 GB `gemma-4-E2B-it-gpu.litertlm`, the
|
|
492
|
+
precompiled GPU_ARTISAN build Google ships for browsers. It runs on the WebGPU
|
|
493
|
+
engine, carries no vision or audio encoder, and needs no projector download.
|
|
518
494
|
|
|
519
495
|
The WebGPU engine asks for a 32768 token context on a desktop and 16384 on a
|
|
520
496
|
phone, both multiples of 1024. Its int8 KV cache holds the full desktop window
|
|
@@ -551,11 +527,9 @@ user: {next turn}
|
|
|
551
527
|
(default 2). Older image parts are dropped and their text stays.
|
|
552
528
|
- Every image part keeps its full frame and targets 128 vision tokens, with at
|
|
553
529
|
most 136. Both sides are snapped to multiples of 32 px and the remainder is
|
|
554
|
-
cropped.
|
|
555
|
-
smallest Gemma 4 encoder signature.
|
|
530
|
+
cropped.
|
|
556
531
|
- Audio is collected from the newest turns first and capped by `maxAudio`
|
|
557
|
-
(default 1). Older audio parts are dropped and their text stays.
|
|
558
|
-
FLAC are accepted, and a short 16 kHz mono WAV is the best input.
|
|
532
|
+
(default 1). Older audio parts are dropped and their text stays.
|
|
559
533
|
- Image and audio payloads do not count against `promptCharBudget`.
|
|
560
534
|
- An image-only turn with a text model reports `Model does not accept images`,
|
|
561
535
|
and an audio-only turn reports `Model does not accept audio`.
|
|
@@ -618,11 +592,9 @@ self hosted. Chromium also accepts
|
|
|
618
592
|
changed. A page inside an iframe is isolated only when every ancestor sets the
|
|
619
593
|
same headers.
|
|
620
594
|
|
|
621
|
-
Each engine keeps its own cache.
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
model in total. The previous model's files always stay, so a switch back skips
|
|
625
|
-
the download.
|
|
595
|
+
Each engine keeps its own cache. One model stays loaded, so a switch between
|
|
596
|
+
the CPU and GPU models skips the download but still loads the new engine. The
|
|
597
|
+
previous model's files always stay, so a switch back skips the download.
|
|
626
598
|
`clearModelCache()` deletes them all and frees the storage. Both engines store
|
|
627
599
|
model files in the origin private file system as numbered parts, under
|
|
628
600
|
`wllama-store` for the CPU engine and `litert-store` for the GPU engine. The GPU
|
|
@@ -650,7 +622,7 @@ wasm under `dist/assets/litert`; model weights remain external.
|
|
|
650
622
|
|
|
651
623
|
LiteRT-LM needs WebGPU and the JSPI and relaxed SIMD wasm features, currently
|
|
652
624
|
available in Chromium. It loads a 22.7 MB wasm binary from `dist/assets/litert`
|
|
653
|
-
and the 2.
|
|
625
|
+
and the 2.0 GB model file. The GPU engine reports progress while the file
|
|
654
626
|
downloads and 100% of the load phase while the model compiles.
|
|
655
627
|
|
|
656
628
|
wllama needs the compat worker on the same origin. When the app serves the
|
|
@@ -739,7 +711,7 @@ The public enums are `ChatModelId`, `LlmRole`, `ChatStatus`, `LlmStatus`,
|
|
|
739
711
|
The suites live in `test/` and run against the local checkout.
|
|
740
712
|
|
|
741
713
|
`yarn test:instruct` starts a local llama.cpp server with the Qwen3-VL 2B
|
|
742
|
-
Instruct
|
|
714
|
+
Instruct Q4_K_M build:
|
|
743
715
|
|
|
744
716
|
```bash
|
|
745
717
|
yarn test:instruct
|
|
@@ -769,6 +741,9 @@ An interactive map of the package lives at
|
|
|
769
741
|
the two entries, every source module with line numbers, the helper edges and the
|
|
770
742
|
send flow.
|
|
771
743
|
|
|
744
|
+
A side-by-side comparison with `@mlc-ai/web-llm` for Gemma 4 E2B on WebGPU lives
|
|
745
|
+
at [degreesign.github.io/ds_chat/vs_web_llm.html](https://degreesign.github.io/ds_chat/vs_web_llm.html).
|
|
746
|
+
|
|
772
747
|
## FAQ
|
|
773
748
|
|
|
774
749
|
### What is @degreesign/chat?
|
|
@@ -783,25 +758,19 @@ No. The host passes the full prompt on every call and keeps the reply.
|
|
|
783
758
|
### Does it support images?
|
|
784
759
|
|
|
785
760
|
Yes, on models with vision support. Put image URLs in a user turn's `images`
|
|
786
|
-
array. Qwen3-VL 2B accepts images on the CPU engine and
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
at most 136, with both sides snapped to multiples of 32 px and the remainder
|
|
791
|
-
cropped. The WebGPU engine instead center-crops a 384 x 384 square with the
|
|
792
|
-
smallest Gemma 4 vision budget. A model without vision drops images
|
|
761
|
+
array. Qwen3-VL 2B accepts images on the CPU engine and is the vision model in
|
|
762
|
+
the catalog. Each image keeps its full frame and targets 128 vision tokens, at
|
|
763
|
+
most 136, with both sides snapped to multiples of 32 px and the remainder
|
|
764
|
+
cropped. The WebGPU build is text only, so a model without vision drops images
|
|
793
765
|
when the turn also has text; an image-only turn reports
|
|
794
766
|
`Model does not accept images`.
|
|
795
767
|
|
|
796
768
|
### Does it support audio?
|
|
797
769
|
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
The package does not transcode, so the host supplies one of those formats. A
|
|
803
|
-
model without audio drops the clip when the turn also has text; an audio-only
|
|
804
|
-
turn reports `Model does not accept audio`.
|
|
770
|
+
No model in the catalog has an audio encoder. A user turn can still carry an
|
|
771
|
+
audio URL in `audio`, and `limits.maxAudio` caps the clips (default 1), but a
|
|
772
|
+
model without audio support drops the clip when the turn also has text, and an
|
|
773
|
+
audio-only turn reports `Model does not accept audio`.
|
|
805
774
|
|
|
806
775
|
### Does it need a server?
|
|
807
776
|
|
|
@@ -824,11 +793,11 @@ when an older version stored the file. The package stores no model data itself.
|
|
|
824
793
|
|
|
825
794
|
### Which model should I pick?
|
|
826
795
|
|
|
827
|
-
Qwen3-VL 2B supports reasoning and images on the CPU
|
|
828
|
-
|
|
829
|
-
but a larger download and a slower first compile. Use Qwen for
|
|
830
|
-
support and for
|
|
831
|
-
|
|
796
|
+
Qwen3-VL 2B supports reasoning and images on the CPU, and it runs on any browser
|
|
797
|
+
with WebAssembly. Gemma 4 E2B limited supports reasoning on WebGPU, with faster
|
|
798
|
+
text generation but a larger download and a slower first compile. Use Qwen for
|
|
799
|
+
broad browser support and for images. Use Gemma on a device with a WebGPU
|
|
800
|
+
adapter when text speed matters.
|
|
832
801
|
|
|
833
802
|
## Licensing
|
|
834
803
|
|