Jev is TypeSafe AI’s first public “System One” model. Instead of generating text, it answers typed questions about the context you provide. For example, which team should handle a support ticket, how frustrated the customer sounds, or whether the message is urgent. Its answers include probabilities.
That makes it worth considering for workflows where software needs a judgment rather than a written response. This article looks at the API, several implementation patterns, and the limitations you should evaluate before using it in production.
The examples reflect documentation and community material available as of September 28, 2026. Jev was still in early access, so check the current SDK reference before using the code.
How Jev works
TypeSafe founder Diogo Almeida, who previously worked on instruction-following research at OpenAI, describes Jev as a “frontier-intelligence function call.” You give it context and ask questions with predefined answer types. Each request has two parts:
- State: the material you want evaluated, such as a support ticket, order record, or customer message. This can be a string, JSON object, or array.
- Questions: the judgments you want the model to make about that material.
You get back one typed answer per question, with probabilities attached. There is no generated text, so there is nothing to parse.
The three question types
| Type | What it asks | what it returns |
|---|---|---|
| Choice | Which option fits? Supports up to 255 options. | The selected option, a probability per option, and a confidence |
| Score | Where does this fall on an ordered rubric? Supports 2–10 levels. | A probability-weighted score, the distribution, and confidence |
| Noul | Is this statement true? | A probability between 0 and 1, with no separate confidence field. |
According to TypeSafe, questions in the same request are evaluated independently and in parallel against the same state. One answer does not become context for another, and adding questions has relatively little effect on latency.
What TypeSafe says is different
TypeSafe describes three differences between Jev and a conventional chat model:
- Training: Jev uses what the company calls Reinforcement Learning for Calibrated Decisions, or RLCD. Its stated objective is accurate probabilities rather than the human-preference objective associated with RLHF.
- Execution: rather than generating an answer token by token, Jev produces its outputs in a single parallel pass.
- Output format: the available answers are defined before the request runs. The typed response is the model’s native output, rather than a format imposed on generated text.
These are the vendor’s descriptions of the architecture. The practical question is whether they produce better decisions, useful uncertainty estimates, and lower costs on your workload.
What Jev is not
Jev does not generate summaries, explanations, code, or reasoning traces. If the application needs a written response, you still need a language model or a template.
It also differs from an LLM running in JSON mode. Both can provide structured results, but TypeSafe says Jev is trained to make typed decisions directly rather than generate a sequence of tokens that conforms to a schema.
The important distinction, though, is between a valid answer and a correct one. Type safe output protects your application from a malformed response. It does not protect it from a bad judgment.
Some responsibilities should stay elsewhere:
- Use deterministic code for calculations, exact matches, and other questions with computable answers.
- Use a text-generating model when you need prose or open-ended generation.
- Keep tool execution, workflow state, and side effects in your application.
- Treat Jev as text-only for the version covered here. It accepts text and JSON, not images, audio, or video.
Why use it?
- It fits the shape of the problem. Most “AI in the workflow” tasks are classify, route, score, pick-from-a-list, or branch. Those are decisions, not essays or long prompts.
- Calibrated confidence enables real automation. If a model can do a task 95% of the time but can’t tell you which 5% it will miss, you can’t automate the task. Confidence you can threshold on lets you act autonomously above a line and escalate to a human below it.
- Speed and cost (vendor claims). TypeSafe lists $42 per billion input tokens ($0.042 per million) with output tokens free, and 70 to 500ms end-to-end latency. Their headline is 193.6x faster and 444.6x cheaper on their own workflow evals, which they say are toward the high end of real-world gains. If the numbers are even 10x better than your current LLM calls, though, that changes what you can put in a hot path: per-row processing, per-request routing, or real-time UX.
- Composability. Because each answer is a typed value with a probability, you combine decisions with ordinary code: weights, thresholds,
ifstatements. When priorities change, you change a coefficient instead of rewriting a prompt.
Getting started
Get an API key from the TypeSafe console (early access is waitlisted at typesafe.ai). Jev is also reachable without the waitlist through gateways such as OpenRouter, Vercel AI Gateway, and Cloudflare Workers AI. Then:
|
1 2 |
pip install typesafe-sdk export TYPESAFE_API_KEY=... |
The client reads TYPESAFE_API_KEY from the environment and defaults to jev-latest.
A first request
This example asks three questions about a support ticket: where it belongs, how frustrated the customer appears, and whether the message conveys urgency.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
from typesafe_sdk import Choice, Noul, Score, TypeSafeClient ticket = ( "I was charged twice for my subscription and need the duplicate " "refunded today. This is the second time this has happened." ) with TypeSafeClient() as client: response = client.system_one( state={"ticket": ticket}, questions={ "department": Choice( instructions="Which team should handle this ticket?", criteria={ "billing": "Payment, refund, or subscription issues", "technical": "Bugs or integration problems", "sales": "Pricing or plan questions", }, ), "frustration": Score( instructions="How frustrated does the customer appear?", criteria=[ "Calm, just stating facts", "Frustrated but civil", "Very angry, strong language", ], ), "is_urgent": Noul( instructions="The message conveys time pressure.", ), }, ) print(response.model) # versioned ID that answered, e.g. "jev-1.13.0" print(response.answers["department"].choice) # e.g. "billing" print(response.answers["frustration"].score) # e.g. 1.0 print(response.answers["is_urgent"].noul) # e.g. 0.97 |
The response includes the versioned model ID and one answer for each question. The SDK also provides accessors such as response.choices[...] and response.nouls[...].
Choice and Score answers include confidence as well as their probability distributions. Noul returns a probability without a separate confidence field.
SDK names and response fields may change during early access, so check these examples against the reference for the version you install.
Calling the REST API
You can also send a request directly:
|
1 2 3 4 5 6 7 8 9 10 11 12 |
curl -X POST https://api.typesafe.ai/v1/systemone \ -H "Authorization: Bearer $TYPESAFE_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "state": "I was charged twice and need the duplicate refunded today.", "questions": { "is_urgent": { "type": "noul", "instructions": "The message conveys time pressure." } } }' |
Community clients are listed for Go, Rust, Ruby, .NET, PHP, Elixir, Haskell, and Scala. There are also integrations for frameworks including LangChain, Pydantic AI, LiteLLM, and Effect. Check the maintenance status and supported features of any community integration before depending on it.
Writing good questions
The documentation and community guides emphasize a few recurring practices.
- Keep each question focused – “Is this urgent and about billing?” combines two judgments. Ask one question about urgency and another about the subject, then combine the results in code. That also makes failures easier to investigate. You can see which judgment went wrong rather than trying to interpret a single answer to a compound question.
- Use straightforward affirmative statements for Noul – For example: “The message conveys time pressure.” Avoid wording that makes a true result mean “no” to the underlying business question. The guidance covered here reports weaker performance with that construction.
- Define the choices carefully – Criteria are part of the instructions. Explain what each option means and make sure those definitions agree with the question. Where appropriate, include an “other” option. Otherwise, the model must select one of your categories even when none fits well.
- Leave exact work to code – Do not ask the model to calculate a total, compare dates, or check an exact identifier when your application can do so reliably. Use named fields in the state when they clarify the input. A JSON object that separates the customer’s message, account details, and order history is easier to specify consistently than an undifferentiated block of text.
Implementation patterns
Pattern 1: Route automatically when confidence is sufficient
A basic routing service can accept a decision above a tested threshold and refer other cases for review.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
from dataclasses import dataclass from typing import Optional @dataclass class Routing: team: Optional[str] needs_human: bool reason: str CONFIDENCE_FLOOR = 0.80 # Illustrative; tune on labeled data. def route_ticket(client, ticket_text: str) -> Routing: response = client.system_one( state={"ticket": ticket_text}, questions={ "team": Choice( instructions="Which team should own this ticket?", criteria={ "billing": "Payments, refunds, invoices", "technical": "Bugs, outages, integrations", "account": "Login, permissions, profile changes", "other": "None of the above clearly fits", }, ), }, ) team = response.choices["team"] if team.choice == "other": return Routing(None, True, "no clear category") if team.confidence < CONFIDENCE_FLOOR: return Routing( None, True, f"low confidence ({team.confidence:.2f})", ) return Routing(team.choice, False, "auto-routed") |
Confidence is a separate signal from the winning option’s probability: the probabilities describe the distribution over options, while confidence describes how much the model trusts that distribution, so a top pick at 0.6 can come with either high or low confidence. And the threshold lives in your code, where you can version it, test it, and change it per workflow.
Log every response, including the versioned model ID it reports, and set thresholds by measuring precision on a labeled sample. Pin a specific model (for example jev-1.13.0) once you’ve tuned against it, since jev-latest moves when TypeSafe ships a new release.
Pattern 2: Combine separate judgments into a business score
Lead scoring is a useful example. Instead of asking for a single assessment of lead quality, evaluate the factors you care about and combine them explicitly.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
LEAD_WEIGHTS = { "fit": 0.4, "intent": 0.35, "budget": 0.25, } def score_lead(client, lead: dict) -> float: response = client.system_one( state=lead, questions={ "fit": Score( instructions=( "How well does this company match our ideal " "customer profile: B2B SaaS, 50–500 employees?" ), criteria=[ "Poor fit", "Partial fit", "Strong fit", ], ), "intent": Score( instructions=( "How strong is the buying intent in the message?" ), criteria=[ "Browsing", "Evaluating", "Ready to buy", ], ), "budget": Noul( instructions=( "The lead has signaled they have budget allocated." ), ), }, ) fit = response.answers["fit"].score / 2 intent = response.answers["intent"].score / 2 budget = response.answers["budget"].noul return ( LEAD_WEIGHTS["fit"] * fit + LEAD_WEIGHTS["intent"] * intent + LEAD_WEIGHTS["budget"] * budget ) |
Score uses the rubric’s zero-based scale. With three levels, the range is 0–2, so this example divides by two before combining it with the Noul result.
The resulting lead score is an application-defined ranking, not automatically a probability of conversion. Its value depends on whether the chosen factors and weights help your sales team prioritize effectively.
Keeping the weights in code also makes changes straightforward. If budget becomes more important, you can adjust its contribution without changing the individual questions.
Pattern 3: Choose which language model handles a request
A lightweight classification step can route requests among models with different capabilities and costs.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
MODELS = { "simple": "small-fast-llm", "standard": "mid-tier-llm", "reasoning": "frontier-reasoning-llm", } def pick_model(client, user_request: str) -> str: response = client.system_one( state={"request": user_request}, questions={ "tier": Choice( instructions=( "What level of model does this request require?" ), criteria={ "simple": "Lookup, rewording, short factual answer", "standard": ( "Multi-paragraph writing or straightforward code" ), "reasoning": ( "Multi-step reasoning, math, or complex debugging" ), }, ), }, ) tier = response.choices["tier"] if tier.confidence < 0.7: return MODELS["reasoning"] return MODELS[tier.choice] |
Here, uncertain requests go to the more capable model. The threshold is illustrative, and the model names are placeholders.
Pattern 4: Guardrails on agent tool calls
You can also ask narrow questions about an action before the application executes it.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
def guard_tool_call( client, tool_name: str, args: dict, user_intent: str, ) -> str: response = client.system_one( state={ "tool": tool_name, "arguments": args, "user_intent": user_intent, }, questions={ "irreversible": Noul( instructions=( "Executing this tool call causes an irreversible " "change, such as deleting data or sending money." ), ), "matches_intent": Noul( instructions=( "This tool call matches what the user asked for." ), ), }, ) if response.answers["irreversible"].noul > 0.3: return "require_confirmation" if response.answers["matches_intent"].noul < 0.8: return "block_and_review" return "allow" |
This is an additional review step, not an authorization mechanism. Authentication, permissions, resource ownership, and input validation still belong in deterministic code. Known destructive actions should remain subject to explicit policy even if the model fails to recognize them.
The thresholds above illustrate the flow; they are not validated safety settings. The source material also describes a Composio integration for this kind of action review.
Pattern 5: Classify a collection of records
Because calls are cheap and quick, you can afford to apply a judgment to every row instead of a sample.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
from concurrent.futures import ThreadPoolExecutor from functools import partial def classify_review(client, review: str) -> dict: response = client.system_one( state={"review": review}, questions={ "topic": Choice( instructions="What is this review mainly about?", criteria={ "shipping": None, "quality": None, "price": None, "support": None, "other": "None of the listed topics fits", }, ), "negative": Noul( instructions="The reviewer is dissatisfied.", ), }, ) return { "topic": response.choices["topic"].choice, "topic_conf": response.choices["topic"].confidence, "negative": response.answers["negative"].noul, } with TypeSafeClient() as client: with ThreadPoolExecutor(max_workers=16) as pool: results = list( pool.map( partial(classify_review, client), reviews, ) ) |
You get typed features you can drop straight into a dataframe or warehouse table. Respect rate limits and use the SDK’s retry handling. One community integration even exposes the primitives as SQL table functions in DuckDB so you can LATERAL JOIN a judgment onto a table.
Pattern 6: Use an LLM and Jev together
Some workflows need both generation and a bounded decision. For extraction, a regex or language model could produce candidate values, with Jev selecting among them. That is different from asking Jev to generate a value that was not included in the choices.
For output review, Jev could assess a draft against specific criteria such as policy compliance, tone, or support from supplied evidence. Any groundedness check would need the relevant evidence in the state; the judgment alone does not establish that the draft is correct.
When to reach for it
Jev is worth evaluating when an application repeatedly makes a bounded judgment: routing tickets, scoring leads, classifying records, reviewing proposed actions, or selecting among predefined options.
The strongest case is a workload where latency and cost matter, the answer space can be defined clearly, and uncertain cases have an acceptable fallback.
For generated language, code, conversation, or open-ended reasoning, use a language model. For exact calculations and rules, use ordinary code. Jev’s potential role is between those two: handling judgments that are difficult to express as rules but narrow enough to evaluate systematically.
References
Blog was written with AI Assist
-
- Diogo Almeida, “Introducing System One Models & Jev,” TypeSafe AI blog, September 15, 2026: https://typesafe.ai/blog/introducing-system-one-models-and-jev
- OpenRouter, “Jev Documentation”: https://openrouter.ai/docs/guides/community/jev
- “Jev AI TypeSafe AI: A Practical Guide to Typed Decisions in Production,” Hugging Face community blog: https://huggingface.co/blog/sora-2/jev-ai-typesafe-ai-a-practical-guide-to-typed-deci
- Flavio Copes, “A deep dive into Jev, TypeSafe’s System One model”: https://flaviocopes.com/jev/
- “How to Use Jev: A practical guide to TypeSafe’s System One model,” DEV Community: https://dev.to/valyuai/how-to-use-jev-a-practical-guide-to-typesafes-system-one-model-g5e
- Haskell
jevpackage README (notes on Score scaling and Noul semantics): https://hackage-content.haskell.org/package/jev - Langfuse, “TypeSafe integration”: https://langfuse.com/integrations/model-providers/typesafe.md
- LangChain, “What Is Jev? A Guide to TypeSafe AI’s System One Model”: https://www.langchain.com/blog/building-a-harness-with-jev