jev-web 0.3.2 → 0.4.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/LICENSE +21 -0
- package/README.md +237 -25
- package/docs/demo-negation.png +0 -0
- package/docs/demo-typed.png +0 -0
- package/docs/demo.gif +0 -0
- package/package.json +15 -2
- package/src/bekko.mjs +391 -0
- package/src/index.mjs +41 -0
- package/src/laya.mjs +15 -10
- package/src/questions.mjs +1 -1
- package/src/registry.mjs +30 -3
- package/src/strands.mjs +535 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 warsang
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,13 +1,145 @@
|
|
|
1
1
|
# jev-web
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://www.npmjs.com/package/jev-web)
|
|
4
|
+
[](./LICENSE)
|
|
5
|
+
[](https://github.com/warsang/jev-web/actions/workflows/test.yml)
|
|
6
|
+
|
|
7
|
+
Run **typed-decision models** in the browser. One *state* text
|
|
4
8
|
plus any number of typed *questions* (`choice` / `score` / `noul`) go in; a
|
|
5
|
-
calibrated probability distribution per question comes back from a single
|
|
6
|
-
forward pass
|
|
9
|
+
calibrated probability distribution per question comes back from a **single
|
|
10
|
+
forward pass**. No server, no API keys, no network calls after the weights are
|
|
11
|
+
cached.
|
|
12
|
+
|
|
13
|
+
Three model families ship today — open-jev (fused DeBERTa graph), Laya
|
|
14
|
+
(ModernBERT encoder + typed head), Strands Decider 2B (Qwen torso + pointer
|
|
15
|
+
head), and Bekko System One v0 (Ettin cross-encoder) — behind one registry:
|
|
16
|
+
`createDecisionRuntime({ family, fallback })`.
|
|
17
|
+
|
|
18
|
+
This is a generic runtime, not a classifier for any particular domain. You bring
|
|
19
|
+
the state text and the questions; the model answers by choosing among the options
|
|
20
|
+
you give it.
|
|
21
|
+
|
|
22
|
+
## ▶ Live demo
|
|
23
|
+
|
|
24
|
+
**https://warsang.github.io/jev-web/**
|
|
25
|
+
|
|
26
|
+
An interactive playground that runs this exact package: type a state, add typed
|
|
27
|
+
questions, and watch the model and a hand-written keyword baseline answer
|
|
28
|
+
side by side. It also counts your actual outbound requests, so the
|
|
29
|
+
"offline / private / no server" claim is checkable rather than asserted.
|
|
30
|
+
|
|
31
|
+

|
|
32
|
+
|
|
33
|
+
<sub>The frames are captured from the live demo at
|
|
34
|
+
[warsang.github.io/jev-web](https://warsang.github.io/jev-web/) — site → typed
|
|
35
|
+
decisions with latency → the negation case where the rules and the model
|
|
36
|
+
disagree → the comparison matrix.</sub>
|
|
37
|
+
|
|
38
|
+
Three questions, one forward pass, and a hard label from the rule parser that
|
|
39
|
+
could not answer the third question at all:
|
|
7
40
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
41
|
+

|
|
42
|
+
|
|
43
|
+
## Quickstart
|
|
44
|
+
|
|
45
|
+
```js
|
|
46
|
+
import { createDecider } from "jev-web";
|
|
47
|
+
|
|
48
|
+
const decider = await createDecider(); // ~340 MB of weights, once
|
|
49
|
+
const { answers } = await decider.decide(
|
|
50
|
+
"I was charged twice for the same order.",
|
|
51
|
+
[{ type: "noul", instructions: "The customer is asking for a refund." }],
|
|
52
|
+
);
|
|
53
|
+
|
|
54
|
+
answers[0].noul; // 0.92 — p(yes)
|
|
55
|
+
answers[0].probabilities; // { no: 0.08, yes: 0.92 }
|
|
56
|
+
answers[0].confidence; // 0.92
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
That is the whole integration. The first call downloads the weights and the
|
|
60
|
+
browser caches them; every later call — and every later visit, including
|
|
61
|
+
offline — is local. **Zero dependencies**: `@huggingface/transformers` is an
|
|
62
|
+
optional peer, imported lazily.
|
|
63
|
+
|
|
64
|
+
## What comes back
|
|
65
|
+
|
|
66
|
+
```js
|
|
67
|
+
const { answers } = await decider.decide(
|
|
68
|
+
"I was charged twice for the same order and nobody answers my emails. I want my money back now.",
|
|
69
|
+
[
|
|
70
|
+
{ type: "choice", instructions: "Which product area is the message about?",
|
|
71
|
+
options: ["fees & charges", "refund & dispute", "card", "other"] },
|
|
72
|
+
{ type: "noul", instructions: "The customer is asking for a refund." },
|
|
73
|
+
{ type: "score", instructions: "How negative is the message?",
|
|
74
|
+
options: ["very negative", "negative", "neutral", "positive", "very positive"] },
|
|
75
|
+
],
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
// [
|
|
79
|
+
// { type: "choice", choice: "fees & charges", index: 0,
|
|
80
|
+
// probabilities: { "fees & charges": 0.796, "refund & dispute": 0.169,
|
|
81
|
+
// card: 0.019, other: 0.016 }, confidence: 0.796 },
|
|
82
|
+
// { type: "noul", noul: 0.927,
|
|
83
|
+
// probabilities: { no: 0.073, yes: 0.927 }, confidence: 0.927 },
|
|
84
|
+
// { type: "score", score: 0.866, level: 1,
|
|
85
|
+
// probabilities: { "very negative": 0.336, negative: 0.478, neutral: 0.175,
|
|
86
|
+
// positive: 0.008, "very positive": 0.003 }, confidence: 0.478 },
|
|
87
|
+
// ]
|
|
88
|
+
// timings: { totalMs, inferMs } · length, truncated, prompts
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Three questions, **one** forward pass, and every question carries the full
|
|
92
|
+
distribution — not just a label. That last part is the point: a hard label
|
|
93
|
+
cannot be thresholded, routed by confidence, or second-guessed, and the
|
|
94
|
+
`choice` answer above is a genuine 79.6 / 16.9 split rather than a coin flip you
|
|
95
|
+
find out about later.
|
|
96
|
+
|
|
97
|
+
## jev-web vs. calling a hosted LLM API with `fetch()`
|
|
98
|
+
|
|
99
|
+
| | jev-web | Hosted LLM API via `fetch()` |
|
|
100
|
+
|---|---|---|
|
|
101
|
+
| **Cost per decision** | **$0.00** — the weights are static files | per-token, forever; a busy form multiplies it by every visitor |
|
|
102
|
+
| **Latency** | one local forward pass, measured live on the demo page | typically 0.4–3 s, plus a network round trip |
|
|
103
|
+
| **Network at answer time** | **0 requests** — works in airplane mode | one round trip per call, minimum |
|
|
104
|
+
| **Output shape** | **calibrated distribution per question, every question in one pass** | prose you then have to regex back into a distribution |
|
|
105
|
+
| **Determinism** | same input → same probabilities, always | temperature 0 + structured outputs helps; still not guaranteed |
|
|
106
|
+
| **Privacy** | your text never leaves the device | your users' text is sent to a third party on every call |
|
|
107
|
+
| **Ops burden** | static files on a CDN, no key, no rate limit | key management, quotas, rate limits, uptime, spend alerts |
|
|
108
|
+
| **Ceiling** | only *picks among options you supply* — never writes, never explains | general purpose; will happily invent an option you never offered |
|
|
109
|
+
|
|
110
|
+
**The honest trade.** jev-web is a classifier, not a chatbot. If you need
|
|
111
|
+
free-form answers you need a generator. If you need a routing decision, a score,
|
|
112
|
+
or a yes/no with a confidence you can threshold on, this is the cheaper, faster
|
|
113
|
+
and private one — and it is also *smaller*: a 0.5 B model plus your options
|
|
114
|
+
beats a frontier model plus a parsing layer, on every axis except generality.
|
|
115
|
+
|
|
116
|
+
## Try it before you install it
|
|
117
|
+
|
|
118
|
+
The live demo covers the interesting cases, and the presets are chosen to break
|
|
119
|
+
keyword parsing rather than to flatter the model:
|
|
120
|
+
|
|
121
|
+
| preset | what it shows |
|
|
122
|
+
|---|---|
|
|
123
|
+
| **Banking triage** | easy text — a fair rule can match, the model adds a confidence |
|
|
124
|
+
| **Negation trap** | *"This is not about a refund. My card was stolen…"* — the keyword rule routes to `refund & dispute`; the model routes to `card` (53.3%) and answers the refund question **no** (0.101) |
|
|
125
|
+
| **Contradictory review** | praise and complaint in one sentence; keyword polarity reads the praise |
|
|
126
|
+
| **Mixed intent** | two intents in one state — a hard label must pick one, a distribution does not have to |
|
|
127
|
+
| **Out of domain** | no in-domain signal at all; the rule parser has nothing to say |
|
|
128
|
+
|
|
129
|
+
The negation case is the one worth looking at. The state is *"This is not about
|
|
130
|
+
a refund. My card was stolen in Madrid and the bank refuses to help. I never
|
|
131
|
+
received the money back I asked about."*
|
|
132
|
+
|
|
133
|
+

|
|
134
|
+
|
|
135
|
+
The rule parser finds the word `refund` and routes to **refund & dispute** — the
|
|
136
|
+
exact opposite of what the customer meant. The model routes to **card** (53.3%)
|
|
137
|
+
and answers *"the customer is asking for a refund"* with **no** (0.101). Both
|
|
138
|
+
the bad routing and the inversion come from the same clause, and neither is
|
|
139
|
+
visible in a hard label.
|
|
140
|
+
|
|
141
|
+
The demo also tracks outbound requests through the Performance API: you will see
|
|
142
|
+
~14 requests while the weights load, then **0** for every decision after that.
|
|
11
143
|
|
|
12
144
|
## Install
|
|
13
145
|
|
|
@@ -22,7 +154,7 @@ lazily). Hosts that already ship their own copy can inject it via
|
|
|
22
154
|
## Use
|
|
23
155
|
|
|
24
156
|
```js
|
|
25
|
-
import { createDecider } from "jev-web";
|
|
157
|
+
import { createDecider, DEFAULT_MODEL, DEFAULT_REVISION } from "jev-web";
|
|
26
158
|
|
|
27
159
|
const decider = await createDecider({
|
|
28
160
|
// defaults to the reference open-jev DeBERTa-v3-large ONNX export,
|
|
@@ -46,12 +178,6 @@ const { answers } = await decider.decide(
|
|
|
46
178
|
},
|
|
47
179
|
],
|
|
48
180
|
);
|
|
49
|
-
|
|
50
|
-
// [
|
|
51
|
-
// { type: "choice", choice: "refund & dispute", index: 1, probabilities: {...}, confidence: 0.61 },
|
|
52
|
-
// { type: "noul", noul: 0.93, probabilities: { no: 0.07, yes: 0.93 }, confidence: 0.93 },
|
|
53
|
-
// { type: "score", score: 0.4, level: 1, probabilities: {...}, confidence: 0.55 },
|
|
54
|
-
// ]
|
|
55
181
|
```
|
|
56
182
|
|
|
57
183
|
## Question types
|
|
@@ -90,7 +216,6 @@ The export must add the `[STATE]`, `[Q]`, `[OPT]` markers to its tokenizer,
|
|
|
90
216
|
take `input_ids, attention_mask, seg, pair_q, pair_opt`, and return per-pair
|
|
91
217
|
`logits`. `typedMarkerIds()` fails loudly when a repo is missing them.
|
|
92
218
|
|
|
93
|
-
|
|
94
219
|
## Multiple families (registry)
|
|
95
220
|
|
|
96
221
|
`jev-web` is a multi-family runtime: `createDecisionRuntime({ family, fallback })`
|
|
@@ -100,10 +225,10 @@ tries the requested family and then any fallbacks, returning the shared contract
|
|
|
100
225
|
```js
|
|
101
226
|
import { createDecisionRuntime, listDecisionFamilies } from "jev-web";
|
|
102
227
|
|
|
103
|
-
listDecisionFamilies(); // ["open-jev", "laya"]
|
|
228
|
+
listDecisionFamilies(); // ["open-jev", "laya", "strands-decider", "bekko"]
|
|
104
229
|
const runtime = await createDecisionRuntime({
|
|
105
|
-
family: "
|
|
106
|
-
fallback: ["open-jev"],
|
|
230
|
+
family: "strands-decider",
|
|
231
|
+
fallback: ["laya", "open-jev"],
|
|
107
232
|
onFallback: ({ family, error }) => console.warn(family, error),
|
|
108
233
|
});
|
|
109
234
|
const { answers, truncated, length, prompts, timings } = await runtime.decide(state, questions);
|
|
@@ -158,6 +283,89 @@ Lower-level helpers are exported for custom runtimes:
|
|
|
158
283
|
`buildLayaSequence`, `collateLayaItems`, `layaAnswersFromLogits`,
|
|
159
284
|
`layaTokenizerAdapter`, `renderLayaOptions`, `layaTempBucket`, `clampTemperature`.
|
|
160
285
|
|
|
286
|
+
## Strands Decider family (Qwen torso + pointer head)
|
|
287
|
+
|
|
288
|
+
`createStrandsDecider(options)` runs
|
|
289
|
+
[Strands Decider 2B](https://strandsagents.com/blog/introducing-strands-decider/),
|
|
290
|
+
a 2B-parameter decision model: a Qwen3.5-2B torso (LoRA merged) with its LM head
|
|
291
|
+
replaced by a pointer head that scores each option from its own hidden state
|
|
292
|
+
against the `<answer>` position. One fused ONNX graph, one forward pass for the
|
|
293
|
+
whole batch:
|
|
294
|
+
|
|
295
|
+
```js
|
|
296
|
+
import { createStrandsDecider } from "jev-web";
|
|
297
|
+
|
|
298
|
+
const decider = await createStrandsDecider({
|
|
299
|
+
// defaults to onnx-community/strands-decider-2B-hobson-v19-ONNX @ pinned
|
|
300
|
+
// revision, q8 (onnx/model_quantized.onnx)
|
|
301
|
+
onProgress: (p) => console.log(p.phase, p.loaded, p.total),
|
|
302
|
+
});
|
|
303
|
+
const { answers } = await decider.decide(stateText, questions);
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
Protocol implemented here (mirrors the reference Python runtime):
|
|
307
|
+
|
|
308
|
+
- prompt `<state>…</state><question type="{noul|choice|score}">…<options>\n1. …\n</options></question><answer>`;
|
|
309
|
+
- graph `input_ids, attention_mask, answer_pos, option_pos → logits`, where
|
|
310
|
+
`answer_pos` is each row's last real token (the `<answer>` pooling position)
|
|
311
|
+
and `option_pos` carries each option line's last-token position (padded with
|
|
312
|
+
0); the prompt is tokenised in chunks at pre-tokeniser piece boundaries so
|
|
313
|
+
those positions are exact with no offset mapping;
|
|
314
|
+
- choice questions accept `criteria` as `{label: description}` (mirroring the
|
|
315
|
+
reference schema); noul slots are fixed `false`/`true` with overridable
|
|
316
|
+
rubric descriptions;
|
|
317
|
+
- the question gets first claim on the 4096-token window (up to 75%), the state
|
|
318
|
+
is truncated from the right into what remains, and an over-long question is
|
|
319
|
+
truncated from the front, keeping the options and `<answer>`;
|
|
320
|
+
- per-kind calibration from the checkpoint (`noul` 0.911 / `choice` 0.734 /
|
|
321
|
+
`score` 1.328); `score` confidence is ordinal (spread-based, with the
|
|
322
|
+
training smoothing floor), not max-probability.
|
|
323
|
+
|
|
324
|
+
Lower-level helpers are exported for custom runtimes:
|
|
325
|
+
`renderStrandsQuestion`, `renderStrandsState`, `renderStrandsContent`,
|
|
326
|
+
`buildStrandsBatch`, `collateStrandsItems`, `strandsOptionTokenIndices`,
|
|
327
|
+
`strandsAnswersFromLogits`, `strandsChoiceConfidence`, `strandsScoreConfidence`.
|
|
328
|
+
|
|
329
|
+
## Bekko family (Ettin cross-encoder)
|
|
330
|
+
|
|
331
|
+
`createBekkoDecider(options)` runs
|
|
332
|
+
[Bekko System One v0](https://github.com/hotchpotch/bekko-system-one), a
|
|
333
|
+
cross-encoder that scores each (query, candidate) pair: the query renders as
|
|
334
|
+
`Instruction: {instruction}\nState: {state}` and every option becomes a
|
|
335
|
+
`Candidate: {id}: {description}` document. One ONNX graph
|
|
336
|
+
(`prefix_ids, prefix_mask, doc_ids, doc_mask, owners → logits [N, tasks]`),
|
|
337
|
+
one forward per question, with candidates batched by the reference
|
|
338
|
+
attention budget:
|
|
339
|
+
|
|
340
|
+
```js
|
|
341
|
+
import { createBekkoDecider } from "jev-web";
|
|
342
|
+
|
|
343
|
+
const decider = await createBekkoDecider({
|
|
344
|
+
// defaults to hotchpotch/bekko-system-one-v0-17m @ pinned revision,
|
|
345
|
+
// onnx_browser/ (29 MB)
|
|
346
|
+
onProgress: (p) => console.log(p.phase, p.loaded, p.total),
|
|
347
|
+
});
|
|
348
|
+
const { answers } = await decider.decide(stateText, questions);
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
Protocol implemented here (mirrors the reference browser runtime,
|
|
352
|
+
`browser/src/core.js` + `decision.js`, MIT):
|
|
353
|
+
|
|
354
|
+
- query `[cls] Instruction: …\nState: … [sep]`; instruction and state split the
|
|
355
|
+
4096-token budget 50/50 with spillover; candidates truncate to 2048 tokens;
|
|
356
|
+
- choice questions accept `criteria` as `{label: description}`; noul slots are
|
|
357
|
+
fixed `true`/`false` with authored meanings (overridable via criteria);
|
|
358
|
+
- score levels map to numeric candidate values `0..n-1`; the answer is the
|
|
359
|
+
expected value plus the argmax level and a normalised score.
|
|
360
|
+
|
|
361
|
+
Larger siblings are available via the exported model/revision constants:
|
|
362
|
+
`BEKKO_68M_MODEL` / `BEKKO_68M_REVISION` and `BEKKO_400M_MODEL` /
|
|
363
|
+
`BEKKO_400M_REVISION` (68M and 400M, same `onnx_browser/` layout).
|
|
364
|
+
|
|
365
|
+
Lower-level helpers are exported for custom runtimes:
|
|
366
|
+
`renderBekkoContent`, `renderBekkoRequest`, `tokenizeBekkoRequest`,
|
|
367
|
+
`collateBekkoDocs`, `bekkoSoftmax`, `bekkoAnswerFromLogits`.
|
|
368
|
+
|
|
161
369
|
## Worker / thread
|
|
162
370
|
|
|
163
371
|
`jev-web` does not bundle a worker, so hosts can create one the way their
|
|
@@ -174,18 +382,22 @@ import { createDecider } from "jev-web";
|
|
|
174
382
|
|
|
175
383
|
- `createDecider(options)` → open-jev family: `{ info, decide, decideMany, dispose }`
|
|
176
384
|
- `createLayaDecider(options)` → Laya family: `{ info, decide, dispose }` (same `decide` contract)
|
|
385
|
+
- `createStrandsDecider(options)` → Strands Decider family: `{ info, decide, dispose }` (same `decide` contract)
|
|
177
386
|
- `normalizeQuestions`, `buildDecisionInput`, `typedMarkerIds`, `answersFromScores`,
|
|
178
387
|
`softmaxWithTemperature`, `resolveDevice`, `resolveDtype`, `detectWebGPU`,
|
|
179
388
|
`loadModelConfig`, defaults (model/revision/temperature/caps).
|
|
180
389
|
|
|
181
|
-
##
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
npm run dev
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
390
|
+
## Demos
|
|
391
|
+
|
|
392
|
+
- **Live demo** — <https://warsang.github.io/jev-web/> (built from `demo/`, deployed on every push)
|
|
393
|
+
- **Local playground** — `npm run dev` → <http://localhost:5188>. Same ideas, plus
|
|
394
|
+
COOP/COEP headers that unlock `SharedArrayBuffer` for the threaded
|
|
395
|
+
onnxruntime-web build (GitHub Pages cannot set those headers, so the deployed
|
|
396
|
+
demo runs single-threaded).
|
|
397
|
+
- **Verify a change visually** — `node tools/shot.mjs jobs.json` drives headless
|
|
398
|
+
Chrome over CDP, waits on a real DOM condition, and reports the page's own
|
|
399
|
+
console errors. The GIF in this README was built with it. It exists because a
|
|
400
|
+
screenshot alone cannot tell you a promise is silently rejecting.
|
|
189
401
|
|
|
190
402
|
## Limits
|
|
191
403
|
|
|
Binary file
|
|
Binary file
|
package/docs/demo.gif
ADDED
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,18 +1,29 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "jev-web",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Run open-jev- and
|
|
3
|
+
"version": "0.4.0",
|
|
4
|
+
"description": "Run open-jev-, Laya- and Strands-Decider-shaped typed-decision models in the browser: one state plus typed questions in, calibrated probabilities out.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/warsang/jev-web.git"
|
|
10
|
+
},
|
|
11
|
+
"homepage": "https://warsang.github.io/jev-web/",
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/warsang/jev-web/issues"
|
|
14
|
+
},
|
|
7
15
|
"exports": {
|
|
8
16
|
".": "./src/index.mjs"
|
|
9
17
|
},
|
|
10
18
|
"files": [
|
|
11
19
|
"src/",
|
|
20
|
+
"docs/",
|
|
12
21
|
"README.md"
|
|
13
22
|
],
|
|
14
23
|
"keywords": [
|
|
15
24
|
"open-jev",
|
|
25
|
+
"laya",
|
|
26
|
+
"strands-decider",
|
|
16
27
|
"typed-decisions",
|
|
17
28
|
"onnx",
|
|
18
29
|
"transformers.js",
|
|
@@ -23,6 +34,8 @@
|
|
|
23
34
|
"scripts": {
|
|
24
35
|
"dev": "vite",
|
|
25
36
|
"build:dev": "vite build",
|
|
37
|
+
"demo": "vite --config vite.demo.config.js",
|
|
38
|
+
"build:demo": "vite build --config vite.demo.config.js",
|
|
26
39
|
"test": "node --test test/*.test.mjs"
|
|
27
40
|
},
|
|
28
41
|
"peerDependencies": {
|