Skip to content

Jev Python SDK: Install, Examples and Error Handling

Last checked · Independent guide, not affiliated with TypeSafe AI

ANSWER

Install the official SDK with pip install typesafe-sdk (Python 3.10 or newer), set TYPESAFE_API_KEY, and call client.system_one(state=..., questions={...}) with Noul, Choice and Score objects. The SDK retries rate limits and server errors automatically and raises typed exceptions such as TypeSafeAuthenticationError.

The official Python client is typesafe-sdk (imported as typesafe_sdk). It talks to TypeSafe’s API by default, and since version 0.7.1 its documentation also shows how to point it at OpenRouter or Vercel AI Gateway (see below). Everything below was first run with version 0.7.0 on Python 3.10 on September 19, 2026; we re-ran the main example on 0.7.1 on September 25, 2026 with the same answers.

Terminal window
pip install typesafe-sdk # or: uv add typesafe-sdk

It requires Python 3.10 or newer.

Mirror gotcha. If pip says No matching distribution found for typesafe-sdk==0.7.1 and lists only older versions, your pip is probably using a package mirror that has not synced yet. That happened to us with a mirror that only had 0.6.0. Install from PyPI directly:

Terminal window
pip install -i https://pypi.org/simple typesafe-sdk

Version 0.7.0 switched the SDK’s serialization library to pydantic; if you pinned 0.6.x, read the changelog before upgrading. Version 0.7.1 (September 21, 2026) is a small fix on top: it checks the key early and keeps the key’s value out of logged exceptions.

The client reads TYPESAFE_API_KEY from the environment. You can also pass api_key= to the constructor. With no key at all, the SDK raises TypeSafeError: No API key was provided... before sending anything.

from typesafe_sdk import Choice, Noul, Score, TypeSafeClient
client = TypeSafeClient(model="jev-1.13.0")
message = "I was charged twice for my annual plan this morning. Please refund one of the charges today."
response = client.system_one(
state=message,
questions={
"wants_refund": Noul(instructions="Is the customer asking for money back?"),
"queue": Choice(
instructions="Which team should handle this message?",
criteria={
"billing": "Charges, invoices, refunds",
"technical": "Bugs, outages, integrations",
"sales": "New plans, upgrades, quotes",
},
),
"urgency": Score(
instructions="How urgent is this message?",
criteria=["Can wait a week", "Should be handled this week", "Needs a reply today"],
),
},
)
print(response.answers["wants_refund"].noul) # 0.99
print(response.answers["queue"].choice) # billing
print(response.answers["urgency"].score) # 2.0
print(response.usage) # input_tokens=418 output_tokens=72

The sync example took 557 ms end to end in our run. Besides response.answers[...], the response has typed shortcuts: response.nouls, response.choices and response.scores.

Since 0.7.0 you can pass a pydantic model to get attribute access and type checking:

from typesafe_sdk import ChoiceAnswer, Noul, Choice, NoulAnswer, SystemOneResponse, TypeSafeClient
class Triage(SystemOneResponse):
is_bug: NoulAnswer
queue: ChoiceAnswer
with TypeSafeClient(model="jev-1.13.0") as client:
r = client.system_one(
"My invoice lists two seats, but only one of us can sign in.",
{
"is_bug": Noul(instructions="Does the customer describe something not working?"),
"queue": Choice(instructions="Which team should handle this message?",
criteria={"billing": None, "technical": None, "other": None}),
},
response_model=Triage,
)
print(r.is_bug.noul, r.queue.choice, r.request_id)

Pass model= to the client; the default is jev-latest. Use the full versioned ID jev-1.13.0. The shortened jev-1.13, which appears in one example in TypeSafe’s docs, fails with TypeSafeBadRequestError: ... 400 Unknown model: jev-1.13. To see what your account can use:

print([m.name for m in client.models.list().models]) # ['jev-latest', 'jev-preview']

The client takes a base_url argument, or reads the TYPESAFE_BASE_URL environment variable. OpenRouter and Vercel AI Gateway both serve TypeSafe’s request format, so the same code runs against them with their key and model name:

Route base_url Key model
TypeSafe (default) https://api.typesafe.ai TYPESAFE_API_KEY jev-1.13.0 or jev-latest
OpenRouter https://openrouter.ai/api OPENROUTER_API_KEY ~typesafe/jev-latest
Vercel AI Gateway https://ai-gateway.vercel.sh/typesafe AI_GATEWAY_API_KEY typesafe-ai/jev
import os
from typesafe_sdk import Noul, TypeSafeClient
with TypeSafeClient(
api_key=os.environ["OPENROUTER_API_KEY"],
base_url="https://openrouter.ai/api",
model="~typesafe/jev-latest",
) as client:
result = client.system_one(
"I was charged twice.",
{"billing": Noul(instructions="Is this about billing?")},
)
print(result.nouls["billing"].noul)

This is the example from TypeSafe’s SDK documentation. We ran the same code against TypeSafe’s own API with TYPESAFE_BASE_URL set on September 25, 2026; we have not run it through OpenRouter or Vercel ourselves. The model-listing call, client.models.list(), does not work on OpenRouter. Channel details: OpenRouter, Vercel AI Gateway.

Exception When
TypeSafeError No API key configured (raised locally)
TypeSafeAuthenticationError 401: invalid key
TypeSafeBadRequestError 400: unknown model, unknown question type, max_tokens_exceeded
TypeSafeUnprocessableEntityError 422: missing required field
TypeSafeRateLimitError 429 after retries are exhausted
TypeSafeInternalServerError 5xx (including 529) after retries
TypeSafeAPITimeoutError, TypeSafeAPIConnectionError Network problems after retries

Messages include the method, URL, status and a request_id, for example POST https://api.typesafe.ai/v1/systemone: 401 Cannot authenticate with the server... (request_id=req_...).

By default the client retries 408, 429 and all 5xx responses, plus timeouts and connection errors, twice after the first attempt, with backoff starting at 0.5 seconds and capped at 5 seconds, and it honors Retry-After. To change this:

from typesafe_sdk import RetryPolicy, TypeSafeClient
client = TypeSafeClient(
model="jev-1.13.0",
retry=RetryPolicy(max_retries=4, backoff_max=10.0),
)
  • Create one client and reuse it; use it as a context manager (with / async with) or close it when done.
  • Put all the questions for one input into one system_one call. It costs about the same and takes the same time as a single question.
  • Keep question text and thresholds in one module so they are easy to review and re-test.

See also: Your first Jev call, Jev API reference and Jev API errors.

Sources

  1. TypeSafe Python SDK (TypeSafe docs)
  2. Python SDK usage (TypeSafe docs)
  3. Python SDK retries (TypeSafe docs)
  4. Python SDK changelog (TypeSafe docs)
  5. typesafe-sdk on PyPI
  6. typesafe-sdk-python on GitHub