Typesafe / Jev with Ax (TypeScript)
Use Typesafe Jev models with @ax-llm/ax in TypeScript. Covers images, box outputs, and boolean/class/rubric score signatures with decision evidence, value descriptions, provider trueThreshold, native Noul/Choice/Score questions, structured criteria, probability-based decisions, and explicit hybrid generation.
Install
Install only this skill for TypeScript:
npx skills add https://ax-llm.github.io/ax/typescript/ --skill 'ax-typesafe'Published skill file: ax-typesafe/SKILL.md.
Source
- Source: src/ax/skills/ax-typesafe.md
- Version:
25.4.7
Skill Instructions
Typesafe / Jev with Ax (TypeScript)
Use name: 'typesafe' for the Typesafe System One API. Jev is the model family;
jev-latest is the default model. Use the Ax exports below when integrating with
Ax. Python, Java, C++, Go, and Rust also support the adapter and native client.
Use their generated ax-<language>-typesafe skills for native syntax and types.
Choose the Interface
| Need | Interface | Result |
|---|---|---|
| Required booleans and fixed classes | ai({ name: 'typesafe', ... }) with ax(...).forward() | Ordinary signature-shaped values |
| PNG/JPEG images and localization (TypeScript) | ax(...) with image inputs and box outputs | Coordinates, optional evidence |
| Probabilities, confidence, and rubric scoring | ax(...) with evidence fields | Typed evidence in normal program results |
| Advanced structured criteria/state | typesafe(...).systemOne(...) | Native request features |
| Decisions followed by prose or tool use | A Typesafe decision step followed by a generative provider | Explicitly composed program results |
A reply:string output requests freeform text and is unsupported by Typesafe.
An output team:class "support, billing" returns one of those two strings.
Numbers, even with min/max bounds, do not define native scoring rubrics.
Decision evidence and reranking
Use signature fields to access decision metadata with Jev, OpenAI Decisions, or a generative provider:
query:string, document:string ->
relevant:boolean(evidence, threshold 0.7),
category:class "support, billing"(evidence),
relevance:score "Irrelevant, Related, Partial answer, Direct answer"(evidence)Boolean evidence contains value, probability, threshold, and provenance.
Class evidence contains value, a complete label-to-probability map, and
provenance. Score evidence contains a continuous value from zero to the last
rubric index, ordered levels, ordered probabilities, and provenance.
Native class and score results can also contain the provider’s confidence.
A score without (evidence) returns only its numeric value. Existing scalar
boolean and class fields keep their existing behavior.
Ax preserves a native provider’s selected class, continuous score, probabilities, and confidence. A native value can differ from the probability argmax or weighted mean. With generative models, Ax requests probabilities, derives the boolean or class locally, and calculates a score as the probability-weighted rubric index. It preserves the supplied probabilities and never invents confidence.
provenance.source distinguishes native from elicited; provider and, when
available, model describe the actual response. These fields come from Ax, not
generated output. The common result shape does not make probabilities or
confidence equally calibrated across providers or models. Evaluate thresholds
and ranking quality on your own data. A score’s meaning depends on its declared
rubric; it is not a probability of correctness.
For reranking, evaluate each query/document pair, retain the candidate ID in your
application, and sort by relevance.value within the same rubric and model.
Ordinary Ax processors, assertions, result selection, traces, and caches receive
the decoded result. Streaming waits for complete evidence before emitting it.
const judge = ax(f().input('query', f.string()).input('document', f.string())
.output('relevance', f.score(['Irrelevant', 'Related', 'Partial answer', 'Direct answer']).withEvidence())
.build());
const result = await judge.forward(model, { query, document });
console.log(result.relevance.value, result.relevance.probabilities);Use boolean(evidence, refusal return) (or an evidence field with refusal return)
to receive {status: "answered", ...evidence} or {status: "refused", provenance}.
By default, a refusal raises AxDecisionRefusalError and stops correction retries.
Generative providers support nested, optional, and array evidence fields. Native Jev and OpenAI Decisions signatures require flat, required scalar fields; Ax rejects unsupported shapes before transport. For advanced structured criteria or native request features, the direct provider client remains available. Label-only examples are accepted without manufactured distributions. Examples containing actual evidence preserve it while omitting runtime-owned result metadata from the inference demonstration.
Boolean and Class Signatures
import { ai, ax, f } from '@ax-llm/ax';
const apiKey = process.env.TYPESAFE_API_KEY ?? process.env.TYPESAFE_APIKEY;
if (!apiKey) throw new Error('Set a Typesafe API key.');
const model = ai({ name: 'typesafe', apiKey, trueThreshold: 0.9 });
const triage = ax(`
ticket:string ->
urgent:boolean(
true "Customers cannot complete a core task",
false "A routine request or minor inconvenience"
) "Does this need immediate attention?",
team:class "support, billing, engineering"(
support "Product usage questions",
billing "Individual invoice or charge disputes",
engineering "Broken functionality or service outages"
) "Which team should investigate?"
`);
const decision = await triage.forward(model, {
ticket: 'Checkout fails for every customer. Payments cannot complete.',
});
// decision.urgent: boolean
// decision.team: 'support' | 'billing' | 'engineering'
trueThreshold defaults to 0.5 and must be finite in [0, 1]. A boolean is
true when noul >= trueThreshold, including equality. This setting applies to
every boolean output on that provider instance. It is local conversion policy
and is never sent to Typesafe. Choice returns the selected label without an
automatic confidence cutoff or abstention.
Field names and descriptions become question instructions. Boolean value
descriptions become criteria.true/criteria.false; class descriptions become
criteria keyed by the exact class labels. Descriptions may be partial; class
labels without descriptions map to null. Duplicate/unknown keys and empty
descriptions fail validation. Quote class labels containing spaces or punctuation.
The fluent equivalent is f.boolean(...).describeValues({ true: '...', false: '...' })
or f.class([...], ...).describeValues({ label: '...' }).
The same signature works with conventional providers: Ax renders the base
description plus value: description lines in the prompt and JSON schema.
Annotations survive serialization and do not change inferred types or allowed
values. Use the signature skill
for the complete syntax. Declare ordered label rubrics with score fields.
Structured JSON criteria and custom numeric rubric entries use native requests
below, rather than encoded JSON inside a quoted description.
The adapter sends the normal Ax prompt as native state and batches the outputs
in one request. Required boolean/class/rubric score/box outputs are supported; optional, array,
nested, ordinary numeric, and freeform outputs fail before network access. PNG/JPEG image inputs are supported; audio, files, tools,
multiple completion samples, and generation controls such as temperature or
maxTokens are unsupported. There is no token streaming; Ax’s
streamingForward() can deliver a completed result.
Image inputs and Box outputs
const vision = ai({
name: 'typesafe', apiKey,
config: { model: 'jev-1.14.0' },
});
const locate = ax(`
photo:image ->
visible:boolean "Is the red square visible in photo?",
target:box(evidence) "The red square in photo"
`);
const result = await locate.forward(vision, {
photo: { mimeType: 'image/png', data: imageBase64 },
});
// result.target.value: { top, bottom, left, right }, each in [0, 1]
// result.target.confidence: native confidence, when supplied
// result.target.provenance.source: 'native'
target:box returns just the four coordinates. The fluent equivalents are
f.box(description) and f.box(description).withEvidence(). Coordinates are
fractions measured from the image’s top-left; multiply left/right by width and
top/bottom by height to get pixels. Ax rejects non-finite, out-of-range, inverted,
and zero-area boxes. It never clamps coordinates. Box always returns a region,
even when the target is absent: ask a presence boolean alongside it and use that
to decide whether to consume the box.
Jev images and Box are experimental. Ax sends the required
TypeSafe-Danger-Zone-Images: true header for image/Box requests. Box needs Jev
1.14 (jev-1.14.0 or jev-preview); the ordinary default remains jev-latest.
Send still PNG/JPEG images as base64. Ax maps image and image[] inputs to
referenced image assets, retaining their prompt order. It accepts raw base64 or
matching image data URLs, never hosted URLs. Jev enforces image dimensions:
each side 256–1568 pixels, total area at least 78,400 pixels. A 256×256 image is
too small. Limits also include 16 MiB per decoded image and 32 MiB per request.
Prepare image orientation and dimensions before calling; Ax does not resize.
The native client accepts assets: { [reference]: { type: 'image', format: 'png' | 'jpg' | 'jpeg', data_base64: string } }. Use each reference in state,
question instructions, or criteria. References match substrings in string values,
so use unique long references, preferably UUIDs. Box questions use
{ type: 'box', instructions: 'The target in photo' }, with no criteria, and
return { type: 'box', box: { top, bottom, left, right }, confidence }.
Multipart uploads are not supported by this client.
Generative vision providers use the same box signature through a validated
JSON schema. Their evidence has provenance.source: 'elicited' and no invented
confidence. OpenAI Decisions already supports inline image inputs for its
boolean/class/score questions, but has no documented native Box primitive;
Ax rejects Box outputs on that endpoint before transport. Use a generative
OpenAI vision model for Box instead.
Image and Box signatures are available in TypeScript, Python, Java, C++, Go, and
Rust. Runnable image localization examples live under each language’s
src/examples/<language>/generation/ directory. See the upstream
Images and
Box contracts.
Native Questions and Application Decisions
import { typesafe } from '@ax-llm/ax';
const apiKey = process.env.TYPESAFE_API_KEY ?? process.env.TYPESAFE_APIKEY;
if (!apiKey) throw new Error('Set a Typesafe API key.');
const client = typesafe({ apiKey });
const result = await client.systemOne({
state: { ticket: 'Checkout fails for every customer.', recentEvents: [] },
questions: {
urgent: {
type: 'noul',
instructions: 'Does this require immediate incident response?',
criteria: {
true: { description: 'Core task blocked', examples: ['Cannot pay'] },
false: 'Routine request or minor inconvenience',
},
},
team: {
type: 'choice',
instructions: 'Which team should investigate?',
criteria: {
support: 'Product usage questions',
engineering: 'Broken functionality or service outages',
},
},
severity: {
type: 'score',
instructions: 'How severe is the customer impact?',
criteria: ['Minor inconvenience', 'Feature impaired', 'Core task blocked'],
},
},
});
const escalate = result.answers.urgent.noul >= 0.9;
const team = result.answers.team.choice; // 'support' | 'engineering'
const severity = result.answers.severity.score; // May be fractional, from 0 to 2.
const severityOnTen = (severity / 2) * 10; // Explicit application scale.
const models = await client.listModels();Keep inline questions or use as const satisfies AxAITypesafeQuestions for
reusable definitions so question keys and Choice labels retain their literal
types. Noul returns noul in [0, 1] and no separate confidence field. The
adapter’s trueThreshold does not apply to the native client. Choice returns
choice, probabilities, and confidence; Score returns score,
probabilities, legend, and confidence. The response also includes model
and usage.input_tokens/usage.output_tokens.
Choice and Score probabilities must be finite values in [0, 1], match the
criteria keys, and sum to one within an inclusive 0.01 tolerance. Totals of 0.99
and 1.01 are accepted with an allowance for floating-point summation error.
Ax preserves the returned probabilities without renormalizing them.
Native state, instructions, and individual criteria accept text, JSON objects,
arrays, or null. Nested JSON may contain finite numbers and booleans; a bare
number or boolean is not a top-level entry. Instructions are optional in the
Ax/native types, but provide an explicit question: native question keys identify
answers and are not themselves instructions. Choice needs 1–255 labels; Score
needs 2–10 ordered levels. null leaves an outcome without a description.
For Jev question design:
- Phrase Noul as one clear yes/no judgment. A value near
0.5expresses uncertainty about that judgment; use Score to measure a degree or severity. - Give similar Choice labels distinct descriptions. Add a catch-all label only when it belongs in the application’s allowed output set.
- Define one dimension per Score rubric. Preserve its fractional, zero-based position; choose rounding, normalization, and weighting explicitly in code.
- Evaluate independent questions together. A question cannot consume another answer from the same request; use a later call when that dependency exists.
- Test wording and decision thresholds against representative labeled inputs. Confidence describes the distribution and does not establish correctness.
These practices follow the provider’s Noul, Choice, and Score guidance. The official API types describe native entries and response fields; Ax’s transport configuration uses the settings below.
Models, Transport, and Diagnostics
| Setting | Signature provider | Native client |
|---|---|---|
| Default model override | config: { model: 'jev-latest' } | model: 'jev-latest' |
| Per-call model override | Third forward() argument: { model } | systemOne({ state, questions, model }) |
| Endpoint | apiURL (default https://api.typesafe.ai) | Same |
| Authentication | apiKey or credentialProvider | Same; optional static headers |
| Transport | options.fetch, timeout, retry, abortSignal | Same; methods accept a second transport-options argument, or the first for listModels() |
listModels() returns native model cards with name, description, and
release_date; it does not change Ax’s configured model aliases. Renewable
credential callbacks receive { profile, operation, method, url } on each
attempt: inference uses operation chat, discovery uses models. Native
instance and per-call cancellation signals both apply.
Context limits apply to the combined state, instructions, and criteria. Consult the provider’s model documentation when choosing input size. Ax neither silently truncates input nor claims an exact local token count. Token usage is returned; Ax does not assume token prices for these models.
For adapter calls, use program.getUsage() and program.getChatLog().
Chat log entries retain providerMetadata.typesafe.answers; direct .chat()
responses retain the same metadata. Native calls return usage and full answers
directly. No separate generic metadata getter or alternate forward result is needed.
Routing and Hybrid Workflows
Typesafe-only balancers preserve schema-required generation for scalar signatures. Mixed pools retain their normal prompting; Typesafe is eligible only when the actual request already includes a supported output schema. Incompatible requests are excluded from selection and fallbacks even when degradation is allowed. A bare chat request without a supported schema fails.
For prose, run a second ax() program with a generative provider and pass the
ticket plus the decision fields to it. Ax does not split a mixed
urgent:boolean, reply:string signature automatically. For agents that need
tools or code generation, use a generative actor and call Typesafe as a separate
decision step.
Runnable Examples
Run from the repository root; the tsx command loads .env:
npm run tsx src/examples/typescript/generation/typesafe.ts
npm run tsx src/examples/typescript/generation/typesafe-native.ts
npm run tsx src/examples/typescript/generation/typesafe-hybrid.ts
npm run tsx src/examples/typescript/generation/value-descriptions.tsThe first two use TYPESAFE_API_KEY or TYPESAFE_APIKEY. The last two also use
OPENAI_API_KEY or OPENAI_APIKEY. The
comparison example
uses the same described signature with both providers. The
native example
covers all primitives and model discovery; the
hybrid example
shows the two-program composition. Assert response contracts in live checks,
not exact model probabilities.