← Back to all files
File 005 · Case StudyStatus: Live

LLM Gateway & Guardrail Router

A gate that checks every query before it's answered. One cheap call decides whether it's safe, how hard it is, and which model should handle it. Nothing reaches a model until the gate clears it.

Classification
Systems design · Cost and latency engineering · Applied AI judgment
Stack
Python · FastAPI · TypeSafe AI (Jev) · Groq · Claude
Subject
Why every query in a support queue shouldn't cost the same, or be trusted the same.
Results

What changes when a cheap gate goes first.

68%
Cost reduction vs. sending every non-adversarial query to Claude
From a 250-query run. Routing, Jev cost and Groq cost are measured. Claude's cost is modelled from measured input tokens, with response length worked out from an earlier $0.88 run so I didn't pay for the same calls twice.
64%
Faster at the median, gated pipeline vs. direct-to-Claude
Measured gate latency plus Groq response time, against a measured Claude baseline. The p95 barely moves, because any query that still needs Claude waits for Claude either way.
5 / 5
Adversarial queries caught before reaching a model
Every jailbreak attempt and fake-PII query in the hand-checked test suite was blocked. No false negatives, which is the failure that matters most here.
19 / 19
Routing decisions verified correct, hand-checked
Simple FAQs, complex queries, jailbreak attempts, fake PII and deliberately ambiguous questions. Each one landed in the right place, tested against live Jev, Groq and Claude calls.
0.4s
Added by the gate itself, before any downstream model runs
The classification call on its own, measured at roughly 0.2 to 0.4 seconds across test runs. It's the price every query pays before reaching either tier.

A gateway that sits in front of a support team's LLMs. One parallel call scores each query for security risk, intent and complexity. Simple questions go to a fast, cheap model, hard ones go to Claude, and anything that looks like an attack or contains personal data is blocked before a model sees it.


Commercial Impact

Without a gate, “what are your hours?” costs as much to answer as a multi-step billing dispute, and both carry the same risk of leaking a system prompt or a card number. On a 250-query benchmark this cut cost by 68% and median response time by 64%, and blocked all five adversarial test queries. That makes an AI support queue affordable to run at volume.

Design Thesis

One judgment instead of three chained models.

Security, intent and complexity are separate questions, but they don't need separate calls. Jev answers all three in parallel on the same input for a fraction of a cent. A slow or expensive gate would just become a new bottleneck. TypeSafe's published benchmarks put it at 20 to 200x faster and 40 to 400x cheaper than a general-purpose LLM on this kind of classification. Those are their numbers; I haven't reproduced them.

The Gate

One call, four possible outcomes.

Jev answers three questions about each query, and those answers decide where it goes. The two kinds of mistake aren't weighted equally. Missing a jailbreak or a PII leak is far worse than an unnecessary block, so the security check has a much lower threshold than the routing check.

JEV · ONE PARALLEL CALL
Security risk · Which agent · How complex
BLOCKED
Refused, logged for review
Jailbreak attempt or PII in the query. Never reaches a model.
HUMAN
Queued for a person
Jev itself isn't confident which agent this belongs to.
CHEAP TIER
Groq, sub-second
Trivial or simple questions, which is most of what a support queue gets.
HIGH TIER
Claude
Complex questions, or ones that need judgment a fast model shouldn't guess at.
ZERO DATA RETENTIONON THE JEV CALL AND WHICHEVER MODEL HANDLES THE QUERYA system that reads queries for PII and jailbreak attempts is handling sensitive input itself, so zero data retention was part of the design from the start.
See It Decide

Four test queries, four outcomes.

Taken from the test suite rather than written for this page. Each outcome is what the build returned when it ran.

Click through actual test cases
1 / 4
“What are your support hours?”
BLOCKED
Refused, logged for review
Jailbreak attempt or PII in the query. Never reaches a model.
HUMAN
Queued for a person
Jev itself isn't confident which agent this belongs to.
CHEAP TIER
Groq, sub-second
Trivial or simple questions, which is most of what a support queue gets.
HIGH TIER
Claude
Complex questions, or ones that need judgment a fast model shouldn't guess at.
Why Jev instead of a classifier prompt

The gate had to be cheap enough to ignore.

The obvious first version is a small LLM with a classification prompt in front of everything. That works until you price the gate itself at volume. At that point the guardrail starts to cost as much as the spending it was meant to prevent.

01Cheap enough to run on everything
$0.04 per million input tokens, output free

That's TypeSafe's published pricing. On this build the gate call finishes in about 0.2 to 0.4 seconds, measured. Any more latency or cost and the guardrail becomes the bottleneck.

02Two kinds of mistake, weighted differently
0.25 for security, 0.50 for everything else

The security check has its own threshold. A missed jailbreak or PII leak is worse than an unnecessary block, so it trips earlier than the routing check does.

03The Build
Typed answers instead of parsed text

There's no prompt template to maintain and no JSON to parse and hope for. Three typed questions go in (a yes/no, a choice between options, and a score on a scale), and typed answers come back with confidence scores. That's the whole interface.

EXHIBIT A — THE ROUTING LOGIC
SECURITY_THRESHOLD = 0.25 # trip early: false negatives cost more than false positives
CONFIDENCE_THRESHOLD = 0.50 # below this, Jev itself is unsure which agent applies
COMPLEXITY_CHEAP_MAX = 1.5 # weighted score below this ~= \"Trivial\"/\"Simple\"
def route(decision: RoutingDecision) -> str:
if decision.security_risk >= SECURITY_THRESHOLD:
return "blocked"
if decision.agent_confidence < CONFIDENCE_THRESHOLD:
return "human"
if decision.complexity_score <= COMPLEXITY_CHEAP_MAX:
return "cheap"
return "hightier"

Verbatim from router.py. The highlighted security check runs first, on its own threshold, separate from the routing confidence check below it.

What This Demonstrates

Three decisions worth explaining.

01

Make the security tradeoff explicit.

Scoring security and routing confidence against the same 0.50 line would be a decision nobody made on purpose. Missing a jailbreak costs more than an unnecessary block, so the two thresholds are different numbers, named as constants, with the reasoning written next to them.

02

Don't publish a number until it's measured.

The first draft of this page quoted a cost saving and a latency figure before any benchmark had run. I pulled both until a 250-query test set existed, weighted toward the mix of traffic a support queue really gets.

FIELD NOTE · ANTHROPIC AUTH

An org-scoped API key isn't the same as a workspace-scoped one. No Claude call worked until I added an explicit workspace header.

FIELD NOTE · RESPONSE PARSING

The first content block in a Claude response isn't always text. A leading reasoning block broke content[0].text until I filtered for the text block.

FIELD NOTE · TEST FIXTURES

Two queries I'd labelled “ambiguous” came back from Jev with full confidence. On a second look, the labels were wrong and the router was right.

FIELD NOTE · THE “CHEAP” TIER

Local inference isn't automatically fast. A local model took 7 to 16 seconds a call on my hardware, slower than Claude, so I moved the cheap tier to hosted inference on Groq.

03

Test the wiring as well as the logic.

The routing logic passed its five unit tests on the first try. Every real failure happened one layer out, in the connections to Jev, Groq and Claude. That's where the field notes above come from, and a logic-only test suite would have missed all of them.

Notes & Provenance
  • Every PII and jailbreak example in the test suite is synthetic and was generated for testing. None of it is real data.
  • Every threshold (security, routing confidence, complexity) is a named constant in router.py.
  • The 250-query benchmark set mirrors realistic traffic: mostly trivial or simple queries, a smaller share of genuinely complex ones, a handful of adversarial attempts, and a few deliberately ambiguous queries.
  • Cost methodology: the baseline prices every non-adversarial query as if it went straight to Claude with no gate; the gated total includes Jev's cost across every query, adversarial included, since the gate still has to inspect it either way.