laya-system-one 1.0.0 → 1.1.0-alpha.1
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 +299 -322
- package/bin/cli.js +119 -81
- package/bin/postinstall.js +96 -0
- package/models/model.manifest.json +107 -0
- package/package.json +68 -9
- package/src/agent.js +342 -201
- package/src/bpe-tokenizer.js +257 -0
- package/src/engine.js +142 -185
- package/src/index.js +2 -4
- package/src/laya-native.js +322 -0
- package/src/laya-wasm-pool.js +139 -0
- package/src/laya-wasm-worker.js +43 -0
- package/src/laya-wasm.js +78 -0
- package/src/model-resolver.js +579 -0
- package/src/server.js +61 -9
- package/src/tokenizer.js +132 -122
- package/src/wasm-pkg/laya_inference.d.ts +71 -0
- package/src/wasm-pkg/laya_inference.js +300 -0
- package/src/wasm-pkg/laya_inference_bg.wasm +0 -0
- package/src/wasm-pkg/laya_inference_bg.wasm.d.ts +16 -0
- package/src/wasm-pkg/package.json +17 -0
- package/.gitattributes +0 -1
package/README.md
CHANGED
|
@@ -1,322 +1,299 @@
|
|
|
1
|
-
# Laya System-One
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
|
53
|
-
|
|
54
|
-
|
|
|
55
|
-
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
|
153
|
-
| `
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
```
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
-
|
|
300
|
-
|
|
301
|
-
---
|
|
302
|
-
|
|
303
|
-
## ⚡ Hardware Acceleration Architecture
|
|
304
|
-
|
|
305
|
-
`laya-system-one` includes a hybrid native runtime that maximizes throughput based on the active runtime:
|
|
306
|
-
|
|
307
|
-
| Environment | Primary Provider | Fallback Provider | Typical Inference Latency |
|
|
308
|
-
| :--- | :--- | :--- | :--- |
|
|
309
|
-
| **Node.js** | Native C++ (`cpu`) | WebGPU (`webgpu`) | ~15 ms / query |
|
|
310
|
-
| **Bun** | WebAssembly SIMD (`wasm`) | WebGPU (`webgpu`) | ~25 ms / query |
|
|
311
|
-
| **Browser** | WebGPU (`navigator.gpu`) | WebAssembly SIMD (`wasm`) | ~18 ms / query |
|
|
312
|
-
|
|
313
|
-
---
|
|
314
|
-
|
|
315
|
-
## 📄 License & Attribution
|
|
316
|
-
|
|
317
|
-
- **License:** [Apache-2.0](LICENSE)
|
|
318
|
-
- **Author:** [Italo Almeida](https://github.com/italoalmeida0)
|
|
319
|
-
- **GitHub Repository:** [https://github.com/italoalmeida0/laya-system-one](https://github.com/italoalmeida0/laya-system-one)
|
|
320
|
-
|
|
321
|
-
### Upstream Attribution
|
|
322
|
-
This project incorporates and builds upon the foundational research and model architecture of **Laya** by [Convai Innovations](https://github.com/NandhaKishorM/laya) (licensed under Apache-2.0). All appropriate copyright notices and license requirements are preserved in compliance with Section 4 of the Apache License 2.0.
|
|
1
|
+
# Laya System-One
|
|
2
|
+
|
|
3
|
+
**System 1 decision engine** — a multilingual INT8 transformer that answers typed questions (choice / score / yes-no) about any text, in milliseconds, offline.
|
|
4
|
+
|
|
5
|
+
Wire-compatible with the **TypeSafe Jev** `/v1/systemone` protocol: send state + typed questions, get structured decisions back.
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install laya-system-one
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
```js
|
|
12
|
+
import { Laya } from 'laya-system-one';
|
|
13
|
+
|
|
14
|
+
const laya = await Laya.load(); // model is acquired on first use (once)
|
|
15
|
+
const out = await laya.predict(
|
|
16
|
+
'We were billed twice on the March invoice and want a refund.',
|
|
17
|
+
{
|
|
18
|
+
department: {
|
|
19
|
+
type: 'choice',
|
|
20
|
+
instructions: 'Which department should handle this?',
|
|
21
|
+
criteria: { billing: 'refunds and invoices', tech: 'bugs', sales: 'upgrades' }
|
|
22
|
+
},
|
|
23
|
+
churn: { type: 'noul', instructions: 'Is the user at churn risk?', threshold: 0.5 },
|
|
24
|
+
severity: { type: 'score', instructions: 'Urgency?', criteria: ['low', 'mid', 'high'] }
|
|
25
|
+
}
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
console.log(out.answers.department.choice); // → "billing"
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Or run it as a service:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
npx laya-system-one --port 8080
|
|
35
|
+
curl -X POST http://localhost:8080/v1/systemone \
|
|
36
|
+
-H "Content-Type: application/json" \
|
|
37
|
+
-d '{"state":"I was charged twice and need a refund.","questions":{"dept":{"type":"choice","instructions":"Which department?","criteria":{"billing":"refunds","tech":"bugs"}}}}'
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
## What it actually is
|
|
43
|
+
|
|
44
|
+
A single ONNX checkpoint (`model.onnx`, ~324 MB, INT8) trained to score answer options for three question types, plus a small runtime that:
|
|
45
|
+
|
|
46
|
+
1. renders the state and the question options into a prompt,
|
|
47
|
+
2. runs one forward pass,
|
|
48
|
+
3. turns the logits into calibrated probabilities.
|
|
49
|
+
|
|
50
|
+
The runtime is JavaScript (Node/Bun/browser) and executes that forward pass in exactly two ways — pick one with `--backend` or `LAYA_BACKEND`:
|
|
51
|
+
|
|
52
|
+
| backend | what it is | when to use |
|
|
53
|
+
|---|---|---|
|
|
54
|
+
| `native` **(default)** | the `laya-serve` binary bundled in the package (Rust: Axum + tokenizers + ONNX Runtime, statically linked) | fastest path, zero system dependencies, any OS |
|
|
55
|
+
| `wasm` | pure-Rust `tract` compiled to WASM, bundled in the package | browsers and extreme portability |
|
|
56
|
+
|
|
57
|
+
There are **no external runtime dependencies**: no `onnxruntime-*`, no `@huggingface/transformers`, nothing to download besides the model. The tokenizer itself is a pure-JS BPE implementation (`src/bpe-tokenizer.js`) verified token-for-token against the reference `tokenizers` crate that the native binary links — so both backends see exactly the same input ids.
|
|
58
|
+
|
|
59
|
+
> **Note on WebGPU:** earlier versions advertised WebGPU acceleration. Measured reality: ONNX Runtime's Node WebGPU execution provider falls back to CPU per-op with large overhead — it is consistently *slower* than the native backend. WebGPU is not used server-side.
|
|
60
|
+
|
|
61
|
+
---
|
|
62
|
+
|
|
63
|
+
## How the heavy pieces arrive
|
|
64
|
+
|
|
65
|
+
The entry package is small on purpose (**~8.5 MB**): source, tokenizer and the
|
|
66
|
+
wasm fallback. The binaries (150+ MB for every platform) and the model
|
|
67
|
+
(324 MB) are published as separate `@sys-one` packages and installed as
|
|
68
|
+
`optionalDependencies`, so npm fetches only what the machine needs:
|
|
69
|
+
|
|
70
|
+
| package | selected by | size |
|
|
71
|
+
| --- | --- | --- |
|
|
72
|
+
| `@sys-one/laya-serve-<os>-<arch>` | npm's `os`/`cpu` fields | 8–26 MB |
|
|
73
|
+
| `@sys-one/laya-serve-universal` | fallback for exotic platforms | ~76 MB |
|
|
74
|
+
| `@sys-one/laya-model-chunk-00` … `-12` | always (all 13) | ~24 MB each |
|
|
75
|
+
|
|
76
|
+
A `linux/x64` machine installs `laya-serve-linux-x64` and nothing else;
|
|
77
|
+
a `linux/ppc64` machine matches none of the specific packages and gets the
|
|
78
|
+
universal one. No package installs all of them.
|
|
79
|
+
|
|
80
|
+
Two things worth knowing about how the selection works, because both were
|
|
81
|
+
verified by experiment rather than assumed:
|
|
82
|
+
|
|
83
|
+
- **`libc` cannot select.** The field exists but is unreliable (undocumented
|
|
84
|
+
shorthand, and real packages have shipped bugs where `--libc=glibc` pulls
|
|
85
|
+
the musl build too). So the glibc and musl builds of a Linux arch travel
|
|
86
|
+
**together** in one package, and the loader picks at runtime.
|
|
87
|
+
- **Exclusions are AND-ed.** `os: ["!darwin", "!win32"]` means "not both",
|
|
88
|
+
not "either", so the complement of the specific packages cannot be spelled
|
|
89
|
+
out. The universal package lists the exotic cpus and OSes explicitly
|
|
90
|
+
instead: skipped wherever a specific package applies, selected everywhere
|
|
91
|
+
else.
|
|
92
|
+
|
|
93
|
+
The model is assembled from its chunk packages on first use and verified
|
|
94
|
+
against `models/model.manifest.json` (sha256 of the model *and* of every
|
|
95
|
+
chunk), written atomically — a killed process cannot leave a corrupt model
|
|
96
|
+
behind, the next run retries. The order is:
|
|
97
|
+
|
|
98
|
+
1. `LAYA_MODEL_PATH` — explicit path to a `model.onnx` (file or directory);
|
|
99
|
+
2. a `model.onnx` already present in the package's `models/` directory;
|
|
100
|
+
3. `~/.cache/laya-system-one/model.onnx` — previously assembled copy;
|
|
101
|
+
4. the installed `@sys-one/laya-model-chunk-*` packages (the normal path);
|
|
102
|
+
5. the npm registry, if the chunks were not installed as dependencies;
|
|
103
|
+
6. the GitHub Release asset — **opt-in only** (`LAYA_ALLOW_GITHUB_FALLBACK=1`),
|
|
104
|
+
kept for compatibility with 1.0.0 installs.
|
|
105
|
+
|
|
106
|
+
### Offline / air-gapped installs
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
LAYA_PREFETCH_MODEL=1 npm install laya-system-one # fetch during install
|
|
110
|
+
# or point at a model you already have:
|
|
111
|
+
LAYA_MODEL_PATH=/opt/models/model.onnx
|
|
112
|
+
# or drop chunk files somewhere and point at them:
|
|
113
|
+
LAYA_MODEL_CHUNKS_DIR=/opt/models/chunks
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
---
|
|
117
|
+
|
|
118
|
+
## API
|
|
119
|
+
|
|
120
|
+
### `Laya.load(options)` → `laya`
|
|
121
|
+
|
|
122
|
+
| option | default | description |
|
|
123
|
+
|---|---|---|
|
|
124
|
+
| `backend` | `'native'` | `native` \| `wasm` (or env `LAYA_BACKEND`) |
|
|
125
|
+
| `modelDir` | `<package>/models` | where `model.onnx` and `tokenizer.json` live |
|
|
126
|
+
| `apiKey` | `null` | Bearer token required by the HTTP layer |
|
|
127
|
+
| `port` / `host` | `0` / `127.0.0.1` | where the native server binds |
|
|
128
|
+
|
|
129
|
+
### `laya.predict(state, questions, model?)` → `Promise<Answer>`
|
|
130
|
+
|
|
131
|
+
`state` is a string, object or array (serialized as JSON). `questions` is a map of question definitions:
|
|
132
|
+
|
|
133
|
+
| type | `criteria` | answer |
|
|
134
|
+
|---|---|---|
|
|
135
|
+
| `choice` | object (label → meaning) or array | `{ type: 'choice', choice, probabilities, confidence }` |
|
|
136
|
+
| `score` | array of ordered levels | `{ type: 'score', score, legend, probabilities, confidence }` |
|
|
137
|
+
| `noul` | optional `{false, true}` text | `{ type: 'noul', noul, confidence, threshold?, decision? }` |
|
|
138
|
+
|
|
139
|
+
`noul` returns `noul` ∈ [0,1] (probability of *true*). With a `threshold`, a boolean `decision` is added. All fields round to 4 decimals.
|
|
140
|
+
|
|
141
|
+
### HTTP server
|
|
142
|
+
|
|
143
|
+
```js
|
|
144
|
+
import { serve } from 'laya-system-one';
|
|
145
|
+
const srv = await serve({ port: 8080, apiKey: process.env.LAYA_API_KEY });
|
|
146
|
+
console.log(srv.url); // http://localhost:8080
|
|
147
|
+
await srv.close(); // releases the engine and any child process
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
| endpoint | method | body | response |
|
|
151
|
+
|---|---|---|---|
|
|
152
|
+
| `/v1/systemone` | `POST` | `{ state, questions, model? }` | `{ model, answers, usage }` |
|
|
153
|
+
| `/health` | `GET` | – | `{ status, model, backend, protocol }` |
|
|
154
|
+
|
|
155
|
+
Errors: `401` missing/invalid API key, `422` invalid payload, `404` unknown route, `413` body over 4 MB.
|
|
156
|
+
|
|
157
|
+
> The `native` backend runs its own internal HTTP server for the engine;
|
|
158
|
+
> `serve()` keeps it on a private loopback port and never exposes it.
|
|
159
|
+
|
|
160
|
+
---
|
|
161
|
+
|
|
162
|
+
## CLI
|
|
163
|
+
|
|
164
|
+
```bash
|
|
165
|
+
npx laya-system-one --port 8080 --backend native
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
```
|
|
169
|
+
--port <number> HTTP port (default 8080, or PORT env)
|
|
170
|
+
--host <string> bind address (default 0.0.0.0, or HOST env)
|
|
171
|
+
--backend <type> native | wasm (default native)
|
|
172
|
+
--api-key <string> require Bearer token auth
|
|
173
|
+
--help / --version
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
---
|
|
177
|
+
|
|
178
|
+
## Environment variables
|
|
179
|
+
|
|
180
|
+
| variable | effect |
|
|
181
|
+
|---|---|
|
|
182
|
+
| `LAYA_BACKEND` | `native` \| `wasm` |
|
|
183
|
+
| `LAYA_MODEL_PATH` | explicit `model.onnx` (file or directory) |
|
|
184
|
+
| `LAYA_MODEL_CHUNKS_DIR` | directory with chunk files / chunk packages |
|
|
185
|
+
| `LAYA_MODEL_URL` | override the download URL of the model asset |
|
|
186
|
+
| `LAYA_CACHE_DIR` | where downloaded models are cached |
|
|
187
|
+
| `LAYA_PREFETCH_MODEL` | `1` = fetch the model during `npm install` |
|
|
188
|
+
| `LAYA_SKIP_MODEL_DOWNLOAD` | `1` = never download, never hint |
|
|
189
|
+
| `LAYA_API_KEY` / `API_KEY` | require `Authorization: Bearer <key>` |
|
|
190
|
+
| `LAYA_SERVE_BIN` | explicit path to the `laya-serve` binary |
|
|
191
|
+
|
|
192
|
+
---
|
|
193
|
+
|
|
194
|
+
## Performance
|
|
195
|
+
|
|
196
|
+
`npm run bench` measures it on your own hardware and writes a JSON report.
|
|
197
|
+
|
|
198
|
+
Measured by CI on every platform we ship a binary for (the bundled
|
|
199
|
+
`laya-serve` binary, 4 questions per call). Shared GitHub runners vary
|
|
200
|
+
~20% between runs, so treat these as orders of magnitude, not promises -
|
|
201
|
+
run `npm run bench` on your own hardware for exact numbers:
|
|
202
|
+
|
|
203
|
+
| platform | init | cold question | warm avg (4 q) | warm p50 / p95 | 1 q per call | throughput |
|
|
204
|
+
|---|---|---|---|---|---|---|
|
|
205
|
+
| **linux-arm64** (musl) | 1.9 s | 247 ms | 154 ms | 143 / 205 | 36 ms | 28.1 q/s |
|
|
206
|
+
| **linux-arm64** (glibc) | 1.6 s | 184 ms | 145 ms | 142 / 163 | 40 ms | 24.9 q/s |
|
|
207
|
+
| **win-x64** | 1.7 s | 175 ms | 149 ms | 142 / 175 | 40 ms | 24.8 q/s |
|
|
208
|
+
| **win-arm64** | 1.4 s | 231 ms | 178 ms | 174 / 204 | 47 ms | 21.2 q/s |
|
|
209
|
+
| **mac-arm64** | 1.3 s | 268 ms | 220 ms | 218 / 250 | 51 ms | 19.5 q/s |
|
|
210
|
+
| **linux-x64** (musl) | 2.3 s | 387 ms | 253 ms | 239 / 311 | 59 ms | 16.9 q/s |
|
|
211
|
+
| **linux-x64** (glibc) | 2.5 s | 331 ms | 264 ms | 259 / 322 | 63 ms | 15.8 q/s |
|
|
212
|
+
| **mac-x64** (Intel, ORT 1.23) | 2.9 s | 387 ms | 360 ms | 356 / 398 | 84 ms | 11.9 q/s |
|
|
213
|
+
|
|
214
|
+
`init` is loading the model, `cold` is the very first question (warmup and
|
|
215
|
+
arena allocation), and the warm numbers are the sustained latency.
|
|
216
|
+
|
|
217
|
+
The `wasm` fallback is ~100x slower by design: `tract` specializes the whole
|
|
218
|
+
model per input shape, so it runs a fixed padded shape (see
|
|
219
|
+
`LayaEngine.padForWasm`) and pays for every position - about 6 s per question
|
|
220
|
+
in the small bucket, 1.8 s to load. It exists for browsers and exotic
|
|
221
|
+
platforms, not for throughput.
|
|
222
|
+
|
|
223
|
+
**If you are on a platform we ship a binary for and you see the wasm backend
|
|
224
|
+
being used, that is a bug** - the install is broken. The tests fail on
|
|
225
|
+
purpose in that situation (`LAYA_ALLOW_WASM_FALLBACK=1` is the only way to
|
|
226
|
+
accept the fallback).
|
|
227
|
+
|
|
228
|
+
## Size
|
|
229
|
+
|
|
230
|
+
| component | size |
|
|
231
|
+
|---|---|
|
|
232
|
+
| npm package | ~88 MB (native binaries for 6 platforms + WASM + tokenizer) |
|
|
233
|
+
| model asset | ~324 MB (acquired once, cached, checksum-verified) |
|
|
234
|
+
| disk after install + model | ~560 MB |
|
|
235
|
+
| Docker (Debian slim + Bun + package) | ~450 MB image |
|
|
236
|
+
|
|
237
|
+
---
|
|
238
|
+
|
|
239
|
+
## Requirements
|
|
240
|
+
|
|
241
|
+
| | |
|
|
242
|
+
|---|---|
|
|
243
|
+
| **Node.js** | ≥ 18.17 (zero runtime dependencies) |
|
|
244
|
+
| **Bun** | ≥ 1.0 (fully supported) |
|
|
245
|
+
| **Browsers** | WASM backend (no install required) |
|
|
246
|
+
| **OS** | Linux (glibc + musl/Alpine), macOS (arm64), Windows (x64 + arm64) |
|
|
247
|
+
| **Docker** | works on Debian slim, Ubuntu, Alpine |
|
|
248
|
+
|
|
249
|
+
The `native` backend needs nothing installed: the binary is statically linked and ships in the package (for musl/Alpine it ships as a single self-extracting bundle with its own `lib/`).
|
|
250
|
+
|
|
251
|
+
---
|
|
252
|
+
|
|
253
|
+
## Storage
|
|
254
|
+
|
|
255
|
+
| what | where | size |
|
|
256
|
+
|---|---|---|
|
|
257
|
+
| package (code + binaries) | `node_modules/laya-system-one` | ~88 MB unpacked |
|
|
258
|
+
| model asset | `models/model.onnx` or `~/.cache/laya-system-one/` | ~324 MB |
|
|
259
|
+
| chunk packages | `dist/release/model-chunks/` (release artifacts) | ~324 MB |
|
|
260
|
+
|
|
261
|
+
The model is written to the package directory when it is writable, otherwise to the user cache — so global installs (`npm i -g`) and read-only containers work out of the box.
|
|
262
|
+
|
|
263
|
+
---
|
|
264
|
+
|
|
265
|
+
## Development
|
|
266
|
+
|
|
267
|
+
```bash
|
|
268
|
+
npm install
|
|
269
|
+
npm test # unit + packaging (fast, offline)
|
|
270
|
+
npm run test:integration # real model + tokenizer
|
|
271
|
+
npm run test:e2e # HTTP protocol + CLI + lifecycle
|
|
272
|
+
npm run test:install # pack + install into a clean dir + run
|
|
273
|
+
npm run smoke # one-shot human-readable verification
|
|
274
|
+
npm run lint # syntax + packaging + docs consistency gate
|
|
275
|
+
npm run tokenizer:diff # prove the JS tokenizer == the native binary's
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
The model-distribution pipeline (the reason the 324 MB asset can live on npm):
|
|
279
|
+
|
|
280
|
+
```bash
|
|
281
|
+
npm run model:chunks # split models/model.onnx into chunk packages
|
|
282
|
+
npm run model:assemble # reassemble from the chunks (byte-identical)
|
|
283
|
+
npm run model:verify # checksum the model against the manifest
|
|
284
|
+
npm run model:publish # publish the chunk packages to npm
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
CI runs the whole matrix (OS × Node) plus model-backed integration tests on every push — see `.github/workflows/ci.yml`.
|
|
288
|
+
|
|
289
|
+
## Migrating from 1.0.0
|
|
290
|
+
|
|
291
|
+
1.0.0 on npm was a different codebase (ONNX Runtime only, model embedded, single language). The `/v1/systemone` wire protocol is unchanged, so HTTP clients keep working. Node API changes:
|
|
292
|
+
|
|
293
|
+
- `predict(state, questions)` takes a *map* of questions (1.0.0 took a single question and returned a bare value);
|
|
294
|
+
- `server.js`/`client.js` were replaced by `serve()` + `Laya.load()`;
|
|
295
|
+
- the model is no longer embedded in the package — it is acquired on first use (see above).
|
|
296
|
+
|
|
297
|
+
## License
|
|
298
|
+
|
|
299
|
+
Apache-2.0 — see [LICENSE](LICENSE).
|