jev-web 0.8.0 → 0.9.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 +180 -32
- package/docs/demo-model-picker.png +0 -0
- package/package.json +9 -5
- package/src/clef.mjs +498 -0
- package/src/decision3.mjs +571 -0
- package/src/index.mjs +59 -0
- package/src/questions.mjs +8 -1
- package/src/registry.mjs +34 -4
- package/src/vega.mjs +539 -50
package/README.md
CHANGED
|
@@ -10,11 +10,12 @@ calibrated probability distribution per question comes back from a **single
|
|
|
10
10
|
forward pass**. No server, no API keys, no network calls after the weights are
|
|
11
11
|
cached.
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
Nine model families ship today — open-jev (fused DeBERTa graph), Laya
|
|
14
14
|
(ModernBERT encoder + typed head), Strands Decider 2B (Qwen torso + pointer
|
|
15
15
|
head), Bekko System One v0 (Ettin cross-encoder), Decision 2.0 (Qwen3 +
|
|
16
|
-
candidate head), Kev (Qwen3 + pointer head),
|
|
17
|
-
torso + latent decision-physics engine)
|
|
16
|
+
candidate head), Kev (Qwen3 + pointer head), Vega 0.8B (frozen Qwen3.5
|
|
17
|
+
torso + latent decision-physics engine), clef (Qwen3 torso + joint schema
|
|
18
|
+
head), and Decision 3.0 (d3 code readout) — behind one registry:
|
|
18
19
|
`createDecisionRuntime({ family, fallback })`.
|
|
19
20
|
|
|
20
21
|
This is a generic runtime, not a classifier for any particular domain. You bring
|
|
@@ -28,10 +29,20 @@ you give it.
|
|
|
28
29
|
An interactive playground that runs this exact package: type a state, add typed
|
|
29
30
|
questions, and watch the model and a hand-written keyword baseline answer
|
|
30
31
|
side by side. It also counts your actual outbound requests, so the
|
|
31
|
-
"offline / private / no server" claim is checkable rather than asserted.
|
|
32
|
+
"offline / private / no server" claim is checkable rather than asserted. The
|
|
33
|
+
model selector loads **any of the nine registered families** — open-jev by
|
|
34
|
+
default, plus Bekko, Kev, Laya, Decision 2.0/3.0, Vega 0.8B (fused or the
|
|
35
|
+
8-bit split export), clef and Strands Decider — and `?model=<family>`
|
|
36
|
+
deep-links one.
|
|
32
37
|
|
|
33
38
|

|
|
34
39
|
|
|
40
|
+
The picker is part of the demo: every family answers through the same
|
|
41
|
+
`decide()` contract, and the pass counter reports each family's real cost
|
|
42
|
+
(split Vega reads the state once per question, the others batch).
|
|
43
|
+
|
|
44
|
+

|
|
45
|
+
|
|
35
46
|
<sub>The frames are captured from the live demo at
|
|
36
47
|
[warsang.github.io/jev-web](https://warsang.github.io/jev-web/) — site → typed
|
|
37
48
|
decisions with latency → the negation case where the rules and the model
|
|
@@ -60,8 +71,9 @@ answers[0].confidence; // 0.92
|
|
|
60
71
|
|
|
61
72
|
That is the whole integration. The first call downloads the weights and the
|
|
62
73
|
browser caches them; every later call — and every later visit, including
|
|
63
|
-
offline — is local. **Zero dependencies**:
|
|
64
|
-
|
|
74
|
+
offline — is local. **Zero bundled dependencies**: `onnxruntime-web` is a peer
|
|
75
|
+
the package manager installs, `@huggingface/transformers` is an optional peer
|
|
76
|
+
imported lazily, and hosts can inject either one.
|
|
65
77
|
|
|
66
78
|
## What comes back
|
|
67
79
|
|
|
@@ -149,9 +161,11 @@ The demo also tracks outbound requests through the Performance API: you will see
|
|
|
149
161
|
npm i jev-web @huggingface/transformers
|
|
150
162
|
```
|
|
151
163
|
|
|
152
|
-
`@huggingface/transformers` is an optional peer
|
|
153
|
-
|
|
154
|
-
`
|
|
164
|
+
`@huggingface/transformers` is an optional peer (imported lazily); hosts that
|
|
165
|
+
already ship their own copy can inject it via `createDecider({ transformers })`.
|
|
166
|
+
`onnxruntime-web` is a regular peer, so `npm i jev-web` installs it; hosts with
|
|
167
|
+
a custom runtime (local files, an older ORT, a worker) inject it via
|
|
168
|
+
`createDecider({ ort })`.
|
|
155
169
|
|
|
156
170
|
## Use
|
|
157
171
|
|
|
@@ -227,7 +241,7 @@ tries the requested family and then any fallbacks, returning the shared contract
|
|
|
227
241
|
```js
|
|
228
242
|
import { createDecisionRuntime, listDecisionFamilies } from "jev-web";
|
|
229
243
|
|
|
230
|
-
listDecisionFamilies(); // ["open-jev", "laya", "strands-decider", "bekko", "decision2", "kev", "vega"]
|
|
244
|
+
listDecisionFamilies(); // ["open-jev", "laya", "strands-decider", "bekko", "decision2", "kev", "vega", "clef", "decision3"]
|
|
231
245
|
const runtime = await createDecisionRuntime({
|
|
232
246
|
family: "strands-decider",
|
|
233
247
|
fallback: ["laya", "open-jev"],
|
|
@@ -502,21 +516,23 @@ and download size. Lower-level helpers are exported for custom runtimes:
|
|
|
502
516
|
|
|
503
517
|
`createVegaDecider(options)` runs [Vega 0.8B](https://huggingface.co/nandakishorm/vega-08b-public-intents)
|
|
504
518
|
(by the Laya author): the language model is frozen and never trained — a 0.8B
|
|
505
|
-
Qwen3.5 torso is read once (layers 13 and 19
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
519
|
+
Qwen3.5 torso is read once (layers 13 and 19) and a 14M-parameter physics
|
|
520
|
+
engine on top does the deciding. Each question drops a particle into a latent
|
|
521
|
+
space whose landscape holds one Gaussian well per option; a damped symplectic
|
|
522
|
+
integrator with a selective-SSM thermostat runs up to 12 steps (settled
|
|
523
|
+
particles freeze), and the Boltzmann occupancy of the wells is the answer
|
|
524
|
+
distribution:
|
|
511
525
|
|
|
512
526
|
```js
|
|
513
527
|
import { createVegaDecider } from "jev-web";
|
|
514
528
|
|
|
515
529
|
const decider = await createVegaDecider({
|
|
516
|
-
//
|
|
517
|
-
// lands — export/vega/05_upload.py prints the sha for VEGA_DEFAULT_REVISION)
|
|
530
|
+
// variant "fp16" (default): warsang/vega-08b-web, fused torso + engine, 1.7 GB
|
|
518
531
|
onProgress: (p) => console.log(p.phase, p.loaded, p.total),
|
|
519
532
|
});
|
|
533
|
+
const q8 = await createVegaDecider({ variant: "q8" });
|
|
534
|
+
// nvkudva/vega-onnx-q8: 8-bit embed + cut decoder + fp32 engine, 840 MB,
|
|
535
|
+
// WebAssembly only (its MatMulNBits graphs have no WebGPU kernel)
|
|
520
536
|
const { answers } = await decider.decide(stateText, questions);
|
|
521
537
|
```
|
|
522
538
|
|
|
@@ -525,16 +541,22 @@ Protocol implemented here (mirrors the reference `vega_common.py` / `vega_api.py
|
|
|
525
541
|
- prompt `[BOS] Observation:\n<state>\n\nMeasurement (choice|score|boolean): <instructions>\nPossible outcomes:\n* <option> …\nOutcome:` — the state comes first so its span never sees the question
|
|
526
542
|
(causal attention), and short states get a query-first prefix so the frozen
|
|
527
543
|
model reads knowing what is asked;
|
|
528
|
-
-
|
|
544
|
+
- perception: the torso emits per-segment span means for the
|
|
529
545
|
state/question/final-token slots, every option, and 16 equal state chunks
|
|
530
|
-
(the engine's memory reader) from the one causal read
|
|
531
|
-
|
|
532
|
-
|
|
546
|
+
(the engine's memory reader) from the one causal read — pooled in-graph in
|
|
547
|
+
the fused variant, in JS over the decoder's layer-13/19 hidden states in
|
|
548
|
+
the split one; the engine graph (unrolled integrator; the settle-freeze
|
|
549
|
+
math is identical to the reference's early exit) emits raw logits plus the
|
|
550
|
+
particle physics;
|
|
533
551
|
- calibration is **thermal and checkpoint-driven**: `vega_config.json`'s
|
|
534
552
|
`calibration.theta` maps the particle's physical state (residual kinetic
|
|
535
553
|
energy, distance to the nearest well, steps used, option count) to a
|
|
536
554
|
temperature per question type; `decider.info.calibrationSource` names the
|
|
537
|
-
file used
|
|
555
|
+
file used. The split export's `vega_web.json` also carries the two gated
|
|
556
|
+
task adapters (`public_intents`, `typed_decisions`): the engine's per-
|
|
557
|
+
question gates pick the adapter whose `calibration` block (theta, conformal,
|
|
558
|
+
abstain, unbound) calibrates that answer, and the answers gain `adapter`
|
|
559
|
+
and `gate`;
|
|
538
560
|
- answers carry the full distribution plus Vega's reliability machinery:
|
|
539
561
|
a split-conformal `predictionSet`, an `abstain` flag at the chosen risk
|
|
540
562
|
(`alpha` conformal level, `risk` abstention level, both per question type),
|
|
@@ -544,19 +566,143 @@ Protocol implemented here (mirrors the reference `vega_common.py` / `vega_api.py
|
|
|
544
566
|
`choice` accepts `criteria` as `{label: description}`, and `score` uses the
|
|
545
567
|
option texts as levels.
|
|
546
568
|
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
569
|
+
Variants (`variant`, or explicit `model`/`revision`/`format`):
|
|
570
|
+
|
|
571
|
+
| variant | export | graphs | size |
|
|
572
|
+
|---|---|---|---|
|
|
573
|
+
| `fp16` (default) | `warsang/vega-08b-web` | fused fp16 torso + fp32 engine, span means pooled in-graph | ~1.7 GB |
|
|
574
|
+
| `q8` | `nvkudva/vega-onnx-q8` (`v2/`) | 8-bit `embed_q8` + cut `decoder_q8` (layers 0–19, text M-RoPE positions, zeroed `past_*` state) + fp32 `engine` with both adapters; JS pools | ~840 MB |
|
|
575
|
+
|
|
576
|
+
Caveats: the fused export's gated task adapters are not shipped in its v1 — it
|
|
577
|
+
uses the base engine, the source repo's default; and that torso ships as the
|
|
578
|
+
exact fp16 graph because q8 on the fused graph drifts on the GatedDeltaNet
|
|
579
|
+
delta rule. The split export is text-only here (its vision encoder is not
|
|
580
|
+
wired in), runs one decoder read per question, is refused — `RangeError` —
|
|
581
|
+
rather than truncated when a state does not fit `maxLen`, and its checkpoints
|
|
582
|
+
were verified end-to-end against the reference (`export/vega/06_verify_q8_export.py`:
|
|
583
|
+
JS-vs-reference pooling exact, engine graph vs PyTorch 1.4e-6, argmax 3/3
|
|
584
|
+
against the fp16 backbone). Both keep the trained 73,728-token window, so pass
|
|
585
|
+
`maxLen` to cap a browser's read. The fused export repo (`warsang/vega-08b-web`,
|
|
586
|
+
~1.7 GB) is uploaded by `export/vega/05_upload.py`; the revision lands in
|
|
587
|
+
`VEGA_DEFAULT_REVISION`.
|
|
555
588
|
|
|
556
589
|
Lower-level helpers are exported for custom runtimes:
|
|
557
590
|
`buildVegaPrompt`, `packVegaBatch`, `packVegaChunks`, `vegaQuestionDef`,
|
|
558
591
|
`vegaOptionTexts`, `vegaThermalFeatures`, `vegaThermalProbabilities`,
|
|
559
|
-
`vegaAnswers`, `vegaOptionLabels
|
|
592
|
+
`vegaAnswers`, `vegaOptionLabels`, `vegaJsonDumps`, `vegaRoute`,
|
|
593
|
+
`vegaTextPositions`, `emptyVegaPast`, `poolVegaHidden`.
|
|
594
|
+
|
|
595
|
+
## clef family (Qwen3 torso + joint schema head)
|
|
596
|
+
|
|
597
|
+
`createClefDecider(options)` runs [clef-text-0.6b](https://huggingface.co/FluidInference/clef-text-0.6b)
|
|
598
|
+
(Apache-2.0): a Qwen3-0.6B torso (LoRA merged) with Cloudflare's **joint schema
|
|
599
|
+
head** — the whole record (state plus every question's schema) becomes one
|
|
600
|
+
prompt, and the head decides every field in one joint pass. It is distilled
|
|
601
|
+
from Cloudflare's clef-flash 9B (79.1 vs 88.2 mean across 12 held-out
|
|
602
|
+
benchmarks) and ships Cloudflare's unchanged reference module
|
|
603
|
+
(`joint_schema_model.py`).
|
|
604
|
+
|
|
605
|
+
```js
|
|
606
|
+
import { createClefDecider } from "jev-web";
|
|
607
|
+
|
|
608
|
+
const decider = await createClefDecider({
|
|
609
|
+
// defaults to warsang/clef-text-0.6b-web @ pinned revision
|
|
610
|
+
onProgress: (p) => console.log(p.phase, p.loaded, p.total),
|
|
611
|
+
});
|
|
612
|
+
const { answers } = await decider.decide(stateText, questions);
|
|
613
|
+
```
|
|
614
|
+
|
|
615
|
+
Protocol implemented here (mirrors `encode_record` / `ClefModel`):
|
|
616
|
+
|
|
617
|
+
- record `<|im_start|>system\n…<|im_end|>\n<|im_start|>user\nSTATE:\n<state>`
|
|
618
|
+
+ `\n\nSCHEMA FIELDS:\n` + one block per question (`\nFIELD n\nID: …\nTYPE:
|
|
619
|
+
…\nINSTRUCTION: …\nALLOWED OPTIONS:\nOPTION n:
|
|
620
|
+
{"description":…,"option_id":…}\n…END FIELD\n`) + `<|im_end|>\n<|im_start|>
|
|
621
|
+
assistant\n<think>\n\n</think>\n\nJOINT SCHEMA DECISIONS:` — every piece
|
|
622
|
+
tokenized separately, the state truncated head-first to the window;
|
|
623
|
+
- `choice` option keys are **sorted** like the reference's
|
|
624
|
+
`sorted(criteria.items())` (probabilities still come back keyed by your
|
|
625
|
+
labels); `noul` maps to the fixed `true`/`false` slots (default descriptions,
|
|
626
|
+
overridable under either `true/false` or `yes/no`); `score` levels map to
|
|
627
|
+
numeric keys `0..n-1` with a `legend`;
|
|
628
|
+
- a question may carry an `id` — it is rendered as the field ID (default
|
|
629
|
+
`"0"`, `"1"`, …);
|
|
630
|
+
- the head's evidence and field passes attend to the full hidden sequence, so
|
|
631
|
+
torso and head ship as **one fused ONNX graph**; span means (question spans,
|
|
632
|
+
option spans, the final token, and the tied-embedding lexical mean per
|
|
633
|
+
option) travel as pooling matrices — the Vega trick;
|
|
634
|
+
- choice/score confidence is the top probability (the reference SystemOne
|
|
635
|
+
measure), not an entropy score.
|
|
636
|
+
|
|
637
|
+
Lower-level helpers are exported for custom runtimes:
|
|
638
|
+
`encodeClefRecord`, `packClefBatch`, `clefQuestionOptions`, `clefAnswers`,
|
|
639
|
+
`renderClefContent`.
|
|
640
|
+
|
|
641
|
+
Caveat: this family ships the **text-only** clef — images and videos (the
|
|
642
|
+
multimodal clef checkpoints' processor path) are not part of the browser
|
|
643
|
+
export. The fused fp16 graph needs an onnxruntime in the 1.31 generation
|
|
644
|
+
(transformers.js 4.3 ships ORT-web 1.31 in the browser); hosts pinned to an
|
|
645
|
+
older runtime can re-export the exact fp32 graph (2.7 GB) with
|
|
646
|
+
`export/clef/01_export.py` and pass it as `file`.
|
|
647
|
+
|
|
648
|
+
## Decision 3.0 family (d3 code readout)
|
|
649
|
+
|
|
650
|
+
`createDecision3Decider(options)` runs the
|
|
651
|
+
[vLLM Semantic Router Decision 3.0](https://huggingface.co/vllm-sr/d3-edge)
|
|
652
|
+
collection (Apache-2.0): Decision 2.0's candidate head is replaced by a **code
|
|
653
|
+
readout** — every option is printed under a single-token answer code (A…Z,
|
|
654
|
+
AA…AZ, up to 255), one question is decided per forward pass, and a 255-way
|
|
655
|
+
readout scores those codes at the last prompt position. d3-edge (0.59B,
|
|
656
|
+
multimodal) scores **28.82** on the Jev Decision Index 0.3 public suite (+12
|
|
657
|
+
over Decision 2.0's 0.6B).
|
|
658
|
+
|
|
659
|
+
```js
|
|
660
|
+
import { createDecision3Decider } from "jev-web";
|
|
661
|
+
|
|
662
|
+
const decider = await createDecision3Decider({
|
|
663
|
+
// defaults to warsang/d3-edge-web @ pinned revision
|
|
664
|
+
onProgress: (p) => console.log(p.phase, p.loaded, p.total),
|
|
665
|
+
});
|
|
666
|
+
// or pick a larger sibling:
|
|
667
|
+
// await createDecision3Decider({ variant: "lite" }); // 0.85B
|
|
668
|
+
// await createDecision3Decider({ variant: "nano" }); // 2.21B
|
|
669
|
+
const { answers } = await decider.decide(stateText, questions);
|
|
670
|
+
```
|
|
671
|
+
|
|
672
|
+
Protocol implemented here (mirrors `d3_format.py` / `d3_runtime.py`):
|
|
673
|
+
|
|
674
|
+
- user prompt `State:\n<state>\n\nQuestion:\n<instructions>\n\nOptions:\nA:
|
|
675
|
+
<option>\nB: …\n\nReply with only the code of the best option.` wrapped in
|
|
676
|
+
the checkpoint's Qwen3.5 chat template with `add_generation_prompt` and
|
|
677
|
+
`enable_thinking=false` (the built-in rendering is byte-identical to the
|
|
678
|
+
template and kicks in when the runtime has no `apply_chat_template`);
|
|
679
|
+
- the answer-code table comes from the checkpoint's `decision_config.json`
|
|
680
|
+
(falling back to the shipped A…Z / AA…ZZ list); questions run `batchSize`
|
|
681
|
+
(8) per forward pass, left-padded like the reference;
|
|
682
|
+
- `choice` keys are the option labels with optional descriptions; `noul` maps
|
|
683
|
+
to the fixed `false`/`true` slots with overridable descriptions; `score`
|
|
684
|
+
takes 2–10 distinct levels, renders as bare option texts, and answers with
|
|
685
|
+
the expected level index, the reference entropy `confidence` and a `legend`;
|
|
686
|
+
- two ONNX graphs ship: the torso (Qwen3.5 backbone with the reference
|
|
687
|
+
**non-causal full attention** — softmax layers see the whole prompt while the
|
|
688
|
+
GatedDeltaNet layers stay causal and padding-safe) emits the last prompt
|
|
689
|
+
token's hidden state, and a 255×d readout graph emits masked code logits
|
|
690
|
+
(fp32); the JS softmaxes them with the checkpoint temperature;
|
|
691
|
+
- a prompt over `maxLen` raises a `RangeError` — like the reference, nothing
|
|
692
|
+
is truncated (`truncated` is always `false`).
|
|
693
|
+
|
|
694
|
+
Caveats: the browser export is **text-only in v1** — the checkpoints'
|
|
695
|
+
image/video inputs run through a separate vision tower that the two shipped
|
|
696
|
+
graphs do not include; the exported window is capped by the browser budget
|
|
697
|
+
(`maxLen`, 8192 default) rather than the checkpoint's uncapped `max_length`;
|
|
698
|
+
and the fp16 torso needs an onnxruntime in the 1.31 generation (the same
|
|
699
|
+
requirement as clef — transformers.js 4.3 ships ORT-web 1.31 in the browser).
|
|
700
|
+
|
|
701
|
+
Lower-level helpers are exported for custom runtimes:
|
|
702
|
+
`buildDecision3Prompt`, `decision3Messages`, `renderDecision3Chat`,
|
|
703
|
+
`normalizeDecision3Question`, `decision3OptionTexts`,
|
|
704
|
+
`decision3ProbsFromLogits`, `decision3Confidence`, `decision3Answers`,
|
|
705
|
+
`packDecision3Batch`.
|
|
560
706
|
|
|
561
707
|
## Worker / thread
|
|
562
708
|
|
|
@@ -575,6 +721,8 @@ import { createDecider } from "jev-web";
|
|
|
575
721
|
- `createDecider(options)` → open-jev family: `{ info, decide, decideMany, dispose }`
|
|
576
722
|
- `createLayaDecider(options)` → Laya family: `{ info, decide, dispose }` (same `decide` contract)
|
|
577
723
|
- `createStrandsDecider(options)` → Strands Decider family: `{ info, decide, dispose }` (same `decide` contract)
|
|
724
|
+
- `createBekkoDecider(options)`, `createDecision2Decider(options)`, `createKevDecider(options)`,
|
|
725
|
+
`createVegaDecider(options)`, `createClefDecider(options)`, `createDecision3Decider(options)` → the same `decide` contract
|
|
578
726
|
- `normalizeQuestions`, `buildDecisionInput`, `typedMarkerIds`, `answersFromScores`,
|
|
579
727
|
`softmaxWithTemperature`, `resolveDevice`, `resolveDtype`, `detectWebGPU`,
|
|
580
728
|
`loadModelConfig`, defaults (model/revision/temperature/caps).
|
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "jev-web",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Run open-jev-, Laya
|
|
3
|
+
"version": "0.9.0",
|
|
4
|
+
"description": "Run open-jev-, Laya-, Strands-Decider-, Bekko-, Decision-, Kev-, Vega-, clef-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
7
|
"repository": {
|
|
@@ -24,6 +24,12 @@
|
|
|
24
24
|
"open-jev",
|
|
25
25
|
"laya",
|
|
26
26
|
"strands-decider",
|
|
27
|
+
"bekko",
|
|
28
|
+
"decision2",
|
|
29
|
+
"decision3",
|
|
30
|
+
"kev",
|
|
31
|
+
"vega",
|
|
32
|
+
"clef",
|
|
27
33
|
"typed-decisions",
|
|
28
34
|
"onnx",
|
|
29
35
|
"transformers.js",
|
|
@@ -45,13 +51,11 @@
|
|
|
45
51
|
"peerDependenciesMeta": {
|
|
46
52
|
"@huggingface/transformers": {
|
|
47
53
|
"optional": true
|
|
48
|
-
},
|
|
49
|
-
"onnxruntime-web": {
|
|
50
|
-
"optional": true
|
|
51
54
|
}
|
|
52
55
|
},
|
|
53
56
|
"devDependencies": {
|
|
54
57
|
"@huggingface/transformers": "^4.3.0",
|
|
58
|
+
"onnxruntime-web": "1.31.0-dev.20260914-8d85527a0",
|
|
55
59
|
"vite": "^6.3.5"
|
|
56
60
|
},
|
|
57
61
|
"engines": {
|