How to Build a Decision Model Like Jev Hugging Face merged native `/v1/systemone` support into its `transformers serve` CLI via PR #49322, turning 250+ open LLM and VLM architectures into logit-scoring decision models that run one prefill pass and generate zero output tokens. On JevBench, small open-weight decision models such as SemIf (Qwen3.5-4B, scoring 73.1) and Winnow-12B Q8 (71.2) now trail hosted Jev 1.13.0 (74.4) at roughly half the cost per thousand decisions, according to the benchmark. The `/v1/systemone` contract answers typed `choice` (up to 255 options), `noul` (binary yes/no probability) and `score` (2 to 10 ordered levels) questions about a single state, replacing chat-completion calls for routing, gating and scoring tasks. How to Build a Decision Model Like Jev In my LLM inference roadmap https://www.fratepietro.com/2026/llm-inference-learning-roadmap/ , I split every model call into two phases. Prefill reads the prompt in parallel and is compute-bound. Decode emits output tokens one at a time, reading the entire weight matrix from HBM on every step, and is memory-bandwidth bound. Most software decisions do not want text generation at all. Routing a support ticket, gating a risky tool call for human review, scoring an incident rubric from 1 to 5, or producing the P bullish signal I used in Laya Trader https://www.fratepietro.com/2026/laya-trader-paper-trading-small-local-model/ all ask for a probability distribution over a known set of outcomes. Calling /v1/chat/completions for those tasks pays the decode tax for dozens of tokens, hopes the JSON parser survives, and still leaves you without a trustworthy probability. TypeSafe’s Jev https://docs.typesafe.ai/models POST /v1/systemone replaced that loop with a typed contract. You send one state a document, JSON record, or ticket and a map of named questions about that state: - choice : pick one option out of up to 255 candidates - noul : return a binary yes/no probability P true - score : rate on an ordered rubric of 2 to 10 levels The model runs prefill once, generates zero output tokens, and returns a probability for every option. Over the last two weeks, this interface moved from a proprietary API to an open pattern you run and train yourself. Hugging Face merged native /v1/systemone support https://huggingface.co/docs/transformers/serve-cli/serving v1systemone into transformers serve , turning 250+ open LLM and VLM architectures into logit-scoring decision models out of the box. And on JevBench https://benchmarkheaven.com/jev-models , small open-weight decision models like SemIf Qwen3.5-4B , scoring 73.1 and Winnow-12B Q8 71.2 now sit right behind hosted Jev 1.13.0 74.4 at roughly half the cost per thousand decisions. This post walks through how to build and serve a decision model from the ground up: starting with Hugging Face’s zero-shot /v1/systemone server, then fixing its ceilings with reserved option tokens, calibrating probabilities with temperature and soft targets, packing multi-question requests with tree batching, and examining how Jev itself is wired under the hood. 1. The fast path: turning any LLM or VLM into /v1/systemone with transformers serve Before training a single weight, serve the /v1/systemone contract locally on top of an existing open checkpoint. Hugging Face’s transformers serve CLI added in PR 49322 formats your state and questions through the model’s chat template with thinking disabled, runs one batched forward pass output tokens: 0 , and slices the next-token logits at the assistant prefix: pip install "transformers serving " transformers serve curl http://localhost:8000/v1/systemone \ -H "Content-Type: application/json" \ -d '{ "model": "Qwen/Qwen2.5-1.5B-Instruct", "state": "Our integration has been failing for 3 days and we are losing sales ", "questions": { "team": { "type": "choice", "instructions": "Which team handles this?", "criteria": { "billing": null, "technical": "Bugs or integration problems" } }, "urgency": { "type": "score", "instructions": "How urgent is this ticket?", "criteria": "low", "medium", "high" }, "upset": { "type": "noul", "instructions": "Is the customer upset?" } } }' The response maps each question key back to its probability distribution and a probability-weighted score: { "model": "Qwen/Qwen2.5-1.5B-Instruct", "answers": { "team": { "type": "choice", "choice": "technical", "probabilities": {"billing": 0.000007, "technical": 0.999993}, "confidence": 0.999986 }, "urgency": { "type": "score", "score": 1.6739, "legend": {"0": "low", "1": "medium", "2": "high"}, "probabilities": {"0": 0.0031, "1": 0.3198, "2": 0.6770}, "confidence": 0.5109 }, "upset": { "type": "noul", "noul": 0.8520 } }, "usage": {"input tokens": 213, "output tokens": 0} } Because transformers serve runs on top of the standard transformers processor pipeline, it also supports vision-language models : passing an images array with up to four data:image/...;base64,... strings attaches those images to the shared state for every question in the call. How does transformers serve score an off-the-shelf chat model without custom training? By default, it maps choice and score options to single-token letters A through Z , maps noul options to Yes and No , and takes the softmax over those token logits at temperature = 1.0 . When you move from a zero-shot chat model to a fine-tuned decision checkpoint, you pass --decision-config decision.json at startup: transformers serve openjev/openjev --decision-config decision.json { "labels": { "noul": "A", "B" }, "temperature": { "choice": 0.85, "score": 0.85, "noul": 1.5547129 } } Those three fields in decision.json labels , temperature , and chat template point straight at the three limits of zero-shot letter scoring. To understand why dedicated decision models beat zero-shot wrappers by 10 to 25 points on JevBench https://benchmarkheaven.com/jev-models , we need to look at what happens at the token and logit layer. 2. Moving past A to Z : reserved option tokens and the