TYPED DECISION LAYER · SELF-HOSTED · MADE IN EUROPE
A router that knows
when to say no.
mtlm-router turns natural-language requests into typed, calibrated routing decisions — on-prem, on CPU, in ~30 milliseconds. When a request doesn't fit a known route, it says escalate instead of guessing.
US decision APIs bill you per token and keep your prompts. mtlm-router is a 7.5MB appliance that runs on the server you already own — flat cost, data stays inside.
Type your own request. Watch it decide.
Live against the hosted router, business-only lanes (IT helpdesk, general tools, chat). Off-topic input should abstain, not guess.
The same POST /v1/route your agents would call. A few tries per visitor; grab a wallet for more.
One call. Three typed answers.
A single request returns a validated route, a calibrated confidence, and a tool-fill proposal — or a clean escalate if it doesn't know.
$ curl -s localhost:8080/assess -d '{"req":"hi team, this is the third time my VPN drops today"}'
{
"route": "create_ticket",
"confidence": 0.87,
"noul": 0.91 // "novel / unhandled / loss" — abstention dial,
"candidate": "open_ticket",
"score": 0.83,
"action": "tool_fill",
"fill": { "subject": "VPN drops repeatedly", "priority": "high" }
}
It doesn't chat
It's a router, not a chatbot. It emits machine-readable decisions your agent, helpdesk, or dispatcher can act on directly.
It doesn't guess
Out-of-scope requests hit the calibrated escalate route. Small models shouldn't pretend to know things — this one doesn't.
It doesn't argue
No temperature, no sampling, no prompt drift. Deterministic softmax over your routes — the same request gets the same answer, every time.
Built for SME workflows, not datacenters.
The requests that eat SME time are decisions, not essays — sort the inbox, dispatch the ticket, route the quote. That's exactly what a router is for.
Email & ticket triage
Quotes, orders, disputes, urgent asks — classified, prioritized, and pushed to the right queue or ERP. No copy-paste, no lost emails.
Ops commands, typed
"Restart the spooler on srv2", "flag order 4412 for review" — natural language becomes a typed action your automation can execute, or an escalate when it shouldn't.
Compliance hand-off
Low confidence or risky requests escalate to a human instead of executing. Audit trail included — every decision is logged with its confidence.
One instance, many clients
Integrators: deploy the same trunk at every customer and ship a per-client head — a ~7KB file trained on their vocabulary in hours, not a custom model.
Front door for bigger AI
Shadow it in front of your hosted LLM to learn what it could have handled locally — then cut the API bill only where the data says so.
No ML team required
One tarball, one binary, one endpoint. If your stack can POST JSON, it can route. GDPR-native: nothing leaves your network, ever.
Mixture of experts — one trunk, many brains.
The frozen 7M trunk is the shared brain. Domain knowledge lives in ~7KB heads you can ship, swap, and audit like config files. A gate head picks the expert; the expert picks the route.
Each head declares its own feature tap — last-token, mean@-3, max@-3 — so a single instance serves the pooling recipe that actually won each domain's eval. Known-domain clients can also pin their expert and skip the gate entirely.
┌─────────────────────────────────────┐
request ──▶ │ mtlm-router trunk — frozen 7M model │
tokens └──────────────┬──────────────────────┘
prefill hidden state (1 × d_model)
┌────────────┐ ┌─────────────┐
│ gate.head │ │ expert.head │ ← per-domain, ~7KB
│ pick a domain │ │ pick a route │
└────┬───────┘ └─────┬───────┘
it_helpdesk ───┘ reset_password ───┘ + calibrated conf
Ship brains, not models
A customer deployment = the frozen trunk + a handful of kilobyte head files. Onboard a new domain by training a head on ~50 labeled examples per route — a working lane in hours; your full export is what scales it.
Per-head pooling
Pooling wins aren't uniform — mean@-3 takes the synthetic experts, max@-3 takes long-prose traffic, last-token keeps the gate. mhd2 flags carry each recipe; the server just obeys.
Pin your lane
{"state":"depot illicite rue des lilas","expert":"mairie"} — a client that knows its domain skips the gate and the OOD vetoes. Its expert's own escalate class is still the abstention path. Try it in the live demo above.
Numbers — held-out, calibrated, honest.
| Metric | Head | Result |
|---|---|---|
| Route accuracy | mhd3 decide head, 800-row holdout, leak-free live eval | 98.4% |
| NOUL calibration | abstention dial, 800-row holdout, leak-free live eval | 99.4% |
| Score accuracy | complexity tier 1–4, 800-row holdout, leak-free live | 98.8% |
| Gate accuracy | mhd3 business gate, 149 holdout rows, 3 domains, leak-free live | 98.7% |
| Multi-tap municipal expert | mhd3 concat3 (max@-4,-2,-3), 6,402 real rows, leak-free live | 78.5% → 81.5% |
| Municipal, merged taxonomy | same head, ambiguous collecte pair merged — the pilot finding, leak-free live | 90.7% · 97.8% auto @ <1% err (conformal α=0.02) |
| Generic intents — CLINC150 | mhd3 mean@-2,-3,-4, 151 classes incl. out-of-scope, 5,500-row holdout | 73.8% · OOS-detection AUROC 0.842 |
| Data a lane needs | few-shot curve, real 16-route municipal corpus, 633-row holdout | 50 ex/route → 68% · full export → 80% (merged: 90.7%) |
| Pooled fleet expert | max@-3 tap on long-prose issues | 48% → 63% |
| Live gate eval | real canary matrix, not holdout — aggregate across lanes | 81.6% · tools 62/63 |
Held-out sets pass candidates that real canary traffic rejects — both published here. The gap between lab accuracy and live aggregate is what per-domain constraints and the flywheel are for.
It trains on its own traffic.
Every /v1/route decision is logged. A daily flywheel harvests the log, files uncertain rows into a persistent review queue, merges judged verdicts into the train-only corpus, and dedupes before retraining. The gate thickens from real requests — never from its own confident predictions.
decisions.jsonl ──▶ harvest ──▶ review queue ──▶ judge ──▶ train corpus ──▶ new head
(replay domain-less rows · seen = resolved only · dedupe at merge)
Watch a real lane decide.
The municipal lane runs the flagship head — trained on a real production export, served live on the public demo (pin expert:"mairie"): dépôt illicite. micro onde → depot_illicite @ 1.00, demande de passeport → escalate → delegate. ~190ms per decision on CPU.
» "Dépôt illicite. micro onde" [router] depot_illicite @ 1.00 (186ms)
» "1ere dotation. bac pour le verre" [router] dotation_bacs @ 1.00 (174ms)
» "demande de passeport" [router] escalate @ 0.42 — delegates, not guesses
The alternatives, honestly compared.
| Runs on your hardware | Cost model | Typed output | Knows when to abstain | |
|---|---|---|---|---|
| mtlm-router | CPU, ~30ms, air-gap OK | Flat — one appliance | route + confidence + fill | escalate, calibrated |
| Jev (TypeSafe) | No — US API only | $0.042/M input tokens | noul / choice / score | Calibrated probabilities |
| Chat LLM + prompt parsing | Only if self-hosted (GPU) | Per-token, in and out | Free text you must parse | Hallucinates an answer anyway |
| Hand-written rules | Yes | Flat | Whatever you code | Only the cases you foresaw |
From zero to routed in days.
Drop the appliance — untar, run one binary, POST requests to /v1/route.
Train your head — ~50 labeled examples per route get you a working lane; your full export + a calibration run gets you the certified point.
Run it in shadow — log decisions against real traffic; the flywheel files what it's unsure about.
Promote with evidence — go authoritative only when the live-eval numbers say so, not the holdout.
Honest limits.
It's a router, not a reasoner
It can't summarize, explain, or compose. If you need text generation, route to a bigger model — that's exactly what escalate is for.
Thin lanes exist
Free paraphrase abstains — the municipal head delegates novel phrasing even when close. That's the design: thicken a lane with client-labeled rows via the flywheel, don't lower the bar. The manifest ships per-domain row counts so you see weak lanes first.
Calibration ages
Temperature was fit on today's request mix. Traffic drifts; refit from labeled production rows before trusting the confidences downstream.
Verify before authority
Run it as a shadow router first. Promote to authoritative only after real traffic proves the gate + head on your request mix.
Free route audit on your own requests.
Leave your email. We reply with a short questionnaire (your routes, 30 example requests); 48 hours later you get a report: accuracy on your vocabulary, calibration, what would escalate. No commitment.
Ready-made lanes. Certified numbers.
A .lanepack is a decision lane for one domain — head weights plus its leak-free eval, calibration point and license, all in one ~500KB tarball. Drop it in the appliance and that request type is handled. Every pack shows its numbers; demo packs are marked as demos.
| lane | covers | holdout | status |
| clinc150 | 151 generic intents + out-of-scope | 73.8% · OOS AUROC 0.842 | free · certified |
| helpdesk | 5 IT-helpdesk routes + escalate | synthetic corpus | free · demo |
Catalog + install: github.com/javimosch/mtlm-heads — a lane fitted on your routes is the custom-head offer below.
Three ways to run it. One of them is free.
Self-host the appliance for nothing, pay per decision on the hosted API, or have us train the head for your routes. Same trunk, same typed answers, same honest numbers.
Apache-2.0, forever.
The v0.3.0 tarball (~8MB): mhd3 multi-tap engine, int8 model, tools/helpdesk/municipal heads, systemd unit. Runs on any x86-64 box, no GPU, no Python. Your data never leaves your network.
Download v0.3.0Pay per decision, prepaid wallet, no subscription.
The same /v1/route on api.mtlm-router.intrane.fr, metered by peage (EUR, Stripe top-ups). New wallets start with free credit, so the first decisions cost nothing. Signed receipt on every charge.
Get a wallet and call itYour routes, trained and calibrated, in 3 business days.
Send us your route table (names plus a few example phrasings each). You get a ~7KB .head for the appliance or the hosted API, an eval report with holdout accuracy and calibration, and one free re-train after a week of shadow traffic.
Send your routesPrices in EUR excl. VAT. Hosted API decisions are served from EU infrastructure; hosted traffic is logged for calibration only and never used to train other customers' heads.
One tarball. Runs anywhere.
The whole stack — server binary, tokenizer, int8 model, heads — ships in one fully self-contained ~7.8MB tarball. Untar, run ./start.sh, done. No anvil install, no pip, no model download, no license server — nothing leaves the machine.
$ tar xzf mtlm-router-v0.3.0-linux-amd64.tar.gz && cd mtlm-router-v0.3.0-linux-amd64
$ ./start.sh &
$ curl localhost:8097/v1/route -d '{"state":"Dépôt illicite. micro onde","expert":"mairie"}'
{"route":"depot_illicite","domain":"mairie","action":"tool_call","confidence":0.999,...}
Agent-operable, not just agent-readable.
The tarball ships a mtlm-router CLI built for AI agents: JSON-only stdout, semantic exit codes, an embedded guide, a health-polled daemon lifecycle, and content-hash self-update. The server self-describes — /guide, /llms.txt, /v1/whoami, /v1/feedback. 28/28 on cli-spec-conformance — checked, not claimed.
$ ./mtlm-router daemon start # loopback, polls /_health — never a fixed sleep
{"daemon":"up","port":8097,"url":"http://127.0.0.1:8097"}
$ ./mtlm-router guide # embedded operator manual (JSON)
$ ./mtlm-router update --check # content-hash self-update → .bak rollback
$ ./mtlm-router daemon stop # clean POST /_shutdown
Small enough to trust. Typed enough to use.
Apache-2.0, pure-Machin source, built in Europe, with the honest-numbers discipline baked in — holdout and live-eval published side by side. Read the product doc, audit the evals, ship your first head.