Python API reference
Status: draft. Success and 401/422 bodies have live evidence. The 429/529
bodies, resolved models other than jev-1.13.0, and untouched fields remain
inferred. See the evidence ledger.
The supported imports enumerate the root exports and policy facades. Generated Python reference in the site provides source signatures; this page records their semantics. Configuration, errors and policies describe the related contracts. The glossary distinguishes probability, confidence and score.
Question types
Question is Noul | Choice | Score. These are mutable local objects, not
service-side validation results. Their constructors do not validate every
vendor constraint. Typed objects and raw question mappings are accepted by the
HTTP adapters, including mixed mappings.
| Type | Constructor arguments | Meaning and source |
|---|---|---|
Noul |
instructions=None, criteria=None |
Yes/no question; optional criteria use true and false descriptions. Noul |
Choice |
required criteria, instructions=None |
Named alternatives mapped to descriptions. Choice |
Score |
required criteria, instructions=None |
Ordered rubric descriptions; positions start at zero. Score |
instructions accepts a string, dictionary, sequence or None in the Python
annotations. Choice descriptions accept those same forms; Score descriptions
accept strings, dictionaries or sequences. Noul criteria accepts a dictionary.
These annotations are not evidence that every possible value has been exercised
against the service. Prefer the simple text examples in the task guides.
Choice criteria values
Each value in a Choice criteria map takes one of four forms.
| Form | Meaning | Source | Evidence |
|---|---|---|---|
| string | A plain-text description of the option. | Choice | Verified. The live tests send only string values. |
| object | Structured guidance, such as what the option covers, what it does not cover and example inputs. | Structured instructions and criteria | Inferred from the page. No call has sent it. |
| array | An accepted form. The page gives no array example. | Choice | Inferred from the page. No call has sent it. |
None (null) |
The option needs no extra detail. | API reference | Inferred from the page. No call has sent it. |
The Choice page states that "each entry in criteria can be a string, an
object, or an array". The API reference types criteria as
map<string, string | object | array | null> and states "use null when an
option needs no extra detail".
Open question: the two pages disagree on null. The Choice page lists
three forms and omits null from that list, although one of its later request
examples sends null descriptions. The API reference lists null as a fourth
form. No call from this repository has sent null, so the service behaviour is
unknown.
Choice copies its criteria into a dictionary; Score copies its criteria into a
list. These are shallow copies. Noul keeps its supplied criteria reference.
The question source defines the exact
Python types. The outbound adapter adds the wire type and omits optional
None instructions/criteria when converting typed questions.
Adapters and ports
HTTPSystemOneAdapter and AsyncHTTPSystemOneAdapter implement the synchronous
and asynchronous calls respectively. Their constructors accept api_key,
base_url, default_model, transport, timeout_seconds, retry, network,
gateway, redactor, spend_cap, audit and fingerprint_key; see the
defaults and validation table.
Both send the documented request to
POST /v1/systemone with bearer authentication by default. Explicit
gateway configuration
selects alternate authentication, path prefixes and metadata. Optional
state redaction transforms a
private copy before serialization and reuses the resulting bytes across retries.
An audit sink receives one JudgmentRecord per logical call. Its
state_fingerprint field holds a keyed HMAC-SHA-256 of the state before
redaction when fingerprint_key is set, and None otherwise; see
audit records.
JsonlAuditSink appends each record to a local file as one JSON line; see
JSONL sink.
| Call argument | Python contract |
|---|---|
state |
Required string, dictionary or list containing the content to judge. |
questions |
Required mapping from caller-chosen names to typed questions or raw mappings. |
model |
Optional string on the concrete adapters; omitted/empty uses the constructor default. |
metadata |
Keyword-only RequestMetadata on concrete HTTP adapters; overrides gateway default headers. |
system_one returns SystemOneResponse; the async call must be awaited. By default, one
call produces one HTTP request. Optional retry limits
permit bounded additional attempts. The adapters parse
wire answers into domain types. Successful parsing does not validate the
judgment's quality or guarantee an answer for every supplied name. See
error boundaries.
Use with or close() for sync ownership and async with or awaited aclose()
for async ownership. The async adapter has no synchronous close/context-manager
API. The caller owns construction and cleanup, including after a failed call.
See the sync and async
recipes.
SystemOnePort and AsyncSystemOnePort are structural protocols, not client
factories. They declare the same state and question inputs and typed return,
but require the model argument. Only the concrete adapters offer a default
for that call argument. Transport-specific metadata is also limited to the
concrete HTTP adapters. The async port declares an async method. Neither port
requires lifecycle methods; ownership belongs to the code that creates the
concrete adapter. See port signatures.
Offline fakes
judgevet.testing provides FakeSystemOnePort for SystemOnePort and
AsyncFakeSystemOnePort for AsyncSystemOnePort. They ship in the installed
package without an extra and import only the domain, the ports and the
request-id binding in judgevet.diagnostics. Each takes seed: int = 0 and
answers: Mapping[str, Answer] | None, and records each call's
(state, questions, model) in calls. A scripted name returns its answer.
Other typed questions receive seeded answers that pass the answer
constructors; an unscripted raw mapping raises TypeError. The response echoes
the model argument and carries the scripted usage, default Usage(). A
scripted error is raised after the call is recorded. The fakes accept the
same spend_cap, audit and fingerprint_key options as the HTTP adapters and honour them the
same way. Seeded answers are local test data, not service behaviour. See the
offline testing recipe and the
fake source.
The conformance extra adds pytest, anyio and the provider conformance kit in
judgevet.testing.conformance. A provider package subclasses
BaseProviderConformance in its own test suite and overrides the
provider_factory and failing_port fixtures, plus media_port when it
supports images. Importing the kit without pytest raises ImportError. See the
kit source.
Source: https://github.com/Alberto-Codes/judgevet/issues/241#issuecomment-5902293909.
An AsyncSystemOnePort provider subclasses BaseAsyncProviderConformance
from the same module. It overrides the same two fixtures, and its
provider_factory returns an async context manager. It runs the shape,
typed-answer and failure rules.
Source: https://github.com/Alberto-Codes/judgevet/issues/246.
It also runs the three scope rules through async_provider_scope.
Source: https://github.com/Alberto-Codes/judgevet/issues/251.
It also runs the three media rules through async_judge_with_images.
Its optional media_port fixture supplies an AsyncMediaSystemOnePort.
Source: https://github.com/Alberto-Codes/judgevet/issues/252.
The synchronous provider_factory fixture returns a ProviderFactory from
judgevet.providers. The async fixture returns an AsyncProviderFactory from
judgevet.testing.conformance.
Source: https://github.com/Alberto-Codes/judgevet/issues/277.
That name is the AsyncProviderFactory Protocol from judgevet.providers.
Source: https://github.com/Alberto-Codes/judgevet/issues/251.
Answer and container types
Answer is NoulAnswer | ChoiceAnswer | ScoreAnswer.
| Type | Required fields | Semantics |
|---|---|---|
NoulAnswer |
noul: float |
Probability of true; no confidence field. Noul source |
ChoiceAnswer |
choice: str, confidence: float, probabilities: dict[str, float] |
Selected label, confidence and distribution over labels. The vendor defines confidence as a number from 0 to 1 computed from how probabilities is spread. It is not probabilities[choice]. See Choice confidence. Source: https://docs.typesafe.ai/primitives/choice. Source: https://docs.typesafe.ai/confidence. |
ScoreAnswer |
score: float, confidence: float, legend: dict[int, str], probabilities: dict[int, float] |
Continuous expected score, confidence and rubric/distribution indexed by level. Score source |
Usage |
None required; input_tokens=None, output_tokens=None |
Optional nonnegative integer token counts; booleans are rejected. Wire fields |
SystemOneResponse |
model: str, usage: Usage; answers defaults to a new empty dictionary; receipts defaults to an empty mapping |
Answer mapping keyed by question name. The observed service model is the resolved version, not necessarily the requested alias. Wire envelope. receipts maps an answer name to provider measurements of type float, bool or None. The HTTP adapters and the fakes leave it empty. See src/judgevet/domain/response.py. Source: https://github.com/Alberto-Codes/judgevet/issues/281. |
Token counts are usage metadata, not a price quote. This client reference makes no current pricing or retention claim.
Answer constructors require finite integer or float values for Noul, Score,
confidence and each probability. Booleans and nonnumeric values raise TypeError.
NaN, either infinity and out-of-range values raise ValueError. Constructors
retain inclusive probability/confidence bounds of [0,1], choice membership,
matching Score legend/distribution keys, inclusive score range and the
probability-sum tolerance of 0.005 per probability. They do not enforce argmax selection or
expected-score equality. Public policy evaluation also checks values when it
consumes an answer; see policy validation.
The answer source is authoritative for
constructor behavior. Do not infer service guarantees from local checks.
Both HTTP adapters and both offline fakes check each Choice answer against the
question the caller sent. The answer's choice and every probabilities key
must be a key of that question's criteria. Otherwise the call raises
JevResponseError with status 200, and the message names the question and the
option. probabilities may cover a subset of the criteria. The adapters run
the check when they parse the body, before the spend cap settles the attempt.
A raw question mapping is checked when its type is "choice" and its
criteria is a mapping. Other questions and unasked answer names are not
checked. The criteria keys are the Choice options. Source:
https://docs.typesafe.ai/primitives/choice. No live call has returned an
off-list option, so the check guards an inferred failure.
Wire precision: one consumer call on 2026-09-24 against jev-1.13.0 returned a
four-level Score whose probabilities summed to 0.99
(issue #175). Two-decimal
rounding can move each probability by up to 0.005, so constructors accept a sum
within 0.005 × k of 1.0 for k probabilities. That call is the only
observation. This repository's live suite has not reproduced it, and it does not
establish a general rounding rule for the service.
Answer, Usage and response instances are frozen dataclasses. Frozen prevents
attribute reassignment, not mutation of nested dictionaries. answers,
probabilities and legend can remain mutable. The container properties
nouls, choices and scores each return a fresh shallow dictionary filtered
by answer type. Their values are the same answer objects. Missing names or
wrong-type names raise normal KeyError when indexed in a filtered mapping.
Choice confidence
The vendor Choice page defines the field: "A number from 0 to 1 computed from
how probabilities is spread. A flat shape, with probability spread across
several options, means low confidence. A single peak on one option means high
confidence." The same page defines choice as "The option with the highest
probability." Source: https://docs.typesafe.ai/primitives/choice.
The page publishes no formula. The vendor Confidence page calls the field "a
statistic computed from the probability distribution the answer already gives
you" and publishes no service formula either. Source:
https://docs.typesafe.ai/confidence. confidence is a spread measure. It is not
probabilities[choice], and callers must not read it as that probability.
The client checks only that confidence lies in [0, 1]. It checks no
relation between confidence and probabilities.
Two live runs recorded a Choice confidence. The 0.11.0 production smoke on
2026-09-25 returned confidence=0.89 for choice='a'. Source:
https://github.com/Alberto-Codes/judgevet/issues/186#issuecomment-5825514560.
That comment elides the probabilities. The issue that reports the run records
the full answer as
ChoiceAnswer(choice='a', confidence=0.89, probabilities={'b': 0.05, 'a': 0.95}).
Source: https://github.com/Alberto-Codes/judgevet/issues/187. The 0.10.2
production smoke on 2026-09-25 returned confidence=0.9 for choice='a' and
elides the probabilities. Source:
https://github.com/Alberto-Codes/judgevet/issues/183#issuecomment-5824552631.
The 0.89 value is consistent with the vendor definition. The 0.9 run recorded
no probabilities, so it cannot be compared.
A differential probe, tests/live/test_choice_confidence_live.py, ran once on
2026-09-30 with Choice questions of 2, 3 and 4 options on one duplicate-charge
state. Each call returned confidence=1.0 with the whole probability mass on
billing and the other options at 0.0, from jev-1.13.0. Maximum
probability, top-two margin and one minus normalised entropy all equal 1.0
there, so that run distinguishes no candidate formula.
Source: https://github.com/Alberto-Codes/judgevet/issues/193#issuecomment-5923966685.
A second run on 2026-09-30 used an ambiguous state. It returned confidence
0.99, 0.94 and 0.92 for 2, 3 and 4 options. The chosen-option probabilities
were 1.0, 0.97 and 0.94. Confidence sat below that probability in every soft
run. The vendor's demo approximation for three options is
(3 × largest probability − 1) / 2.
Source: https://docs.typesafe.ai/confidence.
Generalised to (n × largest − 1) / (n − 1), it matches four of the five
soft runs within 0.01 and misses the 3-option run by 0.015. The top-two
margin also matches four within 0.01 and misses the 4-option run by 0.03.
Source: https://github.com/Alberto-Codes/judgevet/issues/193#issuecomment-5924550851.
Open question: the formula that maps probabilities to confidence.
No source publishes it. Two differential runs narrowed the candidates to the
generalised demo approximation and the top-two margin; neither matches every
run on the two-decimal wire values.
Service limits
The TypeSafe models page states these limits. This section records that page as fetched on 2026-09-24. The values are vendor statements. Two judgevet calls on 2026-09-25 tested the context budget. Neither call established where the threshold lies.
The service enforces these limits. judgevet does not check them before a call. It counts no tokens, because the vendor documents no tokenizer. It does not throttle requests.
| Limit | Value the page states for jev-1.13.0 |
|---|---|
| Context length | "64k tokens per request; 32k tokens for state plus the longest question" |
| Rate limits | "250,000 tokens per second / 1,200 requests per minute" |
| Input | "Text only. String, JSON object, or array of text values. No image, audio, or video input." |
The page explains the two context budgets. The 64k budget "covers the state
plus all questions combined". The 32k budget "applies to the state plus the
single longest question".
Source: https://docs.typesafe.ai/models.md.
Size a request against both budgets. The state and the longest single
question share the 32k-token budget. The whole request shares the 64k-token
budget. Size the state against 32k tokens minus the longest question. The page
does not state how the other questions count toward the 64k budget beyond
"all questions combined".
A request over the budget fails. One call sent a 400,000-character state with
one noul question to jev-1.13.0. The service returned status 400 with the
body {"detail": {"error_type": "max_tokens_exceeded"}}.
Source: https://github.com/Alberto-Codes/judgevet/issues/39#issuecomment-5825759575.
A second call on 2026-09-30, through tests/live/test_max_tokens_live.py,
sent the same size and raised JevMaxTokensExceededError with
retryable is False and status_code == 400. In the same run, a
180,000-character state of repeated English words was answered. That run did
not read usage.input_tokens, so it does not show which budget binds.
Source: https://github.com/Alberto-Codes/judgevet/issues/192#issuecomment-5924025204.
A second run on 2026-09-30 measured the rate first: 60,000 characters of the
same text counted 10,798 input tokens. It then sent 222,263 characters, about
40,000 tokens, with one noul question. The service refused that request
with status 400 and the same body. So the 32k state-plus-question budget
fires on its own, under the 64k request budget. The answered
180,000-character state is about 32,394 tokens by that rate. So the boundary
lies between 32,394 and 40,000 tokens for this text. The rate is specific to
that repetitive English text.
Source: https://github.com/Alberto-Codes/judgevet/issues/192#issuecomment-5924550998.
The client raises JevMaxTokensExceededError, a subclass of JevRequestError
with retryable=False. The adapter matches the max_tokens_exceeded marker,
not the status. The body does not say which budget the request exceeded. That
request exceeded both budgets, so the call proves the marker, not the
threshold. See the error reference.
A second call sent a 2,959-byte JSON state and seven choice questions with up
to 16 options. The service returned 200 and reported 3,110 input tokens.
Source: https://github.com/Alberto-Codes/judgevet/issues/39#issuecomment-5825777868.
The page says a request over either rate limit "returns 429 Too Many
Requests". judgevet has never observed a 429 body. See the
evidence ledger.
The page warns that the rate limits change. Its warning reads "Rate limits are adjusting dynamically." It also says "the limits above can change without notice". Check the cited page before you size a workload.
The page names two aliases. Both point to jev-1.13.0 on the fetch date.
| Alias | Points to |
|---|---|
jev-latest |
jev-1.13.0 |
jev-preview |
jev-1.13.0 |
The page states that "an alias moves when a new release ships, so the answers behind it can change without a change on your side". Pin a versioned ID to keep one model.
On language, the page states that "English is the primary training language". It also states that other languages "are handled but not equally well".
Open question: the page does not say which status code a request over the context length returns. It also does not name the error body for that case.
Open question: the page does not say how the service counts tokens. A caller cannot compute the context budget locally before a call.
Examples
Basic Usage
These examples require TYPESAFE_API_KEY in the process environment. The
application reads it and passes it explicitly. Do not put the key in source
code. Both examples send the supplied state and questions to the service;
read the security policy first.
The answer values vary. The assertions check types and valid ranges, not
whether the model made a correct judgment.
import os
from judgevet import Choice, ChoiceAnswer, HTTPSystemOneAdapter, Noul, NoulAnswer
adapter = HTTPSystemOneAdapter(api_key=os.environ["TYPESAFE_API_KEY"])
try:
response = adapter.system_one(
state="I was charged twice for my plan",
questions={
"is_refund": Noul(instructions="Is this about a refund?"),
"queue": Choice(
criteria={
"billing": "Payments and refunds",
"technical": "Product faults",
},
instructions="Which queue should handle this message?",
),
},
)
finally:
adapter.close()
refund = response.answers["is_refund"]
queue = response.answers["queue"]
assert isinstance(refund, NoulAnswer)
assert isinstance(queue, ChoiceAnswer)
assert 0 <= refund.noul <= 1
assert queue.choice in {"billing", "technical"}
Context Manager
The context manager closes the adapter even when a call raises.
import os
from judgevet import HTTPSystemOneAdapter, Noul, NoulAnswer
with HTTPSystemOneAdapter(api_key=os.environ["TYPESAFE_API_KEY"]) as adapter:
response = adapter.system_one(
state="I was charged twice for my plan",
questions={"is_refund": Noul(instructions="Is this about a refund?")},
model="jev-1.13.0",
)
answer = response.answers["is_refund"]
assert isinstance(answer, NoulAnswer)
assert 0 <= answer.noul <= 1
The vendor documents the request and answer format and the Noul and Choice primitives. Adapter ownership and explicit key injection are judgevet behavior; see the adapter source.