@degreesign/chat 0.1.3 → 0.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 +120 -60
- package/dist/assets/litert/litertlm_wasm_internal.js +1 -0
- package/dist/assets/litert/litertlm_wasm_internal.wasm +0 -0
- package/dist/browser/chat.min.mjs +1 -1
- package/dist/browser/engine-litert.min.mjs +2 -0
- package/dist/browser/engine-litert.min.mjs.LICENSE.txt +1 -0
- package/dist/chat.d.ts +5 -2
- package/dist/engines/engine.d.ts +6 -1
- package/dist/engines/images.d.ts +3 -1
- package/dist/engines/litert.d.ts +5 -0
- package/dist/engines/wllama.d.ts +1 -5
- package/dist/enums.d.ts +3 -1
- package/dist/index.d.ts +2 -2
- package/dist/models.d.ts +3 -1
- package/dist/node/chat.node.min.js +1 -1
- package/dist/node/chat.node.min.mjs +1 -1
- package/dist/node/engine-litert.node.min.js +2 -0
- package/dist/node/engine-litert.node.min.js.LICENSE.txt +1 -0
- package/dist/node/engine-litert.node.min.mjs +2 -0
- package/dist/node/engine-litert.node.min.mjs.LICENSE.txt +1 -0
- package/dist/types.d.ts +56 -7
- package/package.json +7 -3
package/README.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# @degreesign/chat
|
|
2
2
|
|
|
3
3
|
One-shot on-device AI chat for browsers, workers and Node, running a wllama CPU
|
|
4
|
-
engine
|
|
5
|
-
loads one model, streams one reply and reports status.
|
|
4
|
+
engine or a LiteRT-LM WebGPU engine. The host owns the messages and the UI; the
|
|
5
|
+
package loads one model, streams one reply and reports status.
|
|
6
6
|
|
|
7
7
|
[](https://www.npmjs.com/package/@degreesign/chat)
|
|
8
8
|
[](#licensing)
|
|
@@ -32,8 +32,8 @@ loads one model, streams one reply and reports status.
|
|
|
32
32
|
- On device: model weights download once, then every reply is generated locally.
|
|
33
33
|
- Headless: the package returns text through callbacks and renders no UI.
|
|
34
34
|
- Stateless: the host passes the whole message list and keeps its own history.
|
|
35
|
-
-
|
|
36
|
-
runtime contract.
|
|
35
|
+
- Two engines: CPU through wllama and WebGPU through LiteRT-LM, behind one
|
|
36
|
+
`startLLM` runtime contract.
|
|
37
37
|
- Images: a user turn can carry image URLs, sent only to a vision model and
|
|
38
38
|
capped by the prompt budgets.
|
|
39
39
|
- Reusable: the loaded model stays in memory for the next call.
|
|
@@ -55,20 +55,22 @@ yarn add @degreesign/chat
|
|
|
55
55
|
pnpm add @degreesign/chat
|
|
56
56
|
```
|
|
57
57
|
|
|
58
|
-
`@wllama/wllama
|
|
59
|
-
|
|
60
|
-
assets ship beside
|
|
61
|
-
engine import. Only the model weights are downloaded at
|
|
58
|
+
`@wllama/wllama`, `@wllama/wllama-compat` and `@litert-lm/core` are build-time
|
|
59
|
+
dependencies. The engines are bundled into the published build as dynamic
|
|
60
|
+
chunks and their wasm assets ship beside them, so a consumer installs nothing
|
|
61
|
+
extra and resolves no engine import. Only the model weights are downloaded at
|
|
62
|
+
runtime.
|
|
62
63
|
|
|
63
64
|
The build emits:
|
|
64
65
|
|
|
65
66
|
- `dist/browser/chat.min.mjs`, the ESM browser entry.
|
|
66
|
-
- `dist/browser/engine-wllama.min.mjs
|
|
67
|
-
|
|
67
|
+
- `dist/browser/engine-wllama.min.mjs` and
|
|
68
|
+
`dist/browser/engine-litert.min.mjs`, the bundled engines, fetched only when
|
|
69
|
+
a matching model starts.
|
|
68
70
|
- `dist/node/chat.node.min.js` and `chat.node.min.mjs`, the CommonJS and ESM
|
|
69
|
-
Node entries, with the same engine
|
|
70
|
-
- `dist/assets/wllama/`, the wllama wasm binaries and compat worker
|
|
71
|
-
|
|
71
|
+
Node entries, with the same engine chunks beside them.
|
|
72
|
+
- `dist/assets/wllama/`, the wllama wasm binaries and compat worker.
|
|
73
|
+
- `dist/assets/litert/`, the LiteRT-LM wasm glue and binary.
|
|
72
74
|
- `dist/index.d.ts`, declarations for every export.
|
|
73
75
|
|
|
74
76
|
Build from source with `yarn build`; typecheck with `yarn typecheck`; test the
|
|
@@ -85,6 +87,7 @@ import {
|
|
|
85
87
|
ChatModelId,
|
|
86
88
|
LlmRole,
|
|
87
89
|
createChat,
|
|
90
|
+
preloadModel,
|
|
88
91
|
} from "@degreesign/chat";
|
|
89
92
|
|
|
90
93
|
const messages = [
|
|
@@ -92,6 +95,9 @@ const messages = [
|
|
|
92
95
|
{ role: LlmRole.user, content: "Hello" },
|
|
93
96
|
];
|
|
94
97
|
|
|
98
|
+
// Optional: download and load the model before the first send.
|
|
99
|
+
await preloadModel({ model: ChatModelId.qwen3_vl_2b });
|
|
100
|
+
|
|
95
101
|
const cancel = createChat({
|
|
96
102
|
model: ChatModelId.qwen3_vl_2b,
|
|
97
103
|
messages,
|
|
@@ -106,8 +112,10 @@ const cancel = createChat({
|
|
|
106
112
|
cancel();
|
|
107
113
|
```
|
|
108
114
|
|
|
109
|
-
There is no promise and no controller object
|
|
110
|
-
three callbacks, and `onStatus` reports `requesting: false`
|
|
115
|
+
There is no promise and no controller object on `createChat`. Every result
|
|
116
|
+
arrives through the three callbacks, and `onStatus` reports `requesting: false`
|
|
117
|
+
when the reply ends. `preloadModel` is the one promise: it resolves once the
|
|
118
|
+
model is loaded, or `false` when it cannot load.
|
|
111
119
|
|
|
112
120
|
Attach images to a user turn with `images: [dataUrl]`. The package sends them
|
|
113
121
|
only to a model with vision support, center-crops each image to a square,
|
|
@@ -120,6 +128,7 @@ the call and appends the assistant message after the last `onResponse`.
|
|
|
120
128
|
|
|
121
129
|
```ts
|
|
122
130
|
createChat(input: ChatInput): CancelChat
|
|
131
|
+
preloadModel(input: ChatPreloadInput): Promise<boolean>
|
|
123
132
|
```
|
|
124
133
|
|
|
125
134
|
Call it once per response. A new call aborts the previous stream. When the
|
|
@@ -148,6 +157,30 @@ requested model is already loaded, no download or load happens.
|
|
|
148
157
|
|
|
149
158
|
The model stays loaded between calls until a cancel frees it.
|
|
150
159
|
|
|
160
|
+
### Preload
|
|
161
|
+
|
|
162
|
+
```ts
|
|
163
|
+
preloadModel(input: ChatPreloadInput): Promise<boolean>
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
Call it before the first `createChat` to download and load the model early. It
|
|
167
|
+
resolves `true` when the runtime is ready and `false` when the model is unknown,
|
|
168
|
+
unsupported or failed. A later `createChat` for the same model reuses the loaded
|
|
169
|
+
runtime, and either call joins a load already in flight.
|
|
170
|
+
|
|
171
|
+
`ChatPreloadInput` is the subset of `ChatInput` that a preload needs:
|
|
172
|
+
|
|
173
|
+
| Field | Description |
|
|
174
|
+
| ----- | ----------- |
|
|
175
|
+
| `model` | `ChatModelId` to download and load. |
|
|
176
|
+
| `limits?` | Partial `LlmLimits`; `maxThreads` applies when the model loads. |
|
|
177
|
+
| `logging?` | Whether package and engine console logs are written. Defaults to `false`. |
|
|
178
|
+
| `onStatus?` | Receives a `ChatStatusSnapshot` on every progress and state change. |
|
|
179
|
+
|
|
180
|
+
The final snapshot reports `ready: true`, `phase: LlmPhase.load` and
|
|
181
|
+
`percent: 100` on success, or an `error` with `availability: ChatStatus.offline`
|
|
182
|
+
on failure. A preload owns no cancel function; a new load replaces it.
|
|
183
|
+
|
|
151
184
|
## Status
|
|
152
185
|
|
|
153
186
|
`ChatStatusSnapshot` is the only lifecycle signal:
|
|
@@ -156,7 +189,7 @@ The model stays loaded between calls until a cancel frees it.
|
|
|
156
189
|
| ----- | ----------- |
|
|
157
190
|
| `availability` | `ChatStatus.online`, `.away` or `.offline`, derived from `status`. |
|
|
158
191
|
| `status` | `LlmStatus`: `idle`, `downloading`, `ready`, `error` or `unsupported`. |
|
|
159
|
-
| `backend` | `LlmBackend.cpu` or `.none`. |
|
|
192
|
+
| `backend` | `LlmBackend.cpu`, `.gpu` or `.none`. |
|
|
160
193
|
| `supportsImages` | Whether the requested model accepts images. |
|
|
161
194
|
| `ready` | Whether a model is loaded and accepts a reply. |
|
|
162
195
|
| `loading` | Whether a download or load is in flight. |
|
|
@@ -171,20 +204,27 @@ with `requesting: false` marks the end of the call.
|
|
|
171
204
|
## Logging
|
|
172
205
|
|
|
173
206
|
Console output stays off by default. Pass `logging: true` in a call to receive
|
|
174
|
-
the package's censored engine logs and the engine's own logs for that
|
|
175
|
-
flag is per call, so a later call without it goes quiet again.
|
|
207
|
+
the package's censored engine logs and the wllama engine's own logs for that
|
|
208
|
+
call. The flag is per call, so a later call without it goes quiet again.
|
|
209
|
+
LiteRT-LM writes its wasm diagnostics straight to the console and does not
|
|
210
|
+
follow the flag.
|
|
176
211
|
|
|
177
212
|
## Models
|
|
178
213
|
|
|
179
|
-
| `ChatModelId` | Name | Reasoning | Images | CPU |
|
|
180
|
-
| ------------- | ---- | --------- | ------ | --- |
|
|
181
|
-
| `qwen3_vl_2b` | Qwen3-VL 2B | Yes | Yes | Yes |
|
|
214
|
+
| `ChatModelId` | Name | Reasoning | Images | CPU | WebGPU |
|
|
215
|
+
| ------------- | ---- | --------- | ------ | --- | ------ |
|
|
216
|
+
| `qwen3_vl_2b` | Qwen3-VL 2B | Yes | Yes | Yes | No |
|
|
217
|
+
| `gemma4_e2b` | Gemma 4 E2B | Yes | Yes | No | Yes |
|
|
182
218
|
|
|
183
|
-
The first call downloads the model through
|
|
219
|
+
The first call downloads the model through its engine and caches it. Later
|
|
184
220
|
calls reuse the loaded runtime. Only one model stays in memory.
|
|
185
221
|
|
|
186
222
|
Qwen3-VL 2B downloads a vision projector of about 445 MB with the model, so its
|
|
187
|
-
total download is about 1.5 GB.
|
|
223
|
+
total download is about 1.5 GB. It runs on the CPU engine.
|
|
224
|
+
|
|
225
|
+
Gemma 4 E2B downloads one `gemma-4-E2B-it.litertlm` file of about 2.6 GB. It
|
|
226
|
+
runs on the WebGPU engine with its vision encoder in the same file, so an
|
|
227
|
+
image prompt does not download a projector.
|
|
188
228
|
|
|
189
229
|
## Prompt assembly
|
|
190
230
|
|
|
@@ -216,22 +256,25 @@ user: {next turn}
|
|
|
216
256
|
|
|
217
257
|
## Engines and assets
|
|
218
258
|
|
|
219
|
-
The engine is chosen per
|
|
259
|
+
The engine is chosen per model:
|
|
220
260
|
|
|
221
|
-
1.
|
|
222
|
-
|
|
261
|
+
1. WebGPU through LiteRT-LM when the model has a `litert` artifact and the
|
|
262
|
+
browser exposes `navigator.gpu` and `WebAssembly`.
|
|
263
|
+
2. CPU through wllama when the model has a `cpu` artifact and the process
|
|
264
|
+
exposes `WebAssembly`.
|
|
265
|
+
3. `LlmStatus.unsupported` otherwise, including Node.
|
|
223
266
|
|
|
224
|
-
|
|
225
|
-
onProgress })` contract and
|
|
226
|
-
`contextSize`, `chat()` and `unload()`. `createChat` starts
|
|
227
|
-
model.
|
|
267
|
+
Both engines implement the internal `startLLM({ model, limits, signal,
|
|
268
|
+
onProgress })` contract and return the runtime object with `model`, `backend`,
|
|
269
|
+
`contextSize`, `chat()` and `unload()`. `createChat` starts the engine that
|
|
270
|
+
matches the requested model.
|
|
228
271
|
|
|
229
272
|
The CPU engine derives a raw thread count from the device, then caps it at
|
|
230
273
|
`limits.maxThreads`, default 2. The cap is applied after the raw maximum, so the
|
|
231
274
|
engine never exceeds the device or the caller budget.
|
|
232
275
|
|
|
233
|
-
|
|
234
|
-
|
|
276
|
+
The CPU engine needs cross-origin isolation for its threads. Serve the page with
|
|
277
|
+
both headers:
|
|
235
278
|
|
|
236
279
|
```
|
|
237
280
|
Cross-Origin-Opener-Policy: same-origin
|
|
@@ -250,21 +293,30 @@ self hosted. Chromium also accepts
|
|
|
250
293
|
changed. A page inside an iframe is isolated only when every ancestor sets the
|
|
251
294
|
same headers.
|
|
252
295
|
|
|
253
|
-
|
|
254
|
-
and
|
|
255
|
-
|
|
296
|
+
Each engine keeps its own cache. Loading a model unloads the previous runtime
|
|
297
|
+
and removes the previous model's cache. The CPU engine uses the wllama cache
|
|
298
|
+
manager; the GPU engine stores the single `.litertlm` file in Cache Storage
|
|
299
|
+
under `litertlm-models`. A quota failure purges the cache of the engine being
|
|
300
|
+
loaded. The GPU cache write is best effort: if the browser rejects it, the model
|
|
301
|
+
still loads from the network stream and the next call downloads it again.
|
|
302
|
+
|
|
303
|
+
A CPU vision model downloads its projector with the model and caches both. A
|
|
304
|
+
GPU vision model keeps its encoder in the single `.litertlm` file.
|
|
256
305
|
|
|
257
|
-
|
|
258
|
-
|
|
306
|
+
Both engines are bundled into `dist/browser` and `dist/node` as dynamic chunks.
|
|
307
|
+
The package ships the wllama binaries under `dist/assets/wllama` and the LiteRT
|
|
308
|
+
wasm under `dist/assets/litert`; model weights remain external.
|
|
259
309
|
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
310
|
+
LiteRT-LM needs WebGPU and the JSPI and relaxed SIMD wasm features, currently
|
|
311
|
+
available in Chromium. It loads a 22.7 MB wasm binary from `dist/assets/litert`
|
|
312
|
+
and the 2.6 GB model file. The GPU engine reports progress while the file
|
|
313
|
+
downloads and 100% of the load phase while the model compiles.
|
|
263
314
|
|
|
264
315
|
wllama needs the compat worker on the same origin. When the app serves the
|
|
265
316
|
package from `node_modules` in development, auto resolution works. In
|
|
266
317
|
production, copy `dist/assets/wllama` next to the app bundle so the resolved
|
|
267
|
-
asset URLs stay valid.
|
|
318
|
+
asset URLs stay valid. The LiteRT wasm glue finds its binary in the same
|
|
319
|
+
directory, so copy `dist/assets/litert` beside the bundle as well.
|
|
268
320
|
|
|
269
321
|
## Node.js
|
|
270
322
|
|
|
@@ -272,16 +324,18 @@ The package imports and runs without a DOM, so it works in scripts, tests, SSR
|
|
|
272
324
|
and server side orchestration. Everything except inference is available: prompt
|
|
273
325
|
assembly, budgets, status state and cancellation.
|
|
274
326
|
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
`
|
|
327
|
+
Both engines need a browser: wllama needs a worker and a wasm runtime, and
|
|
328
|
+
LiteRT-LM needs WebGPU. In Node, `createChat` reports
|
|
329
|
+
`LlmStatus.unsupported` and `ChatStatus.offline` through `onStatus`, and the
|
|
330
|
+
reply callbacks stay silent. `preloadModel` resolves `false` and reports the
|
|
331
|
+
same unsupported status.
|
|
278
332
|
|
|
279
333
|
```js
|
|
280
|
-
const { createChat, ChatModelId, LlmRole } = require("@degreesign/chat");
|
|
334
|
+
const { createChat, preloadModel, ChatModelId, LlmRole } = require("@degreesign/chat");
|
|
281
335
|
```
|
|
282
336
|
|
|
283
337
|
```js
|
|
284
|
-
import { createChat, ChatModelId, LlmRole } from "@degreesign/chat";
|
|
338
|
+
import { createChat, preloadModel, ChatModelId, LlmRole } from "@degreesign/chat";
|
|
285
339
|
```
|
|
286
340
|
|
|
287
341
|
The Node entries are `dist/node/chat.node.min.js` for `require` and
|
|
@@ -326,6 +380,7 @@ const useReply = () => {
|
|
|
326
380
|
| Type | Description |
|
|
327
381
|
| ---- | ----------- |
|
|
328
382
|
| `ChatInput` | One request: model, messages, budgets, reasoning and callbacks. |
|
|
383
|
+
| `ChatPreloadInput` | Model, limits, logging and status, the subset `preloadModel` needs. |
|
|
329
384
|
| `CancelChat` | The function returned by `createChat`. |
|
|
330
385
|
| `ChatMessage` | One prompt turn, `role`, `content` and optional `images`. |
|
|
331
386
|
| `ChatStatusSnapshot` | Availability, engine status, backend, progress and request state. |
|
|
@@ -356,10 +411,11 @@ No. The host passes the full prompt on every call and keeps the reply.
|
|
|
356
411
|
### Does it support images?
|
|
357
412
|
|
|
358
413
|
Yes, on models with vision support. Put image URLs in a user turn's `images`
|
|
359
|
-
array. Qwen3-VL 2B
|
|
360
|
-
engine. Each image is center-cropped to a square and
|
|
361
|
-
before the engine sees it. A model without vision drops
|
|
362
|
-
also has text; an image-only turn reports
|
|
414
|
+
array. Qwen3-VL 2B accepts images on the CPU engine and Gemma 4 E2B accepts
|
|
415
|
+
images on the WebGPU engine. Each image is center-cropped to a square and
|
|
416
|
+
resized to 288 x 288 before the engine sees it. A model without vision drops
|
|
417
|
+
images when the turn also has text; an image-only turn reports
|
|
418
|
+
`Model does not accept images`.
|
|
363
419
|
|
|
364
420
|
### Does it need a server?
|
|
365
421
|
|
|
@@ -368,20 +424,23 @@ includes no server component.
|
|
|
368
424
|
|
|
369
425
|
### Does it have runtime dependencies?
|
|
370
426
|
|
|
371
|
-
No runtime dependencies. `@wllama/wllama
|
|
372
|
-
build-time dependencies, bundled into the dynamic engine
|
|
373
|
-
assets, so consumers resolve no engine import and
|
|
374
|
-
the chunk they use.
|
|
427
|
+
No runtime dependencies. `@wllama/wllama`, `@wllama/wllama-compat` and
|
|
428
|
+
`@litert-lm/core` are build-time dependencies, bundled into the dynamic engine
|
|
429
|
+
chunks and their wasm assets, so consumers resolve no engine import and
|
|
430
|
+
download no engine code beyond the chunk they use.
|
|
375
431
|
|
|
376
432
|
### Where are the models stored?
|
|
377
433
|
|
|
378
|
-
The CPU engine owns its cache through the wllama cache manager. The
|
|
379
|
-
stores
|
|
434
|
+
The CPU engine owns its cache through the wllama cache manager. The GPU engine
|
|
435
|
+
stores its model in Cache Storage under `litertlm-models`. The package stores no
|
|
436
|
+
model data itself.
|
|
380
437
|
|
|
381
438
|
### Which model should I pick?
|
|
382
439
|
|
|
383
|
-
Qwen3-VL 2B
|
|
384
|
-
on
|
|
440
|
+
Qwen3-VL 2B supports reasoning and images on the CPU. Gemma 4 E2B supports
|
|
441
|
+
reasoning and images on WebGPU, with much faster image prompts but a larger
|
|
442
|
+
download and a slower first compile. Use Qwen for broad browser support and
|
|
443
|
+
Gemma when the host has Chromium WebGPU.
|
|
385
444
|
|
|
386
445
|
## Licensing
|
|
387
446
|
|
|
@@ -391,5 +450,6 @@ listed in `THIRD_PARTIES.md`. See `LICENSE`.
|
|
|
391
450
|
|
|
392
451
|
## Keywords
|
|
393
452
|
|
|
394
|
-
`@degreesign/chat`, on-device AI chat, local LLM, wllama,
|
|
395
|
-
headless chat, TypeScript, browser, speech friendly
|
|
453
|
+
`@degreesign/chat`, on-device AI chat, local LLM, wllama, litert, webgpu, gemma,
|
|
454
|
+
wasm, chat controller, headless chat, TypeScript, browser, speech friendly
|
|
455
|
+
replies.
|