@degreesign/chat 1.1.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 +145 -126
- package/dist/browser/chat.min.mjs +1 -1
- package/dist/browser/engine-wllama.min.mjs +1 -1
- package/dist/chat.d.ts +4 -4
- package/dist/describe.d.ts +16 -7
- package/dist/engines/engine.d.ts +12 -12
- package/dist/engines/images.d.ts +8 -8
- package/dist/engines/litert.d.ts +22 -10
- package/dist/engines/store.d.ts +4 -4
- package/dist/engines/threads.d.ts +1 -1
- package/dist/engines/wllama.d.ts +4 -4
- package/dist/enums.d.ts +6 -7
- package/dist/index.d.ts +2 -1
- package/dist/log.d.ts +1 -1
- package/dist/models.d.ts +12 -12
- 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/dist/tools/calculator.d.ts +2 -2
- package/dist/tools/currency.d.ts +3 -3
- package/dist/tools/index.d.ts +1 -1
- package/dist/tools/search.d.ts +3 -3
- package/dist/tools/text.d.ts +3 -3
- package/dist/types.d.ts +216 -173
- package/package.json +4 -2
package/README.md
CHANGED
|
@@ -1,14 +1,21 @@
|
|
|
1
1
|
# @degreesign/chat
|
|
2
2
|
|
|
3
|
-
One-shot on-device AI chat for browsers, workers and Node,
|
|
4
|
-
engine
|
|
5
|
-
package
|
|
3
|
+
One-shot on-device AI chat for browsers, workers and Node, using wllama CPU
|
|
4
|
+
engine and LiteRT-LM WebGPU engine.
|
|
5
|
+
The package is an A-Z solution for local AI chat and more.
|
|
6
|
+
Includes voice transcription to many languages
|
|
7
|
+
and a super accurate image vision function.
|
|
8
|
+
The package manages download of AI models automatically,
|
|
9
|
+
with auto resume for low network connections.
|
|
10
|
+
|
|
11
|
+
`@degreesign/chat` is plug and play solution for any UI.
|
|
6
12
|
|
|
7
13
|
[](https://www.npmjs.com/package/@degreesign/chat)
|
|
8
14
|
[](#licensing)
|
|
9
15
|
[](#faq)
|
|
10
16
|
[](#faq)
|
|
11
17
|
[](https://degreesign.github.io/ds_chat/architecture.html)
|
|
18
|
+
[](https://degreesign.github.io/ds_chat/vs_web_llm.html)
|
|
12
19
|
|
|
13
20
|
## Table of contents
|
|
14
21
|
|
|
@@ -25,6 +32,7 @@ package loads one model, streams one reply and reports status.
|
|
|
25
32
|
- [Node.js](#nodejs)
|
|
26
33
|
- [React](#react)
|
|
27
34
|
- [Types](#types)
|
|
35
|
+
- [Testing](#testing)
|
|
28
36
|
- [Architecture](#architecture)
|
|
29
37
|
- [FAQ](#faq)
|
|
30
38
|
- [Licensing](#licensing)
|
|
@@ -39,7 +47,7 @@ package loads one model, streams one reply and reports status.
|
|
|
39
47
|
- Images: a user turn can carry image URLs, sent only to a vision model and
|
|
40
48
|
capped by the prompt budgets.
|
|
41
49
|
- Audio: a user turn can carry an audio URL, sent only to a model with audio
|
|
42
|
-
support
|
|
50
|
+
support. No model in the catalog has an audio encoder.
|
|
43
51
|
- Reusable: the loaded model stays in memory for the next call.
|
|
44
52
|
- Typed: written in TypeScript with declarations for every export.
|
|
45
53
|
- Portable: importing and calling works in browsers, workers, React and Node.
|
|
@@ -126,38 +134,28 @@ second, measured from the first token so prompt processing is excluded.
|
|
|
126
134
|
Attach images to a user turn with `images: [dataUrl]`. The package sends them
|
|
127
135
|
only to a model with vision support, fits each image to 128 to 136 vision
|
|
128
136
|
tokens, snaps both sides to multiples of 32 px, crops the remainder, and caps
|
|
129
|
-
the number with `limits.maxImages`.
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
`
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
A model without audio drops the clip when the turn also has text; an audio-only
|
|
139
|
-
turn reports `Model does not accept audio`. On a phone or tablet
|
|
140
|
-
`gemma4_e2b_limited` is text only, so audio follows the same rule as images
|
|
141
|
-
there. The package does not transcode, so the host supplies one of those
|
|
142
|
-
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).
|
|
143
146
|
|
|
144
147
|
The package never writes storage, so the host appends the user message before
|
|
145
148
|
the call and appends the assistant message after the last `onResponse`.
|
|
146
149
|
|
|
147
150
|
## Enable WebGPU
|
|
148
151
|
|
|
149
|
-
A request without `model` picks
|
|
150
|
-
and `qwen3_vl_2b` when it does not.
|
|
151
|
-
`
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
or resolved, every status snapshot reports the build that runs in `model`, so a
|
|
157
|
-
host that asks for `gemma4_e2b` on a phone reads `gemma4-e2b-limited` back.
|
|
158
|
-
Hosts that need images on mobile pass `qwen3_vl_2b`; audio is desktop only. The
|
|
159
|
-
GPU engine is far faster on a desktop, so check the browser before blaming the
|
|
160
|
-
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:
|
|
161
159
|
|
|
162
160
|
```js
|
|
163
161
|
// Run in the console of the page that loads the package
|
|
@@ -210,19 +208,21 @@ CPU, not the GPU. Remove it when measuring speed. Headless Chromium hides
|
|
|
210
208
|
`--enable-features=Vulkan,WebGPU` and `--use-angle=vulkan`; check the adapter
|
|
211
209
|
there too, because a missing adapter silently selects the CPU model.
|
|
212
210
|
|
|
213
|
-
### GPU memory
|
|
211
|
+
### GPU memory
|
|
214
212
|
|
|
215
|
-
The WebGPU engine keeps the model
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
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.
|
|
219
|
+
|
|
220
|
+
While a reply streams, the GPU engine rests after every token readback, so the
|
|
221
|
+
card stays near 70% of wall time instead of pegging at 100% and starving the
|
|
222
|
+
desktop compositor. The rest applies after the prefill, so the first token is
|
|
223
|
+
not delayed. Unloading releases the WebGPU device and resets the LiteRT wasm,
|
|
224
|
+
because a deleted engine leaves both unusable; a later load rebuilds them from
|
|
225
|
+
the stored model file without downloading it again.
|
|
226
226
|
|
|
227
227
|
## Public API
|
|
228
228
|
|
|
@@ -232,16 +232,17 @@ preloadModel(input?: ChatPreloadInput): Promise<boolean>
|
|
|
232
232
|
describeImages(input: DescribeImagesInput): Promise<ImageDescription[]>
|
|
233
233
|
describeTranslated(input: DescribeTranslatedInput): Promise<string[]>
|
|
234
234
|
describeModelId: ChatModelId
|
|
235
|
+
imageReplyTokens: number
|
|
236
|
+
gpuAdapter(): Promise<boolean>
|
|
235
237
|
clearModelCache(): Promise<void>
|
|
236
238
|
```
|
|
237
239
|
|
|
238
240
|
Call it once per response. A new call aborts the previous stream. When the
|
|
239
|
-
requested model is already loaded, no download or load happens.
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
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.
|
|
245
246
|
|
|
246
247
|
`ChatInput`:
|
|
247
248
|
|
|
@@ -258,10 +259,11 @@ file from this device.
|
|
|
258
259
|
| `onStatus?` | Receives a `ChatStatusSnapshot` on every state change. |
|
|
259
260
|
|
|
260
261
|
Without `model` the package picks the GPU build when the browser provides a
|
|
261
|
-
WebGPU adapter and the CPU build when it does not.
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
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.
|
|
265
267
|
|
|
266
268
|
`CancelChat` is `() => void`. Its meaning depends on the current state:
|
|
267
269
|
|
|
@@ -271,10 +273,9 @@ characters and 32 CPU threads.
|
|
|
271
273
|
| Ready | Aborts the reply and unloads the model that answered. |
|
|
272
274
|
| An older call | Nothing, because a newer call owns the runtime. |
|
|
273
275
|
|
|
274
|
-
The model stays loaded between calls until a cancel frees it, and a newer
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
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.
|
|
278
279
|
|
|
279
280
|
### Preload
|
|
280
281
|
|
|
@@ -289,9 +290,8 @@ runtime, and either call joins a load already in flight. A preload owns no cance
|
|
|
289
290
|
function, so a chat that joins its download and is cancelled leaves the download
|
|
290
291
|
running.
|
|
291
292
|
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
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.
|
|
295
295
|
|
|
296
296
|
`ChatPreloadInput` is the subset of `ChatInput` that a preload needs:
|
|
297
297
|
|
|
@@ -312,21 +312,23 @@ on failure. A preload owns no cancel function; a new load replaces it.
|
|
|
312
312
|
describeImages(input: DescribeImagesInput): Promise<ImageDescription[]>
|
|
313
313
|
```
|
|
314
314
|
|
|
315
|
-
Describe every image in English with `
|
|
315
|
+
Describe every image in English with `qwen3_vl_2b_instruct`, the Q4_K_M
|
|
316
316
|
build of Unsloth Qwen3-VL 2B Instruct, one image at a time. When a WebGPU
|
|
317
317
|
adapter exists and `targetLanguage` is not English, the English descriptions
|
|
318
318
|
are then translated one by one with `gemma4_e2b_limited`. Without a GPU, when
|
|
319
319
|
the translation model cannot load, or when the translator replies
|
|
320
320
|
`CANNOT_TRANSLATE`, `description` stays equal to `descriptionEN`. Both the
|
|
321
|
-
description and translation calls use
|
|
322
|
-
`limits.maxReplyTokens` overrides it. The describer
|
|
323
|
-
|
|
324
|
-
models leave the engine image limits
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
321
|
+
description and translation calls use the `imageReplyTokens` budget of 128
|
|
322
|
+
tokens unless `limits.maxReplyTokens` overrides it. The describer requests
|
|
323
|
+
192 vision tokens for its image resize; the CPU engine keeps its instruct
|
|
324
|
+
window at 16 to 128 tokens, and other models leave the engine image limits
|
|
325
|
+
unset.
|
|
326
|
+
|
|
327
|
+
The describer receives a system message that asks for every element of the
|
|
328
|
+
image and adds rules against lying, verdicts and vague referrals.
|
|
329
|
+
`DescribeImagesInput.additionalRules` appends host rules to that message. The
|
|
330
|
+
translator translates its own previous reply, never the user's instruction,
|
|
331
|
+
preserving meaning, names and numbers without adding or omitting anything.
|
|
330
332
|
|
|
331
333
|
`DescribeImagesInput`:
|
|
332
334
|
|
|
@@ -421,7 +423,7 @@ search and currency tools fetch over the network; `calculate` does not.
|
|
|
421
423
|
|
|
422
424
|
| Field | Description |
|
|
423
425
|
| ----- | ----------- |
|
|
424
|
-
| `model?` | The
|
|
426
|
+
| `model?` | The model that runs, or the requested build before it resolves. |
|
|
425
427
|
| `availability` | `ChatStatus.online`, `.away` or `.offline`, derived from `status`. |
|
|
426
428
|
| `status` | `LlmStatus`: `idle`, `downloading`, `ready`, `error` or `unsupported`. |
|
|
427
429
|
| `backend` | `LlmBackend.cpu`, `.gpu` or `.none`. |
|
|
@@ -436,7 +438,7 @@ search and currency tools fetch over the network; `calculate` does not.
|
|
|
436
438
|
|
|
437
439
|
The first snapshot fires synchronously inside `createChat`. A final snapshot
|
|
438
440
|
with `requesting: false` marks the end of the call. `describeImages` reports the
|
|
439
|
-
same snapshot for each model it loads: `
|
|
441
|
+
same snapshot for each model it loads: `qwen3_vl_2b_instruct` while it
|
|
440
442
|
describes, then `gemma4_e2b_limited` while it translates.
|
|
441
443
|
|
|
442
444
|
A stream that never produces its first chunk ends with an error after 5
|
|
@@ -461,43 +463,39 @@ without it goes quiet again.
|
|
|
461
463
|
| `qwen3_vl_2b` | Qwen3-VL 2B | Yes | Yes | No | Yes | No |
|
|
462
464
|
| `qwen3_vl_2b_instruct` | Qwen3-VL 2B Instruct | No | Yes | No | Yes | No |
|
|
463
465
|
| `qwen3_vl_2b_instruct_q3` | Qwen3-VL 2B Instruct Q3 | No | Yes | No | Yes | No |
|
|
464
|
-
| `gemma4_e2b` | Gemma 4 E2B | Yes | Yes | Yes | No | Yes |
|
|
465
466
|
| `gemma4_e2b_limited` | Gemma 4 E2B limited | Yes | No | No | No | Yes |
|
|
466
467
|
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
`gemma4_e2b_limited` loads that same file on a desktop. The build that actually
|
|
470
|
-
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
|
|
471
470
|
vision or audio encoder, so `supportsImages` and `supportsAudio` report `false`
|
|
472
|
-
there. Use `qwen3_vl_2b` for images
|
|
471
|
+
there. Use `qwen3_vl_2b` for images.
|
|
473
472
|
|
|
474
473
|
The first call downloads the model before its engine loads it. Later calls
|
|
475
|
-
reuse the loaded runtime.
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
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.
|
|
479
477
|
|
|
480
478
|
Qwen3-VL 2B downloads a vision projector of about 445 MB with the model, so its
|
|
481
479
|
total download is about 1.5 GB. It runs on the CPU engine, with a 4096 token
|
|
482
480
|
context on a phone and 8192 on a desktop. Its projector uses the same CPU thread
|
|
483
|
-
budget as the text pass, so image turns
|
|
484
|
-
Use the GPU model when images need to be fast.
|
|
481
|
+
budget as the text pass, so image turns wait behind the CPU queue.
|
|
485
482
|
|
|
486
|
-
`
|
|
487
|
-
`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
|
|
488
485
|
builds use Qwen's Q8_0 projector, about 445 MB, so the describer totals about
|
|
489
|
-
1.
|
|
490
|
-
prompt directly. `
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
Gemma 4 E2B downloads `gemma-4-E2B-it.litertlm`,
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
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.
|
|
494
|
+
|
|
495
|
+
The WebGPU engine asks for a 32768 token context on a desktop and 16384 on a
|
|
496
|
+
phone, both multiples of 1024. Its int8 KV cache holds the full desktop window
|
|
497
|
+
in about 300 MB, so the larger window stays a small part of the 2 GB model
|
|
498
|
+
footprint.
|
|
501
499
|
|
|
502
500
|
The WebGPU engine keeps one LiteRT conversation for consecutive turns. When the
|
|
503
501
|
next prompt continues that history, only the new user turn is sent. A changed
|
|
@@ -529,11 +527,9 @@ user: {next turn}
|
|
|
529
527
|
(default 2). Older image parts are dropped and their text stays.
|
|
530
528
|
- Every image part keeps its full frame and targets 128 vision tokens, with at
|
|
531
529
|
most 136. Both sides are snapped to multiples of 32 px and the remainder is
|
|
532
|
-
cropped.
|
|
533
|
-
smallest Gemma 4 encoder signature.
|
|
530
|
+
cropped.
|
|
534
531
|
- Audio is collected from the newest turns first and capped by `maxAudio`
|
|
535
|
-
(default 1). Older audio parts are dropped and their text stays.
|
|
536
|
-
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.
|
|
537
533
|
- Image and audio payloads do not count against `promptCharBudget`.
|
|
538
534
|
- An image-only turn with a text model reports `Model does not accept images`,
|
|
539
535
|
and an audio-only turn reports `Model does not accept audio`.
|
|
@@ -596,11 +592,9 @@ self hosted. Chromium also accepts
|
|
|
596
592
|
changed. A page inside an iframe is isolated only when every ancestor sets the
|
|
597
593
|
same headers.
|
|
598
594
|
|
|
599
|
-
Each engine keeps its own cache.
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
model in total. The previous model's files always stay, so a switch back skips
|
|
603
|
-
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.
|
|
604
598
|
`clearModelCache()` deletes them all and frees the storage. Both engines store
|
|
605
599
|
model files in the origin private file system as numbered parts, under
|
|
606
600
|
`wllama-store` for the CPU engine and `litert-store` for the GPU engine. The GPU
|
|
@@ -628,7 +622,7 @@ wasm under `dist/assets/litert`; model weights remain external.
|
|
|
628
622
|
|
|
629
623
|
LiteRT-LM needs WebGPU and the JSPI and relaxed SIMD wasm features, currently
|
|
630
624
|
available in Chromium. It loads a 22.7 MB wasm binary from `dist/assets/litert`
|
|
631
|
-
and the 2.
|
|
625
|
+
and the 2.0 GB model file. The GPU engine reports progress while the file
|
|
632
626
|
downloads and 100% of the load phase while the model compiles.
|
|
633
627
|
|
|
634
628
|
wllama needs the compat worker on the same origin. When the app serves the
|
|
@@ -712,6 +706,34 @@ const useReply = () => {
|
|
|
712
706
|
The public enums are `ChatModelId`, `LlmRole`, `ChatStatus`, `LlmStatus`,
|
|
713
707
|
`LlmBackend` and `LlmPhase`.
|
|
714
708
|
|
|
709
|
+
## Testing
|
|
710
|
+
|
|
711
|
+
The suites live in `test/` and run against the local checkout.
|
|
712
|
+
|
|
713
|
+
`yarn test:instruct` starts a local llama.cpp server with the Qwen3-VL 2B
|
|
714
|
+
Instruct Q4_K_M build:
|
|
715
|
+
|
|
716
|
+
```bash
|
|
717
|
+
yarn test:instruct
|
|
718
|
+
```
|
|
719
|
+
|
|
720
|
+
It runs `llama serve` on `http://127.0.0.1:8080` with a 160k context, 4
|
|
721
|
+
threads and CPU inference. llama.cpp
|
|
722
|
+
downloads the weights on first run and caches them outside the repository.
|
|
723
|
+
llama.cpp runs about 10x faster than wllama, so prefer the live server
|
|
724
|
+
whenever a test needs real inference. Keep the server in its own terminal
|
|
725
|
+
while the live and image suites run.
|
|
726
|
+
|
|
727
|
+
The image suite bundles the browser harness, serves it from `test/drive.cjs`
|
|
728
|
+
and proxies `/llama/` to the server on port 8080, so start `test:instruct`
|
|
729
|
+
first:
|
|
730
|
+
|
|
731
|
+
```bash
|
|
732
|
+
yarn test:images
|
|
733
|
+
```
|
|
734
|
+
|
|
735
|
+
The fake-engine harness needs no server and never reaches the network.
|
|
736
|
+
|
|
715
737
|
## Architecture
|
|
716
738
|
|
|
717
739
|
An interactive map of the package lives at
|
|
@@ -719,6 +741,9 @@ An interactive map of the package lives at
|
|
|
719
741
|
the two entries, every source module with line numbers, the helper edges and the
|
|
720
742
|
send flow.
|
|
721
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
|
+
|
|
722
747
|
## FAQ
|
|
723
748
|
|
|
724
749
|
### What is @degreesign/chat?
|
|
@@ -733,25 +758,19 @@ No. The host passes the full prompt on every call and keeps the reply.
|
|
|
733
758
|
### Does it support images?
|
|
734
759
|
|
|
735
760
|
Yes, on models with vision support. Put image URLs in a user turn's `images`
|
|
736
|
-
array. Qwen3-VL 2B accepts images on the CPU engine and
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
at most 136, with both sides snapped to multiples of 32 px and the remainder
|
|
741
|
-
cropped. The WebGPU engine instead center-crops a 384 x 384 square with the
|
|
742
|
-
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
|
|
743
765
|
when the turn also has text; an image-only turn reports
|
|
744
766
|
`Model does not accept images`.
|
|
745
767
|
|
|
746
768
|
### Does it support audio?
|
|
747
769
|
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
The package does not transcode, so the host supplies one of those formats. A
|
|
753
|
-
model without audio drops the clip when the turn also has text; an audio-only
|
|
754
|
-
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`.
|
|
755
774
|
|
|
756
775
|
### Does it need a server?
|
|
757
776
|
|
|
@@ -774,11 +793,11 @@ when an older version stored the file. The package stores no model data itself.
|
|
|
774
793
|
|
|
775
794
|
### Which model should I pick?
|
|
776
795
|
|
|
777
|
-
Qwen3-VL 2B supports reasoning and images on the CPU
|
|
778
|
-
|
|
779
|
-
but a larger download and a slower first compile. Use Qwen for
|
|
780
|
-
support and for
|
|
781
|
-
|
|
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.
|
|
782
801
|
|
|
783
802
|
## Licensing
|
|
784
803
|
|