How it works

Two small encoder models run on CPU, one pass over your text per call, with chunking at 2,000 characters.

decision-machine-1 is not one large model. It is two small encoder models behind one API, plus a scheduler that keeps them busy.

Each call runs one pass over your text. It uses no sampling loop, no retry chain and no prompt parser. That is where the speed comes from.

The two models

ModelJobCapabilities
The classifierScores every label you send in one pass over the textYes / no, Classify, Rate, and the tool pick in function calling
The extractorFinds the best span in the text for each label you sendAnswer, Extract, Entities, Verify

The classifier scores labels. The extractor finds spans. Every capability maps to one of those two jobs.

Labels are the interface. The classifier reads your label text and scores it against the input. The words yes and no carry no meaning for it. See Writing good statements and labels.

What the classifier does

The classifier scores every label independently in the same pass, so adding labels adds their length to the input and nothing else.

The API then normalises those scores. Decisions and probabilities gives the exact formulas.

What the extractor does

The extractor finds the best span for each label you supply. extract sends your JSON Schema as a structure. answer sends each question as a label, plus one decoy label named other. The decoy sharpens the spans, and the API discards it. entities sends each type as a label. verify checks one field against one value.

The extractor is multilingual. The classifier performs best in English. See Languages.

One pass per call

Every capability call does the same three things:

1

The Worker builds the labels

A Cloudflare Worker turns your request into labels or structures. It validates the field limits first, so bad input fails in under 0.2 s.

2

The scheduler leases a slot

A single Durable Object holds every inference slot. It leases one call per slot and queues the rest. It opens the circuit when a box fails.

3

The model runs once

The model reads the text once and returns scores or spans. The Worker normalises, rounds to three decimals, and returns typed JSON.

Inference runs on CPU. The runner is a FastAPI service on two boxes, three processes per box. A lock holds each process to one inference at a time.

Chunking at 2,000 characters

Both models slow down past roughly 500 tokens. The pipeline splits any text over 2,000 characters on whitespace, so every chunk stays in the linear regime.

CapabilityBehaviour over 2,000 characters
yes-no, classify, rateA label’s score is its max over chunks. A claim that holds only in the last paragraph still scores high.
extractPer-chunk records merge. A single-record structure folds into one record, first non-empty value per field. A multi-record structure concatenates.
entities, verifyThe extractor runs overlapping windows and remaps the offsets back to your text.

A single over-long token becomes its own chunk. extract also collapses runs of two or more spaces to " | " first, so table cells read as distinct spans.

Cost is linear in characters. Long text and chunking covers the practical guidance on pre-splitting.

Measured latency

Two sets of numbers. The first is the runner benchmark on a Xeon E-2136 (6c/12t), 3 slots x 2 threads, 1,000-character texts.

Call groupOne slot aloneWhole box, 3 slots busy
classify / yes-no / rate (classifier, any label count)~1.0 s~1.5 calls/s
entities / answer / extract / verify (extractor)~0.4 s~4 calls/s

Hyperthreads add nothing. The box saturates near 4 extractor calls per second whatever the process and thread layout.

The second set is end-to-end wall clock, measured with curl from a laptop against production on 2026-09-16. Inputs were 55 to 198 characters. These numbers include TLS, internet transit and the Worker hop.

CallObserved
yes-no, one statement0.75 s – 1.25 s
yes-no, 2 statements1.12 s
yes-no, 2 texts x 2 statements1.08 s
classify, 3 labels1.18 s
rate, 4 levels1.02 s
answer, 1 question0.56 s
answer, 3 questions0.56 s
entities, 3 types0.50 s
verify0.48 s
extract, 9 properties0.72 s
extract, 2 texts1.03 s
chat json_schema0.63 s
chat tools, 2 tools1.16 s
chat forced tool_choice0.43 s
Every 400 / 4040.07 s – 0.19 s

The split is clean: classifier calls land near 1 s, extractor calls near 0.5 s.

Batch the cheap axis. Extra statements or questions ride in one runner call and add only their own length to the billed input. Extra texts cost one call each. See Batching.

What you can measure yourself

Every capability response carries the input size in headers. Use them to predict cost and to size your batches.

curl -sS -D - -o /dev/null -X POST https://api.milliseconds.ai/v1/decision-machine-1/classify \
-H "Content-Type: application/json" \
-d '{
"text": "My card was charged twice this month.",
"labels": ["billing", "bug", "feature request"]
}' | grep -i "x-input"
x-input-chars: 37
x-input-tokens: 10

x-input-tokens is the input tokens billed for this call. x-input-chars is the number of input characters, and it sums every item for texts. The OpenAI-compatible routes report the request’s input tokens inside usage instead. Input tokens cost $0.04 per million and output tokens cost $0. See Pricing.

Backpressure

The scheduler answers 529 overloaded when no slot frees in time. A failed call marks its slot down for 30 seconds and retries once on another slot. A second failure returns 502 runner_error.

Both are transient. Retry with backoff. See Errors.

Next