Introducing decision-machine-1: a language model for decisions, not prose
milliseconds.ai is live. decision-machine-1 classifies, extracts and verifies text in one pass, returns a typed decision with a probability, and costs $0.04 per million input tokens.
- One pass, no generation.
decision-machine-1reads your text once and returns a decision. It never writes an answer token by token, so nothing is invented and latency does not grow with the output. - Eight typed decisions. Yes / no, classify, classify tree, rate, answer, extract, entities and verify. Each one is a single POST that returns fixed-shape JSON your code can branch on.
- It always says how sure it is. Every answer carries a probability. Classifications carry the full score distribution and a confidence. When the text does not hold the answer, you get
null, never a guess. - $0.04 per million input tokens. $0 for output. The free plan includes 125 million input tokens every month. A short support message costs a fraction of a cent.
- Keep your OpenAI client. Point it at
api.milliseconds.ai/v1for JSON-schema extraction and tool calls. The native API reaches all eight capabilities and returns the numbers you route on.
Today we are releasing decision-machine-1, a language model built to make decisions about text and nothing else. It lives at milliseconds.ai, with its own API, console and documentation. It classifies, extracts, rates and verifies. It always states how confident it is. Do not ask it for an opinion. Do not ask it to make something up. It cannot.
The problem with asking an LLM for a value
Is this ticket urgent? Which queue does it belong in? What is the deductible on this policy? For the past few years, developers have answered these questions with an LLM and a structured output. It works, most of the time. The problem is that LLMs are slow, they are expensive, and they hallucinate.
Ask an LLM a question the text cannot answer, and force it to return a value anyway. It will return one. In the best case it writes "I could not locate that information." In the worst case it writes a value that does not exist. To the software that consumes the output, both look the same. A properly formatted value and a sentence about not finding one are just two strings.
So the industry built guardrails. Validation before the model. Validation after the model. A human in the loop. All of it to protect a workflow from a tool that should never have powered it.
At CloudRaker, we process millions of pages and audio minutes every month. Most of the work our clients run is exactly this kind of work: extraction and structuring at scale, where speed and accuracy are the whole job. So we kept pushing.
Generative AI, without the generation
decision-machine-1 is not one large model. It is two small encoder models behind one API. The classifier scores every label you send in one pass over the text. The extractor finds the best span in the text for every field you send. Every capability maps to one of those two jobs.
Neither model generates. There is no sampling loop, no chain of thought, no retry chain and no prompt parser. The model reads the input once and returns scores or spans. The API normalizes the numbers and returns typed JSON. That is where the speed comes from, and it is also where the honesty comes from. answer returns a span of your own text with character offsets. It cannot return words that are not in the text.
Here is what that looks like when a question has no answer in the source. A real response from /answer, with three questions over one press release:
{
"results": [
{"question":"Who announced the product?","answer":"Tim Cook","probability":0.999,"start":0,"end":8},
{"question":"How much does it cost?","answer":"$999","probability":0.989,"start":85,"end":89},
{"question":"What is the CEO salary?","answer":null,"probability":0,"start":null,"end":null}
]
}
The third answer is null, the probability is 0, and the offsets are null. Your code checks one field. No string matching on "I could not find", no second model to grade the first one.
It tells you when it is unsure
A prompt gives you an answer. decision-machine-1 gives you an answer and the distribution behind it.
probabilityis how strongly the text supports the decision.confidenceis one minus the normalized entropy of the whole distribution. One clear winner scores 1. A flat spread scores 0.scorescarries every option, so you can see the runner-up.
A real /rate response over the scale ["Calm","Annoyed","Frustrated","Threatening to leave"]:
{"score":1.585,"level":2,"label":"Frustrated","confidence":0.371,"scores":[0,0.471,0.472,0.057]}
The winning level beat the runner-up by 0.001. The score of 1.585 sits between the two. The confidence of 0.371 reports the ambiguity. A prompt would have named one level and stopped. With these numbers your application sets one bar per action: act above it, confirm in the middle, escalate below. The thresholds guide walks through it.
One model, eight decisions
| Capability | Question it answers | Measured latency |
|---|---|---|
| Yes / no | Is this statement true of the text? | 0.42 s |
| Classify | Which one label applies? | 0.43 s (3 labels) |
| Classify tree | Which path through this label tree applies? | 0.77 s (2 levels) |
| Rate | Where on this scale does the text sit? | 0.42 s (4 levels) |
| Answer | Which span of the text answers this question? | 0.40 s |
| Extract | What fills this JSON Schema? | 0.41 s (9 properties) |
| Entities | Where is every span of these types? | 0.42 s |
| Verify | Does the text support this value? | 0.40 s |
Wall-clock timings from a laptop against production on 2026-09-18, median of 7 runs, over inputs of 51 to 199 characters. They include TLS, internet transit and the API hop. Every call lands near 0.4 s, and inference runs on GPU. Every response carries an x-inference-ms header with the model time alone: 36 to 54 ms on these calls, 108 ms for the two-level tree.
Labels are the interface. You do not write a prompt; you describe the case. Send "billing": "payments, invoices, charges, refunds" and the classifier scores that description against the text. Send a JSON Schema and extract fills it, with null for every field the text does not carry. Send a value you already hold and verify tells you whether the text supports it, and what the text actually says when it does not.
What it will not do
decision-machine-1 decides. It does not write, reason or hold a conversation.
- Generate text. Every answer is a label you supplied or a span of your input. Summarizing and rewriting are not capabilities.
- Reason in steps. One pass, one decision. There is no chain of thought and no planning.
- Chat. Plain chat on the OpenAI-compatible surface returns
400 unsupported_requeston purpose. - Bring outside knowledge. If the judgment needs facts beyond the text you sent, send it to a large model.
That list is the point. Every one of those is a place where a model can make something up.
A place beside your LLM
Most teams will run both. Call decision-machine-1 first. Accept the results that meet your thresholds. Send the ambiguous cases to a large model or a person. That removes the large-model call from the easy majority of traffic, and it removes the guardrail code from the rest, because the cheap model already told you which cases are uncertain.
The same pattern works in the other direction. One batched /yes-no call screens every message going into or out of an LLM app. One /verify call checks a field an LLM extracted against the source document before you write it to a record. The patterns section covers six of these, with the cost math.
Pricing
| Price | |
|---|---|
| Input tokens | $0.04 per million |
| Output tokens | $0 |
| Free plan | 125 million input tokens per month |
One price for every capability and for the OpenAI-compatible endpoint. Cost follows the text you send, plus the labels, questions or schema. The number of labels does not change it. The size of the response does not change it either. Full distributions, spans and batch results are included.
Getting started
Grab a key at console.milliseconds.ai. Every capability is one POST.
curl -X POST https://api.milliseconds.ai/v1/decision-machine-1/classify \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $MILLISECONDS_API_KEY" \
-d '{
"text": "I was charged twice for my subscription this month and support has not replied.",
"labels": {
"billing": "payments, invoices, charges or refunds",
"shipping": "delivery, tracking or returns",
"account": "login, password or profile settings"
}
}'
{"label":"billing","probability":0.999,"confidence":0.995,"scores":{"billing":0.999,"shipping":0,"account":0.001}}
Already on the OpenAI SDK? Set the base URL to https://api.milliseconds.ai/v1, select decision-machine-1, and keep your response_format.json_schema and tools calls as they are. The OpenAI-compatible guide lists what the facade does and does not read.
Every capability has a Try it panel in the API reference. It needs no key. The docs also serve an llms.txt and an MCP server, so your coding agent can read them too.
Start with a classification or extraction call you already send to an LLM. Compare accuracy, latency and cost on your own inputs. Then send us the cases that break. That is how the next version gets trained.
Where it fits. decision-machine-1 is the decisions model behind CloudRaker. The Paperwork API turns documents and audio into text. milliseconds.ai turns that text into decisions. Docs: docs.milliseconds.ai.