Brand Logo

Liquid AI d1-3B Explained: How a Decision Model Answers in One Forward Pass With Zero Output Tokens (2026)

Aerin Kim

Written by

Aerin Kim

Liquid AI's open-weight d1-3B returns calibrated yes/no, choice and rubric-score answers in one forward pass with zero output tokens. Here is how it works, runnable code, and honest caveats.

If you run a moderation queue, a clip-ranking job, or a support router, you already know the awkward truth about using a chat LLM for small decisions. You ask a question whose real answer is one bit, such as "is this comment harassment?", and you pay for a model that wakes up, writes a polite sentence, and then makes you parse it. On October 7, 2026, Liquid AI released two open-weight models built for exactly that gap: d1-3B and d1-omni-600M, which it calls decision models. Their model card describes the contract in one line: you give it a state and a set of questions, and it returns calibrated, typed answers in one forward pass with zero output tokens [1] [2].

This guide explains what that actually means mechanically, and I did not stop at the press release. I installed the stack, ran d1-3B on a CPU sandbox, read the remote code that ships with the checkpoint, and built a plain logprob baseline with an ordinary small LLM so you can compare the two on your own data. Every number labeled as vendor-reported below comes from Liquid AI's own posts, and I flag the places where independent confirmation does not exist yet.

TL;DR: what to know before you read 5,000 words

  • d1-3B is a 3.12B parameter model built on LFM2.5-VL-3B, with a SigLIP2 NaFlex 400M vision encoder and a 32,768 token context. It accepts text, JSON, and images [2].
  • It supports three question types: yes/no (noul), choice among named options, and an ordered score with 2 to 10 levels. Every call reports output_tokens: 0 [2].
  • Liquid reports 8 ms for one question on an RTX 4090 and 30 ms on an Apple M5 Pro, measured warm [2]. My own CPU-only sandbox took 4.6 to 7.8 seconds per call, which is a reminder that the headline numbers assume a GPU.
  • The headline benchmark is a Decision Index 0.2.1 score of 48.57, scored by Liquid with the official scorer and not submitted to the leaderboard. Nobody independent has reproduced it yet [2] [7].
  • The license is the LFM Open License v1.0, which includes a $10 million annual revenue threshold for commercial use. Read it before you ship [4].
  • The readout is a softmax over the logits of a few option tokens at the answer position. That idea is old and you can approximate it with any LLM, so the real question is how much the post-training buys you over the DIY version. Step 5 gives you a runnable baseline.

What Liquid AI released on October 7, 2026

Liquid AI published "Open d1: Edge decision models for text, vision, and audio" on October 7, 2026, and released two checkpoints on Hugging Face [1]. The family is deliberately split into a capable model and a tiny experimental one. d1-3B is the one most readers will use. d1-omni-600M is a research checkpoint that adds audio and, as Liquid itself admits, scores far lower on the main index.

Specd1-3Bd1-omni-600M
Base modelLFM2.5-VL-3B (decoder-only)LFM2.5-Encoder-350M (bidirectional encoder)
Parameters3.12B587M
InputsText, JSON, imagesText plus image, or text plus audio (not both in one request)
Vision encoderSigLIP2 NaFlex 400MSigLIP2 tower from LFM2.5-VL-450M (94M)
Context length32,768 tokens16,384 tokens
Decision Index 0.2.148.57 (vendor scored)15.95 (vendor scored)
Published latency8 ms on RTX 4090 (one question)None, early research release
LicenseLFM Open License v1.0LFM Open License v1.0

Several details in that table deserve a closer look. d1-3B starts from a decoder-only backbone, while d1-omni-600M starts from LFM2.5-Encoder-350M, a bidirectional encoder, and bolts on a 94M vision encoder and a 112M FastConformer audio encoder [1] [3]. That is a meaningful design difference. A bidirectional encoder reads the whole input at once and is the same family of architecture as BERT, while a decoder reads left to right. Liquid says all modalities in d1-omni-600M share one trunk of weights, which is why the model stays at 587M parameters.

The training recipe Liquid describes for d1-3B is refreshingly specific. It averaged the weights of LFM2.5-2.6B and the text backbone of LFM2.5-VL-3B to make a better base, fine-tuned several checkpoints with different random seeds and data mixtures, and merged them again. The post says training on long inputs, shuffling answer options, and fixing shortcuts in the data made a bigger difference than more advanced techniques [1]. Shuffling answer options is the part worth underlining, because position bias is the classic failure of a multiple-choice readout, and I come back to it in the calibration step.

The lineage matters for credibility too. The LFM2 family has a public technical report [12], and the vision tower in d1-3B is SigLIP2, a published multilingual vision-language encoder family whose NaFlex variants handle native aspect ratios and resolutions [13]. The decision-specific post-training, on the other hand, has no paper. The only documentation is the blog post, the model cards, and the code that ships inside the repository, which is why I read that code in Step 2.

liquid-ai-d1-decision-models-single-forward-pass-explained-2026-latency-index-cards-1.jpg

Step 1: Understand why a normal LLM call is slow for a one-bit answer

To see what d1 removes, split an ordinary LLM request into its two phases. Prefill reads your whole prompt in parallel and builds the key-value cache. Decode then produces output tokens one at a time, and each new token needs another full pass through the model because it depends on the token before it. The speculative decoding paper states the cost plainly: decoding K tokens takes K serial runs of the model [28], and the Transformer architecture [27] underlies most of the models that run this loop. If you want the full treatment of how engineers hide that serial cost, Miraflow already has a deep dive on speculative decoding and 2 to 3 times faster generation.

Now apply that to a moderation question. A chat model asked "Is this comment harassment? Answer yes or no" might prefill a few hundred tokens and then decode anywhere from 1 token (if it obeys perfectly) to several dozen (if it explains itself), an illustrative range and not a measurement. Latency scales with that decode length, and so does cost on a metered API. Worse, the output is free text, so a production system needs a parser, a retry path, and a policy for answers like "It depends".

A decision model collapses the second phase. The model reads the state and the question (prefill), and the answer is taken from the logits at the very next position. There is no loop, so the time is one prefill plus a lookup. That is the entire basis for the "one forward pass" claim, and it explains a pattern in Liquid's latency tables that would otherwise look odd: the cost grows with input length, not with answer complexity. On the RTX 4090, a 384 px image takes 17 ms while a 3.4k-token text state takes 102 ms [2]. The long text dominates because prefill scales with tokens, and nothing at all is decoded.

liquid-ai-d1-decision-models-single-forward-pass-explained-2026-readout-slot-cards-1.jpg

What you lose when you skip decoding

Skipping decode also removes the model's ability to think before it answers. A reasoning model can spend 2,000 tokens working through an ambiguous case, and a decision model cannot. Whatever judgment d1 has was compressed into its weights during post-training, and the model card says the same in other words: it does not write text, and its answers are limited to the questions and options you supply [2]. That makes d1 a strong fit for questions where the right answer is recognizable on sight (is this spam, which team owns this, does this photo contain a person) and a weak fit for questions that need multi-step reasoning.

Step 2: Learn how the decision readout actually works

The model card says answers come from one forward pass, but it does not spell out the readout. Because the repository ships its own Python code (you must load with trust_remote_code=True), I could read it. What follows describes the revision I downloaded (commit 051bcc464b01b9f92942b364d9586b0ef5912432), and the files may change, so treat it as a snapshot of the design rather than a specification [2].

The module docstring in prompt.py summarizes the approach: a decision is a single forward pass that stops at the answer slot, and the answer is a softmax over its options' tokens. The code builds a chat-formatted prompt with the state, then a block like this for each question type:

  • Choice: the options are listed with single-token codes (A, B, C, falling back to 00, 01 and so on), followed by the instruction "Reply with the option code only." The readout scores the logits for each option's code token.
  • Yes/no (noul): the prompt ends with "Reply with yes or no only." The readout takes the best of the token variants for yes (yes, Yes, YES) and for no, and renormalizes over the two groups. The reported number is P(yes).
  • Score: levels are numbered 0 to N-1 with descriptions, and the prompt asks for a single digit. The readout softmaxes over the digit tokens, and the reported score is the expected level, meaning the probability-weighted average of the digits, which is why it can come back as a fraction like 0.72.

Two engineering details stand out. First, the softmax is taken only over the option tokens, so the answer is always a valid member of the schema and a malformed output is impossible. The docstring in runner.py says exactly that: nothing is decoded, so a schema violation cannot happen [2]. Second, the code supports a calibration object that rescales the option scores before the softmax. The d1-omni-600M card states that its text answers use per-type temperatures stored in config.json and that image and audio answers use the model's softmax as trained [3]. The d1-3B card promises calibrated answers, but I did not verify which calibration parameters its checkpoint applies, so I measure calibration empirically in Step 6.

Why this is both new and old

If you have used logprobs from an API, this probably looks familiar, and it should. Reading the next-token distribution over a fixed label set is a standard trick. Kadavath et al. showed that large models can be reasonably well calibrated on multiple-choice questions when the options are formatted in a way the model recognizes [15], and Zhao et al. showed that few-shot prompting biases the same readout toward majority labels, recent examples, and common tokens, which you can partly fix by calibrating against content-free inputs [16]. Guo et al. showed that modern neural networks are often overconfident and that a single learned temperature parameter is a surprisingly strong fix [14].

What Liquid adds is the training: the weights were post-trained so that the first token after the prompt is itself the decision, with shuffled option order and calibration in mind. A general instruction-tuned LLM can be coaxed into the same readout, but nothing in its training optimized the answer slot for probability quality. That is the honest way to frame the claim. The mechanism is shared with logprob scoring, and the difference is how well the distribution at that slot reflects reality. Step 5 and Step 6 give you the tools to test that on your own data instead of taking either side's word for it.

How the batch and multi-question paths save work

The API exposes two calls. system_one(state, questions, images=None) runs named questions over one state, and the card says the state and its images are read once for all questions. system_one_batch takes many (state, questions[, images]) requests and packs them together without padding [2]. In the code, the shared prefix (the state) is rendered once, and each question appends its own suffix up to the answer slot. That is why Liquid's table shows three questions over one state costing 21 ms on an RTX 4090 against 8 ms for one: the model re-reads only the per-question suffixes, not the full state. On the Jetson AGX Thor the same step goes from 16 ms to 20 ms [1].

Step 3: Run d1-3B with Hugging Face transformers

The model card documents the transformers path, and I ran it. The requirement is transformers>=5.14; I used 5.19.0 with the CPU build of PyTorch 2.14.1 on a 4-core machine with 15 GB of RAM [2].

bash
/code # Requires Python 3.10+. d1-3B needs transformers 5.14 or newer (the model card's requirement). pip install "transformers>=5.14" torch torchvision pillow

The next snippet is the card's text example, trimmed to the parts I executed. The model class comes from AutoModel with remote code, and the first call downloads the remote Python files, so pin a revision in production. The warning transformers prints about newly downloaded code files is worth taking seriously, because remote code runs with your privileges.

python
/code import torch from transformers import AutoModel device = "cuda" if torch.cuda.is_available() else "mps" if torch.backends.mps.is_available() else "cpu" dtype = torch.float32 if device == "cpu" else torch.bfloat16 # the card's choice # trust_remote_code runs Python that ships with the repo. For production, add revision="<commit-hash>". model = AutoModel.from_pretrained("LiquidAI/d1-3B", trust_remote_code=True, dtype=dtype).to(device) # Named questions over one state, answered in one pass (copied from the model card) questions = { "refund": {"type": "noul", "instructions": "Is the customer asking for a refund?"}, "team": { "type": "choice", "instructions": "Which team should handle this?", "criteria": { "billing": "Charges, refunds, invoices", "technical": "App or site faults", "fraud": "Suspected unauthorised use", }, }, "urgency": { "type": "score", "instructions": "How urgent is this?", "criteria": ["Can wait", "Today", "Blocking the customer now"], # lowest level first }, } out = model.system_one("I was charged twice this month, please refund one of them.", questions) print(out["answers"]["refund"]["noul"]) # P(yes) print(out["answers"]["team"]["probabilities"]) # distribution over the named options print(out["usage"]) # {'input_tokens': ..., 'output_tokens': 0} # A creator-flavoured example: one comment, two questions, one forward pass moderation = { "verdict": { "type": "choice", "instructions": "Does this YouTube comment violate a no-harassment policy?", "criteria": { "ok": "Fine, criticism or neutral", "harass": "Personal attack or harassment", "spam": "Spam or scam link", }, }, "severity": {"type": "score", "instructions": "How severe is the violation?", "criteria": ["None", "Mild", "Serious"]}, } comment = "Great edit, but the audio is a bit quiet in the second half." ans = model.system_one(comment, moderation)["answers"] print(ans["verdict"]["choice"], round(ans["verdict"]["confidence"], 3), round(ans["severity"]["score"], 2))

Here is the output I got from the three-question ticket example on CPU (bf16, my sandbox, not the GPU path the vendor benchmarks use):

  • refund: P(yes) = 0.989
  • team: billing, with probabilities billing 0.983, technical 0.011, fraud 0.006
  • urgency: score 0.724, with 0.369 on "Can wait", 0.537 on "Today", and 0.093 on "Blocking the customer now"
  • usage: 116 input tokens and 0 output tokens

Notice the shape of the score answer. The score field is the expected level (0.72 on a 0 to 2 scale), while confidence is the probability of the single most likely level (0.537). For the urgency question the model is genuinely split between "Can wait" and "Today", and the probability vector shows that split, which a generated answer of "Today" would have hidden. This is the practical payoff of typed outputs: you can threshold on confidence, route low-confidence cases to a human, and log the whole distribution for later analysis.

A moderation-style example I ran

To test a creator-flavored case, I asked d1-3B whether three YouTube-style comments violated a no-harassment policy, with a choice question and a 3-level severity score. The results from my run:

  • "Great edit, but the audio is a bit quiet in the second half." gave ok with confidence 0.981, and a severity score of 1.02.
  • "You are pathetic and everyone should unsubscribe from your trash channel." gave harass at 0.957, with severity 1.76.
  • "FREE GIFT CARDS click bit.ly/xx now!!!" gave spam at 0.938, with severity 0.71.

The choice answers are what a human reviewer would pick, which is encouraging for three hand-written examples and proves little beyond that. The severity numbers are the more interesting detail. The harmless feedback received an expected severity of 1.02, which reads as "Mild" on my scale even though the choice question was confident it was fine. Questions asked together are answered independently, so a score question and a choice question are not forced to agree. If you use both, build your own consistency rule, for example by ignoring the severity score whenever the choice answer is ok.

liquid-ai-d1-decision-models-single-forward-pass-explained-2026-three-input-test-cards-1.jpg

Timing and what to expect on real hardware

My CPU timings were 7.8 seconds for the first yes/no call (including warmup), then roughly 4.6 to 4.8 seconds for subsequent single-question calls on 36 to 39 token states. Those are slow because a 3B model in bf16 on four CPU cores is the wrong tool, and because I did nothing to optimize it. Liquid's warm numbers are 8 ms on an RTX 4090 with model.compile(mode="reduce-overhead"), which the card says runs single questions as CUDA graphs, and 16 ms without it [2]. The card also notes that the first call with a new shape pays for kernel selection or compilation, so warm up the shapes you serve. In practice that means running one dummy request per input length bucket at startup before you take traffic.

Step 4: Serve it with llama.cpp for edge and CPU deployments

Liquid says both models have day-one llama.cpp support, with quantized builds covering Apple, AMD, Qualcomm, and NVIDIA hardware [1]. The GGUF repository for d1-3B lists BF16, F16, Q8_0, and Q4_K_M weights plus multimodal projector files, and its card documents a dedicated /v1/systemone HTTP endpoint on llama-server [5] [26]. I did not run this path myself, so the commands below are copied in structure from the model card. You need a llama.cpp build recent enough to include the systemone route.

bash
/code # Start the server (per the d1-3B-GGUF model card; needs a llama.cpp build that includes the systemone route) llama-server -hf LiquidAI/d1-3B-GGUF:Q8_0 # In a second terminal: send a state and typed questions to /v1/systemone curl http://127.0.0.1:8080/v1/systemone -H "Content-Type: application/json" -d '{ "state": "I was charged twice this month, please refund one of them.", "questions": { "refund": {"type": "noul", "instructions": "Is the customer asking for a refund?"}, "team": {"type": "choice", "instructions": "Which team should handle this?", "criteria": {"billing": "Charges, refunds, invoices", "technical": "App or site faults", "fraud": "Suspected unauthorised use"}} } }'

Choosing a quantization is a trade between memory and decision quality, and Liquid has not published a per-quantization accuracy table that I could find. The sensible approach is to treat quantization as a hyperparameter you validate: run your labeled sample through Q8_0 and Q4_K_M and compare both accuracy and calibration, because quantization noise tends to hit the tail probabilities that you threshold on before it hurts top-1 accuracy. That claim is a general property of low-bit quantization that I am stating as a testing hypothesis rather than a measured fact about this model.

Step 5: Build the generic logprob baseline you should beat

Before adopting any specialized model, build the cheapest honest baseline: take a small ordinary instruction-tuned LLM, ask the question, and read the probability of "Yes" versus "No" at the first answer position instead of generating. The code below genuinely runs with transformers and has no dependency on Liquid's remote code. I used Qwen2.5-0.5B-Instruct because it is small enough for a laptop CPU [24], and it makes one forward call rather than calling generate, which is the standard way to get logits from a causal model [25].

python
/code # Generic baseline (not Liquid's code): P(Yes) from one forward pass of an ordinary small instruct LLM. # pip install "transformers>=5" torch import time, torch from transformers import AutoModelForCausalLM, AutoTokenizer MODEL = "Qwen/Qwen2.5-0.5B-Instruct" tok = AutoTokenizer.from_pretrained(MODEL) lm = AutoModelForCausalLM.from_pretrained(MODEL, dtype=torch.float32).eval() def first_token_id(word): ids = tok.encode(word, add_special_tokens=False) return ids[0] YES = [first_token_id(w) for w in ("Yes", "yes")] NO = [first_token_id(w) for w in ("No", "no")] @torch.no_grad() def p_yes(state: str, question: str) -> float: messages = [ {"role": "system", "content": "Answer with exactly one word: Yes or No."}, {"role": "user", "content": f"{state}\n\nQuestion: {question}"}, ] prompt = tok.apply_chat_template(messages, tokenize=False, add_generation_prompt=True) inputs = tok(prompt, return_tensors="pt") logits = lm(**inputs).logits[0, -1].float() # one forward pass, no generate() logp = torch.log_softmax(logits, dim=-1) yes = torch.logsumexp(logp[YES], dim=0) no = torch.logsumexp(logp[NO], dim=0) return torch.sigmoid(yes - no).item() # renormalise over {Yes, No} only if __name__ == "__main__": q = "Is the customer asking for a refund?" for text in ["I was charged twice this month, please refund one of them.", "Where is my parcel? It was due Monday.", "asdf qwer zxcv 12345"]: t = time.perf_counter(); p = p_yes(text, q); ms = (time.perf_counter() - t) * 1000 print(f"P(yes)={p:.3f} {ms:.0f} ms | {text}")

I ran it on the same three inputs as the d1 refund question. The results are below, and the latencies are CPU timings for a 0.5B model in fp32, so they say nothing about relative speed against d1-3B on the same hardware.

Input text (question: is the customer asking for a refund?)d1-3B P(yes), my CPU runQwen2.5-0.5B-Instruct logprob baseline P(yes)
I was charged twice this month, please refund one of them.0.9890.919
Where is my parcel? It was due Monday.0.0200.231
asdf qwer zxcv 123450.0470.703

Read the table carefully, because it shows why calibration is not a footnote. On the two sensible inputs the baseline separated the cases, though its 0.231 for the parcel question is far less decisive than d1's 0.020. On the nonsense string, the baseline said 0.703 for "is the customer asking for a refund?", which is a confident yes about gibberish. d1-3B returned 0.047 for the same input, which is the behavior you want from a decision primitive. Three inputs prove nothing statistically, and a larger baseline model would likely do better than a 0.5B one, but the experiment illustrates the failure mode you need to measure: an untrained readout can be confidently wrong on out-of-distribution input.

There is a counterweight worth stating clearly. An independent hands-on write-up cited by AI Weekly reports 0.907 confidence from d1-omni-600M on nonsense input and a 208 ms median CPU latency, both of which are much worse than the vendor framing suggests [7]. I could not locate that post directly, so I am relaying AI Weekly's summary and treating it as unconfirmed. It does match the intuition that the 600M research checkpoint, with a Decision Index of 15.95, is not a drop-in classifier. My single nonsense probe was against d1-3B only.

Step 6: Measure calibration on your own labeled data

A decision model is only useful if "0.9" means roughly nine in ten. The standard tools are expected calibration error (ECE), which bins predictions by confidence and compares each bin's average confidence to its actual hit rate, and the Brier score, the mean squared error of the probabilities. Guo et al. popularized ECE for neural networks and showed temperature scaling can fix much of the overconfidence on a held-out set [14].

The script below takes (p_yes, label) pairs from any scorer, whether that is d1's noul, your logprob baseline, or an LLM judge, and prints a reliability table. It uses only the standard library, and I verified it on a toy list.

python
/code # Reliability check for any yes/no scorer: pass a list of (p_yes, label) pairs, label in {0, 1}. def calibration_report(pairs, bins=5): n = len(pairs) brier = sum((p - y) ** 2 for p, y in pairs) / n ece = 0.0 print("bin n mean_p frac_yes") for b in range(bins): lo, hi = b / bins, (b + 1) / bins rows = [(p, y) for p, y in pairs if lo <= p < hi or (b == bins - 1 and p == 1.0)] if not rows: continue mean_p = sum(p for p, _ in rows) / len(rows) frac = sum(y for _, y in rows) / len(rows) ece += len(rows) / n * abs(mean_p - frac) print(f"{lo:.1f}-{hi:.1f} {len(rows):6d} {mean_p:7.3f} {frac:8.3f}") print(f"Brier={brier:.4f} ECE={ece:.4f}") return ece, brier if __name__ == "__main__": demo = [(0.99, 1), (0.97, 1), (0.95, 0), (0.80, 1), (0.60, 1), (0.55, 0), (0.30, 0), (0.20, 0), (0.05, 0), (0.02, 0)] calibration_report(demo)

A practical protocol for a real evaluation looks like this:

  1. Sample 300 to 500 items from your real traffic, because a policy question about comments reads differently from a benchmark question about Wikipedia.
  2. Have two people label them and resolve disagreements. Disagreement rate is itself a ceiling on what any model can achieve.
  3. Score each item with d1-3B, your logprob baseline, and, if budget allows, a generative LLM judge.
  4. Compare ECE and Brier alongside accuracy, and look at the 0.8 to 1.0 bin specifically, because that bin is where you will auto-act without review.
  5. Fit a temperature or a threshold on half of the data and report on the other half.

If d1 passes that test on your data, its calibrated probabilities become a control surface. You can auto-approve above 0.95, auto-reject below 0.05, and send the middle to a human, with the middle band sized from measured error instead of guesswork.

Step 7: Compare d1 against the alternatives

Decision models compete with four existing approaches. Each wins somewhere, and the comparison below is deliberately qualitative about speed and cost because I do not have a shared benchmark across them.

ApproachWhat it returnsTime profileFlexibilityBest fit
Generative LLM judgeFree text, parsed into a verdictPrefill plus a decode loop of many tokensHighest, can explain and reasonHard or novel rubrics, audits
Logprob scoring of a general LLMNext-token probabilities over option tokensOne forward passHigh, prompt definedQuick baselines, tasks a big model already knows
Constrained decoding (Outlines style)Schema-valid textDecode loop with token masksHigh, schema definedStructured extraction
Fine-tuned BERT or ModernBERTProbabilities over a fixed label setOne small forward passLow, retrain to change labelsStable, high-volume classification with labeled data
Guard model (Llama Guard)Safe or unsafe plus category codesShort decodeMedium, taxonomy is configurableChatbot input and output safety
Decision model (d1)Typed answer plus probabilities, 0 output tokensOne forward passHigh, questions in the prompt, multimodalRouting, triage, rubric scoring, visual checks
liquid-ai-d1-decision-models-single-forward-pass-explained-2026-alternatives-comparison-cards-1.jpg

Versus LLM-as-judge with generation

Using a strong model to grade outputs became standard after Zheng et al. showed that GPT-4 as a judge matched human preferences with over 80% agreement on MT-Bench and Chatbot Arena data, comparable to the agreement between humans, while also documenting position, verbosity, and self-enhancement biases [17]. A generative judge can explain itself, handle novel rubrics, and reason through hard cases. It also costs hundreds of milliseconds to seconds per item and returns text you have to parse.

d1's pitch for judge-style work is the score type: ordered rubric levels read as an expected value in one pass. Liquid lists LLM-as-a-judge scoring as a recommended use [2]. The tradeoff is explanation. If a human needs to know why a clip was ranked low, a decision model cannot tell them. One sensible architecture uses d1 as the high-volume first pass and a generative judge on the uncertain middle slice and on a random audit sample.

Versus constrained decoding and logprob scoring of a normal LLM

Constrained decoding frameworks such as Outlines compile a schema or regular expression into a finite-state machine and mask invalid tokens at each step, guaranteeing syntactically valid output [18]. That solves the parsing problem but still decodes token by token. Logprob scoring, as in Step 5, solves latency and gives you probabilities, and its weakness is the quality of those probabilities. The practical comparison is d1 versus the best general model you can afford, read at its first answer token. If your own test in Step 6 shows a general 8B model matches d1-3B on calibration for your task, use the general model and keep one fewer dependency. If not, d1's post-training is paying for itself.

Versus small fine-tuned classifiers (BERT and ModernBERT)

A fine-tuned encoder is the incumbent for narrow classification. BERT introduced the recipe of adding one output layer to a pretrained bidirectional encoder and fine-tuning it [23], and ModernBERT updated the encoder for 2024 with an 8,192 token context and efficiency work, in 149M and 395M parameter sizes [21] [22]. For a single stable label set with thousands of labeled examples, a fine-tuned encoder is hard to beat on cost: it is much smaller than 3B parameters and can run on a CPU in single-digit milliseconds.

The decision model wins on flexibility. The label set and the policy live in the prompt, so you can add a category or reword the policy without retraining, and you get multimodal input (the same call handles a comment and a thumbnail). The encoder wins on cost per decision once your task is fixed and you have data. Many teams will end up using d1 to bootstrap labels and prototype policies, then distill the stable parts into an encoder.

Versus guard models such as Llama Guard

Llama Guard is an LLM-based input-output safeguard that classifies prompts and responses against a configurable taxonomy of risks [19]. Later versions are published on Hugging Face, for example Llama Guard 3 8B [20]. Guard models are specialized for safety classification and generate a verdict such as safe or unsafe plus category codes, so they pay a short decode. They come with an established taxonomy and are the better-documented choice when your task is exactly AI-safety moderation of chatbot traffic.

d1 is a general decision primitive, and Liquid lists moderation among many uses rather than claiming a safety taxonomy [2]. The Civil Comments result in Liquid's table (93.0 for d1-3B, and 95.8 for d1-omni-600M) is a public toxicity dataset and a useful hint, but it is not a policy-specific safety evaluation [1]. For regulated or high-stakes moderation, benchmark both on your policy, and keep a human appeal path either way.

Reading the benchmarks honestly

Liquid's headline is a Decision Index 0.2.1 score of 48.57 for d1-3B, described as the best under 10B parameters and on par with Decider 35B-A3B at 47.11 [1] [2]. The model card's own table also lists Winnow-12B at 50.02, which beats d1-3B, so "best under 10B" is a size-class claim and not a claim to be the best overall.

liquid-ai-d1-decision-models-single-forward-pass-explained-2026-decision-index-ladder-cards.jpg

Here is how I would weigh that evidence:

  • The scoring was done by the vendor. The card says d1-3B was scored with the official scorer and was not a leaderboard submission, while the other rows come from the public leaderboard v0.2.1 [2]. That is a reasonable method and still not independent. AI Weekly notes that no independent lab has yet reproduced the results [7].
  • The index is new and I could not verify who runs it. BenchLM describes Decision Index 0.2.1 as 38 component benchmarks scored in chance-adjusted skill points, hosts figures for reference without rerunning them, and shows Fastino publishing results with the official 0.2.1 scorer [9] [8]. AI Weekly describes the index as one Liquid controls, which I could not confirm and which conflicts with the Fastino material. Treat the maintainer question as open.
  • Subscores tell a more complicated story. d1-3B leads its table on Tools (74.5) and Arts (36.3) but scores 23.8 on Knowledge against 31.8 for Decider 35B-A3B [2]. That fits intuition. A 3B model is a fine pattern recognizer, and it has less room for world knowledge.
  • Public datasets double as sanity checks. The card also reports 71.8 on DecisionBench (23,900 rows) [10] and 69.3 on the Fast Decisions dev split [11]. These are external datasets, but the model was evaluated by Liquid, and the card notes some training-data overlap concerns for HelpSteer2 on the omni model, which is the sort of detail you want to see disclosed. Benchmark contamination in general is a real problem, covered in Miraflow's piece on contamination in SWE-Bench and LiveCodeBench.
  • The mean differs between the two documents. The blog reports a seven-benchmark text mean of 82.9 for d1-3B, and the model card reports 77.1 across eight benchmarks including HelpSteer2 (36.7) [1] [2]. The difference is explained by which benchmarks are included, not by a contradiction, but it is a good example of why you should read the table and not the headline.

On vision, the card reports d1-3B at a mean of 74.1 across 11 public benchmarks versus 73.9 for its base LFM2.5-VL-3B, with gains on VL-RewardBench (65.0 versus 50.9) and MMVP, and drops on VisualWebBench and CV-Bench [2]. With images removed the same questions score 45.1, so the answers genuinely depend on the pixels. Liquid says the Decision Index v0.3 includes a private vision split it does not report on, and that dedicated audio decision benchmarks are still an open problem [1].

What about d1-omni-600M?

At 15.95 on the Decision Index, d1-omni-600M sits near the bottom of the table, and its card says outright that it is an early research release with no published inference numbers [3]. It posts strong results on a handful of simple text tasks (95.8 on Civil Comments and 79.5 on PAWS-X), which suggests it is useful for narrow classification, and Liquid's own sentence about it is that it fits "where footprint matters" [1]. The audio warning in the card deserves repeating: audio capabilities were trained on requests between an English speaker and an assistant, with clips cut at 30 seconds [3]. Do not assume it transcribes or moderates arbitrary podcast audio.

One more detail from the omni card is practical: it recommends float16 on GPUs, reporting identical top answers on every text, image, and audio row in its checks, and warns that bfloat16 changed the top answer on 0.8% of text rows and 1.7% of audio rows [3]. That is a rare, concrete numerical-precision note, and it is the sort of thing that bites in production if you ignore it. The d1-3B card, by contrast, uses bfloat16 on GPU.

Where decision models fit for creators and video teams

Most creator workflows contain dozens of tiny judgments hiding inside bigger tasks. A decision model is a good fit when the judgment is frequent, answerable from the input alone, and tolerant of a human fallback for unclear cases. Here are the use cases I would test first, with the caveat that none of these are claims Liquid has benchmarked.

Comment and community moderation. A channel with thousands of comments per upload can run a choice question (fine, harassment, spam, off-topic) over each comment and use the confidence to prioritize a human queue. Moderate in layers: auto-hide only above a high confidence, and send the 0.3 to 0.9 band to a person. Calibrate on your own community, since sarcasm and in-jokes vary widely.

Clip ranking. If you cut long videos into Shorts, a score question such as "How strong is the opening hook of this transcript segment?" with 5 ordered levels gives you a cheap first-pass rank across hundreds of candidate segments. Miraflow's AI Clipping already analyzes a long video, finds candidate moments, and scores them, and the same pattern (many cheap judgments, then a human choice) is what a decision model enables for your own pipelines. For the creator-side workflow, see Miraflow's guide to turning long videos into 10 viral Shorts automatically and its comparison of Opus Clip alternatives.

Thumbnail screening. d1-3B accepts images, so you can ask yes/no questions over a thumbnail: is the text readable at small size, is there a face, is the main subject centered. These are checks for obvious defects before you spend an A/B test slot, and not predictors of click-through rate. Click-through depends on audience and context, and a model that has never seen your channel's analytics cannot know it. Use the output as a quality gate and keep real performance data as the final judge. If you generate candidates with the YouTube Thumbnail Maker, a gate like this can filter variants before you pick finalists, and the list of thumbnail mistakes that kill CTR is a good source of yes/no questions to encode.

Routing. Choice questions are a natural router: which model, which template, which queue. Miraflow has a longer look at routing AI requests to the right model, and a decision model is one way to implement the classifier inside such a router without adding a visible latency step. The same logic helps a creative pipeline: is this brief a thumbnail, a short, or a music request. If you build with Miraflow, you pick the tool yourself, but a router like this is a sensible building block for your own automation. Agents that call tools benefit similarly, and Miraflow's primer on LLM agents and tool use covers where cheap gating decisions fit in an agent loop.

Where it does not fit. Anything that requires writing: captions, scripts, replies, explanations. For those you still want a generative model, for example the script step in Text2Shorts, and the AI image generator for visuals. Decision models score and route content, and generative models make it.

Common mistakes when adopting a decision model

  1. Trusting the confidence number without measuring it. Calibrated on a vendor benchmark is not calibrated on your comments. Run Step 6 before you automate any action.
  2. Putting several unrelated policies in one question. Each question should test one thing, because the model reads the instruction and the options as a single prompt. Split a compound policy into separate questions over the same state, which costs little because the state is read once.
  3. Writing vague option descriptions. The choice descriptions are part of the prompt, and the model sees them. "Fine, criticism or neutral" works better than "ok", and the card's own examples use descriptive criteria for every option.
  4. Ignoring option order and wording sensitivity. Liquid shuffled option order in training to reduce position bias, but you should still test a reordered option list on a sample. A decision that flips when you swap two options is a decision you should not auto-act on.
  5. Skipping the warm-up. The card warns that the first call with a new shape pays for compilation. Warm each input-length bucket at startup before you measure latency or accept traffic.
  6. Treating scores from different questions as consistent. As my moderation test showed, a choice answer and a score answer are independent readouts. Add your own consistency rules.
  7. Loading remote code without pinning. trust_remote_code=True executes the repository's Python. Pin the commit hash, review the files, and mirror them internally for production.
  8. Using the 600M model as a drop-in. It is an experimental checkpoint with a 15.95 index score, so evaluate it for narrow tasks only.
  9. Assuming "open-weight" means unrestricted. See the license section below.

Production architecture and best practices

A robust deployment treats the decision model as a fast, probabilistic first stage inside a larger system, and not as the final authority.

Use a cascade. Stage one is d1 with thresholds: act automatically above a high-confidence bound, reject or approve automatically below a low bound, and escalate the middle band. Stage two is either a human queue or a generative judge that can explain its reasoning. Size the middle band from your measured calibration, and revisit it monthly because your content distribution drifts.

Log the full distribution. Because the output includes probabilities, store them with the input hash and decision. That gives you the data for threshold tuning, calibration fitting, and later distillation into a small encoder. Keep a random audit sample (for example 2% of decisions) reviewed by humans regardless of confidence, so you can detect silent drift.

Batch and share prefixes. Use system_one_batch for throughput. Liquid's batch figures are 475 decisions per second on an RTX 4090 and 1,106 per second on an AMD MI325X with 64 states packed [2]. Ask all of a state's questions in one call so the state is read once.

Pick hardware to match latency goals. The card's edge table is unusually concrete: 16 ms on a Jetson AGX Thor, 26 ms on an AGX Orin 64 GB, 50 ms on an Orin Nano, and 30 ms on an Apple M5 Pro for one question [1]. Long states change the picture, since a 3.4k-token state takes 640 ms on the M5 Pro and 1,640 ms on the Orin Nano. Truncate or summarize long inputs, or chunk them.

Plan for evaluation drift. Re-run your labeled set whenever you change a quantization, the model revision, or your option wording. Treat them like code changes with a regression test.

Latency in context. If your pipeline already includes slow generative steps (video generation, voice synthesis), the 8 ms versus 800 ms difference will not be the bottleneck. It matters for per-frame or per-comment loops: Liquid's own demos run d1-3B in a loop over live camera input, reading one pass per frame, including a live content moderation demo you can try in the Open d1 Arcade Space [6]. For a broader view of what dedicated inference hardware does to LLM latency, see Miraflow's pieces on OpenAI's Jalapeno inference chip and Inception's Mercury Voice, which attack the decode loop from other directions.

The license, in plain terms

The model cards list the license as lfm1.0, and the license text in the repository is the LFM Open License v1.0 from Liquid AI, Inc. [4]. It grants a perpetual, royalty-free copyright and patent license, but conditions Commercial Use on not exceeding a Threshold, defined as annual revenue of $10,000,000 or more for the Legal Entity (which includes controlled and controlling affiliates). A commercial user above the threshold is not licensed under the agreement, and a qualified non-profit is exempt for non-commercial or research use. The blog's phrase that you can download, fine-tune, and deploy "without restrictions" is therefore looser than the actual terms [1]. I am not a lawyer, and if your company is near the threshold you should read the full text and contact Liquid, whose model cards invite inquiries about custom solutions through its sales team.

liquid-ai-d1-decision-models-single-forward-pass-explained-2026-license-threshold-card.jpg

Frequently Asked Questions

What is a decision model?

A decision model returns a typed answer (yes/no, a choice among named options, or an ordered score) in a single forward pass, rather than generating text. Liquid AI's d1-3B and d1-omni-600M are open-weight examples, and the answer is read from the model's output distribution over the option tokens [1] [2].

How is d1-3B different from just using LLM logprobs?

The readout mechanism is similar, because both take the next-token distribution over a fixed set of option tokens. The difference is training: d1 was post-trained so that the answer slot carries calibrated decision probabilities, with shuffled options and long inputs in the data. Whether that beats a general model on your task is an empirical question, which Steps 5 and 6 let you test.

Is d1-3B really 8 ms per decision?

Liquid reports 8 ms for one question on an NVIDIA RTX 4090 with model.compile(mode="reduce-overhead"), warm, as a median of 20 runs, and 16 ms without compilation [2]. The numbers are vendor-measured. On my 4-core CPU sandbox the same call took about 4.6 seconds, so hardware matters a great deal.

Can I use d1-3B commercially?

Under the LFM Open License v1.0, commercial use is licensed only for entities below a $10 million annual revenue threshold, with a non-profit exemption for non-commercial or research use [4]. Check the full text for your situation.

Does d1-3B work with llama.cpp and Ollama?

The GGUF repository and the blog describe llama.cpp support, with a /v1/systemone endpoint in llama-server [5]. I tested only the transformers path, and I did not verify Ollama or LM Studio support myself.

Can d1 replace Llama Guard for content moderation?

Not automatically. Llama Guard is purpose-built for safety classification with a defined taxonomy [19], while d1 is a general decision primitive that lists moderation as one use case. Benchmark both on your own policy and labeled data, and keep human review for high-stakes cases.

Can it rank YouTube thumbnails by click-through rate?

It can answer image questions such as legibility or composition, and a score question can rate those qualities on a rubric. It cannot predict CTR for your audience, because that depends on data it has not seen. Use it as a quality filter ahead of real A/B testing.

Should I use d1-omni-600M?

Only for narrow experiments. It scores 15.95 on the Decision Index, has no published inference numbers, and its audio training is limited to English requests with 30-second clips [3].

Are the benchmark results trustworthy?

They are plausible and vendor-produced. The Decision Index score was computed by Liquid using the official scorer, and independent reproduction had not been reported at the time of writing [2] [7]. Validate on your own data before relying on them.

Conclusion

Liquid AI's d1 release makes a precise engineering bet: for the large class of tasks where the answer is a choice, a yes/no, or a rating, you should not pay for token generation at all. After reading the shipped code and running the model, the mechanism is easy to describe. The prompt asks for a single option code, the logits at the next position are restricted to the option tokens, and a softmax turns them into probabilities, with typed outputs that cannot violate your schema. The speed comes from removing the decode loop, and the quality depends entirely on how well the post-training made that first-token distribution trustworthy.

My own small tests were encouraging and limited. d1-3B returned sensible, confident answers on a ticket and three moderation-style comments, and it gave a low probability for a nonsense string where a 0.5B logprob baseline said 0.703. I also saw inconsistent severity scores next to confident choice answers, and my CPU latency was nowhere near the vendor GPU figures. The honest summary is that d1 looks like a strong new option for high-volume routing, moderation triage, and rubric scoring, with unverified benchmarks, a revenue-gated license, and a research-grade 600M sibling.

The practical next step is a one-afternoon bake-off. Label 300 of your own items, run d1-3B, a logprob baseline, and your current approach, then compare accuracy and calibration with the Step 6 script. If you build content workflows, you can pair a decision gate with generation tools such as Miraflow's AI Clipping and the YouTube Thumbnail Maker, using cheap decisions to pick and filter, and generative models to create.

References

  1. Open d1: Edge decision models for text, vision, and audio, Liquid AI Blog (Oct 7, 2026)
  2. LiquidAI/d1-3B model card, Hugging Face
  3. LiquidAI/d1-omni-600M model card, Hugging Face
  4. LFM Open License v1.0, LiquidAI/d1-3B repository
  5. LiquidAI/d1-3B-GGUF model card, Hugging Face
  6. Open d1 Arcade (Hugging Face Space), Liquid AI
  7. Liquid AI Opens d1-3B and d1-omni-600M Decision Models, AI Weekly
  8. Introducing GLiDE, Fastino
  9. Decision Index 0.2.1 (Fastino results), BenchLM
  10. DecisionBench dataset, Hanno Labs, Hugging Face
  11. Fast Decisions dataset, Fastino, Hugging Face
  12. LFM2 Technical Report, Liquid AI (arXiv 2511.23404)
  13. SigLIP 2: Multilingual Vision-Language Encoders with Improved Semantic Understanding, Localization, and Dense Features (arXiv 2502.14786)
  14. On Calibration of Modern Neural Networks, Guo et al. (arXiv 1706.04599)
  15. Language Models (Mostly) Know What They Know, Kadavath et al. (arXiv 2207.05221)
  16. Calibrate Before Use: Improving Few-Shot Performance of Language Models, Zhao et al. (arXiv 2102.09690)
  17. Judging LLM-as-a-Judge with MT-Bench and Chatbot Arena, Zheng et al. (arXiv 2306.05685)
  18. Efficient Guided Generation for Large Language Models (Outlines), Willard and Louf (arXiv 2307.09702)
  19. Llama Guard: LLM-based Input-Output Safeguard for Human-AI Conversations (arXiv 2312.06674)
  20. Llama Guard 3 8B model card, Meta, Hugging Face
  21. Smarter, Better, Faster, Longer: A Modern Bidirectional Encoder (ModernBERT) (arXiv 2412.13663)
  22. Finally, a Replacement for BERT: Introducing ModernBERT, Hugging Face Blog
  23. BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding, Devlin et al. (arXiv 1810.04805)
  24. Qwen2.5-0.5B-Instruct model card, Hugging Face
  25. Text generation, Hugging Face Transformers documentation
  26. llama.cpp, ggml-org (GitHub)
  27. Attention Is All You Need, Vaswani et al. (arXiv 1706.03762)
  28. Fast Inference from Transformers via Speculative Decoding, Leviathan et al. (arXiv 2211.17192)