autotrust/JEV-27B-VL, autotrust/JEV-27B with vision, is now online.
Fast, calibrated System 1 decisions that are indistinguishable from the closed TypeSafe Jev 1.13 by KL, and deliberate System 2 generation and reasoning from an untouched Qwen3.8-27B — one set of weights, one vLLM engine, routed per request. It succeeds JEV-9B with closer System 1 fidelity, stronger transfer to unseen tasks and a stronger System 2, and it answers a single decision in about half the time the hosted API takes.
Scorecard. System 1 fidelity, calibration and transfer, System 2 and speed; grey tags show JEV-9B · click to enlarge
serve_decide.py (in this repository) adds POST /v1/decide to the vLLM server: send
{kind, state, question, options} and get a calibrated probability for every option. See
Quickstart.Six public text-decision benchmarks. AutoTrust ran them in full for autotrust/JEV-27B and for the hosted TypeSafe Jev 1.13 (26 September 2026); the other six rows are the values reported in the NeoHorse-Jev-4B evaluation and were not re-run by us. Scores are in %, higher is better; bold marks the best score in each column. Results updated 27 September 2026.
| Model | JevBench | Kev | OpenJev text | Nimble | VitaminC | MASSIVE-en | Six-group mean |
|---|---|---|---|---|---|---|---|
| autotrust/JEV-27B (this model) | 88.70 | 83.75 | 73.89 | 92.91 | 77.46 | 87.71 | 84.07 |
| TypeSafe Jev 1.13, hosted API (our run) | 87.18 | 85.52 | 72.96 | 91.84 | 78.46 | 87.14 | 83.85 |
| NeoHorse-Jev-4B | 75.73 | 81.92 | 58.74 | 87.23 | 77.13 | 85.43 | 77.70 |
| Open-Jev-9B | 77.13 | 77.87 | 65.39 | 80.50 | 68.28 | 84.86 | 75.67 |
| Kev-4B | 73.71 | 81.47 | 54.75 | 73.40 | 76.46 | 85.71 | 74.25 |
| Laya English | 55.82 | 61.30 | 40.07 | 45.04 | 78.63 | 68.57 | 58.24 |
| Laya Typed Decisions | — | — | — | 48.94 | 78.30 | 65.43 | — |
| NeoHorse-1-4B | — | — | — | 69.15 | 63.27 | 82.86 | — |
The six-group mean weights the six groups equally, using the available precision of each score. JEV-27B uses its
unrounded measurements; TypeSafe uses the six two-decimal measurements above (mean 83.85%).
Baseline means are as reported by NeoHorse. — means no result is available, and models missing a group get no six-group mean. Nimble has
282 items, VitaminC 599 and MASSIVE-en 350.
Six-group comparison. JEV-27B's six-group mean is 0.22 percentage points higher than TypeSafe Jev 1.13's (84.07 vs 83.85, with the difference calculated before rounding). JEV-27B scores higher on JevBench, OpenJev text, Nimble and MASSIVE-en; TypeSafe Jev 1.13 scores higher on Kev and VitaminC. Among the open models with results on all six groups, the next best mean is NeoHorse-Jev-4B's 77.70, 6.37 points below JEV-27B (the baseline scores come from NeoHorse's runs, not ours).
JEV-27B minus TypeSafe Jev 1.13, in percentage points. Differences use JEV-27B's unrounded measurements and TypeSafe's reported two-decimal measurements, and are rounded only after subtraction.
| JevBench | Kev | OpenJev text | Nimble | VitaminC | MASSIVE-en | Six-group mean |
|---|---|---|---|---|---|---|
| +1.52 | −1.77 | +0.93 | +1.07 | −1.00 | +0.57 | +0.22 |
typesafe-ai provider. Its OpenJev main score takes the option
with the highest entailment probability, ties going to the first
option; the API rounds probabilities, and the resulting tie diagnostics are kept in our evaluation records.Integrated System 1 + System 2, second generation. JEV-27B is AutoTrust's second open model to serve both modes
of thinking from a single set of weights, after autotrust/JEV-9B.
System 1 answers typed questions (noul yes/no ·
choice over 2–256 options · score on a 0–5 scale) in one forward pass and returns a calibrated probability
distribution, over prompts of up to 256K tokens. System 2 is ordinary text generation with step-by-step reasoning (thinking mode). Both run on the
same backbone in the same engine, and a request chooses its system.
Blocks of Experts recipe. Rather than fine-tuning one monolithic model, the Blocks of Experts (BoE) recipe keeps a strong pretrained model as a frozen expert block and adds a small, detachable expert block trained for one capability. In JEV-27B the System 2 block is Qwen3.8-27B, bit-identical to the release; the System 1 block is 108.9 M trained parameters (0.4 % of the backbone), trained in ≈ 9.2 hours on one B200. Because the blocks stay separate, adding System 1 costs System 2 nothing: HumanEval is 78.0 % before and after, with all 164 completions byte-identical.
Indistinguishable from the closed original on System 1, by KL. On the 25,376 held-out questions (53 domains) whose labels are TypeSafe Jev 1.13's own output distributions, the mean KL divergence is ≈ 0.017 (0 = identical). An observer who sees sampled decisions gains on average 0.017 nats of evidence per decision about which model produced it, so it takes about 60 decisions to gather a single nat. The fidelity extends to the teacher's mistakes (see System 1: indistinguishable from TypeSafe Jev 1.13). Among the open Jev reproductions we could find, only the JEV models publish this distribution-level measure (see How JEV-27B compares with other open Jev reproductions).
Faster than the hosted API. On one B200, a single decision takes a median 137 ms, against 238–301 ms measured independently for the hosted TypeSafe Jev 1.13 API, and one GPU sustains about 6× the decisions per second an independent benchmark achieved against that API (see Speed).
Better than JEV-9B. Same recipe, same API, a larger backbone: mean KL to Jev's distributions drops from ≈ 0.019 to ≈ 0.017, KL to the ground-truth labels of unseen task families falls by more than half (0.234 → 0.104), accuracy on an independent 16-option benchmark rises from 90 % to 96 % of the teacher's, and the System 2 path climbs from 70.7 % to 78.0 % on HumanEval. JEV-9B remains the faster option (see JEV-27B vs JEV-9B).
Two models, two organisations. TypeSafe Jev 1.13 is the hosted, closed-source model made by TypeSafe AI; it is the teacher whose published output distributions this model was trained on. autotrust/JEV-27B (this repository) is an independent open-weights student built by AutoTrust AI from the Apache-2.0 corpus
SargeDev/jev-distill-corpus-v3. It is not affiliated with, endorsed by, or a product of TypeSafe AI, and shares no weights or code with it.
System 1 numbers are on the held-out test_set_30k of jev-distill-corpus-v3. Its 29,955 rows come from three
sources: 25,376 rows labelled with TypeSafe Jev 1.13's own output distributions (yuri_v3), 2,319 Open-Jev rows with
programmatic ground-truth labels (openjev_v2), and 2,260 placeholder rows (yuri_v1). Rows marked Jev-labelled use
only the first group.
| What is measured | autotrust/JEV-27B | How to read it | |
|---|---|---|---|
| System 1 | Mean KL divergence from TypeSafe Jev 1.13's distributions, Jev-labelled rows, 0 = identical | ≈ 0.017 | Indistinguishable from the teacher's decisions at this resolution: ≈ 60 sampled decisions to gather one nat of evidence |
| Mean KL to all test targets (Jev, programmatic and placeholder labels) | 0.019 | The figure in the model index above | |
Yes/no AUROC (noul), Jev-labelled rows | 0.995 | Ranks true vs. false almost perfectly (0.996 over all rows) | |
| Choice top-1 agreement with Jev, Jev-labelled rows | 90.5 % | 95.8 % over all rows where the target's top two options differ by ≥ 0.1; on near ties any faithful copy agrees about half the time | |
Rating error (score, 0–5 scale), mean absolute error of the expected rating | 0.098 | About one tenth of a rating step | |
| Expected calibration error | 0.0009 | A stated 80 % is an 80 %; fitted temperatures ≈ 1.00, no post-hoc correction needed | |
| KL to the programmatic labels of task families never seen in training (Open-Jev OOD split) | 0.104 | Transfer to new tasks; these labels are ground truth, not Jev's outputs | |
| Independent benchmark with human gold labels, 16 options | 96 % of the teacher (0.740 vs 0.769) | 98 % of the teacher at 2, 4 and 8 options; see Benchmark highlights | |
| Six public decision benchmarks, mean (JevBench, Kev, OpenJev text, Nimble, VitaminC, MASSIVE-en) | 84.07 % | TypeSafe Jev 1.13: 83.85 % in the updated results (+0.22 pts for JEV-27B); JEV-27B is higher on 4 of the 6; see Public decision benchmarks | |
| System 2 | HumanEval pass@1, greedy | 78.0 % | Identical to Qwen3.8-27B (128/164); all 164 completions byte-identical to the base model |
| Speed | Single decision, median, one B200 | 137 ms | Hosted TypeSafe Jev 1.13, measured independently: 238 ms mean, 291–301 ms median |
| Decisions per second on the independent benchmark, one B200 | ≈ 130 | ≈ 6× the 23 per second measured against the hosted API; see Speed | |
| Batched, 128 decisions per batch | 4.2 ms per decision | With vLLM: 141 decisions/s offline, text generation ≈ 48× faster than the PyTorch path | |
| Efficiency | Trained parameters | 108.9 M (0.4 % of 25.6 B) | ≈ 9.2 B200-hours, 0.98 epoch ≈ 640 k rows |
autotrust/JEV-9B was AutoTrust's first integrated System 1 + System 2 model. JEV-27B uses the same recipe, code, hyper-parameters, API and two-block packaging; only the backbone and memory settings changed. Both are evaluated on the same held-out test set and the same independent benchmark.
| JEV-9B | JEV-27B | change | |
|---|---|---|---|
| Backbone | Qwen3.5-9B | Qwen3.8-27B | |
| System 1 — mean KL to TypeSafe Jev 1.13, Jev-labelled rows | ≈ 0.019 | ≈ 0.017 | ≈ −11 % |
| Mean KL to all test targets | 0.021 | 0.019 | −11 % |
| KL to ground-truth labels, unseen task families (OOD) | 0.234 | 0.104 | −56 % |
| Top-1 accuracy, unseen task families (OOD) | 0.918 | 0.942 | +2.4 pts |
| Choice top-1 agreement with Jev, Jev-labelled rows | 90.2 % | 90.5 % | +0.3 pts |
Rating error (score MAE, all Jev-labelled) | 0.103 | 0.098 | −5 % |
| Top-1 flips under option shuffle (test set) | 3.9 % | 2.9 % | −1.0 pt |
Yes/no AUROC (noul), Jev-labelled rows | 0.994 | 0.995 | +0.001 |
| Calibration error (ECE) | 0.0007 | 0.0009 | JEV-9B slightly lower; both below 0.001 |
| Independent benchmark, 16 options — % of teacher accuracy | 90 % | 96 % | +6 pts |
| Independent benchmark — answers changed by option order alone (teacher: 7.0 %) | 11.5 % | 7.4 % | close to the teacher's 7.0 % |
| System 2 — HumanEval pass@1 (greedy) | 70.7 % | 78.0 % | +7.3 pts |
| Latency on one B200 — single request / batched | ≈ 90 ms / 2.5 ms | 137 ms / 4.2 ms | JEV-9B is faster |
| Trained parameters / compute | 40.2 M / ≈ 3 B200-hours | 108.9 M / ≈ 9.2 B200-hours |
On the fresh Hacker News, V2EX and community examples (illustrations, not a benchmark), JEV-27B got 95 of 96 decisions right against 92 of 96 for JEV-9B. The difference is on the harder tasks: JEV-27B flags a TypeScript port that breaks a "branded, range-checked integer" rule (0.93; JEV-9B 0.33, a miss) and a CEO wire-transfer fraud message with more confidence (0.84; JEV-9B 0.56).
Which to pick. For routing, moderation, topic triage and short option lists, JEV-9B gives nearly the same answers 2.6× faster (14,400 benchmark decisions in 42 s vs 110 s on one B200). For long option lists (more than about 8), unfamiliar task families, code-rule checks, fraud screening, or when the System 2 path matters, use JEV-27B.
Dozens of open reproductions of TypeSafe Jev appeared within weeks of its launch; the community Jev Decision Index (formerly the Jev Reproductions Tracker) evaluates 55 of them. Most are trained on human or programmatic gold labels, or on their own synthetic data, so they aim to match or beat Jev's accuracy rather than reproduce its probabilities. "Closest to Jev" therefore depends on how closeness is measured:
| measure of closeness to TypeSafe Jev 1.13 | published results (snapshot of 25 September 2026) | where JEV-27B stands |
|---|---|---|
| Distribution level: KL to Jev's own output distributions on held-out rows | JEV-27B ≈ 0.017 and JEV-9B ≈ 0.019 on 25,376 Jev-labelled rows. We found no other open reproduction that publishes this measure. | Lowest published |
Accuracy relative to Jev on decision-models-under-pressure, 16 options, human gold labels | JEV-27B 96 % · JEV-9B 90 % · Laya 90 % · DeBERTa-v3-large zero-shot 90 % · DeBERTa-v3-base zero-shot 83 % · GLiClass-large 81 % · bge-large 73 % · gte-large 69 % | Closest of the models measured there (JEV rows are AutoTrust re-runs of the same items; the others were run by the benchmark's author) |
| Head-to-head on six public decision benchmarks (our runs of JEV-27B and Jev; other rows as reported by NeoHorse) | JEV-27B 84.07 · TypeSafe Jev 1.13 83.85 · NeoHorse-Jev-4B 77.70 · Open-Jev-9B 75.67 · Kev-4B 74.25 · Laya English 58.24 | 0.22 points above Jev; highest six-group mean among the listed models with complete results (details) |
| Score parity on community leaderboards | Decision Index 0.2: Jev 51.67, AutoJev-27B 50.94. JevBench v1.4.2: decider-4b v2 64.13, Jev 63.29, JevK5 62.04. Open-Jev public JevBench subset: Jev 200/231, Open-Jev 27B v1.1 197/231 | Not yet submitted to these leaderboards. On the public JevBench set in our own runs: JEV-27B 88.70, Jev 87.18 (family-macro score) |
On the evidence published today, JEV-27B is the closest open model to TypeSafe Jev 1.13 at the level of output distributions, the closest of the models measured on the independent benchmark above, and 0.22 points above Jev's six-benchmark mean in the updated results of our head-to-head runs. It has not yet been submitted to the Decision Index or the JevBench v1.4.2 leaderboard, where AutoJev-27B scores within about one point of Jev and decider-4b v2 edges ahead of it, so we do not claim it is the closest by every measure. Note that some reproductions report beating Jev on their own test sets (AutoJev-27B reports 84.60 % against Jev's 82.79 %); that is a different goal from reproducing Jev's behaviour.
Not to be confused with AutoJev-27B (denis-pplx/autojev-27b), an unrelated Qwen3.8-27B decision model trained with
full-weight SFT on its own data.
TypeSafe does not publish Jev's size or hardware; it reports 70–500 ms end to end. Independent measurements, and ours:
| TypeSafe Jev 1.13, hosted API | JEV-9B, one B200 | JEV-27B, one B200 | |
|---|---|---|---|
| One decision, single request | 238 ms mean over 29,600 calls (decision-models-under-pressure); 291–301 ms median on three workloads (Open-Jev) | ≈ 90 ms median | 137 ms median |
Decisions per second on decision-models-under-pressure | 23, with 5 client workers and one question per call | ≈ 340 (14,400 in 42 s) | ≈ 130 (14,400 in 110 s) |
| Batched, 128 decisions per batch | — | 2.5 ms per decision | 4.2 ms per decision |
So JEV-27B answers a single decision in roughly half the time (JEV-9B in roughly a third), and one GPU sustains about 6× (JEV-9B: about 15×) the throughput the benchmark's author achieved against the hosted API. Read these with the caveats: our latencies are measured on the serving host with no network hop, while the hosted numbers include internet, TLS and queueing; hosted throughput depends on client concurrency and the API's rate limits; Jev's latency is roughly flat in the number of questions per request, so bundling questions narrows the throughput gap; and our figures are self-reported while Jev's come from third parties. The two throughput runs use the same benchmark but not an identical call set (ours stops at 16 options).
What the number means. KL(Jev ‖ model) is the expected log-likelihood ratio, per sampled decision, between TypeSafe Jev 1.13 and the student when the decision comes from Jev. On the 25,376 held-out rows whose targets are Jev's own output distributions, the mean is ≈ 0.017 nats (computed from the per-slice values below, which are published to three decimals): one decision carries almost no evidence about which of the two models produced it, and an observer needs about 1 / KL ≈ 60 independent decisions to accumulate one nat (a likelihood ratio of about e ≈ 2.7 : 1).
For scale, Jev is not deterministic itself: an independent study found it changes its answer on 4.3 % of repeated, identical 64-option calls, and it returns probabilities rounded to two decimals, which is the resolution of the targets used here.
Jev-labelled slice (yuri_v3, test_set_30k) | n | KL | ≈ decisions to gather one nat (1 / KL) |
|---|---|---|---|
noul | 8,537 | 0.004 | ≈ 250 |
choice | 8,312 | 0.025 | ≈ 40 |
score | 8,527 | 0.021 | ≈ 48 |
| all Jev-labelled rows | 25,376 | ≈ 0.017 | ≈ 60 |
The other test rows are not labelled by Jev and are not part of this claim: Open-Jev rows carry programmatic ground
truth (in-distribution KL 0.003 for noul, 0.146 for choice; 0.104 on the OOD split of unseen task families), and
the yuri_v1 rows carry placeholder labels. No Jev-labelled out-of-distribution set exists in the corpus, so the claim
is established on the 53 training domains; outside them, the independent benchmark with human labels (96–98 % of Jev's
accuracy) is the best available evidence.
Fidelity includes the teacher's mistakes. On a poker spot where a solver always checks, TypeSafe Jev 1.13 shoves with 0.62 in a published test; JEV-27B shoves with 0.63. On an independent benchmark, about 7 % of 16-option answers change when only the option order changes, for the teacher (7.0 %) and the student (7.4 %) alike. A faithful copy of System 1 is also a faithful copy of its blind spots.
The Blocks of Experts recipe. (1) Train the System 1 block on Jev 1.13 distributions; (2) serve both systems from one frozen backbone, routed per request · click to enlarge
| block | what it is | parameters | trained? | used for |
|---|---|---|---|---|
| Backbone | Qwen/Qwen3.8-27B text tower (vision tower and MTP head dropped), bf16 | 25.6 B | no — bit-identical to the base | both systems |
| System 2 block | the original lm_head (248,320 × 5120) | part of the base | no | text generation and step-by-step reasoning |
| System 1 block | LoRA r=16 on the decoder projections + a 24-slot fp32 decision head initialised from lm_head rows | 108.8 M + 123 k | yes, ≈ 9.2 B200-hours | calibrated typed decisions |
| Router | per request: the vLLM LoRA module jev-decision, or peft adapter on/off | — | — | chooses the system |
Why separate blocks rather than one merged fine-tune. Folding the System 1 LoRA into the backbone would let a single weight set serve both heads, but it costs generation quality: on JEV-9B the merged backbone scores 61.6 % on HumanEval against 70.7 % for the base, a 9-point drop, even though prose perplexity barely moves (3.15 → 3.30). Keeping the backbone pristine and applying the System 1 block only on the decision path removes that trade-off. For decision serving the adapter is merged in memory at start-up, so decision latency matches a merged bundle. (The merged variant was not re-measured at 27 B.)
Why the recipe is this efficient.
lm_head rows for the verbalizer tokens (false/true, 0–5, A–P), so at step 0 its output equals the
pretrained model's zero-shot restricted next-token distribution (verified to |Δp| < 1e-5; measured 4.5e-07). Before
seeing a single label it already agrees with the test targets on 58 % of choice questions with noul AUROC 0.88;
distillation takes it to 90 % / 0.996.choice options is 2.9 %; the same backbone before distillation flips 41 % of the time.score) gives fitted temperatures of 1.014 / 1.016 / 1.004 and ECE 0.0009 with no post-hoc correction.model_path and memory settings changed. Going from 9 B to 27 B lowers KL to Jev from
≈ 0.019 to ≈ 0.017, halves OOD KL (0.234 → 0.104), and raises the System 2 path from 70.7 % to 78.0 % on HumanEval.
Benchmark highlights. A · KL to Jev by question type · B · accuracy as % of Jev on an independent benchmark · C · System 2 unchanged · D · speed vs the hosted API · click to enlarge
gazelle93/decision-models-under-pressure (published
25 Sep 2026) asks decision models to pick the right label for real texts from CLINC-150, MTOP, GoEmotions, DBpedia and
financial tweets under three kinds of pressure: more options, near-miss options, and shuffled option order. The labels
are human gold labels, none of this data is in our training set, and TypeSafe Jev 1.13's results are published with it.
We re-ran the same items with autotrust/JEV-27B, up to our 16-option limit.
| TypeSafe Jev 1.13 (published) | autotrust/JEV-27B | |
|---|---|---|
| Accuracy with 2 / 4 / 8 / 16 options (800 items, 4 domains) | 0.890 / 0.801 / 0.782 / 0.769 | 0.876 / 0.784 / 0.767 / 0.740 |
| 16 options — CLINC / DBpedia / GoEmotions / MTOP | 0.945 / 0.900 / 0.470 / 0.760 | 0.930 / 0.885 / 0.415 / 0.730 |
| 16 options, near-miss vs. unrelated wrong options (CLINC + MTOP, 400 items) | 0.912 vs 0.985 | 0.907 vs 0.983 |
| Answers changed by shuffling the options alone (16 options, 5 orderings) | 7.0 % | 7.4 % |
On data it was never trained on, the student reaches 96–98 % of the teacher's accuracy, loses the same amount when the wrong options become near misses, and shares the teacher's sensitivity to option order — a weakness inherited from the teacher, not one that distillation fixed. Our run follows the benchmark's published method (gold plus the first K−1 distractors of a pool, shuffled per item); the orderings are seeded differently, so compare aggregates, not individual items. 14,400 decisions took 110 s on one B200.
Expected answers were written by hand before the model was run. These are illustrations (≈ 110 decisions), not a benchmark.
| task | result |
|---|---|
| Topic of 19 HN front-page stories (10 options) + "is it about AI?" | 38 / 38 |
| 12 comments from a heated HN thread: "insults or attacks someone?" + "what is it mainly doing?" (6 options) | 23 / 24 |
| 10 V2EX hot posts in Chinese: "contains a referral / invite code?" + "promotes a product or paid offer?" | 19 / 19 |
Community use cases: code-rule checks in the style of adhere, injection filtering, ticket routing, phishing, code-review diffs, urgency scores | 15 / 15 |
| input | question | autotrust/JEV-27B |
|---|---|---|
| HN: "Two-tier encryption in the UK" | topic (10 options) | security & privacy · 0.99 |
| HN: "Show HN: Agentic CUDA Kernel Optimizer" | topic (10 options) | programming tools · 0.44 (genuinely ambiguous: AI 2nd) |
| HN comment: "Please stop this. We've asked you before to observe the guidelines…" | what is it mainly doing? | moderating the discussion · 1.00 |
| V2EX: "一个不需要 gemini pro 的完全免费的注册 Muse 的方法 … 邀请码:…" | contains a referral / invite code? | P(true) = 0.99 |
TypeScript file with const port: number = Number(process.env.PORT) | breaks "a port must be a branded, range-checked integer"? | P(true) = 0.93 (0.08 for the branded version) |
| "Hi, are you at your desk? I need you to process a wire of $48,500 … Don't mention this to anyone" | phishing or fraud? | P(true) = 0.84 |
Diff replacing a parameterised query with "… WHERE id = " + request.args["id"] | introduces a security vulnerability? | P(true) = 0.98 (0.04 for a variable rename) |
| "I'm not happy with the fit. What are my options here?" | asking for a refund? | P(true) = 0.15 (TypeSafe's docs report 0.22 for Jev on this exact text) |
Where it failed or wavered:
Per-example outputs and the benchmark aggregates are in reports/realworld_27b.json (the HN and V2EX inputs came from
their public APIs on 25 September 2026).
One vLLM engine serves both systems from the same pristine weights. Ordinary requests go through the base
lm_head (System 2, exactly Qwen3.8-27B); requests addressed to the LoRA module jev-decision go through the decision
head (System 1). adapter_vllm/ contains the backbone LoRA plus the 24-slot decision head re-expressed as an lm_head
LoRA (only the 24 verbalizer rows change), so a typed decision is a single prefill step with max_tokens=1,
constrained to the option tokens and read back as log-probabilities.
hf download autotrust/JEV-27B --local-dir JEV-27B # ~54 GB
python3 JEV-27B/serve_decide.py --model JEV-27B --served-model-name autotrust/JEV-27B \
--enable-lora --max-lora-rank 32 --lora-modules jev-decision=JEV-27B/adapter_vllm \
--logprobs-mode processed_logprobs --max-model-len 32768
serve_decide.py (in this repository) is the standard vLLM OpenAI server, with the same flags as vllm serve, plus a
POST /v1/decide route for System 1 (step 3). vllm serve JEV-27B … with the same flags also works; System 1 then goes
through /v1/completions with the client-side code in step 3.
--max-model-len 32768 covers most decisions and System 2 thinking; the backbone supports up to 262,144 (see
Context length). Add --enable-prefix-caching --mamba-cache-mode align if you ask many questions
about the same state.
curl -s localhost:8000/v1/chat/completions -H 'content-type: application/json' -d '{
"model": "autotrust/JEV-27B",
"messages": [{"role": "user", "content": "In one sentence, what is safety stock?"}],
"max_tokens": 60, "chat_template_kwargs": {"enable_thinking": false}}'
Set "enable_thinking": true for deliberate, step-by-step reasoning. This path is Qwen3.8-27B unchanged; see the
Qwen3.8-27B model card for its reasoning benchmarks and recommended sampling
settings.
POST /v1/decide)Send a question and get back a calibrated probability for every option. The server builds the decision prompt, reads the option tokens and applies the decision head's bias and temperature, so any HTTP client works.
curl localhost:8000/v1/decide -H 'Content-Type: application/json' -d '{
"kind": "choice",
"state": "SKU AX-330 stock at 8% of safety level; supplier late twice this quarter.",
"question": "Supplier response for this scenario.",
"options": ["issue_warning", "renegotiate", "dual_source", "maintain"]}'
{"kind": "choice", "effective_kind": "choice",
"options": ["issue_warning", "renegotiate", "dual_source", "maintain"],
"probabilities": [0.2541, 0.1253, 0.6200, 0.0007], "choice_index": 2, "choice": "dual_source",
"adaptation": "native", "protocol": "jev27-bare-v1", "model": "autotrust/JEV-27B",
"usage": {"prompt_tokens": 65, "completion_tokens": 1, "total_tokens": 66}, "num_model_requests": 1, "elapsed_seconds": ...}
| field | value |
|---|---|
kind | noul: yes/no, probabilities for ["false", "true"] · score: a 0–5 scale, for "0"…"5" · choice: for your options |
state | what the decision is about: a string or a JSON object |
question | one question about the state |
options | choice only: 2–256 strings |
In Python:
import requests
URL = "http://localhost:8000"
def decide(kind, state, question, options=None):
body = {"kind": kind, "state": state, "question": question, **({"options": options} if options else {})}
r = requests.post(f"{URL}/v1/decide", json=body).json()
return dict(zip(r["options"], r["probabilities"]))
print(decide("choice", "SKU AX-330 stock at 8% of safety level; supplier late twice this quarter.",
"Supplier response for this scenario.", ["issue_warning", "renegotiate", "dual_source", "maintain"]))
# ≈ {'issue_warning': 0.25, 'renegotiate': 0.13, 'dual_source': 0.62, 'maintain': 0.001}
print(decide("noul", "Customer says the parcel arrived damaged and wants their money back.",
"Is the customer asking for a refund?"))
Values can differ in the third decimal between runs: vLLM computes in bf16 and results depend slightly on which
requests are batched together. GET /v1/decide/info reports the option limit and the temperatures.
serve_decide.py: plain vllm serve and client-side math (up to 16 options)Ask for the option tokens through /v1/completions and apply the bundled bias and temperature yourself. Pass
top_k: 0 and top_p: 1.0: the model's generation config sets top_k=20 and top_p=0.95, vLLM applies them as
request defaults, and with --logprobs-mode processed_logprobs they would truncate the returned probabilities and zero
out less likely options.
import json, math, requests
from huggingface_hub import hf_hub_download
REPO, URL = "autotrust/JEV-27B", "http://localhost:8000"
dh = json.load(open(hf_hub_download(REPO, "adapter_vllm/decision_head.json"))) # bias + verbalizer token ids
T = json.load(open(hf_hub_download(REPO, "calibration.json")))["per_kind"] # per-kind temperatures
def decide(kind, state, question, options=None):
options = {"noul": ["false", "true"], "score": [str(i) for i in range(6)]}.get(kind, options)
lines = options if kind != "choice" else [f"{'ABCDEFGHIJKLMNOP'[i]}) {o}" for i, o in enumerate(options)]
prompt = f"[kind] {kind}\n[state] {state}\n[question] {question}\n[options]\n" + "\n".join(lines) + "\n[decision]:"
s = dh["slots"]["ranges"][kind][0]
ids = dh["verbalizer_ids"][s : s + len(options)] # the option tokens of this kind
r = requests.post(f"{URL}/v1/completions", json={
"model": "jev-decision", "prompt": prompt, "max_tokens": 1, "temperature": 1.0, "top_k": 0, "top_p": 1.0,
"logprobs": len(options), "allowed_token_ids": ids,
"add_special_tokens": False, "return_tokens_as_token_ids": True}).json()
lp = {int(k.split(":")[1]): v for k, v in r["choices"][0]["logprobs"]["top_logprobs"][0].items()}
z = [(lp.get(t, -1e9) + dh["bias"][s + i]) / T[kind] for i, t in enumerate(ids)] # + head bias, / temperature
e = [math.exp(x - max(z)) for x in z]
return {o: x / sum(e) for o, x in zip(options, e)}
The log-softmax normaliser that vLLM applies cancels out, so the result is exactly the decision head's calibrated distribution.
Because both systems live in one engine, a common pattern is to let System 1 answer when it is confident and hand the
rest to System 2. This is a usage pattern, not a configuration we have benchmarked; pick the threshold on your own
validation data, and serve with a --max-model-len large enough for the reasoning budget.
def solve(state, question, options, threshold=0.90):
p = decide("choice", state, question, options) # System 1: one prefill pass
best = max(p, key=p.get)
if p[best] >= threshold:
return {"system": 1, "answer": best, "distribution": p}
prompt = (f"{state}\n\nQuestion: {question}\nOptions: " + "; ".join(options)
+ "\nThink it through, then give exactly one option on the last line.")
r = requests.post(f"{URL}/v1/chat/completions", json={ # System 2: same engine, base lm_head
"model": "autotrust/JEV-27B",
"messages": [{"role": "user", "content": prompt}],
"max_tokens": 8192, "chat_template_kwargs": {"enable_thinking": True}}).json()
return {"system": 2, "reply": r["choices"][0]["message"]["content"], "system1_distribution": p}
from vllm import LLM, SamplingParams
from vllm.lora.request import LoRARequest
llm = LLM("JEV-27B", enable_lora=True, max_lora_rank=32, logprobs_mode="processed_logprobs", max_model_len=32768)
decision = LoRARequest("jev-decision", 1, "JEV-27B/adapter_vllm")
gen = llm.generate(["..."], SamplingParams(temperature=0.0, max_tokens=256)) # System 2, no LoRA
dec = llm.generate([prompt], [SamplingParams(max_tokens=1, temperature=1.0, top_k=0, top_p=1.0, # System 1
allowed_token_ids=ids, logprobs=len(ids))],
lora_request=decision) # then + bias, / T as above
Mixed batches work too: pass a per-request lora_request list (None for System 2, decision for System 1) and both
systems are served in the same generate call.
| workload | PyTorch path | vLLM |
|---|---|---|
| System 2 — 164 HumanEval completions (greedy, ≤ 384 new tokens) | 350 s | 7.3 s (≈ 48×) |
| System 1 — offline batch, 29,955 test questions | 368 s (81 q/s) | 212 s (141 q/s) |
| System 1 fidelity vs. the PyTorch path | test KL 0.0186 | test KL 0.0186, choice top-1 0.903 |
Notes:
[kind] before [state], so
only questions of the same kind share a prefix. Measured on JEV-9B: on 293 real states × 7.7 yes/no
questions each (≈ 480-token states), prefix caching served 19.6 % of prompt tokens from cache (+14–20 % throughput)
with identical outputs.qwen3_5) support, LoRA on lm_head, --logprobs-mode and
allowed_token_ids; tested with a vLLM development build from September 2026. serve_decide.py also uses
logprob_token_ids and that build's server layout. Start-up takes 3–8 minutes
(CUDA-graph capture with LoRA enabled).choice takes 2–256 options in one question. The first 16 are labelled A–P, the labels the decision head was trained
on. Beyond 16, the labels continue with Q–Z and then two-letter labels that are single tokens (AA, AB, …), read the same
way, with no retraining. The response's adaptation field says native (up to 16 options) or wide-labels.
Zero-shot intent classification with every intent offered at once (accuracy on 400 test utterances per row):
| dataset | options | intent names only | names + a one-line description each |
|---|---|---|---|
| MASSIVE (en) | 59 | 83.2% | — |
| BANKING77 | 64 | 79.0% | 83.8% |
| BANKING77 | 77 (all) | 74.5% | 81.0% |
| CLINC150 | 64 | 94.8% | 97.3% |
| CLINC150 | 128 | 90.7% | 94.5% |
| CLINC150 | 150 (all) | 89.5% | 93.8% |
choice question beats one yes/no question per option, and costs one forward pass instead of one per option:
83.2% vs 78.0% on MASSIVE, 78.8% vs 67.0% on BANKING77 (64 options), 94.5% vs 82.0% on CLINC150 (64 options).What we measured on the intent-classification sets above (400 utterances per variant):
state, one question in question. state holds everything the decision depends on; question asks
one thing about it. Plain text and JSON both work: a JSON state scored the same as plain text (83.8% vs 83.2%,
79.0% vs 79.0%, 94.5% vs 94.8%).choice question, not one yes/no question per candidate (see above).[options]
A) card arrival: use when users ask about the status or delivery of a physical card.
B) card delivery estimate: use when users ask about the expected arrival time or delivery speed of a new card.
C) Refund not showing up: use when a customer cannot see a completed refund in their account or statement.
D) request refund: use when a user asks how to initiate or the timing of a refund for a purchase.
More text did not help further. Longer rules with "do not use for …" clauses (about three times the tokens) and two
worked examples per option scored 83.8% and 84.0% on BANKING77 (vs 83.8% for one line), and 97.3% and 96.0% on CLINC150
(vs 97.3%). Generic descriptions did not help either: CLINC150's own intent descriptions scored 94.5%, against 94.8%
for names alone.state and alphabetical option order all stayed within ±1.5 points of the plain prompt, inside the
±1.8-point sampling noise of 400 items. Keep prompts plain.noul so that "true" is the outcome you want the probability of, e.g. "Is the customer asking for a
refund?". Our demos use "Is this scenario one where: …?" for properties of a text or an image.The backbone's native context is 262,144 tokens (256K), and System 1 accepts prompts up to that length when vLLM is
started with --max-model-len 262144. We tested decisions that hinge on a single sentence placed at a random depth in
long real text (concatenated PubMedQA abstracts): a yes/no question (was the refund for order #N approved or rejected?)
and a 16-option question (which room is the spare part for machine M kept in?), 10 of each per length.
| prompt length | yes/no correct | 16-option correct | mean probability on the right answer | median latency |
|---|---|---|---|---|
| 4K (4,235 tokens) | 10/10 | 10/10 | 0.999 | 0.6 s |
| 32K (32,157 tokens) | 10/10 | 10/10 | 0.998 | 3.0 s |
| 64K (64,070 tokens) | 10/10 | 10/10 | 0.997 | 5.7 s |
| 128K (127,895 tokens) | 10/10 | 10/10 | 0.997 | 12.2 s |
| 192K (191,659 tokens) | 10/10 | 10/10 | 0.997 | 20.2 s |
| 250K (249,507 tokens) | 10/10 | 10/10 | 0.997 | 28.7 s |
Latency is one request at a time on one B200 and is almost all prompt processing (about 8,700 tokens per second at 250K). Measured with JEV-27B-VL, which has the same language weights and adapter, with its serving settings.
Memory: the KV cache takes about 65 KB per token, so a full 256K prompt needs about 17 GB on top of the 52 GB of weights
(tested on a 183 GB B200). On an 80 GB GPU, start with --max-model-len 131072.
| kind | question | returns |
|---|---|---|
noul | "Is this statement true?" | [P(false), P(true)] |
choice | "Which of these 2–256 options?" | one probability per option, aligned with your options |
score | "Where on this ordered 0–5 scale?" | a distribution over the six levels (+ expected score) |
[kind] choice
[state] SKU AX-330 stock at 8% of safety level; supplier late twice this quarter.
[question] Supplier response for this scenario.
[options]
A) issue_warning
B) renegotiate
C) dual_source
D) maintain
[decision]:
The template is tokenised as one string; the last token's final-norm hidden state goes through a
linear fp32 head H → 24 slots (noul → slots 0–1, score → 2–7, choice → 8–23). Inactive slots are masked,
a per-kind temperature is applied, and a softmax yields the distribution aligned with your options. One prefill
pass, no decoding. On the vLLM path, choices with 17–256 options continue the labels past P (Q–Z, then AA, AB, …) and
read them from the language-model head the same way (see
Choice questions with up to 256 options).
transformers + peftimport json, torch
from huggingface_hub import hf_hub_download
from peft import PeftModel
from safetensors.torch import load_file
from transformers import AutoModelForCausalLM, AutoTokenizer
repo = "autotrust/JEV-27B"
tok = AutoTokenizer.from_pretrained(repo)
base = AutoModelForCausalLM.from_pretrained(repo, dtype=torch.bfloat16, device_map="cuda") # == Qwen3.8-27B text model
# --- System 2: the pristine base model, no adapter ----------------------------------------------
msgs = [{"role": "user", "content": "In two sentences, what is safety stock?"}]
enc = tok.apply_chat_template(msgs, add_generation_prompt=True, return_tensors="pt", return_dict=True).to("cuda")
out = base.generate(**enc, max_new_tokens=80)
print(tok.decode(out[0, enc["input_ids"].shape[1]:], skip_special_tokens=True))
# --- System 1: attach the LoRA adapter (merged here for speed) + the 24-slot head ---------------
model = PeftModel.from_pretrained(base, repo, subfolder="adapter").merge_and_unload()
head = load_file(hf_hub_download(repo, "head.safetensors"))
cfg = json.load(open(hf_hub_download(repo, "judge_config.json")))
temp = json.load(open(hf_hub_download(repo, "calibration.json")))["per_kind"]
W, b = head["proj.weight"].cuda(), head["proj.bias"].cuda()
def decide(kind, state, question, options):
letters = "ABCDEFGHIJKLMNOP"
lines = options if kind != "choice" else [f"{letters[i]}) {o}" for i, o in enumerate(options)]
text = f"[kind] {kind}\n[state] {state}\n[question] {question}\n[options]\n" + "\n".join(lines) + "\n[decision]:"
ids = tok(text, return_tensors="pt", add_special_tokens=False).to("cuda")
with torch.no_grad(), torch.autocast("cuda", dtype=torch.bfloat16):
h = model.model(**ids).last_hidden_state[0, -1].float() # backbone only, last token
z = (W @ h + b) / temp[kind]
s, _ = cfg["slots"]["ranges"][kind]
p = torch.softmax(z[s : s + len(options)], 0)
return dict(zip(options, p.tolist()))
print(decide("noul", "Customer says the parcel arrived damaged and wants their money back.",
"Is the customer asking for a refund?", ["false", "true"]))
# {'false': 0.022, 'true': 0.978}
options are validated: noul must be ["false","true"], score must be ["0".."5"], choice takes 2–16
free-text options on this head-only path (the 24-slot head has 16 choice slots; 17–256 options need the vLLM path). Note that merge_and_unload() above changes the backbone for the rest of the process; to keep both
systems in one process, leave the adapter unmerged and run System 2 inside with model.disable_adapter():.
test_set_30k, temperature applied)| metric | autotrust/JEV-27B |
|---|---|
noul Brier score against the target probability, all rows (lower is better) | 0.0013 |
score ranked probability score (lower is better) | 0.0077 |
| Fitted temperatures noul / choice / score | 1.014 / 1.016 / 1.004 |
Top-1 flip rate when choice options are shuffled (1,000 rows × 4 permutations) | 2.9 % |
Out-of-distribution split — top-1 agreement · noul AUROC | 0.942 · 0.996 |
| Throughput — batch of 128 requests on one B200 | 4.2 ms per decision (≈ 240 decisions/s) |
| Single request on one B200 (median of 20) | 137 ms |
| weights | pass@1 | note |
|---|---|---|
| Qwen3.8-27B (base) | 78.0 % (128/164) | same loader and protocol as below |
autotrust/JEV-27B — System 2 path (backbone + lm_head, adapter off) | 78.0 % (128/164) | all 164 completions byte-identical to the base model |
JEV-9B: LoRA folded into the backbone + base lm_head (not shipped) | 61.6 % vs 70.7 % base | why the blocks are kept separate |
test_set_30k, temperature applied)| source | kind | n | KL | top-1 | ECE | noul AUROC | score MAE |
|---|---|---|---|---|---|---|---|
| yuri_v3 — synthetic operational scenarios, labelled by TypeSafe Jev 1.13 | noul | 8,537 | 0.004 | 0.959 | 0.001 | 0.995 | — |
| yuri_v3 | choice | 8,312 | 0.025 | 0.905 | 0.002 | — | — |
| yuri_v3 | score | 8,527 | 0.021 | 0.891 | 0.003 | — | 0.098 |
| openjev_v2 — Open-Jev programmatic tasks, ground-truth labels (not Jev) | noul | 1,432 | 0.003 | 0.999 | 0.002 | 1.000 | — |
| openjev_v2 | choice | 887 | 0.146 | 0.888 | 0.022 | — | — |
yuri_v1 — placeholder [0.5, 0.5] labels (see Limitations) | noul | 2,260 | 0.000 | — | 0.003 | — | — |
OOD split (13,058 Open-Jev rows from task families not in training, programmatic labels): KL 0.104, top-1 0.942, noul AUROC 0.996;
choice KL 0.187 / top-1 0.859 (game-state decisions are the hardest slice).
Choice option-permutation consistency (1,000 rows × 4 random permutations): mean max |Δp| 0.022, p90 0.051, top-1 flip rate 2.9 %.
Validation KL on a fixed 4 k-row subset; test_set_30k metrics after calibration.
| stage | rows seen | val KL | t30k KL | choice top-1 | score MAE | noul AUROC | ECE |
|---|---|---|---|---|---|---|---|
| autotrust/JEV-27B v0.8.0 — released weights (step 5,000 ≈ 0.98 epoch, LR annealed to ≈ 0.02×) | 640 k | 0.017 | 0.0186 | 0.903 | 0.098 | 0.996 | 0.0009 |
| step 2000 | 256 k | 0.027 | 0.031 | 0.872 | 0.133 | 0.993 | 0.0028 |
| step 500 | 64 k | 0.058 | 0.058 | 0.828 | 0.199 | 0.984 | 0.0032 |
| untrained backbone with the initialised head (reference point, not the model) | 0 | — | 0.430 | 0.581 | 0.842 | 0.876 | 0.050 |
Validation KL every 500 steps: 0.058 → 0.038 → 0.041 → 0.027 → 0.032 → 0.025 → 0.021 → 0.019 → 0.018 → 0.017 (steps 500 … 5,000). The single cosine schedule does the annealing: most of the second-half gain comes as the learning rate decays, the same effect JEV-9B needed an explicit cool-down for.
| item | value |
|---|---|
| teacher / data | SargeDev/jev-distill-corpus-v3 (740,957 rows; train 655,806) with three streams: yuri_v3 (498,010 rows, TypeSafe Jev 1.13 full output distributions via OpenRouter), openjev_v2 (94,801 rows, Open-Jev programmatic labels, CC0), yuri_v1 (148,154 rows, placeholder labels, down-weighted) |
| backbone | Qwen/Qwen3.8-27B text tower only (vision tower and MTP head dropped), bf16, frozen |
| System 1 block (trainable) | LoRA r=16, α=32, dropout 0.05 on in_proj_qkv, in_proj_z, out_proj, q/k/v/o_proj, gate/up/down_proj (108.8 M, shipped unmerged in adapter/) + 24-slot head (123 k, fp32, initialised from lm_head rows) |
| System 2 block | the original lm_head, not trained |
| loss | KL(target ‖ model) over active slots + 0.5 · RPS (ranked probability score) for score |
| augmentation | 30 % random permutation of choice options (targets permuted consistently) |
| batching | 128 rows / step, kind-stratified (≥ 1/6 per primitive), length-bucketed, micro-batches capped at 24 k padded tokens, gradient checkpointing |
| optimiser | AdamW (fused), β=(0.9, 0.98), lr head 2e-4 / LoRA 1e-4, one cosine schedule over 5,124 steps (min ×0.02), warmup 3 %, grad-clip 1.0; released checkpoint = step 5,000 (0.98 epoch), selected by validation KL |
| label hygiene | yuri_v1 rows carry exact-uniform [0.5, 0.5] placeholder labels (137,203 rows, 100 %); down-weighted ×0.05 in training and excluded from temperature fitting |
| calibration | per-kind scalar temperature (L-BFGS on the calibration split, 10,954 rows): noul 1.014 · choice 1.016 · score 1.004 |
| compute | 1× NVIDIA B200 (183 GB); ≈ 9.2 GPU-hours for 5,000 steps; ≈ 2.4 k tokens/s; peak memory 79 GB |
| software | torch 2.13 + cu130, transformers 5.16, peft 0.21, flash-linear-attention 0.5.2 |
choice) are measured against
programmatic ground truth, and on the independent benchmark the student reaches 96–98 % of Jev's accuracy, not 100 %.choice distributions are soft (median top-1
probability 0.70). On the 14 % of rows where the teacher's top two options are within 0.1 of each other, argmax
agreement is near chance for any faithful mimic (0.46 where the gap is < 0.05). On teacher-decisive rows agreement
is 0.958, and the student's argmax captures 98.1 % of the teacher probability mass a perfect mimic could (0.696 vs
0.709).noul and score accept only their canonical options; choice accepts 2–256 options
(2–16 on the transformers head-only path). Labels beyond P were never seen in training: accuracy holds up (see the
intent-classification table), but their calibration has been measured on one dataset only.transformers code above use the whole prompt, up to --max-model-len
(at most 262,144 tokens). The long-context test above is a single-fact retrieval decision; harder long-document
reasoning has not been measured. The original reference server truncates states longer than 1,024 tokens (head 60 % /
tail 40 %) unless you raise its limit.yuri_v1 memory-relevance stream is 100 % exact-uniform [0.5, 0.5] —
those rows teach nothing about relevance. The model outputs ≈ 0.5 on them by design; do not use it for
memory-relevance scoring without further training.model-000{01..13}-of-00013.safetensors Qwen3.8-27B text backbone incl. lm_head — bit-identical to the base model
(bf16; GDN A_log / gated-norm weights fp32 as in the original), 53.8 GB
model.safetensors.index.json · config.json
adapter/ System 1 LoRA (peft format, r=16, 108.8 M params, 416 MB) — apply only for decisions
head.safetensors 24-slot decision head (fp32): proj.weight [24, 5120], proj.bias [24]
judge_config.json slot layout, verbalizer token ids, template version, weights_mode=unmerged, provenance
calibration.json per-kind temperatures (+ fit diagnostics)
serve_decide.py vLLM OpenAI server + POST /v1/decide (System 1 over HTTP, 2–256 options)
adapter_vllm/ the same adapter for vLLM: backbone LoRA (zero-padded to r=32) + decision head as an
lm_head LoRA, plus decision_head.json (head bias, verbalizer token ids)
tokenizer.json · tokenizer_config.json · chat_template.jinja
27b-3.jpg · 27b1.jpg · 27b-2.jpg scorecard, Blocks of Experts recipe and benchmark charts used in this card
reports/ evaluation reports: test-set evaluation, bundle checks, HumanEval per-problem
results, vLLM measurements, real-world tests, training-milestone reviews
Weights: Apache-2.0 (base model Qwen/Qwen3.8-27B is Apache-2.0; training corpus
SargeDev/jev-distill-corpus-v3 is Apache-2.0, its openjev_v2 stream additionally CC0). The System One framing and
the noul / choice / score primitives originate with TypeSafe AI's Jev; autotrust/JEV-27B is an independent
student model trained on public data and shares no weights, code or affiliation with TypeSafe AI.
@misc{autotrust_jev27b_2026,
title = {autotrust/JEV-27B: a second-generation integrated System 1 + System 2 open model built with the Blocks of Experts recipe (Qwen3.8-27B; System 1 distilled from TypeSafe Jev 1.13)},
author = {{AutoTrust AI}},
year = {2026},
url = {https://huggingface.co/autotrust/JEV-27B}
}
autotrust/JEV-27B-VL, autotrust/JEV-27B with vision, is now online.
Fast, calibrated System 1 decisions that are indistinguishable from the closed TypeSafe Jev 1.13 by KL, and deliberate System 2 generation and reasoning from an untouched Qwen3.8-27B — one set of weights, one vLLM engine, routed per request. It succeeds JEV-9B with closer System 1 fidelity, stronger transfer to unseen tasks and a stronger System 2, and it answers a single decision in about half the time the hosted API takes.
Scorecard. System 1 fidelity, calibration and transfer, System 2 and speed; grey tags show JEV-9B · click to enlarge
serve_decide.py (in this repository) adds POST /v1/decide to the vLLM server: send
{kind, state, question, options} and get a calibrated probability for every option. See
Quickstart.Six public text-decision benchmarks. AutoTrust ran them in full for autotrust/JEV-27B and for the hosted TypeSafe Jev 1.13 (26 September 2026); the other six rows are the values reported in the NeoHorse-Jev-4B evaluation and were not re-run by us. Scores are in %, higher is better; bold marks the best score in each column. Results updated 27 September 2026.
| Model | JevBench | Kev | OpenJev text | Nimble | VitaminC | MASSIVE-en | Six-group mean |
|---|---|---|---|---|---|---|---|
| autotrust/JEV-27B (this model) | 88.70 | 83.75 | 73.89 | 92.91 | 77.46 | 87.71 | 84.07 |
| TypeSafe Jev 1.13, hosted API (our run) | 87.18 | 85.52 | 72.96 | 91.84 | 78.46 | 87.14 | 83.85 |
| NeoHorse-Jev-4B | 75.73 | 81.92 | 58.74 | 87.23 | 77.13 | 85.43 | 77.70 |
| Open-Jev-9B | 77.13 | 77.87 | 65.39 | 80.50 | 68.28 | 84.86 | 75.67 |
| Kev-4B | 73.71 | 81.47 | 54.75 | 73.40 | 76.46 | 85.71 | 74.25 |
| Laya English | 55.82 | 61.30 | 40.07 | 45.04 | 78.63 | 68.57 | 58.24 |
| Laya Typed Decisions | — | — | — | 48.94 | 78.30 | 65.43 | — |
| NeoHorse-1-4B | — | — | — | 69.15 | 63.27 | 82.86 | — |
The six-group mean weights the six groups equally, using the available precision of each score. JEV-27B uses its
unrounded measurements; TypeSafe uses the six two-decimal measurements above (mean 83.85%).
Baseline means are as reported by NeoHorse. — means no result is available, and models missing a group get no six-group mean. Nimble has
282 items, VitaminC 599 and MASSIVE-en 350.
Six-group comparison. JEV-27B's six-group mean is 0.22 percentage points higher than TypeSafe Jev 1.13's (84.07 vs 83.85, with the difference calculated before rounding). JEV-27B scores higher on JevBench, OpenJev text, Nimble and MASSIVE-en; TypeSafe Jev 1.13 scores higher on Kev and VitaminC. Among the open models with results on all six groups, the next best mean is NeoHorse-Jev-4B's 77.70, 6.37 points below JEV-27B (the baseline scores come from NeoHorse's runs, not ours).
JEV-27B minus TypeSafe Jev 1.13, in percentage points. Differences use JEV-27B's unrounded measurements and TypeSafe's reported two-decimal measurements, and are rounded only after subtraction.
| JevBench | Kev | OpenJev text | Nimble | VitaminC | MASSIVE-en | Six-group mean |
|---|---|---|---|---|---|---|
| +1.52 | −1.77 | +0.93 | +1.07 | −1.00 | +0.57 | +0.22 |
typesafe-ai provider. Its OpenJev main score takes the option
with the highest entailment probability, ties going to the first
option; the API rounds probabilities, and the resulting tie diagnostics are kept in our evaluation records.Integrated System 1 + System 2, second generation. JEV-27B is AutoTrust's second open model to serve both modes
of thinking from a single set of weights, after autotrust/JEV-9B.
System 1 answers typed questions (noul yes/no ·
choice over 2–256 options · score on a 0–5 scale) in one forward pass and returns a calibrated probability
distribution, over prompts of up to 256K tokens. System 2 is ordinary text generation with step-by-step reasoning (thinking mode). Both run on the
same backbone in the same engine, and a request chooses its system.
Blocks of Experts recipe. Rather than fine-tuning one monolithic model, the Blocks of Experts (BoE) recipe keeps a strong pretrained model as a frozen expert block and adds a small, detachable expert block trained for one capability. In JEV-27B the System 2 block is Qwen3.8-27B, bit-identical to the release; the System 1 block is 108.9 M trained parameters (0.4 % of the backbone), trained in ≈ 9.2 hours on one B200. Because the blocks stay separate, adding System 1 costs System 2 nothing: HumanEval is 78.0 % before and after, with all 164 completions byte-identical.
Indistinguishable from the closed original on System 1, by KL. On the 25,376 held-out questions (53 domains) whose labels are TypeSafe Jev 1.13's own output distributions, the mean KL divergence is ≈ 0.017 (0 = identical). An observer who sees sampled decisions gains on average 0.017 nats of evidence per decision about which model produced it, so it takes about 60 decisions to gather a single nat. The fidelity extends to the teacher's mistakes (see System 1: indistinguishable from TypeSafe Jev 1.13). Among the open Jev reproductions we could find, only the JEV models publish this distribution-level measure (see How JEV-27B compares with other open Jev reproductions).
Faster than the hosted API. On one B200, a single decision takes a median 137 ms, against 238–301 ms measured independently for the hosted TypeSafe Jev 1.13 API, and one GPU sustains about 6× the decisions per second an independent benchmark achieved against that API (see Speed).
Better than JEV-9B. Same recipe, same API, a larger backbone: mean KL to Jev's distributions drops from ≈ 0.019 to ≈ 0.017, KL to the ground-truth labels of unseen task families falls by more than half (0.234 → 0.104), accuracy on an independent 16-option benchmark rises from 90 % to 96 % of the teacher's, and the System 2 path climbs from 70.7 % to 78.0 % on HumanEval. JEV-9B remains the faster option (see JEV-27B vs JEV-9B).
Two models, two organisations. TypeSafe Jev 1.13 is the hosted, closed-source model made by TypeSafe AI; it is the teacher whose published output distributions this model was trained on. autotrust/JEV-27B (this repository) is an independent open-weights student built by AutoTrust AI from the Apache-2.0 corpus
SargeDev/jev-distill-corpus-v3. It is not affiliated with, endorsed by, or a product of TypeSafe AI, and shares no weights or code with it.
System 1 numbers are on the held-out test_set_30k of jev-distill-corpus-v3. Its 29,955 rows come from three
sources: 25,376 rows labelled with TypeSafe Jev 1.13's own output distributions (yuri_v3), 2,319 Open-Jev rows with
programmatic ground-truth labels (openjev_v2), and 2,260 placeholder rows (yuri_v1). Rows marked Jev-labelled use
only the first group.
| What is measured | autotrust/JEV-27B | How to read it | |
|---|---|---|---|
| System 1 | Mean KL divergence from TypeSafe Jev 1.13's distributions, Jev-labelled rows, 0 = identical | ≈ 0.017 | Indistinguishable from the teacher's decisions at this resolution: ≈ 60 sampled decisions to gather one nat of evidence |
| Mean KL to all test targets (Jev, programmatic and placeholder labels) | 0.019 | The figure in the model index above | |
Yes/no AUROC (noul), Jev-labelled rows | 0.995 | Ranks true vs. false almost perfectly (0.996 over all rows) | |
| Choice top-1 agreement with Jev, Jev-labelled rows | 90.5 % | 95.8 % over all rows where the target's top two options differ by ≥ 0.1; on near ties any faithful copy agrees about half the time | |
Rating error (score, 0–5 scale), mean absolute error of the expected rating | 0.098 | About one tenth of a rating step | |
| Expected calibration error | 0.0009 | A stated 80 % is an 80 %; fitted temperatures ≈ 1.00, no post-hoc correction needed | |
| KL to the programmatic labels of task families never seen in training (Open-Jev OOD split) | 0.104 | Transfer to new tasks; these labels are ground truth, not Jev's outputs | |
| Independent benchmark with human gold labels, 16 options | 96 % of the teacher (0.740 vs 0.769) | 98 % of the teacher at 2, 4 and 8 options; see Benchmark highlights | |
| Six public decision benchmarks, mean (JevBench, Kev, OpenJev text, Nimble, VitaminC, MASSIVE-en) | 84.07 % | TypeSafe Jev 1.13: 83.85 % in the updated results (+0.22 pts for JEV-27B); JEV-27B is higher on 4 of the 6; see Public decision benchmarks | |
| System 2 | HumanEval pass@1, greedy | 78.0 % | Identical to Qwen3.8-27B (128/164); all 164 completions byte-identical to the base model |
| Speed | Single decision, median, one B200 | 137 ms | Hosted TypeSafe Jev 1.13, measured independently: 238 ms mean, 291–301 ms median |
| Decisions per second on the independent benchmark, one B200 | ≈ 130 | ≈ 6× the 23 per second measured against the hosted API; see Speed | |
| Batched, 128 decisions per batch | 4.2 ms per decision | With vLLM: 141 decisions/s offline, text generation ≈ 48× faster than the PyTorch path | |
| Efficiency | Trained parameters | 108.9 M (0.4 % of 25.6 B) | ≈ 9.2 B200-hours, 0.98 epoch ≈ 640 k rows |
autotrust/JEV-9B was AutoTrust's first integrated System 1 + System 2 model. JEV-27B uses the same recipe, code, hyper-parameters, API and two-block packaging; only the backbone and memory settings changed. Both are evaluated on the same held-out test set and the same independent benchmark.
| JEV-9B | JEV-27B | change | |
|---|---|---|---|
| Backbone | Qwen3.5-9B | Qwen3.8-27B | |
| System 1 — mean KL to TypeSafe Jev 1.13, Jev-labelled rows | ≈ 0.019 | ≈ 0.017 | ≈ −11 % |
| Mean KL to all test targets | 0.021 | 0.019 | −11 % |
| KL to ground-truth labels, unseen task families (OOD) | 0.234 | 0.104 | −56 % |
| Top-1 accuracy, unseen task families (OOD) | 0.918 | 0.942 | +2.4 pts |
| Choice top-1 agreement with Jev, Jev-labelled rows | 90.2 % | 90.5 % | +0.3 pts |
Rating error (score MAE, all Jev-labelled) | 0.103 | 0.098 | −5 % |
| Top-1 flips under option shuffle (test set) | 3.9 % | 2.9 % | −1.0 pt |
Yes/no AUROC (noul), Jev-labelled rows | 0.994 | 0.995 | +0.001 |
| Calibration error (ECE) | 0.0007 | 0.0009 | JEV-9B slightly lower; both below 0.001 |
| Independent benchmark, 16 options — % of teacher accuracy | 90 % | 96 % | +6 pts |
| Independent benchmark — answers changed by option order alone (teacher: 7.0 %) | 11.5 % | 7.4 % | close to the teacher's 7.0 % |
| System 2 — HumanEval pass@1 (greedy) | 70.7 % | 78.0 % | +7.3 pts |
| Latency on one B200 — single request / batched | ≈ 90 ms / 2.5 ms | 137 ms / 4.2 ms | JEV-9B is faster |
| Trained parameters / compute | 40.2 M / ≈ 3 B200-hours | 108.9 M / ≈ 9.2 B200-hours |
On the fresh Hacker News, V2EX and community examples (illustrations, not a benchmark), JEV-27B got 95 of 96 decisions right against 92 of 96 for JEV-9B. The difference is on the harder tasks: JEV-27B flags a TypeScript port that breaks a "branded, range-checked integer" rule (0.93; JEV-9B 0.33, a miss) and a CEO wire-transfer fraud message with more confidence (0.84; JEV-9B 0.56).
Which to pick. For routing, moderation, topic triage and short option lists, JEV-9B gives nearly the same answers 2.6× faster (14,400 benchmark decisions in 42 s vs 110 s on one B200). For long option lists (more than about 8), unfamiliar task families, code-rule checks, fraud screening, or when the System 2 path matters, use JEV-27B.
Dozens of open reproductions of TypeSafe Jev appeared within weeks of its launch; the community Jev Decision Index (formerly the Jev Reproductions Tracker) evaluates 55 of them. Most are trained on human or programmatic gold labels, or on their own synthetic data, so they aim to match or beat Jev's accuracy rather than reproduce its probabilities. "Closest to Jev" therefore depends on how closeness is measured:
| measure of closeness to TypeSafe Jev 1.13 | published results (snapshot of 25 September 2026) | where JEV-27B stands |
|---|---|---|
| Distribution level: KL to Jev's own output distributions on held-out rows | JEV-27B ≈ 0.017 and JEV-9B ≈ 0.019 on 25,376 Jev-labelled rows. We found no other open reproduction that publishes this measure. | Lowest published |
Accuracy relative to Jev on decision-models-under-pressure, 16 options, human gold labels | JEV-27B 96 % · JEV-9B 90 % · Laya 90 % · DeBERTa-v3-large zero-shot 90 % · DeBERTa-v3-base zero-shot 83 % · GLiClass-large 81 % · bge-large 73 % · gte-large 69 % | Closest of the models measured there (JEV rows are AutoTrust re-runs of the same items; the others were run by the benchmark's author) |
| Head-to-head on six public decision benchmarks (our runs of JEV-27B and Jev; other rows as reported by NeoHorse) | JEV-27B 84.07 · TypeSafe Jev 1.13 83.85 · NeoHorse-Jev-4B 77.70 · Open-Jev-9B 75.67 · Kev-4B 74.25 · Laya English 58.24 | 0.22 points above Jev; highest six-group mean among the listed models with complete results (details) |
| Score parity on community leaderboards | Decision Index 0.2: Jev 51.67, AutoJev-27B 50.94. JevBench v1.4.2: decider-4b v2 64.13, Jev 63.29, JevK5 62.04. Open-Jev public JevBench subset: Jev 200/231, Open-Jev 27B v1.1 197/231 | Not yet submitted to these leaderboards. On the public JevBench set in our own runs: JEV-27B 88.70, Jev 87.18 (family-macro score) |
On the evidence published today, JEV-27B is the closest open model to TypeSafe Jev 1.13 at the level of output distributions, the closest of the models measured on the independent benchmark above, and 0.22 points above Jev's six-benchmark mean in the updated results of our head-to-head runs. It has not yet been submitted to the Decision Index or the JevBench v1.4.2 leaderboard, where AutoJev-27B scores within about one point of Jev and decider-4b v2 edges ahead of it, so we do not claim it is the closest by every measure. Note that some reproductions report beating Jev on their own test sets (AutoJev-27B reports 84.60 % against Jev's 82.79 %); that is a different goal from reproducing Jev's behaviour.
Not to be confused with AutoJev-27B (denis-pplx/autojev-27b), an unrelated Qwen3.8-27B decision model trained with
full-weight SFT on its own data.
TypeSafe does not publish Jev's size or hardware; it reports 70–500 ms end to end. Independent measurements, and ours:
| TypeSafe Jev 1.13, hosted API | JEV-9B, one B200 | JEV-27B, one B200 | |
|---|---|---|---|
| One decision, single request | 238 ms mean over 29,600 calls (decision-models-under-pressure); 291–301 ms median on three workloads (Open-Jev) | ≈ 90 ms median | 137 ms median |
Decisions per second on decision-models-under-pressure | 23, with 5 client workers and one question per call | ≈ 340 (14,400 in 42 s) | ≈ 130 (14,400 in 110 s) |
| Batched, 128 decisions per batch | — | 2.5 ms per decision | 4.2 ms per decision |
So JEV-27B answers a single decision in roughly half the time (JEV-9B in roughly a third), and one GPU sustains about 6× (JEV-9B: about 15×) the throughput the benchmark's author achieved against the hosted API. Read these with the caveats: our latencies are measured on the serving host with no network hop, while the hosted numbers include internet, TLS and queueing; hosted throughput depends on client concurrency and the API's rate limits; Jev's latency is roughly flat in the number of questions per request, so bundling questions narrows the throughput gap; and our figures are self-reported while Jev's come from third parties. The two throughput runs use the same benchmark but not an identical call set (ours stops at 16 options).
What the number means. KL(Jev ‖ model) is the expected log-likelihood ratio, per sampled decision, between TypeSafe Jev 1.13 and the student when the decision comes from Jev. On the 25,376 held-out rows whose targets are Jev's own output distributions, the mean is ≈ 0.017 nats (computed from the per-slice values below, which are published to three decimals): one decision carries almost no evidence about which of the two models produced it, and an observer needs about 1 / KL ≈ 60 independent decisions to accumulate one nat (a likelihood ratio of about e ≈ 2.7 : 1).
For scale, Jev is not deterministic itself: an independent study found it changes its answer on 4.3 % of repeated, identical 64-option calls, and it returns probabilities rounded to two decimals, which is the resolution of the targets used here.
Jev-labelled slice (yuri_v3, test_set_30k) | n | KL | ≈ decisions to gather one nat (1 / KL) |
|---|---|---|---|
noul | 8,537 | 0.004 | ≈ 250 |
choice | 8,312 | 0.025 | ≈ 40 |
score | 8,527 | 0.021 | ≈ 48 |
| all Jev-labelled rows | 25,376 | ≈ 0.017 | ≈ 60 |
The other test rows are not labelled by Jev and are not part of this claim: Open-Jev rows carry programmatic ground
truth (in-distribution KL 0.003 for noul, 0.146 for choice; 0.104 on the OOD split of unseen task families), and
the yuri_v1 rows carry placeholder labels. No Jev-labelled out-of-distribution set exists in the corpus, so the claim
is established on the 53 training domains; outside them, the independent benchmark with human labels (96–98 % of Jev's
accuracy) is the best available evidence.
Fidelity includes the teacher's mistakes. On a poker spot where a solver always checks, TypeSafe Jev 1.13 shoves with 0.62 in a published test; JEV-27B shoves with 0.63. On an independent benchmark, about 7 % of 16-option answers change when only the option order changes, for the teacher (7.0 %) and the student (7.4 %) alike. A faithful copy of System 1 is also a faithful copy of its blind spots.
The Blocks of Experts recipe. (1) Train the System 1 block on Jev 1.13 distributions; (2) serve both systems from one frozen backbone, routed per request · click to enlarge
| block | what it is | parameters | trained? | used for |
|---|---|---|---|---|
| Backbone | Qwen/Qwen3.8-27B text tower (vision tower and MTP head dropped), bf16 | 25.6 B | no — bit-identical to the base | both systems |
| System 2 block | the original lm_head (248,320 × 5120) | part of the base | no | text generation and step-by-step reasoning |
| System 1 block | LoRA r=16 on the decoder projections + a 24-slot fp32 decision head initialised from lm_head rows | 108.8 M + 123 k | yes, ≈ 9.2 B200-hours | calibrated typed decisions |
| Router | per request: the vLLM LoRA module jev-decision, or peft adapter on/off | — | — | chooses the system |
Why separate blocks rather than one merged fine-tune. Folding the System 1 LoRA into the backbone would let a single weight set serve both heads, but it costs generation quality: on JEV-9B the merged backbone scores 61.6 % on HumanEval against 70.7 % for the base, a 9-point drop, even though prose perplexity barely moves (3.15 → 3.30). Keeping the backbone pristine and applying the System 1 block only on the decision path removes that trade-off. For decision serving the adapter is merged in memory at start-up, so decision latency matches a merged bundle. (The merged variant was not re-measured at 27 B.)
Why the recipe is this efficient.
lm_head rows for the verbalizer tokens (false/true, 0–5, A–P), so at step 0 its output equals the
pretrained model's zero-shot restricted next-token distribution (verified to |Δp| < 1e-5; measured 4.5e-07). Before
seeing a single label it already agrees with the test targets on 58 % of choice questions with noul AUROC 0.88;
distillation takes it to 90 % / 0.996.choice options is 2.9 %; the same backbone before distillation flips 41 % of the time.score) gives fitted temperatures of 1.014 / 1.016 / 1.004 and ECE 0.0009 with no post-hoc correction.model_path and memory settings changed. Going from 9 B to 27 B lowers KL to Jev from
≈ 0.019 to ≈ 0.017, halves OOD KL (0.234 → 0.104), and raises the System 2 path from 70.7 % to 78.0 % on HumanEval.
Benchmark highlights. A · KL to Jev by question type · B · accuracy as % of Jev on an independent benchmark · C · System 2 unchanged · D · speed vs the hosted API · click to enlarge
gazelle93/decision-models-under-pressure (published
25 Sep 2026) asks decision models to pick the right label for real texts from CLINC-150, MTOP, GoEmotions, DBpedia and
financial tweets under three kinds of pressure: more options, near-miss options, and shuffled option order. The labels
are human gold labels, none of this data is in our training set, and TypeSafe Jev 1.13's results are published with it.
We re-ran the same items with autotrust/JEV-27B, up to our 16-option limit.
| TypeSafe Jev 1.13 (published) | autotrust/JEV-27B | |
|---|---|---|
| Accuracy with 2 / 4 / 8 / 16 options (800 items, 4 domains) | 0.890 / 0.801 / 0.782 / 0.769 | 0.876 / 0.784 / 0.767 / 0.740 |
| 16 options — CLINC / DBpedia / GoEmotions / MTOP | 0.945 / 0.900 / 0.470 / 0.760 | 0.930 / 0.885 / 0.415 / 0.730 |
| 16 options, near-miss vs. unrelated wrong options (CLINC + MTOP, 400 items) | 0.912 vs 0.985 | 0.907 vs 0.983 |
| Answers changed by shuffling the options alone (16 options, 5 orderings) | 7.0 % | 7.4 % |
On data it was never trained on, the student reaches 96–98 % of the teacher's accuracy, loses the same amount when the wrong options become near misses, and shares the teacher's sensitivity to option order — a weakness inherited from the teacher, not one that distillation fixed. Our run follows the benchmark's published method (gold plus the first K−1 distractors of a pool, shuffled per item); the orderings are seeded differently, so compare aggregates, not individual items. 14,400 decisions took 110 s on one B200.
Expected answers were written by hand before the model was run. These are illustrations (≈ 110 decisions), not a benchmark.
| task | result |
|---|---|
| Topic of 19 HN front-page stories (10 options) + "is it about AI?" | 38 / 38 |
| 12 comments from a heated HN thread: "insults or attacks someone?" + "what is it mainly doing?" (6 options) | 23 / 24 |
| 10 V2EX hot posts in Chinese: "contains a referral / invite code?" + "promotes a product or paid offer?" | 19 / 19 |
Community use cases: code-rule checks in the style of adhere, injection filtering, ticket routing, phishing, code-review diffs, urgency scores | 15 / 15 |
| input | question | autotrust/JEV-27B |
|---|---|---|
| HN: "Two-tier encryption in the UK" | topic (10 options) | security & privacy · 0.99 |
| HN: "Show HN: Agentic CUDA Kernel Optimizer" | topic (10 options) | programming tools · 0.44 (genuinely ambiguous: AI 2nd) |
| HN comment: "Please stop this. We've asked you before to observe the guidelines…" | what is it mainly doing? | moderating the discussion · 1.00 |
| V2EX: "一个不需要 gemini pro 的完全免费的注册 Muse 的方法 … 邀请码:…" | contains a referral / invite code? | P(true) = 0.99 |
TypeScript file with const port: number = Number(process.env.PORT) | breaks "a port must be a branded, range-checked integer"? | P(true) = 0.93 (0.08 for the branded version) |
| "Hi, are you at your desk? I need you to process a wire of $48,500 … Don't mention this to anyone" | phishing or fraud? | P(true) = 0.84 |
Diff replacing a parameterised query with "… WHERE id = " + request.args["id"] | introduces a security vulnerability? | P(true) = 0.98 (0.04 for a variable rename) |
| "I'm not happy with the fit. What are my options here?" | asking for a refund? | P(true) = 0.15 (TypeSafe's docs report 0.22 for Jev on this exact text) |
Where it failed or wavered:
Per-example outputs and the benchmark aggregates are in reports/realworld_27b.json (the HN and V2EX inputs came from
their public APIs on 25 September 2026).
One vLLM engine serves both systems from the same pristine weights. Ordinary requests go through the base
lm_head (System 2, exactly Qwen3.8-27B); requests addressed to the LoRA module jev-decision go through the decision
head (System 1). adapter_vllm/ contains the backbone LoRA plus the 24-slot decision head re-expressed as an lm_head
LoRA (only the 24 verbalizer rows change), so a typed decision is a single prefill step with max_tokens=1,
constrained to the option tokens and read back as log-probabilities.
hf download autotrust/JEV-27B --local-dir JEV-27B # ~54 GB
python3 JEV-27B/serve_decide.py --model JEV-27B --served-model-name autotrust/JEV-27B \
--enable-lora --max-lora-rank 32 --lora-modules jev-decision=JEV-27B/adapter_vllm \
--logprobs-mode processed_logprobs --max-model-len 32768
serve_decide.py (in this repository) is the standard vLLM OpenAI server, with the same flags as vllm serve, plus a
POST /v1/decide route for System 1 (step 3). vllm serve JEV-27B … with the same flags also works; System 1 then goes
through /v1/completions with the client-side code in step 3.
--max-model-len 32768 covers most decisions and System 2 thinking; the backbone supports up to 262,144 (see
Context length). Add --enable-prefix-caching --mamba-cache-mode align if you ask many questions
about the same state.
curl -s localhost:8000/v1/chat/completions -H 'content-type: application/json' -d '{
"model": "autotrust/JEV-27B",
"messages": [{"role": "user", "content": "In one sentence, what is safety stock?"}],
"max_tokens": 60, "chat_template_kwargs": {"enable_thinking": false}}'
Set "enable_thinking": true for deliberate, step-by-step reasoning. This path is Qwen3.8-27B unchanged; see the
Qwen3.8-27B model card for its reasoning benchmarks and recommended sampling
settings.
POST /v1/decide)Send a question and get back a calibrated probability for every option. The server builds the decision prompt, reads the option tokens and applies the decision head's bias and temperature, so any HTTP client works.
curl localhost:8000/v1/decide -H 'Content-Type: application/json' -d '{
"kind": "choice",
"state": "SKU AX-330 stock at 8% of safety level; supplier late twice this quarter.",
"question": "Supplier response for this scenario.",
"options": ["issue_warning", "renegotiate", "dual_source", "maintain"]}'
{"kind": "choice", "effective_kind": "choice",
"options": ["issue_warning", "renegotiate", "dual_source", "maintain"],
"probabilities": [0.2541, 0.1253, 0.6200, 0.0007], "choice_index": 2, "choice": "dual_source",
"adaptation": "native", "protocol": "jev27-bare-v1", "model": "autotrust/JEV-27B",
"usage": {"prompt_tokens": 65, "completion_tokens": 1, "total_tokens": 66}, "num_model_requests": 1, "elapsed_seconds": ...}
| field | value |
|---|---|
kind | noul: yes/no, probabilities for ["false", "true"] · score: a 0–5 scale, for "0"…"5" · choice: for your options |
state | what the decision is about: a string or a JSON object |
question | one question about the state |
options | choice only: 2–256 strings |
In Python:
import requests
URL = "http://localhost:8000"
def decide(kind, state, question, options=None):
body = {"kind": kind, "state": state, "question": question, **({"options": options} if options else {})}
r = requests.post(f"{URL}/v1/decide", json=body).json()
return dict(zip(r["options"], r["probabilities"]))
print(decide("choice", "SKU AX-330 stock at 8% of safety level; supplier late twice this quarter.",
"Supplier response for this scenario.", ["issue_warning", "renegotiate", "dual_source", "maintain"]))
# ≈ {'issue_warning': 0.25, 'renegotiate': 0.13, 'dual_source': 0.62, 'maintain': 0.001}
print(decide("noul", "Customer says the parcel arrived damaged and wants their money back.",
"Is the customer asking for a refund?"))
Values can differ in the third decimal between runs: vLLM computes in bf16 and results depend slightly on which
requests are batched together. GET /v1/decide/info reports the option limit and the temperatures.
serve_decide.py: plain vllm serve and client-side math (up to 16 options)Ask for the option tokens through /v1/completions and apply the bundled bias and temperature yourself. Pass
top_k: 0 and top_p: 1.0: the model's generation config sets top_k=20 and top_p=0.95, vLLM applies them as
request defaults, and with --logprobs-mode processed_logprobs they would truncate the returned probabilities and zero
out less likely options.
import json, math, requests
from huggingface_hub import hf_hub_download
REPO, URL = "autotrust/JEV-27B", "http://localhost:8000"
dh = json.load(open(hf_hub_download(REPO, "adapter_vllm/decision_head.json"))) # bias + verbalizer token ids
T = json.load(open(hf_hub_download(REPO, "calibration.json")))["per_kind"] # per-kind temperatures
def decide(kind, state, question, options=None):
options = {"noul": ["false", "true"], "score": [str(i) for i in range(6)]}.get(kind, options)
lines = options if kind != "choice" else [f"{'ABCDEFGHIJKLMNOP'[i]}) {o}" for i, o in enumerate(options)]
prompt = f"[kind] {kind}\n[state] {state}\n[question] {question}\n[options]\n" + "\n".join(lines) + "\n[decision]:"
s = dh["slots"]["ranges"][kind][0]
ids = dh["verbalizer_ids"][s : s + len(options)] # the option tokens of this kind
r = requests.post(f"{URL}/v1/completions", json={
"model": "jev-decision", "prompt": prompt, "max_tokens": 1, "temperature": 1.0, "top_k": 0, "top_p": 1.0,
"logprobs": len(options), "allowed_token_ids": ids,
"add_special_tokens": False, "return_tokens_as_token_ids": True}).json()
lp = {int(k.split(":")[1]): v for k, v in r["choices"][0]["logprobs"]["top_logprobs"][0].items()}
z = [(lp.get(t, -1e9) + dh["bias"][s + i]) / T[kind] for i, t in enumerate(ids)] # + head bias, / temperature
e = [math.exp(x - max(z)) for x in z]
return {o: x / sum(e) for o, x in zip(options, e)}
The log-softmax normaliser that vLLM applies cancels out, so the result is exactly the decision head's calibrated distribution.
Because both systems live in one engine, a common pattern is to let System 1 answer when it is confident and hand the
rest to System 2. This is a usage pattern, not a configuration we have benchmarked; pick the threshold on your own
validation data, and serve with a --max-model-len large enough for the reasoning budget.
def solve(state, question, options, threshold=0.90):
p = decide("choice", state, question, options) # System 1: one prefill pass
best = max(p, key=p.get)
if p[best] >= threshold:
return {"system": 1, "answer": best, "distribution": p}
prompt = (f"{state}\n\nQuestion: {question}\nOptions: " + "; ".join(options)
+ "\nThink it through, then give exactly one option on the last line.")
r = requests.post(f"{URL}/v1/chat/completions", json={ # System 2: same engine, base lm_head
"model": "autotrust/JEV-27B",
"messages": [{"role": "user", "content": prompt}],
"max_tokens": 8192, "chat_template_kwargs": {"enable_thinking": True}}).json()
return {"system": 2, "reply": r["choices"][0]["message"]["content"], "system1_distribution": p}
from vllm import LLM, SamplingParams
from vllm.lora.request import LoRARequest
llm = LLM("JEV-27B", enable_lora=True, max_lora_rank=32, logprobs_mode="processed_logprobs", max_model_len=32768)
decision = LoRARequest("jev-decision", 1, "JEV-27B/adapter_vllm")
gen = llm.generate(["..."], SamplingParams(temperature=0.0, max_tokens=256)) # System 2, no LoRA
dec = llm.generate([prompt], [SamplingParams(max_tokens=1, temperature=1.0, top_k=0, top_p=1.0, # System 1
allowed_token_ids=ids, logprobs=len(ids))],
lora_request=decision) # then + bias, / T as above
Mixed batches work too: pass a per-request lora_request list (None for System 2, decision for System 1) and both
systems are served in the same generate call.
| workload | PyTorch path | vLLM |
|---|---|---|
| System 2 — 164 HumanEval completions (greedy, ≤ 384 new tokens) | 350 s | 7.3 s (≈ 48×) |
| System 1 — offline batch, 29,955 test questions | 368 s (81 q/s) | 212 s (141 q/s) |
| System 1 fidelity vs. the PyTorch path | test KL 0.0186 | test KL 0.0186, choice top-1 0.903 |
Notes:
[kind] before [state], so
only questions of the same kind share a prefix. Measured on JEV-9B: on 293 real states × 7.7 yes/no
questions each (≈ 480-token states), prefix caching served 19.6 % of prompt tokens from cache (+14–20 % throughput)
with identical outputs.qwen3_5) support, LoRA on lm_head, --logprobs-mode and
allowed_token_ids; tested with a vLLM development build from September 2026. serve_decide.py also uses
logprob_token_ids and that build's server layout. Start-up takes 3–8 minutes
(CUDA-graph capture with LoRA enabled).choice takes 2–256 options in one question. The first 16 are labelled A–P, the labels the decision head was trained
on. Beyond 16, the labels continue with Q–Z and then two-letter labels that are single tokens (AA, AB, …), read the same
way, with no retraining. The response's adaptation field says native (up to 16 options) or wide-labels.
Zero-shot intent classification with every intent offered at once (accuracy on 400 test utterances per row):
| dataset | options | intent names only | names + a one-line description each |
|---|---|---|---|
| MASSIVE (en) | 59 | 83.2% | — |
| BANKING77 | 64 | 79.0% | 83.8% |
| BANKING77 | 77 (all) | 74.5% | 81.0% |
| CLINC150 | 64 | 94.8% | 97.3% |
| CLINC150 | 128 | 90.7% | 94.5% |
| CLINC150 | 150 (all) | 89.5% | 93.8% |
choice question beats one yes/no question per option, and costs one forward pass instead of one per option:
83.2% vs 78.0% on MASSIVE, 78.8% vs 67.0% on BANKING77 (64 options), 94.5% vs 82.0% on CLINC150 (64 options).What we measured on the intent-classification sets above (400 utterances per variant):
state, one question in question. state holds everything the decision depends on; question asks
one thing about it. Plain text and JSON both work: a JSON state scored the same as plain text (83.8% vs 83.2%,
79.0% vs 79.0%, 94.5% vs 94.8%).choice question, not one yes/no question per candidate (see above).[options]
A) card arrival: use when users ask about the status or delivery of a physical card.
B) card delivery estimate: use when users ask about the expected arrival time or delivery speed of a new card.
C) Refund not showing up: use when a customer cannot see a completed refund in their account or statement.
D) request refund: use when a user asks how to initiate or the timing of a refund for a purchase.
More text did not help further. Longer rules with "do not use for …" clauses (about three times the tokens) and two
worked examples per option scored 83.8% and 84.0% on BANKING77 (vs 83.8% for one line), and 97.3% and 96.0% on CLINC150
(vs 97.3%). Generic descriptions did not help either: CLINC150's own intent descriptions scored 94.5%, against 94.8%
for names alone.state and alphabetical option order all stayed within ±1.5 points of the plain prompt, inside the
±1.8-point sampling noise of 400 items. Keep prompts plain.noul so that "true" is the outcome you want the probability of, e.g. "Is the customer asking for a
refund?". Our demos use "Is this scenario one where: …?" for properties of a text or an image.The backbone's native context is 262,144 tokens (256K), and System 1 accepts prompts up to that length when vLLM is
started with --max-model-len 262144. We tested decisions that hinge on a single sentence placed at a random depth in
long real text (concatenated PubMedQA abstracts): a yes/no question (was the refund for order #N approved or rejected?)
and a 16-option question (which room is the spare part for machine M kept in?), 10 of each per length.
| prompt length | yes/no correct | 16-option correct | mean probability on the right answer | median latency |
|---|---|---|---|---|
| 4K (4,235 tokens) | 10/10 | 10/10 | 0.999 | 0.6 s |
| 32K (32,157 tokens) | 10/10 | 10/10 | 0.998 | 3.0 s |
| 64K (64,070 tokens) | 10/10 | 10/10 | 0.997 | 5.7 s |
| 128K (127,895 tokens) | 10/10 | 10/10 | 0.997 | 12.2 s |
| 192K (191,659 tokens) | 10/10 | 10/10 | 0.997 | 20.2 s |
| 250K (249,507 tokens) | 10/10 | 10/10 | 0.997 | 28.7 s |
Latency is one request at a time on one B200 and is almost all prompt processing (about 8,700 tokens per second at 250K). Measured with JEV-27B-VL, which has the same language weights and adapter, with its serving settings.
Memory: the KV cache takes about 65 KB per token, so a full 256K prompt needs about 17 GB on top of the 52 GB of weights
(tested on a 183 GB B200). On an 80 GB GPU, start with --max-model-len 131072.
| kind | question | returns |
|---|---|---|
noul | "Is this statement true?" | [P(false), P(true)] |
choice | "Which of these 2–256 options?" | one probability per option, aligned with your options |
score | "Where on this ordered 0–5 scale?" | a distribution over the six levels (+ expected score) |
[kind] choice
[state] SKU AX-330 stock at 8% of safety level; supplier late twice this quarter.
[question] Supplier response for this scenario.
[options]
A) issue_warning
B) renegotiate
C) dual_source
D) maintain
[decision]:
The template is tokenised as one string; the last token's final-norm hidden state goes through a
linear fp32 head H → 24 slots (noul → slots 0–1, score → 2–7, choice → 8–23). Inactive slots are masked,
a per-kind temperature is applied, and a softmax yields the distribution aligned with your options. One prefill
pass, no decoding. On the vLLM path, choices with 17–256 options continue the labels past P (Q–Z, then AA, AB, …) and
read them from the language-model head the same way (see
Choice questions with up to 256 options).
transformers + peftimport json, torch
from huggingface_hub import hf_hub_download
from peft import PeftModel
from safetensors.torch import load_file
from transformers import AutoModelForCausalLM, AutoTokenizer
repo = "autotrust/JEV-27B"
tok = AutoTokenizer.from_pretrained(repo)
base = AutoModelForCausalLM.from_pretrained(repo, dtype=torch.bfloat16, device_map="cuda") # == Qwen3.8-27B text model
# --- System 2: the pristine base model, no adapter ----------------------------------------------
msgs = [{"role": "user", "content": "In two sentences, what is safety stock?"}]
enc = tok.apply_chat_template(msgs, add_generation_prompt=True, return_tensors="pt", return_dict=True).to("cuda")
out = base.generate(**enc, max_new_tokens=80)
print(tok.decode(out[0, enc["input_ids"].shape[1]:], skip_special_tokens=True))
# --- System 1: attach the LoRA adapter (merged here for speed) + the 24-slot head ---------------
model = PeftModel.from_pretrained(base, repo, subfolder="adapter").merge_and_unload()
head = load_file(hf_hub_download(repo, "head.safetensors"))
cfg = json.load(open(hf_hub_download(repo, "judge_config.json")))
temp = json.load(open(hf_hub_download(repo, "calibration.json")))["per_kind"]
W, b = head["proj.weight"].cuda(), head["proj.bias"].cuda()
def decide(kind, state, question, options):
letters = "ABCDEFGHIJKLMNOP"
lines = options if kind != "choice" else [f"{letters[i]}) {o}" for i, o in enumerate(options)]
text = f"[kind] {kind}\n[state] {state}\n[question] {question}\n[options]\n" + "\n".join(lines) + "\n[decision]:"
ids = tok(text, return_tensors="pt", add_special_tokens=False).to("cuda")
with torch.no_grad(), torch.autocast("cuda", dtype=torch.bfloat16):
h = model.model(**ids).last_hidden_state[0, -1].float() # backbone only, last token
z = (W @ h + b) / temp[kind]
s, _ = cfg["slots"]["ranges"][kind]
p = torch.softmax(z[s : s + len(options)], 0)
return dict(zip(options, p.tolist()))
print(decide("noul", "Customer says the parcel arrived damaged and wants their money back.",
"Is the customer asking for a refund?", ["false", "true"]))
# {'false': 0.022, 'true': 0.978}
options are validated: noul must be ["false","true"], score must be ["0".."5"], choice takes 2–16
free-text options on this head-only path (the 24-slot head has 16 choice slots; 17–256 options need the vLLM path). Note that merge_and_unload() above changes the backbone for the rest of the process; to keep both
systems in one process, leave the adapter unmerged and run System 2 inside with model.disable_adapter():.
test_set_30k, temperature applied)| metric | autotrust/JEV-27B |
|---|---|
noul Brier score against the target probability, all rows (lower is better) | 0.0013 |
score ranked probability score (lower is better) | 0.0077 |
| Fitted temperatures noul / choice / score | 1.014 / 1.016 / 1.004 |
Top-1 flip rate when choice options are shuffled (1,000 rows × 4 permutations) | 2.9 % |
Out-of-distribution split — top-1 agreement · noul AUROC | 0.942 · 0.996 |
| Throughput — batch of 128 requests on one B200 | 4.2 ms per decision (≈ 240 decisions/s) |
| Single request on one B200 (median of 20) | 137 ms |
| weights | pass@1 | note |
|---|---|---|
| Qwen3.8-27B (base) | 78.0 % (128/164) | same loader and protocol as below |
autotrust/JEV-27B — System 2 path (backbone + lm_head, adapter off) | 78.0 % (128/164) | all 164 completions byte-identical to the base model |
JEV-9B: LoRA folded into the backbone + base lm_head (not shipped) | 61.6 % vs 70.7 % base | why the blocks are kept separate |
test_set_30k, temperature applied)| source | kind | n | KL | top-1 | ECE | noul AUROC | score MAE |
|---|---|---|---|---|---|---|---|
| yuri_v3 — synthetic operational scenarios, labelled by TypeSafe Jev 1.13 | noul | 8,537 | 0.004 | 0.959 | 0.001 | 0.995 | — |
| yuri_v3 | choice | 8,312 | 0.025 | 0.905 | 0.002 | — | — |
| yuri_v3 | score | 8,527 | 0.021 | 0.891 | 0.003 | — | 0.098 |
| openjev_v2 — Open-Jev programmatic tasks, ground-truth labels (not Jev) | noul | 1,432 | 0.003 | 0.999 | 0.002 | 1.000 | — |
| openjev_v2 | choice | 887 | 0.146 | 0.888 | 0.022 | — | — |
yuri_v1 — placeholder [0.5, 0.5] labels (see Limitations) | noul | 2,260 | 0.000 | — | 0.003 | — | — |
OOD split (13,058 Open-Jev rows from task families not in training, programmatic labels): KL 0.104, top-1 0.942, noul AUROC 0.996;
choice KL 0.187 / top-1 0.859 (game-state decisions are the hardest slice).
Choice option-permutation consistency (1,000 rows × 4 random permutations): mean max |Δp| 0.022, p90 0.051, top-1 flip rate 2.9 %.
Validation KL on a fixed 4 k-row subset; test_set_30k metrics after calibration.
| stage | rows seen | val KL | t30k KL | choice top-1 | score MAE | noul AUROC | ECE |
|---|---|---|---|---|---|---|---|
| autotrust/JEV-27B v0.8.0 — released weights (step 5,000 ≈ 0.98 epoch, LR annealed to ≈ 0.02×) | 640 k | 0.017 | 0.0186 | 0.903 | 0.098 | 0.996 | 0.0009 |
| step 2000 | 256 k | 0.027 | 0.031 | 0.872 | 0.133 | 0.993 | 0.0028 |
| step 500 | 64 k | 0.058 | 0.058 | 0.828 | 0.199 | 0.984 | 0.0032 |
| untrained backbone with the initialised head (reference point, not the model) | 0 | — | 0.430 | 0.581 | 0.842 | 0.876 | 0.050 |
Validation KL every 500 steps: 0.058 → 0.038 → 0.041 → 0.027 → 0.032 → 0.025 → 0.021 → 0.019 → 0.018 → 0.017 (steps 500 … 5,000). The single cosine schedule does the annealing: most of the second-half gain comes as the learning rate decays, the same effect JEV-9B needed an explicit cool-down for.
| item | value |
|---|---|
| teacher / data | SargeDev/jev-distill-corpus-v3 (740,957 rows; train 655,806) with three streams: yuri_v3 (498,010 rows, TypeSafe Jev 1.13 full output distributions via OpenRouter), openjev_v2 (94,801 rows, Open-Jev programmatic labels, CC0), yuri_v1 (148,154 rows, placeholder labels, down-weighted) |
| backbone | Qwen/Qwen3.8-27B text tower only (vision tower and MTP head dropped), bf16, frozen |
| System 1 block (trainable) | LoRA r=16, α=32, dropout 0.05 on in_proj_qkv, in_proj_z, out_proj, q/k/v/o_proj, gate/up/down_proj (108.8 M, shipped unmerged in adapter/) + 24-slot head (123 k, fp32, initialised from lm_head rows) |
| System 2 block | the original lm_head, not trained |
| loss | KL(target ‖ model) over active slots + 0.5 · RPS (ranked probability score) for score |
| augmentation | 30 % random permutation of choice options (targets permuted consistently) |
| batching | 128 rows / step, kind-stratified (≥ 1/6 per primitive), length-bucketed, micro-batches capped at 24 k padded tokens, gradient checkpointing |
| optimiser | AdamW (fused), β=(0.9, 0.98), lr head 2e-4 / LoRA 1e-4, one cosine schedule over 5,124 steps (min ×0.02), warmup 3 %, grad-clip 1.0; released checkpoint = step 5,000 (0.98 epoch), selected by validation KL |
| label hygiene | yuri_v1 rows carry exact-uniform [0.5, 0.5] placeholder labels (137,203 rows, 100 %); down-weighted ×0.05 in training and excluded from temperature fitting |
| calibration | per-kind scalar temperature (L-BFGS on the calibration split, 10,954 rows): noul 1.014 · choice 1.016 · score 1.004 |
| compute | 1× NVIDIA B200 (183 GB); ≈ 9.2 GPU-hours for 5,000 steps; ≈ 2.4 k tokens/s; peak memory 79 GB |
| software | torch 2.13 + cu130, transformers 5.16, peft 0.21, flash-linear-attention 0.5.2 |
choice) are measured against
programmatic ground truth, and on the independent benchmark the student reaches 96–98 % of Jev's accuracy, not 100 %.choice distributions are soft (median top-1
probability 0.70). On the 14 % of rows where the teacher's top two options are within 0.1 of each other, argmax
agreement is near chance for any faithful mimic (0.46 where the gap is < 0.05). On teacher-decisive rows agreement
is 0.958, and the student's argmax captures 98.1 % of the teacher probability mass a perfect mimic could (0.696 vs
0.709).noul and score accept only their canonical options; choice accepts 2–256 options
(2–16 on the transformers head-only path). Labels beyond P were never seen in training: accuracy holds up (see the
intent-classification table), but their calibration has been measured on one dataset only.transformers code above use the whole prompt, up to --max-model-len
(at most 262,144 tokens). The long-context test above is a single-fact retrieval decision; harder long-document
reasoning has not been measured. The original reference server truncates states longer than 1,024 tokens (head 60 % /
tail 40 %) unless you raise its limit.yuri_v1 memory-relevance stream is 100 % exact-uniform [0.5, 0.5] —
those rows teach nothing about relevance. The model outputs ≈ 0.5 on them by design; do not use it for
memory-relevance scoring without further training.model-000{01..13}-of-00013.safetensors Qwen3.8-27B text backbone incl. lm_head — bit-identical to the base model
(bf16; GDN A_log / gated-norm weights fp32 as in the original), 53.8 GB
model.safetensors.index.json · config.json
adapter/ System 1 LoRA (peft format, r=16, 108.8 M params, 416 MB) — apply only for decisions
head.safetensors 24-slot decision head (fp32): proj.weight [24, 5120], proj.bias [24]
judge_config.json slot layout, verbalizer token ids, template version, weights_mode=unmerged, provenance
calibration.json per-kind temperatures (+ fit diagnostics)
serve_decide.py vLLM OpenAI server + POST /v1/decide (System 1 over HTTP, 2–256 options)
adapter_vllm/ the same adapter for vLLM: backbone LoRA (zero-padded to r=32) + decision head as an
lm_head LoRA, plus decision_head.json (head bias, verbalizer token ids)
tokenizer.json · tokenizer_config.json · chat_template.jinja
27b-3.jpg · 27b1.jpg · 27b-2.jpg scorecard, Blocks of Experts recipe and benchmark charts used in this card
reports/ evaluation reports: test-set evaluation, bundle checks, HumanEval per-problem
results, vLLM measurements, real-world tests, training-milestone reviews
Weights: Apache-2.0 (base model Qwen/Qwen3.8-27B is Apache-2.0; training corpus
SargeDev/jev-distill-corpus-v3 is Apache-2.0, its openjev_v2 stream additionally CC0). The System One framing and
the noul / choice / score primitives originate with TypeSafe AI's Jev; autotrust/JEV-27B is an independent
student model trained on public data and shares no weights, code or affiliation with TypeSafe AI.
@misc{autotrust_jev27b_2026,
title = {autotrust/JEV-27B: a second-generation integrated System 1 + System 2 open model built with the Blocks of Experts recipe (Qwen3.8-27B; System 1 distilled from TypeSafe Jev 1.13)},
author = {{AutoTrust AI}},
year = {2026},
url = {https://huggingface.co/autotrust/JEV-27B}
}