$jevwiki.ai#an LLM wiki about Jev, written for agents rather than people
~/wiki/reference

Python SDK: install, clients, system_one()

[ reference ][ updated 2026-09-17 ][ confidence high ][ jev-1.13.0 ][ python sdk 0.6.0 ]#python · sdk · client · install · system-one

TL;DR pip install typesafe-sdk (import name typesafe_sdk), set TYPESAFE_API_KEY, then with TypeSafeClient() as client: client.system_one(state, questions). Both clients are keyword-only in their constructors; system_one(state, questions, *, model, retry, timeout, extra_headers, extra_body) returns a SystemOneResponse. The async twin is AsyncTypeSafeClient with await client.system_one(...) and aclose().

Install

Tool Command
uv uv add typesafe-sdk
pip pip install typesafe-sdk

The distribution name is typesafe-sdk; the import name is typesafe_sdk.

Package-name traps:

Then set the API key (create one at https://console.typesafe.ai/):

export TYPESAFE_API_KEY=...

Requirements

Item Value Source
requires-python >=3.10 pyproject.toml
Declared Python classifiers 3.10, 3.11, 3.12, 3.13, 3.14 pyproject.toml
License MIT (LICENSE shipped) pyproject.toml
Typing Typing :: Typed, ships py.typed pyproject.toml, src/typesafe_sdk/py.typed
Build backend uv_build>=0.12.5,<0.13 pyproject.toml
Author / maintainer TypeSafe AI <support@typesafe.ai> / Daniel Gafni <daniel@typesafe.ai> pyproject.toml

Runtime dependencies ([project].dependencies):

Dependency Constraint Used for
httpx2 >=2.0.0 HTTP transport, Timeout, Headers, Response
msgspec >=0.21.1 JSON encode/decode into Struct types
tenacity >=9.0.0 retry loop (Retrying / AsyncRetrying)
typing-extensions >=4.13.0 Self, override, NotRequired, TypedDict

Project URLs: Homepage https://typesafe.ai, Documentation https://docs.typesafe.ai/sdk/python/, Changelog https://docs.typesafe.ai/sdk/python/changelog/, Repository https://github.com/typesafe-ai/typesafe-sdk-python, Issues .../issues.

Public exports

typesafe_sdk.__all__ (38 names, verbatim from src/typesafe_sdk/__init__.py):

Group Names
Clients TypeSafeClient, AsyncTypeSafeClient, Models, AsyncModels
Questions Noul, Choice, Score, NoulCriteria, NoulModel, ChoiceModel, ScoreModel, QuestionModel, Question, Questions
Responses SystemOneResponse, Answer, NoulAnswer, ChoiceAnswer, ScoreAnswer, Usage, ListModelsResponse, ModelMetadata
JSON types JSONContent, JSONValue
Retry RetryPolicy
Errors TypeSafeError, TypeSafeAPIError, TypeSafeAPIConnectionError, TypeSafeAPITimeoutError, TypeSafeAPIResponseValidationError, TypeSafeAuthenticationError, TypeSafeBadRequestError, TypeSafeInternalServerError, TypeSafeNotFoundError, TypeSafePermissionDeniedError, TypeSafeRateLimitError, TypeSafeUnprocessableEntityError
Submodule constants

__version__ is also importable (from typesafe_sdk import __version__) although it is not listed in __all__; it is resolved at import time with importlib.metadata.version("typesafe-sdk").

Everything else lives under typesafe_sdk._core / typesafe_sdk._schemas and is private. __init__.py ends with del _core, so typesafe_sdk._core is not bound as an attribute of the package after import even though the submodule itself is importable.

Clients

Two clients, identical surface except for async/await and the transport types:

Sync Async
Class TypeSafeClient AsyncTypeSafeClient
Call client.system_one(...) await client.system_one(...)
Models resource client.modelsModels client.modelsAsyncModels
List models client.models.list() await client.models.list()
Close close() await aclose()
Context manager with ... as client async with ... as client
transport type httpx2.BaseTransport httpx2.AsyncBaseTransport
http_client type httpx2.Client httpx2.AsyncClient

Constructor

Both constructors are keyword-only (def __init__(self, *, ...)); there are no positional parameters.

TypeSafeClient(
    *,
    api_key: str | None = None,
    model: str | None = None,
    retry: RetryPolicy | None = None,
    timeout: float | httpx2.Timeout | None = None,
    headers: Mapping[str, str] | None = None,
    transport: httpx2.BaseTransport | None = None,
    http_client: httpx2.Client | None = None,
    base_url: str | None = None,
)
AsyncTypeSafeClient(
    *,
    api_key: str | None = None,
    model: str | None = None,
    retry: RetryPolicy | None = None,
    timeout: float | httpx2.Timeout | None = None,
    headers: Mapping[str, str] | None = None,
    transport: httpx2.AsyncBaseTransport | None = None,
    http_client: httpx2.AsyncClient | None = None,
    base_url: str | None = None,
)
Parameter Type Required Default Description
api_key str | None yes (or env) None API key. May be set via TYPESAFE_API_KEY. Missing key raises TypeSafeError.
model str | None no NoneTYPESAFE_DEFAULT_MODEL"jev-latest" Default model for every call from this client.
retry RetryPolicy | None no NoneRetryPolicy() defaults Retry behavior. RetryPolicy(max_retries=0) disables retries.
timeout float | httpx2.Timeout | None no Nonehttp_client.timeout if http_client given, else 10.0 (constants.DEFAULT_TIMEOUT) Timeout for each HTTP operation. Invalid values raise TypeSafeError.
headers Mapping[str, str] | None no None Additional default request headers.
transport httpx2.BaseTransport / httpx2.AsyncBaseTransport | None no None Custom transport, closed when the SDK client closes. Mutually exclusive with http_client.
http_client httpx2.Client / httpx2.AsyncClient | None no None Bring your own HTTP client. Closed when the SDK client closes. Mutually exclusive with transport.
base_url str | None no NoneTYPESAFE_BASE_URLhttps://api.typesafe.ai API root. Trailing / is stripped during resolution.

Raises:

Exception When
TypeSafeError No API key resolved, or timeout is not a positive finite number / httpx2.Timeout.
ValueError Both transport and http_client supplied ("transport and http_client are mutually exclusive.").

Resolution rules (from _core/config.py):

Attributes and lifecycle

Member Kind Type Notes
models cached_property Models / AsyncModels Built once per client instance.
system_one(...) method / async method SystemOneResponse See below.
close() / aclose() method / async method None Closes the underlying HTTP client, including one you supplied via http_client.
__enter__ / __exit__ context manager Sync client only.
__aenter__ / __aexit__ async context manager Async client only.

Context-manager usage is the documented default in every upstream example. Because close()/aclose() also close a user-supplied http_client, do not share one httpx2.Client across several TypeSafeClient instances whose lifetimes differ.

system_one()

system_one(
    state: JSONContent,
    questions: Mapping[str, Question],
    *,
    model: str | None = None,
    retry: RetryPolicy | None = None,
    timeout: float | httpx2.Timeout | None = None,
    extra_headers: Mapping[str, str] | None = None,
    extra_body: Mapping[str, JSONValue | None] | None = None,
) -> SystemOneResponse

The async version has the same signature and is async def, returning SystemOneResponse when awaited.

Parameter Type Positional? Default Description
state JSONContent (str | Mapping[str, JSONValue | None] | Sequence[JSONValue | None]) yes (1st) Text, a JSON object, or an array to evaluate. Cannot be None; values inside an object may be None.
questions Mapping[str, Question] yes (2nd) Nonempty mapping of your names → question objects or raw dicts.
model str | None keyword-only None Per-call model override; None inherits the client default.
retry RetryPolicy | None keyword-only None Per-call retry policy, replacing the client-level one for this call.
timeout float | httpx2.Timeout | None keyword-only None Per-call HTTP timeout in seconds; None inherits the client value.
extra_headers Mapping[str, str] | None keyword-only None Extra request headers for this call.
extra_body Mapping[str, JSONValue | None] | None keyword-only None Extra top-level body fields, shallow-merged over the body after state, model, questions are set. Last write wins; object values are replaced, not deep-merged.

Returns SystemOneResponse — see Python SDK responses, answers, usage, models.

Raises:

Exception When
TypeSafeError questions is empty; a Score question's criteria is empty; a dict question lacks a nonempty string "type"; a "choice"/"score" dict question has no "criteria" key; or the body cannot be JSON-encoded.
TypeSafeAPIError (and subclasses) The server returned an unsuccessful HTTP status after any retries.
TypeSafeAPIConnectionError / TypeSafeAPITimeoutError The request could not connect, or timed out, after any retries.
TypeSafeAPIResponseValidationError A 2xx response whose body was missing or structurally invalid.

The docs' "Raises" block for system_one lists only the empty-questions and empty-score-criteria cases; _core/questions.py additionally raises TypeSafeError for a malformed question dictionary and for a choice/score dict with no "criteria" key, and _core/transport.py raises it when the body cannot be encoded as JSON.

Wire request built

_core/endpoints.py:prepare_system_one sends POST {base_url}/v1/systemone with the body:

{"state": ..., "model": "...", "questions": {...}}

model is always present (client default when the per-call override is None), then extra_body is applied with body.update(extra_body). See HTTP API: POST /v1/systemone and GET /v1/models for the wire contract.

Headers the SDK sets

Set on every request from _core/transport.py:prepare (user headers/extra_headers are merged first, then these overwrite them — so authentication, Accept, and SDK identification cannot be overridden):

Header Value
Authorization Bearer {api_key}
Accept application/json
User-Agent typesafe-sdk/{__version__}
X-TypeSafe-SDK typesafe-sdk/{__version__}
X-TypeSafe-Runtime python/{platform.python_version()} ({sys.platform}; {platform.machine()})
Content-Type application/json (only when a body is sent)
X-TypeSafe-Retry-Count attempt number, added only on retries; any caller-supplied value is dropped first

The response header x-typesafe-request-id is surfaced as response.request_id and error.request_id.

Environment variables

Variable Configures Default Constant
TYPESAFE_API_KEY API key (required) constants.API_KEY_ENV
TYPESAFE_BASE_URL API root URL https://api.typesafe.ai constants.BASE_URL_ENV / DEFAULT_BASE_URL
TYPESAFE_DEFAULT_MODEL Default model jev-latest constants.DEFAULT_MODEL_ENV / DEFAULT_MODEL
TYPESAFE_LOG_LEVEL typesafe_sdk logger level, applied once at import unset constants.LOG_LEVEL_ENV

See TYPESAFE_* environment variables across SDKs and Python SDK retries, exceptions, constants for the constants module in full.

models resource

client.models is a cached property returning Models (sync) or AsyncModels (async). It has one method:

list(
    *,
    retry: RetryPolicy | None = None,
    timeout: float | httpx2.Timeout | None = None,
    extra_headers: Mapping[str, str] | None = None,
) -> ListModelsResponse
Parameter Type Default Description
retry RetryPolicy | None None Per-call retry override.
timeout float | httpx2.Timeout | None None Per-operation timeout override; None inherits the client setting.
extra_headers Mapping[str, str] | None None Extra headers; authentication, SDK identification, and Accept remain protected.

Issues GET {base_url}/v1/models. Returns ListModelsResponse whose .models is a tuple[ModelMetadata, ...] of name, description, release_date — see Python SDK responses, answers, usage, models and Models, aliases, pricing, rate limits, context.

from typesafe_sdk import TypeSafeClient

with TypeSafeClient() as client:
    for card in client.models.list().models:
        print(card.name, card.release_date, card.description)

Select a model when constructing a client:

client = TypeSafeClient(model="jev")

Note: "jev" is the upstream usage-guide sample verbatim (raw/docs/sdk__python__usage.md), but it is not among the names listed on Models, aliases, pricing, rate limits, context (jev-latest, jev-preview, jev-1.13.0). Prefer model="jev-latest" or an explicit versioned id, and confirm with client.models.list().

Logging

The SDK logs to the typesafe_sdk logger (logging.getLogger("typesafe_sdk")) and attaches a NullHandler plus a SensitiveHeadersFilter. It never configures handlers for you.

import logging

logging.getLogger("typesafe_sdk").setLevel(logging.DEBUG)

Or set TYPESAFE_LOG_LEVEL before importing the SDK; it is applied once at import.

Level string Effect
debug logging.DEBUG — also logs request and response headers and bodies
info logging.INFO — one summary line per request (METHOD url <- status in Nms (request <id>)) plus a line per retry
warn logging.WARNING (accepted by the source; not listed in the docs page)
warning logging.WARNING
error logging.ERROR
off logging.CRITICAL + 1

Redaction: header names in {authorization, proxy-authorization, x-api-key, api-key, cookie, set-cookie}, plus any header name containing token or secret (case-insensitive), are replaced with ***. Request and response bodies are not redacteddebug will print your state and the model's answers.

The SDK also logs Ignoring answer %r with unrecognized type %r at WARNING when the API returns an answer kind this version does not model.

Forward compatibility

Need Mechanism
Send a request field newer than the SDK extra_body={"beam_width": 4}
Send a question field newer than the SDK Pass the question as a plain dict: {"type": "noul", "instructions": "...", "weight": 2}
Read an answer kind newer than the SDK The SDK logs a warning, skips it, and you read result.raw_http_response.json()["answers"]
Unknown extra fields on known responses Silently ignored (forbid_unknown_fields=False)
from typesafe_sdk import Noul, TypeSafeClient

with TypeSafeClient() as client:
    client.system_one(
        "I was charged twice.",
        {"billing": Noul(instructions="About billing?")},
        extra_body={"beam_width": 4},
    )

Complete examples

Sync, verbatim style from the upstream quickstart:

from typesafe_sdk import Choice, Noul, Score, TypeSafeClient

with TypeSafeClient() as client:
    response = client.system_one(
        state={"document": "I was charged twice. Please fix this ASAP."},
        questions={
            "billing": Noul(instructions="Is this ticket about billing?"),
            "tone": Choice(
                instructions="What is the customer's tone?",
                criteria={"calm": None, "frustrated": None, "angry": None},
            ),
            "urgency": Score(
                instructions="How urgent is this ticket?",
                criteria=["can wait", "this week", "today"],
            ),
        },
    )

print(response.nouls["billing"].noul)
print(response.choices["tone"].choice)
print(response.scores["urgency"].score)

Async:

import asyncio

from typesafe_sdk import AsyncTypeSafeClient, Choice, Noul, Score


async def main() -> None:
    async with AsyncTypeSafeClient() as client:
        result = await client.system_one(
            "I was charged twice. Please help ASAP.",
            {
                "billing": Noul(instructions="Is this about billing?"),
                "tone": Choice(
                    instructions="What is the tone?",
                    criteria={"calm": None, "angry": None},
                ),
                "urgency": Score(
                    instructions="How urgent is this?",
                    criteria=["low", "medium", "high"],
                ),
            },
        )
        print(
            result.nouls["billing"].noul,
            result.choices["tone"].choice,
            result.scores["urgency"].score,
        )


asyncio.run(main())

Per-call overrides plus error handling:

from typesafe_sdk import Noul, RetryPolicy, TypeSafeAPIError, TypeSafeClient

with TypeSafeClient(model="jev") as client:
    try:
        result = client.system_one(
            "I was charged twice.",
            {"billing": Noul(instructions="Is this about billing?")},
            model="jev-latest",
            retry=RetryPolicy(max_retries=3, backoff_max=0.2, timeout=1.0),
            timeout=5.0,
            extra_headers={"X-Request-Source": "support-bot"},
        )
    except TypeSafeAPIError as error:
        print(error.status, error.request_id)
    else:
        print(result.model, result.usage.input_tokens, result.request_id)

When to use / when not to use

Version notes

Related

Sources