Jev as an LLM Router: Fast Typed Decisions for Model Selection
Using Jev to classify each query and route it to the right model tier on Amazon Bedrock, with a confidence-based fallback for uncertain cases.
Most teams pick one model and send everything to it. A large reasoning model ends up answering a two-line extraction task, and a small model gets handed a problem it cannot really do. Neither is a good outcome: the first wastes money and time, the second wastes quality. The better pattern is to decide, per request, which model the work actually deserves, and then send it there. This post builds that runtime router. Jev classifies each query, ordinary code maps the result to a model tier, and the chosen model on Amazon Bedrock produces the answer.
This idea started as a design-time skill I built, the AI Route Advisor , which recommends a route class for a use case before any model is chosen. That skill answers "what kind of route does this work deserve?" up front. This demo is its runtime half: the same route types applied live on every query and mapped onto a concrete Bedrock stack.
A quick recap of Jev
In the previous article, Jev and LLMs: Fast Typed Decisions Plus Generated Language , we covered what Jev is, so here is just the short version. Jev is a model from TypeSafe AI that does not generate text. It is a System One model: you send it a state and typed questions, and it returns typed answers with calibrated probabilities. A Choice picks one option from a set with a confidence score, a Score rates against ordered levels, and a Noul answers yes/no. Several questions can be asked in one parallel call, and the output is always typed.
For a router this is a good fit. Classifying a query is exactly a Choice, and doing that classification on a small, fast model instead of asking a large LLM "which model should handle this?" is the whole point of routing. AWS even lists routing as a use case for the fast tier we will route to.
The models: an example tier setup on Amazon Bedrock
For this demo the tiers map to the OpenAI GPT-6 family on Amazon Bedrock, which comes in named capability tiers that line up neatly with a router. Any set of models on Bedrock would work; these are just the ones used here:
- Luna is built for repeatable work at scale, such as summarizing documents, extracting information, and answering focused questions.
- Sol is built for demanding development work, such as building features, debugging, refactoring, and multi-step work across tools.
- Astra is OpenAI's most capable model, for the hardest end-to-end work: complex reasoning, research, and document creation.
All three are called through the same Bedrock Converse API on the
bedrock-runtime endpoint, so the router talks to one interface regardless of which tier a query lands on. One note worth flagging: these OpenAI models on Bedrock reject the temperature field in Converse, so the inference config below sets only maxTokens.How the router works
The flow for each query is three steps.
First, Jev classifies the query with two questions in a single call: a Choice for the route type and a Score for how much reasoning the query needs. The route types come from the advisor skill idea I wrote before and are :
low_cost_tier, mid_tier, and frontier_or_reasoning_tier.Second, ordinary code turns those answers into a model choice. The route type maps directly to a Bedrock model. If Jev's confidence in the route is below a floor, the query falls back to the frontier tier rather than risking a hard query on a cheap model. If the complexity score is high, the query is upgraded to the frontier tier regardless of the route. The routing is plain
if statements you can read and test.Third, the chosen model answers the query through the Bedrock Converse API.

The route-to-model mapping is your own configuration, not something Jev returns:
| Route type | Bedrock model | Model ID |
|---|---|---|
| low_cost_tier | GPT-6 Luna | us.openai.gpt-6-luna |
| mid_tier | GPT-6 Sol | us.openai.gpt-6-sol |
| frontier_or_reasoning_tier | GPT-6 Astra | us.openai.gpt-6-astra |
The code
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
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
"""LLM router demo: Jev classifies the query, code routes it to a Bedrock model.
A user query comes in. Jev (TypeSafe's System One model) classifies it with a
couple of typed questions, and ordinary code maps the result to a model tier on
Amazon Bedrock. The cheap tier handles simple work, the mid tier handles routine
work, and the frontier tier handles complex reasoning. When Jev is not confident
about the route, the query falls back to the frontier tier.
The route types come from the R.E.S.E.T. framework in AI Route Advisor:
https://github.com/enitinb/ai-engineering-lab/tree/main/ai-route-advisor-skill
There, the route class is chosen at design time. Here
the same route types are applied at runtime and mapped to a concrete Bedrock stack.
Docs:
- TypeSafe primitives : https://docs.typesafe.ai/primitives
- TypeSafe confidence : https://docs.typesafe.ai/confidence
- Bedrock Converse API : https://docs.aws.amazon.com/bedrock/latest/userguide/conversation-inference.html
"""
import boto3
from typesafe_sdk import Choice, Score, TypeSafeClient
# ---- Config ----
TYPESAFE_API_KEY = "<<TYPESAFE_API_KEY>>"
AWS_PROFILE = None # None = default credential chain
AWS_REGION = "us-east-1"
# Route type (R.E.S.E.T. taxonomy) -> Bedrock model.
ROUTE_TO_MODEL = {
"low_cost_tier": "us.openai.gpt-6-luna", # repeatable work at scale
"mid_tier": "us.openai.gpt-6-sol", # demanding development work
"frontier_or_reasoning_tier": "us.openai.gpt-6-astra", # most capable, hardest work
}
FRONTIER_ROUTE = "frontier_or_reasoning_tier" # fallback route
CONFIDENCE_FLOOR = 0.6 # route confidence below this falls back to frontier
typesafe = TypeSafeClient(api_key=TYPESAFE_API_KEY)
bedrock = boto3.Session(profile_name=AWS_PROFILE, region_name=AWS_REGION).client(
"bedrock-runtime"
)
# ---- 1. Jev: classify the query ----
def classify(query: str) -> dict:
r = typesafe.system_one(
state=query,
questions={
"route_type": Choice(
instructions="Which route type best fits this request",
criteria={
"low_cost_tier": "Simple, high-volume work: extraction, formatting, tagging, short summaries",
"mid_tier": "Routine work: drafting, longer summarization, structured analysis, or writing straightforward code",
"frontier_or_reasoning_tier": "Complex, multi-step reasoning: architecture decisions, strategy, or hard problem solving",
},
),
"complexity": Score(
instructions="How much reasoning does this request require",
criteria=[
"Trivial, single step",
"Moderate, a few steps",
"Hard, deep multi-step reasoning",
],
),
},
)
return {
"route_type": r.answers["route_type"].choice,
"route_confidence": r.answers["route_type"].confidence,
"complexity": r.answers["complexity"].score,
}
# ---- 2. Code: pick the model ----
def pick_route(c: dict) -> str:
# Low confidence on the route -> send to the frontier tier.
if c["route_confidence"] < CONFIDENCE_FLOOR:
return FRONTIER_ROUTE
# High complexity -> upgrade to the frontier tier regardless of route.
if c["complexity"] >= 2.0:
return FRONTIER_ROUTE
return c["route_type"]
# ---- 3. Bedrock: answer with the chosen model ----
def answer(query: str, model_id: str) -> str:
resp = bedrock.converse(
modelId=model_id,
messages=[{"role": "user", "content": [{"text": query}]}],
inferenceConfig={"maxTokens": 1500},
)
# These models can return reasoning blocks alongside the answer, so pick the
# text block rather than assuming it is first.
blocks = resp["output"]["message"]["content"]
texts = [b["text"] for b in blocks if "text" in b]
if texts:
return "\n".join(texts)
reason = resp.get("stopReason", "unknown")
return f"(no text returned; stopReason={reason})"
# ---- Demo: one query per route ----
QUERIES = [
"Extract the names, dates, and dollar amounts from this note: "
"Invoice #204 dated March 3 for $1,250 to Acme, due April 2.",
"Summarize this in two sentences: Our Q3 launch shipped three features, "
"grew signups 18 percent, and cut onboarding time from 12 minutes to 4.",
"Write a Python function that reads a CSV file and returns the rows where "
"the 'amount' column is greater than 1000, with basic error handling.",
"Compare a monolith and microservices for a 5-person startup that expects "
"rapid growth. Recommend one and explain the tradeoffs.",
"Thoughts?",
]
def main() -> None:
for query in QUERIES:
c = classify(query)
route = pick_route(c)
model_id = ROUTE_TO_MODEL[route]
print("=" * 70)
print(f"QUERY : {query}")
print(f"JEV : route={c['route_type']} (conf {c['route_confidence']:.2f}) "
f"complexity={c['complexity']:.2f}")
print(f"ROUTE : {route} -> {model_id}")
print("-" * 70)
print(answer(query, model_id))
print()
if __name__ == "__main__":
main()
Each query runs through
classify, then pick_route, then answer with the mapped model. Before running, set your TypeSafe key in place of the placeholder, and make sure your AWS credentials and Region have Bedrock access to the GPT-6 models.What a run looks like
Five queries across the routes:
| Query | Jev classification | Model |
|---|---|---|
| Extract names, dates, and amounts from a note | low_cost_tier, confidence 1.00 | GPT-6 Luna |
| Summarize a paragraph in two sentences | low_cost_tier, confidence 1.00 | GPT-6 Luna |
| Write a Python function with basic error handling | mid_tier, confidence 1.00 | GPT-6 Sol |
| Compare monolith vs microservices, recommend one | frontier_or_reasoning_tier, confidence 0.99, complexity 1.46 | GPT-6 Astra |
| "Thoughts?" | low_cost_tier, confidence 0.46 | GPT-6 Astra (fallback) |
The first four route cleanly, one to each tier. The fifth is the interesting one. "Thoughts?" has no content to classify, so Jev returns
low_cost_tier but at only 0.46 confidence. That sits below the 0.6 floor, so the query does not go to the cheap model on a coin-flip classification. It falls back to the frontier tier instead. The routing rule never changed; what triggered the fallback was Jev reporting that it was not sure. That built-in "I'm not sure" signal is what makes the router safe to run without a human watching every decision.Example outputs
The routed model then answers. A few of the actual responses, truncated for length:
low_cost_tier (GPT-6 Luna), extraction:
1
2
3
QUERY : Extract the names, dates, and dollar amounts from this note: Invoice #204 dated March 3 for $1,250 to Acme, due April 2.
JEV : route=low_cost_tier (conf 1.00) complexity=0.50
ROUTE : low_cost_tier -> us.openai.gpt-6-lunamid_tier (GPT-6 Sol), the coding task:
1
2
3
QUERY : Write a Python function that reads a CSV file and returns the rows where the 'amount' column is greater than 1000, with basic error handling.
JEV : route=mid_tier (conf 1.00) complexity=0.99
ROUTE : mid_tier -> us.openai.gpt-6-solfrontier_or_reasoning_tier (GPT-6 Astra), the architecture question (truncated):
1
2
3
QUERY : Compare a monolith and microservices for a 5-person startup that expects rapid growth. Recommend one and explain the tradeoffs.
JEV : route=frontier_or_reasoning_tier (conf 0.99) complexity=1.51
ROUTE : frontier_or_reasoning_tier -> us.openai.gpt-6-astraWhy this split works
The router keeps two concerns apart. Jev owns the decision: it is fast, cheap, and typed, and its confidence gives you a clean way to escalate uncertain cases. The Bedrock model owns the answer, and you spend frontier-model cost only on the queries that actually need it. The decision itself is a sub-second typed call on every request, not a full LLM round trip, so routing never becomes the expensive part of the pipeline.
It also connects design time to runtime. This demo is one such mapping, from those neutral route types to GPT-6 tiers on Bedrock, applied automatically on every query.
Wrap-up
A single model for everything is either too expensive for the easy work or too weak for the hard work. Letting Jev classify each request and routing it to the right GPT-6 tier on Bedrock gives you both: cheap and fast where that is enough, frontier reasoning where it is needed, and an honest fallback when the classifier is unsure. The route types are model-neutral, so the same pattern extends to any approved model stack, and the decision layer stays in plain, auditable code.
Notes: the GPT-6 models are invoked with the Region inference-profile IDs (
us.openai.gpt-6-*) and require Bedrock model access enabled in your account and Region. Replace the API key placeholder with your own TypeSafe key, and do not commit a real key.Enjoyed reading this content? Let the author know!
Your likes, comments, shares, and saves help creators reach more builders.
Loading recommendations
Loading article