Every LLM you’ve used, GPT, Claude, Gemini, generates text one token at a time. That’s true whether you ask it to write an email or classify a support ticket into billing vs technical. Under the hood, the classification task is also modeled as a transformer predicting the next token, just constrained to output something like billing or technical as text.
TypeSafe AI built a model that skips that step entirely. Jev, their first “System One” model, doesn’t generate text at all. You send it a state (some data) and a set of typed questions, and it returns typed answers with calibrated probabilities. No string to parse, no chance of the model wandering off-schema.
This post covers what Jev actually is, how its three primitives work, why it can’t do token generation (and what that costs you), and the more interesting question: if Jev only makes decisions, should you run it standalone or as one piece of a larger system that also includes LLMs?
Key Takeaways
- Jev is trained with a new method TypeSafe calls RLCD (Reinforcement Learning for Calibrated Decisions), not RLHF or RLVR. It optimizes for calibrated probabilities on narrow decisions, not for text people prefer to read.
- It has three primitives: Choice (pick one of N options), Score (a position on a defined scale), and Noul (probability that a yes/no statement is true). That’s the entire output vocabulary, there’s no free text.
- Because outputs are constrained to a schema you define, Jev cannot produce an off-schema value. TypeSafe calls this “zero hallucinations,” which is true by construction for schema conformance, but it’s not the same claim as “the decision is always correct.”
- Jev is explicitly not a replacement for LLMs. TypeSafe’s own patterns (confidence-gated routing, intent routing, speculative fan-out) are built around Jev classifying or scoring first, then handing off to deterministic code, a specialist LLM, or a human based on a confidence threshold.
- Jev is 200x faster, 70-100ms latency, $42/billion-token price (as quoted by TypeSafe, not independently benchmarked)
“System One” is the name TypeSafe gave to the model class Jev belongs to. The name is a reference to Daniel Kahneman’s book Thinking, Fast and Slow. In that book, System 1 is the mind’s fast, intuitive mode of thinking. System 2 is the slower, deliberate mode that reasons step by step.
TypeSafe applies that split to AI models. A System One model is built for fast, narrow judgments, the kind of call a knowledgeable person could make in a few seconds given the right context. It is not built for slow, multi-step reasoning. Jev is TypeSafe’s first System One model. It takes a state and a set of typed questions, and it returns a typed answer for each question in one parallel pass.
This is different from what most people mean by “reasoning models” today. Reasoning models (trained with RLVR, covered below) spend more compute per query to work through a problem step by step. A System One model spends less compute per query on purpose, because the judgments it answers do not need that step-by-step process. Ask it something that does need one, and it is the wrong tool.
TypeSafe’s manifesto frames the whole company around one observation: LLMs are trained to be helpful conversational partners for people, and that training objective (RLHF, reinforcement learning from human feedback) produces a specific failure mode when you try to embed the model inside software instead of a chat window. The model is optimized for text a human rater would prefer, not for a decision your code can safely act on unattended.
TypeSafe’s AI primer lays out three post-training approaches side by side:
Jev is TypeSafe’s first model built on this training path. It’s a text-input model (JSON, strings, arrays; no images or audio yet, per their docs) that evaluates a state against one or more questions and returns typed answers in parallel.
Before getting into the mechanics, here’s the boundary in plain terms:

Here’s the shape of that contract compared to a normal LLM call:

Jev’s entire output vocabulary is three question types, described in the primitives docs:
| Primitive | Question shape | Returns | Example |
|---|---|---|---|
| Choice | Which of these options? | choice, probabilities, confidence |
“Which team should handle this ticket?” -> billing |
| Score | Which level on my scale? | score, legend, probabilities, confidence |
“How frustrated is this customer?” -> 1.4 on a 0-2 scale |
| Noul | Is this true? | noul (a 0-1 probability) |
“Does this message request a refund?” -> 0.93 |
Every question needs an ID (for your code, never sent to the model), a type, and instructions written as an explicit, well-scoped question. Choice and Score also take criteria, the actual options or levels.
A single API call can carry many questions against the same state, and they’re evaluated independently and in parallel.
A request has a state (the data) and a questions object (what you want to know about it). Here’s a real example adapted from TypeSafe’s quickstart:
from typesafe_sdk import Choice, Noul, Score, TypeSafeClient
client = TypeSafeClient()
ticket = (
"Hi, I've been trying to connect my Stripe account for 3 days "
"and the integration keeps failing. I'm losing sales. Please help ASAP."
)
response = client.system_one(
state=ticket,
questions={
"department": Choice(
instructions="Which team should handle this",
criteria={
"billing": "Payment or subscription issues",
"technical": "Bugs or integration problems",
"sales": "Pricing or account questions",
},
),
"frustration": Score(
instructions="How frustrated the customer appears",
criteria=[
"Calm, just stating facts",
"Frustrated but civil",
"Very angry, strong language",
],
),
"is_urgent": Noul(
instructions="The message conveys urgency or time-sensitivity",
),
},
)
print(response.answers["department"].choice) # "technical"
print(response.answers["frustration"].score) # 1.0
print(response.answers["is_urgent"].noul) # 1.0The response carries a confidence field on Choice and Score answers, derived from how concentrated the probability distribution is across your options. All Score and Choice answers from TypeSafe include a probabilities property representing the probability distribution across the options (for Choice) or levels (for Score). The shape of that distribution tells how certain the model is for a choice.
No. Jev does not generate text. It does not write code. It does not produce explanations. It does not pick its own next action the way an agent does. TypeSafe’s how-to-build guide states this directly: “System One is TypeSafe’s model for building AI-powered software, not agents.” You call Jev for one narrow judgment. You do not call it to plan a multi-step task.
This restriction buys three things:
Jev competes with one step inside an LLM call: the decision itself. It does not compete with an LLM’s ability to write or reason at length. If your workflow reads a ticket and replies to the customer, that reply still needs an LLM.
TypeSafe’s own documentation is built around this pairing, not around Jev replacing LLMs outright. Their patterns page lists four architectural patterns, and three of them are explicitly about deciding when to hand off to something else: a specialist LLM, deterministic code, or a human.
Here’s the pattern that shows this most clearly, confidence-gated intent routing for a support system:

The logic behind that diagram, adapted from TypeSafe’s intent routing and confidence-gated routing docs:
def route_ticket(ticket_id, response):
intent = response.answers["intent"]
complexity = response.answers["complexity"]
if intent.confidence < 0.5:
# Not enough signal to classify safely. Don't guess.
return route_to_human_agent(ticket_id)
if intent.choice == "order_status":
handle_order_status(ticket_id) # deterministic code, no model at all
elif intent.choice == "product_question":
handle_with_llm(ticket_id, PRODUCT_SPECIALIST) # hand off to an LLM
elif intent.choice == "return_exchange":
handle_with_llm(ticket_id, RETURNS_SPECIALIST) # a different specialist LLM
elif intent.choice == "complaint":
if complexity.score > 1 or complexity.confidence < 0.5:
route_to_human_agent(ticket_id) # too ambiguous, escalate to a person
else:
handle_with_llm(ticket_id, COMPLAINT_RESOLUTION)Ask Jev one Choice question, “what kind of request is this”, before deciding what handles it. The diagram and code above are this pattern in action: one Choice question classifies the ticket into order_status, product_question, return_exchange, or complaint, and each intent maps to a different handler. order_status goes straight to a database lookup, no model involved at all. product_question and return_exchange each go to a different specialist LLM, one loaded with product knowledge, the other with return policy. complaint goes to a third specialist LLM, but only after a second check (confidence-gated routing, described next) confirms the case isn’t too complex or too uncertain to hand to that LLM safely. The benefit is Jev’s one fast, cheap call decides which of four paths a request takes, and the expensive LLM calls only fire for the three paths that actually need one.
Use the same Choice answer’s confidence field to decide how much autonomy to give the model, and vary the threshold by how costly a wrong answer would be. Let’s take an example (from TypeSafe’s documentation): a voice banking app asks Jev one question, “what does the user want to do”, with two possible answers, check_balance or approve_transfer. Checking a balance is low-stakes: if Jev is wrong, the user just sees the wrong screen and tries again, so the app acts on that answer whenever confidence is 0.6 or higher. Approving a transfer is high-stakes, so the app sets a much higher bar there, above 0.85 confidence, before it approves anything automatically. Below that threshold, even though Jev still picked approve_transfer, the app asks the user to confirm instead of acting right away. Same model, same answer type, two different confidence thresholds, because the two actions carry different amounts of risk.
Split one broad, hard-to-audit judgment into several narrow Score questions. Let’s take an example (from TypeSafe’s documentation): a support ticket gets three separate Score questions instead of one broad rating, how severe the bug is, how frustrated the customer sounds, and how much detail the report gives an engineer to work with. Each comes back as its own number with its own confidence, for example severity 1.24 out of 2, frustration 1.28 out of 2, report quality 3.0 out of 3. The lengths varies because the choices lengths are different for each question. Later, we can normalise and combine our own weights that live in your code: 0.6 * severity + 0.3 * frustration + 0.1 * report_quality. If your team later decides frustration should matter more, you change the 0.3 to something higher and rerun, no rewriting of instructions required.
Ask every question your workflow might need in one call, even ones that will only matter for some inputs, because Jev evaluates every question in a request in parallel. Let’s take an example (from TypeSafe’s documentation): a support ticket triage system needs to sort a ticket into a category, and it also needs a few extra details that only apply to certain categories, how severe a bug is, whether the steps to reproduce it are clear, whether the customer asked for a refund, and how frustrated they sound. Only some of these apply to any single ticket. A bug report needs severity and reproduction steps. A billing complaint needs the refund flag. Neither needs the other’s fields.
One way to handle this is to ask two questions in sequence. First call: ask Jev only for category. Wait for that answer. Second call: based on the category, ask only the follow-up questions that apply to it, bug_severity and has_reproducible_steps for a bug report, or refund_requested for a billing complaint. This avoids asking questions that turn out to be irrelevant, at the cost of two round trips instead of one, since the second call cannot start until the first one returns.
Speculative fan-out skips that wait. Send all five questions, category, bug_severity, has_reproducible_steps, refund_requested, and frustration, in one call, before you know the category. Jev answers all five in parallel. If the ticket turns out to be a feature request, the bug_severity and refund_requested answers do not apply, so the code just ignores them. Nothing was wasted by asking, because a question that gets ignored costs a few extra tokens, not a second network round trip. TypeSafe ran a benchmark on this pattern at a larger scale: batching 13 questions into one call was 11.5x cheaper than making 13 separate calls, and 9.6x faster, with no change in the answers.
A few limitations worth knowing before you reach for it:
billing and technical, Jev will always return one of those two, but it can still return technical when the correct answer was billing.