Jev recipe / architecture comparison

Jev vs Instructor: Zero-Retry Structured Decisions

Last updated: 2026-11-12

Compare Instructor Pydantic retry loops with Jev native typed calls. See latency, token cost, and format-error differences on the same routing decision.

AD

Migrate from Instructor to Jev in three steps

01Define the same bounded Choice schema you would enforce with Instructor Pydantic models — queues, scores, or binary gates.
02Replace the generate → parse → validate → retry loop with one Jev evaluate call that returns typed probabilities in a single forward pass.
03Route low-confidence results to human triage in application code instead of exponential backoff retry storms.
schema / shared decision contract
{
  "routing": {
    "type": "choice",
    "instructions": "Route this support ticket",
    "criteria": {
      "billing": "Payment or invoice",
      "technical": "Product malfunction",
      "sales": "Buying or upgrade question",
      "abuse": "Safety or abuse report"
    }
  }
}
This schema mirrors your Instructor Pydantic model — validate zero-retry results in the Playground.

Architecture: contract-first vs generate-then-validate

METRIC
Jev
Instructor / Guardrails
What is generated
No text. The model scores your declared options and returns one typed answer per question.
JSON prose, then validated against your Pydantic or Zod model.
Output token cost
None - TypeSafe bills input tokens only ($0.042/M on 2026-11-12).
Full generation pricing: you pay for every output token, including discarded retries.
Latency
One forward pass, no autoregressive decoding. TypeSafe publishes no latency SLA, so measure your own path.
Depends entirely on the model and response length. Measure it on your own schema and provider.
Failure mode
The answer is always one of your declared options. The failure to design for is a low-confidence answer.
Validation can fail and trigger a retry loop; output length and nesting drive the failure rate.
Confidence
Per-question probabilities and a confidence value, which still need calibration on your own labels.
Self-reported by the model, if at all; not a probability you can gate on.
Schema richness
Choice (up to 255 options), Score, Noul, and Box for image regions. Flat answers, not nested documents.
Arbitrary nested schemas, long strings, lists of objects - the reason it exists.

Side-by-side code: retry loop vs zero-retry

python / instructor retry loop
import time
import instructor
from openai import OpenAI
from pydantic import BaseModel, ValidationError

class RouteDecision(BaseModel):
    queue: str

client = instructor.from_openai(OpenAI())
MAX_RETRIES = 3

def route_ticket(ticket_text: str) -> RouteDecision:
    for attempt in range(MAX_RETRIES):
        try:
            return client.chat.completions.create(
                model="gpt-4o-mini",
                response_model=RouteDecision,
                messages=[{"role": "user", "content": ticket_text}],
            )
        except ValidationError:
            if attempt == MAX_RETRIES - 1:
                raise
            time.sleep(2 ** attempt)  # exponential backoff

# Typical path: 1800ms+ latency, 20–50 output tokens billed per attempt
python / typesafe-sdk, typed decision
from typesafe_sdk import Choice, TypeSafeClient

client = TypeSafeClient(model="jev-latest")

def route_ticket(ticket_text: str) -> dict:
    response = client.system_one(
        state=ticket_text,
        questions={
            "routing": Choice(
                instructions="Route this support ticket",
                criteria={
                    "billing": "Payment or invoice",
                    "technical": "Product malfunction",
                    "sales": "Buying or upgrade question",
                },
            ),
        },
    )

    routing = response.choices["routing"]
    return {
        "queue": routing.choice,           # always one of the declared options
        "confidence": routing.confidence,  # calibrate before gating on it
        "probabilities": routing.probabilities,
    }
typescript / instructor retry loop
import OpenAI from "openai";
import Instructor from "@instructor-ai/instructor";
import { z } from "zod";

const RouteDecision = z.object({ queue: z.string() });
const client = Instructor({ client: new OpenAI(), mode: "TOOLS" });

async function routeTicket(ticket: string, maxRetries = 3) {
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    try {
      return await client.chat.completions.create({
        model: "gpt-4o-mini",
        response_model: RouteDecision,
        messages: [{ role: "user", content: ticket }],
      });
    } catch {
      if (attempt === maxRetries - 1) throw new Error("schema validation failed");
      await new Promise((r) => setTimeout(r, 2 ** attempt * 1000));
    }
  }
}
typescript / @typesafe-ai/sdk, typed decision
import { choice, TypeSafeClient } from "@typesafe-ai/sdk";

const client = new TypeSafeClient({ defaultModel: "jev-latest" });

export async function routeTicket(ticket: string) {
  const { answers } = await client.systemOne({
    state: ticket,
    questions: {
      routing: choice("Route this support ticket", {
        billing: "Payment or invoice",
        technical: "Product malfunction",
        sales: "Buying or upgrade question",
      }),
    },
  });

  return {
    queue: answers.routing.choice,           // one of the declared options
    confidence: answers.routing.confidence,  // calibrate before gating on it
  };
}

Evaluate cost and integration next