Most of the language model calls inside an automated system are not asking for prose. They are asking for a decision: send this ticket to billing or to engineering, allow this tool call or refuse it, mark this comment as spam or leave it. The usual way to get one from a large language model is to ask it to write the answer out as JSON, parse the text, validate it against a schema and retry when it comes back malformed. Jev, released by TypeSafe AI in September 2026, removes the writing step. It reads the input, reads a set of named questions, and returns each answer as a typed value with a probability attached. It never produces a sentence.
A decision, not a sentence
TypeSafe calls Jev a System One model. The phrase borrows from Daniel Kahneman’s description of two modes of thought: System 1 is fast, automatic judgement, the kind a person makes before they could explain it; System 2 is slow, deliberate reasoning. A chat model with a reasoning budget is built to imitate System 2. Jev is built for the other kind, the snap call that is either right or wrong and needs no essay attached.
The name is a statement of intent too. It honours William Stanley Jevons, the economist who observed that making coal use more efficient increased total coal consumption rather than reducing it. The bet is the same shape: if a judgement becomes cheap and quick enough, people will put one in places where nobody would currently place a model call, such as every row of a table, every event on a stream, or every step of an agent.
The difference in mechanism is what the whole design follows from. A generative model produces its answer one token at a time, and a structured answer is only structured if every one of those tokens comes out right. Jev evaluates all of the questions in a request together and fills in a schema that the API guarantees, so there is nothing to parse and no type error to handle.
Text-generating model
{"team":"payments","impact":"high","urgent":true}parsevalidate
Each token depends on the ones before it. One wrong token means a failed parse and another call. The answer carries no probability unless you ask for one in words.
System One model
- team
payments p=0.88 - impact
score 1.2 of 2 - urgent
p(yes)=0.93
Each question is answered independently in the same pass. The shape of the answer is fixed by the question type, and a probability comes with it.
| Generative language model | Jev | |
|---|---|---|
| Output | Text, which you ask to be formatted as JSON | Typed answers: a choice, a score or a yes probability |
| Structure | Requested in the prompt, then parsed and validated by you | Guaranteed by the API schema |
| Uncertainty | Not exposed, or stated in words by the model | A probability distribution with every answer |
| Several decisions | Written out one after another | Evaluated independently and in parallel |
| Can explain or summarise | Yes | No |
| Input | Text, and images or audio on multimodal models | Text only |
| Pricing basis | Input and output tokens | Input tokens; output tokens are free |
How it is trained, according to TypeSafe
The vendor describes a training method it calls RLCD, reinforcement learning for calibrated decisions, and sets it against the two methods behind most current models. Treat this as the vendor’s own account; the training details are not independently documented.
| Method | What is rewarded | TypeSafe’s view of the side effect |
|---|---|---|
| RLHF (reinforcement learning from human feedback) | Answers that people prefer | Rewards agreeable and overconfident answers, and narrows the range of outputs a model will give |
| RLVR (reinforcement learning from verifiable rewards) | Answers that can be checked, such as a passing test or a correct proof | Produces capable reasoning models, which are slow and expensive per decision |
| RLCD | Probabilities that match how often the answer turns out right | The target is calibration itself, so the probability is meant to be usable as a number |
Calibrated has a specific meaning here. If you collect every answer the model gave with a probability of 0.8, about eight in ten of them should be correct. It is a property of many predictions taken together. It says nothing certain about any single answer, and a 0.95 can still be wrong on the case in front of you.
Anatomy of one request
A request has two parts you write and one part you get back.
State
What the model looks at: a string, a JSON object or an array of text. Objects are recommended because questions can point at their fields.
Questions
Named questions, each of type Choice, Score or Noul, with instructions written in full. The question ids are not shown to the model.
Jev
Evaluates each question against the state, independently of the others and in parallel. Nothing is generated.
Typed answers
One answer per question id, in the fixed shape of its type, with probabilities. Your code branches on them.
State is the material the decision is about. It can be a plain string, a JSON object or an
array of text, and the documentation recommends an object, because question instructions can then
refer to a field with a backtick path such as ticket.messages[0].text. That keeps a question
pointed at the part of the input it concerns instead of the whole thing. State is text only:
images, audio and video are not accepted. English is the primary language, and the documentation
states that accuracy is currently lower in other languages, including Chinese, Japanese and Korean
scripts.
Questions are a map from an id you choose to a question definition. Two details catch people
out. First, the id is for your code, not for the model, so a question called is_refund with the
instruction “Check this” asks the model nothing useful; the full question belongs in
instructions. Second, questions in the same request cannot see each other’s answers. If one
decision depends on another, that is two requests.
The three question types
TypeSafe calls these the primitives. Every question is exactly one of them, and the type decides the shape of the answer.
| Type | What it asks | What comes back | Use it when | Watch for |
|---|---|---|---|---|
| Choice | Which one of these named options fits? | choice, a probability per option, and confidence |
Each option leads to a different code path | Options must be mutually exclusive; up to 255 are supported |
| Score | Which of these ordered levels applies? | A continuous score, a legend mapping levels to their text, probabilities and confidence |
The answer is a spectrum you compare with a threshold | Write the levels in order from low to high |
| Noul | Is this statement true? | noul, the probability of yes, and nothing else |
Your code has a single if on the result |
There is no confidence; a 0.5 means the model is evenly split, not that the answer is “medium” |
A Noul is the one people misuse. Because the probability is the answer, there is no separate confidence value, and reading 0.5 as “somewhat” treats a coin toss as a measurement. If the thing you want is genuinely a matter of degree, it is a Score with levels you define.
Writing questions that work
The documentation and the LangChain post agree on a few habits, and they follow from how the model works rather than being matters of style:
- Ask for a snap judgement. “Does this message ask for a refund?” is a System One question. “Analyse the message, weigh the customer history and decide” asks for reasoning the model does not do.
- One decision per question. If a question contains “and”, it is usually two Nouls.
- Point at the field. Refer to the part of the state that matters with a backtick path.
- Split a hard judgement into parts. Instead of one Score for “how good is this supplier”, ask several narrower Scores and combine them with weights in your own code. TypeSafe calls this composite scoring. It also makes the final number explainable, since each part is visible.
A worked example: routing an on-call alert
The example below is ours, not the vendor’s. An alert fires, and the question is who gets paged. The state carries the alert, recent deploys and the paging policy as a JSON object, so each question can point at the field it needs.
curl -X POST https://api.typesafe.ai/v1/systemone \
-H "Authorization: Bearer $TYPESAFE_API_KEY" \
-H "Content-Type: application/json" \
-d @alert-request.json
{
"model": "jev-1.13.0",
"state": {
"alert": {
"service": "checkout-api",
"summary": "p99 latency above 4 seconds for 12 minutes",
"region": "eu-west-1"
},
"recent_deploys": [
{ "service": "payments-gateway", "minutes_ago": 9 }
],
"policy": "Latency alerts that begin within 30 minutes of a deploy are paged to the team that deployed."
},
"questions": {
"owner": {
"type": "choice",
"instructions": "Following `policy` and `recent_deploys`, which team should be paged for `alert`?",
"criteria": {
"payments": "Owns payments-gateway and anything it deployed",
"checkout": "Owns checkout-api when no recent deploy explains the alert",
"platform": "Owns shared infrastructure such as load balancers and DNS"
}
},
"impact": {
"type": "score",
"instructions": "How much customer impact does `alert.summary` describe?",
"criteria": [
"No visible customer impact",
"Slower for some customers",
"Customers cannot complete purchases"
]
},
"deploy_related": {
"type": "noul",
"instructions": "Is the alert plausibly caused by one of the `recent_deploys`?"
}
}
}
The response has one entry per question id, each in the shape of its type. The values here are illustrative, chosen to show the shape; they are not a recorded response.
{
"model": "jev-1.13.0",
"answers": {
"owner": {
"type": "choice",
"choice": "payments",
"confidence": 0.81,
"probabilities": { "payments": 0.88, "checkout": 0.1, "platform": 0.02 }
},
"impact": {
"type": "score",
"score": 1.2,
"confidence": 0.7,
"legend": {
"0": "No visible customer impact",
"1": "Slower for some customers",
"2": "Customers cannot complete purchases"
},
"probabilities": { "0": 0.05, "1": 0.7, "2": 0.25 }
},
"deploy_related": { "type": "noul", "noul": 0.93 }
},
"usage": { "input_tokens": 410, "output_tokens": 70 }
}
The model field reports which release answered. Log it: when an alias such as jev-latest moves
to a new release, this field is how you notice.
Confidence, and what to do with it
A probability is only useful if something in your code reacts to it. TypeSafe’s confidence guidance describes three bands, and the useful part is that the boundaries are not fixed: they scale with what happens if the answer is wrong.
input
Event arrives
An alert, a ticket, a proposed tool call: the state for one decision.
Jev
Typed answer with a probability
Choice and Score carry a confidence value; a Noul carries the probability of yes.
your code
Compare with a threshold set by the stakes
A reversible, low-cost action can go ahead on modest confidence. An irreversible one needs a high bar and a check.
Then one of three bands
act
Act automatically
Confidence clears the bar for this level of risk.
confirm
Proceed with a check
Ask the user to confirm, or take a reversible step first.
escalate
Route to a person
Confidence is low, or the action is too costly to leave to a probability.
The documentation’s example puts the human hand-off below a confidence of 0.5, lets low-stakes actions proceed, and asks for more than 0.9 plus a confirmation before a high-stakes one. In code, for the alert above, that looks like this:
from typesafe_sdk import Choice, Noul, Score, TypeSafeClient
client = TypeSafeClient() # reads TYPESAFE_API_KEY from the environment
def page_team(team: str, alert: dict, note: str = "") -> str:
return f"paged {team}: {alert['service']} {note}".strip()
def page_incident_commander(alert: dict, reason: str) -> str:
return f"paged incident commander: {alert['service']} ({reason})"
def open_ticket(team: str, alert: dict) -> str:
return f"ticket for {team}: {alert['service']}"
def route_alert(state: dict) -> str:
response = client.system_one(
state=state,
questions={
"owner": Choice(
instructions="Following `policy` and `recent_deploys`, which team should be paged for `alert`?",
criteria={
"payments": "Owns payments-gateway and anything it deployed",
"checkout": "Owns checkout-api when no recent deploy explains the alert",
"platform": "Owns shared infrastructure such as load balancers and DNS",
},
),
"impact": Score(
instructions="How much customer impact does `alert.summary` describe?",
criteria=[
"No visible customer impact",
"Slower for some customers",
"Customers cannot complete purchases",
],
),
"deploy_related": Noul(
instructions="Is the alert plausibly caused by one of the `recent_deploys`?",
),
},
)
owner = response.answers["owner"]
impact = response.answers["impact"]
deploy_related = response.answers["deploy_related"].noul
alert = state["alert"]
if owner.confidence < 0.5:
return page_incident_commander(alert, reason="owner unclear")
if impact.score >= 1.5:
# Paging the wrong team during an outage costs minutes, so the bar is higher.
if owner.confidence > 0.9:
note = "check the recent deploy first" if deploy_related > 0.8 else ""
return page_team(owner.choice, alert, note)
return page_incident_commander(alert, reason=f"suggested owner {owner.choice}")
return open_ticket(owner.choice, alert)
The SDK calls jev-latest unless told otherwise; the SDK reference covers selecting a versioned
model, which you should do before relying on thresholds like these.
The trade-off in every threshold is between two costs you can count. Raise it and more decisions land on a person, which is slower and uses someone’s attention. Lower it and more wrong actions happen automatically. Neither number is knowable in advance. The way to set them is to log each probability next to the eventual outcome, group the logged answers by probability, and check whether each group was right about as often as it claimed. That comparison is a calibration check on your data, and it is the only evidence that the vendor’s calibration carries over to your inputs. The evaluation page covers building the labelled set it needs.
Asking everything at once
Because questions in one request are independent and evaluated in parallel, an extra question adds tokens but very little waiting. TypeSafe turns this into a pattern it calls speculative fan-out: ask every question you might need in a single call, then let ordinary code decide which answers to use. In its own cookbook, sending thirteen questions in one request instead of thirteen separate requests came out 11.5x cheaper and 9.6x faster (vendor-reported).
The cost of the pattern is that you pay for questions whose answers you throw away, and that it only works when the questions really are independent. When the second question only makes sense after the first has been answered, for example “which product” followed by a question whose options depend on that product, the honest design is two requests.
Where it sits in an agent
An agent loop has a handful of decision points that are not really reasoning: which model should
handle this request, and is the next tool call safe to run. LangChain’s langchain-typesafe
package puts Jev at exactly those points, as middleware for agents built with create_agent.
input
User message arrives
The latest human message starts a run of the agent.
ModelRouterMiddleware
Jev chooses the model for the run
A Choice across the models you list, each with a criterion. Hooks: before_agent and wrap_model_call. The pick is kept for the whole run.
LLM
The chosen model plans and proposes a tool call
Ordinary agent behaviour: reasoning, then a call with arguments.
AutoModeMiddleware
Jev scores the call for risk
Runs on wrap_tool_call, and only for the tools you list. Asks how likely it is that the call is risky or not properly authorised.
Then one of
allow
Tool runs
The result goes back to the model and the loop continues.
refuse
Error ToolMessage returned
The tool does not run. The model sees a refusal and has to adapt.
approve
Person approves, if you add it
AutoMode never asks. Approval needs separate human-in-the-loop middleware.
Install the package, with the experimental extra for the middleware:
uv add "langchain-typesafe[experimental]" langchain-openai
export TYPESAFE_API_KEY=your-key-from-the-typesafe-console
Tool-risk gating, with one destructive tool gated and one read-only tool left alone:
from langchain.agents import create_agent
from langchain.tools import tool
from langchain_typesafe.experimental.middleware import AutoModeMiddleware
@tool
def rotate_database_password(environment: str) -> str:
"""Rotate the application database password in the named environment. Connected clients are disconnected."""
return f"Password rotated in {environment}."
@tool
def read_service_status(service: str) -> str:
"""Return the current health status of a service."""
return f"{service}: healthy"
agent = create_agent(
"openai:gpt-6-astra",
tools=[rotate_database_password, read_service_status],
middleware=[AutoModeMiddleware(tools=[rotate_database_password])],
)
result = agent.invoke(
{"messages": [{"role": "user", "content": "Rotate the production database password now."}]}
)
print(result["messages"][-1].content)
Only the tools passed to AutoModeMiddleware are classified, so read_service_status runs without
a check. You can replace the default risk question with your own instructions, or describe what
counts as risky with NoulCriteria(true=..., false=...). Two warnings in the LangChain
documentation deserve repeating. The middleware refuses; it does not pause and ask anyone, so
approval needs LangChain’s human-in-the-loop middleware alongside it. And the tool arguments and
conversation are sent to TypeSafe to be classified, so secrets should not be in either unless
sending them to a third party is acceptable.
Model routing works the same way from the other end:
from langchain.agents import create_agent
from langchain_typesafe.experimental.middleware import ModelChoice, ModelRouterMiddleware
router = ModelRouterMiddleware(
choices={
"fast": ModelChoice(
model="openai:gpt-5.6-terra",
criteria="Lookups, extraction and small edits where the target is named explicitly.",
),
"deep": ModelChoice(
model="openai:gpt-6-astra",
criteria="Design questions, unfamiliar failures and decisions that are costly to reverse.",
),
},
instructions="Choose the least costly model that can complete the task safely.",
)
agent = create_agent("openai:gpt-5.6-terra", middleware=[router])
result = agent.invoke(
{"messages": [{"role": "user", "content": "Which region is the orders table replicated to?"}]}
)
print(result["model_route"].choice)
The router classifies the latest human message once and uses its pick for every model call in the
run. That is cheap, and it also means a conversation that starts simple and turns hard stays on the
small model until the next run. If that matters, a custom middleware can reclassify on
before_model after each tool result. TypeSafeClassifier accepts LangChain message objects
directly, so custom hooks (before_agent, before_model, wrap_tool_call) can pass the
conversation in as state without converting it. Every classification is traced in LangSmith,
alongside the rest of the run, with its token usage.
Both middleware classes are marked experimental, and their interfaces may change without notice. The agent frameworks page covers the orchestration layer they sit in.
The two-model pipeline
The pattern that recurs across the sources, and the one MindStudio concluded fits most of its tests, is not replacing a language model with Jev but putting Jev in front of one.
input
A large batch of items
Emails, comments, transcripts, log lines: anything that arrives in volume.
Jev
Every item labelled in parallel
Several questions per item in one request, using fan-out. Only input tokens are billed.
your code
Filter on labels and probabilities
Drop what needs no action, set aside what is uncertain, keep what needs work.
LLM
The generative model handles the subset
Drafting replies, summaries and explanations: the parts Jev cannot do.
output
Results, with uncertain items queued for review
The review queue is where the calibration check gets its labelled outcomes.
The economics come from the ratio. If most items need only a label, most of the cost moves to the cheap step, and the generative model runs on a fraction of the input. Where nearly every item needs a written response anyway, the triage step adds a call and saves little.
Use cases that fit, and one that did not
| Use | Question types | Why it fits |
|---|---|---|
| Support ticket routing | Choice for team, Score for frustration, Noul for urgency | Each answer maps to a queue or a priority; the documentation’s own example |
| Email triage against a rule set | A Noul per rule | Rules are independent, so one request covers all of them |
| Comment and post labelling | Choice or several Nouls | High volume, low stakes per item, needs to be quick enough for a live feed |
| Choosing a model per request | Choice over named models | A routing decision made before any expensive call |
| Tool-call risk gating | Noul for “risky or unauthorised” | Runs on every call without a noticeable delay |
| Meeting transcript tagging | Choice for topic, Nouls for decisions and action items | Tags drive filing and follow-up; the summary is left to a generative model |
| Game and browser control loops | Choice for the next action | TypeSafe’s Doom demo and Wikiracing, and LangChain’s mention of Browserbase browser agents |
MindStudio also built a Bitcoin bot that chose between buy, sell and hold about once a second, and it lost money in its first hour. The tester’s own conclusion is the right one: answering quickly is not answering correctly, and a fast loop repeats a poor decision more often. Jev is a good fit where the judgement itself is easy for a competent reader and the problem is volume or latency. It does not make a hard prediction easier.
What Jev is not for
- Writing anything. No replies, summaries, explanations or code. Use a generative model, and see the two-model pipeline above for how to combine them.
- Multi-step reasoning. Jev gives snap judgements. A decision that needs a chain of inference belongs with a reasoning model, and possibly with a person.
- Long inputs. The request limit is 64k tokens, and the state plus the longest question must fit in 32k. Long documents need to be split, or handled by a model with a larger context.
- Images, audio or video. State is text only.
- Rules you can write down exactly. If the answer follows from a field value or a regular expression, code is cheaper, faster and never wrong about it.
- Approval.
AutoModeMiddlewarerefuses risky calls; it does not ask a person. For approval flows, see human in the loop. A risk classifier is also one layer of control, not a replacement for narrow tool permissions; the excessive agency page covers the rest.
Limits, versions and data
| Item | Value | Note |
|---|---|---|
| Endpoint | POST https://api.typesafe.ai/v1/systemone |
Bearer token in Authorization |
| Model ids | jev-latest and jev-preview resolve to jev-1.13.0 |
Aliases move; pin the versioned id once thresholds are tuned |
| Price | $0.042 per million input tokens | Output tokens are free. The vendor says pricing may currently be subsidised |
| Rate limits | 250,000 tokens per second and 1,200 requests per minute | Adjusted dynamically; over-limit requests get HTTP 429 |
| Context | 64k tokens per request; 32k for state plus the longest question | |
| Input | Text only; English primary | Lower accuracy on other languages, per the documentation |
| Customisation | None; one set of weights serves every customer | There is no per-customer fine-tuning to drift |
| Data | Not trained on customer data; zero data retention for enterprise customers | Anything in state still leaves your network |
| Region | US West Coast | Adds network time for callers elsewhere |
| Python SDK | typesafe-sdk, Python 3.10 or later |
Reads TYPESAFE_API_KEY |
| LangChain | langchain-typesafe; middleware under the experimental extra |
TYPESAFE_BASE_URL points it at a gateway or private deployment |
MindStudio also reports that Jev can be reached through the Vercel AI Gateway and OpenRouter, as well as TypeSafe’s own early access.
Failure modes and how to see them
| Failure | What you see | How to confirm it | What to do |
|---|---|---|---|
| Alias moved to a new release | Decision rates shift without a code change | The model field in responses changes |
Pin a versioned id; re-run the calibration check before moving |
| Rate limit reached | HTTP 429 responses | Count 429s against request and token rates | Back off and retry; batch questions into fewer requests |
| State too large | Requests rejected | Measure state plus longest question against 32k tokens | Send only the fields the questions need, or split the input |
| Noul read as a scale | Values near 0.5 treated as “moderate” | Look for thresholds between 0.3 and 0.7 on a Noul | Replace it with a Score that has defined levels |
| Question relies on its id | Answers look random for one question | Read the instructions without the id: is the question complete? |
Write the whole question in instructions |
| Dependent questions in one request | Answers contradict each other | Check whether one question assumes another’s answer | Split into two requests |
| Calibration does not transfer | Probabilities look confident but outcomes disagree | Group logged answers by probability and compare with outcomes | Raise thresholds, route more to people, or keep that decision elsewhere |
| Non-English input | Lower accuracy on some traffic | Break the calibration check down by language | Route those inputs to a different model or to a person |
| Secrets in state | Credentials in third-party logs or traces | Search the payloads you send, and LangSmith traces | Strip secrets before calling; consider the zero-retention option |
| Fast loop, wrong answer | Errors accumulate faster than anyone reviews them | Error rate per hour against decisions per hour | Add a rate limit on actions and a human checkpoint |
The speed and cost claims
This section is analysis. It separates what each source reports from how far that supports a decision to adopt.
What TypeSafe reports. Jev answers in 70 to 500 milliseconds end to end, against 3 to 329 seconds for frontier language models on the same work. On its own workflow evaluations it reports being 193.6x faster and 444.6x cheaper, and says real-world gains should be expected at the higher end. The same announcement lists the caveats: the reference answers in those evaluations are the average of two frontier models (GPT-6 Astra and Fable 5.1), so the comparison measures agreement with those models rather than correctness; the evaluation may carry bias; pricing may be subsidised; and the service runs from the US West Coast.
What LangChain reports. Up to 200x faster inference and 400x lower cost on classification tasks, in a post announcing its own integration.
What an independent tester measured. MindStudio ran 1,000 emails against seven rules in about 70 seconds sequentially, and about 6 seconds in parallel, for about nine cents either way. The comparison run on a GPT 5.6-class model, covering one category, took about five minutes and 62 cents. Classifying 1,000 YouTube comments took about five seconds and five cents, and the whole test series came to close to 20,000 requests for under a dollar.
What supports the claims. Three parties with different interests report a gap of the same order. The mechanism explains it without any special pleading: generating even a short JSON answer takes many sequential steps, and Jev takes none, answers questions in parallel and does not bill output. The MindStudio figures are consistent with the published price.
What argues against relying on them. None of the three measured accuracy against labelled ground truth. The vendor’s reference is agreement with two other models, the LangChain figure comes from a partner announcement, and MindStudio’s runs are informal tests by one person, which show speed and cost clearly and correctness hardly at all. The price is described by the vendor as possibly subsidised, so a cost model built on it may not survive. Latency from a single region will be worse for callers far from it. And the middleware that makes agent integration easy is experimental.
Our reading. The speed and cost advantage over a generative model call is well supported for short classification work; the size of the multiplier on any particular workload is not, and accuracy on your data is unknown until you measure it. The reasonable way to adopt Jev is on a decision where a wrong answer is cheap and recoverable, with probabilities logged against outcomes from the first day, and with thresholds set from that evidence rather than from the vendor’s examples.
What to watch
Specific things that would change what this page concludes.
- Independent calibration results against labelled ground truth, rather than agreement with frontier models chosen by the vendor.
- Whether the input token price holds, since the vendor itself says current pricing may be subsidised.
- Serving regions beyond the US West Coast, which decides the latency anyone elsewhere actually sees.
- The LangChain middleware leaving experimental status, and any change to its hooks.
- Accuracy on languages other than English, which the documentation currently describes as lower.