I wanted a reason to use TypeSafe that was not another chatbot wrapper. Most of the AI I wire into software still wants to talk. I needed something that would decide, then get out of the way.

Image generation is a good place to feel that gap. A vague prompt like “make it look cool” should not spend. A Nike swoosh and a celebrity should not spend. A specific product still should spend, and it should spend on the cheaper Gemini tier unless there is a real reason to go premium. That is not a paragraph I want to parse out of a chat model. That is a fork in code.

So I built a small FastAPI demo, Jev Image Gate. Jev never paints. It answers three typed questions about the prompt. Python decides whether Gemini may run. The repo is github.com/sprider/jev-image-gate.

The mismatch

Large language models are trained to produce text for people. When your program needs a judgment - allow or deny, cheap or expensive, specific enough or not - you end up coercing a text generator into a JSON blob, then hoping the next response still parses.

That hop is where a lot of “AI in production” work actually lives. Prompt. Parse. Retry. Soften the prompt. Parse again. Meanwhile the thing you were trying to protect, an image API call, is already one hallucinated yes away from spending.

TypeSafe starts from a different bet. Their AI primer puts it plainly: large-scale automation will be mostly machine-to-machine, so the machine interface matters more than the chat interface. They train for calibrated decisions instead of preferred-sounding prose.

What TypeSafe Jev is

Jev is TypeSafe’s flagship model, and the first System One model. You send a state and a set of typed questions. You get structured answers back. No generated paragraph. No “here is my reasoning.” Values your code can branch on.

The name comes from Kahneman’s System 1: fast, focused judgments. Jev currently takes text - strings, JSON, arrays of text. It does not see images. That is the point in this demo. The decision model never looks at pixels. The image model never decides whether it is allowed to run.

TypeSafe gives you three primitives. You can mix them in one request. Each question is evaluated in parallel against the same state:

Question What you are asking What you get back
Noul Is this statement true? a probability from 0 to 1
Score How does this sit on a rubric? a score, a distribution across levels, and confidence
Choice Which option from this closed list? the option, probabilities, and confidence

Their guidance is the part I took most seriously: ask one well-scoped thing per question. If the judgment has three independent factors, ask three questions and combine them in code. When the weights change later, you change a threshold, not a prompt novel.

Confidence is the other half. Probability says what. Confidence says whether to act. Low confidence is a usable “I don’t know.” That is the signal I wanted in front of spend.

The problem I wanted Jev to own

The demo policy is boring on purpose: commercial product stills. One product. Studio light. No third-party marks. No readable slogans. No real people. No medical or financial claims.

The questions I did not want Jev to own:

  • How do I call Gemini?
  • Which HTTP client?
  • What happens if Jev is down?
  • What if it returns an action I never defined?

Those are software problems. Jev is good at the gut-check in the middle: does this prompt look like a policy miss, is it specific enough to paint, and which spend bucket should I consider.

How the demo uses it

Architecture

One System One request. Three answers. Python still owns spend.

The state is small. Prompt, policy rules, leftover budget. No chat history. No hidden system prompt that the image model has to interpret as law.

{
  "prompt": "studio photo of a red ceramic mug on a white sweep, no text, no logos",
  "policy": {
    "id": "product_stills_v1",
    "rules": [
      "Generate a single commercial product still only.",
      "No celebrity or recognizable real person.",
      "No third-party brand marks, logos, or wordmarks.",
      "No readable text, labels, or slogans.",
      "No medical, financial, or legal claims.",
      "Clean studio or simple environment; one primary product."
    ]
  },
  "budget": {"remaining_usd": 0.05, "attempt": 1, "max_attempts": 1}
}

Then three questions, one call:

  1. Noul - policy violation. Does this prompt ask for something the rules forbid?
  2. Score - specificity. Empty, mood-only, adequate, or specific enough to paint.
  3. Choice - next action. block, ask_clarify, allow_lite, or allow_premium.

Jev answers all three independently. That is speculative fan-out in a tiny form: I always ask the policy question, even when the prompt is obviously vague, because the extra question is cheap and I do not want one fat “do the right thing” prompt rotting as I add cases.

Python then applies guardrails TypeSafe itself tells you to keep in code:

  • Missing key, timeout, or a junk payload → do not spend.
  • Policy noul ≥ 0.35 → block.
  • Specificity ≤ 1 → ask_clarify.
  • Action confidence below 0.35 → do not spend.
  • allow_premium with confidence below 0.50 → drop to allow_lite.
  • Unknown action string → treat it as unavailable. Jev cannot invent a fifth verb.

Only allow_lite and allow_premium reach Gemini. Exactly one image call. No automatic Pro retry if Flash Lite is ugly. The image path is a consequence, not a negotiation.

That last bit is confidence-gated routing. Jev can prefer premium. Code still refuses to pay for it unless the confidence clears a higher bar. Showing a slightly worse still is cheaper than a confident-sounding mistake.

What that looks like on a prompt

make it look cool comes back as ask_clarify. There is no product. Specificity sits on the vague end of the rubric. will_spend is false. Gemini is never called.

Vague prompt held

Mood-only prompt. Jev asks for a product. No image API call.

studio photo of a red ceramic mug on a white sweep, no text, no logos comes back as allow_lite. On-policy, specific enough, not a reason to spend on Pro. One Flash Lite image call.

On-policy mug spends

allow_lite, then one Gemini still. The red mug is the spend path.

Nike Air Force 1 on marble, add the swoosh, Tom Cruise holding it comes back as block. Brand mark and a real person. Again, no image call.

Policy miss blocked

Brand mark and a celebrity. Blocked before Gemini.

The playground buttons are only samples. Any prompt walks the same path. I also ran a live labeled suite of about a hundred preflight cases against Jev with Gemini turned off. Agreement with my labels was high. The miss that stuck with me was a “Nike-ish shoe without saying Nike” prompt that I wanted blocked and Jev treated as generic enough to allow. That is not a Jev failure so much as a reminder from their own docs: if lookalike brands matter, make that its own atomic question, or tighten the policy text, instead of hoping one Choice option absorbs every edge.

What I would not do with this

I would not ask Jev to write the image prompt. I would not ask it to explain the decision in prose for the API response. I would not send it the finished picture and ask “is this on brand?” until they support vision, and even then I would keep that as a second, separate question.

I also would not let the image model be the policy engine. Gemini is good at painting. It is a bad place to hide “please do not draw the swoosh” and then trust the pixels.

The split I ended up with is the one TypeSafe keeps repeating. Jev makes the narrow judgment. Code owns spend, retries, and what the user sees.

If you want to try TypeSafe

The demo is at github.com/sprider/jev-image-gate. Start at the TypeSafe introduction. Create a key at console.typesafe.ai/keys. The Python SDK installs from https://pypi.typesafe.ai/.

The shape I used is the one in their quick start: build a state, ask Noul / Score / Choice in one system_one call, then branch on the typed answers.

If this is the kind of AI you also want in front of a paid API - not a chat window, a gate - find me on LinkedIn.