TypeSafe
The TypeSafe provider supports the experimental decision
API with native Choice, Score, and Boolean answers. It sends all questions in one
request against the same state.
You can use TypeSafe models through AI Gateway (recommended) or connect directly with @ai-sdk/typesafe-ai.
import { experimental_decide } from 'ai';
const result = await experimental_decide({
model: 'typesafe-ai/jev',
state: 'I was charged twice. Please refund the duplicate.',
questions: {
requestsRefund: {
type: 'boolean',
instructions: 'Is the customer requesting money back?',
},
},
});
console.log(result.answers.requestsRefund.probability);Using AI Gateway
The AI SDK uses AI Gateway by default for model strings. Gateway provides access to models across providers through a single API key, with usage and cost tracking. Install ai:
pnpm add ainpm install aiyarn add aibun add aiSet an AI Gateway API key in your environment:
export AI_GATEWAY_API_KEY=your_api_key_hereVercel deployments can also use OIDC authentication. See the AI Gateway provider documentation for configuration and the model catalog for available models.
Gateway feature and provider-option support depends on the model and the provider serving the request. See Gateway provider options.
Using @ai-sdk/typesafe-ai
For direct access to TypeSafe, install ai and @ai-sdk/typesafe-ai:
pnpm add ai @ai-sdk/typesafe-ainpm install ai @ai-sdk/typesafe-aiyarn add ai @ai-sdk/typesafe-aibun add ai @ai-sdk/typesafe-aiSet your TypeSafe API credential in the TYPESAFE_AI_API_KEY environment variable:
export TYPESAFE_AI_API_KEY=your_api_key_hereUse the direct provider for a custom TypeSafe-compatible endpoint or direct TypeSafe authentication. Both examples use the experimental experimental_decide API.
Note
The following sections document the direct @ai-sdk/typesafe-ai provider,
including its model factories and settings. Gateway model and feature
availability can differ.
Import the default provider or configure an instance:
import { createTypeSafeAi, typeSafeAi } from '@ai-sdk/typesafe-ai';
const customTypeSafe = createTypeSafeAi({
apiKey: 'your-api-key',
});createTypeSafeAi accepts:
| Setting | Description |
|---|---|
apiKey | API key; defaults to TYPESAFE_AI_API_KEY. |
baseURL | Defaults to TYPESAFE_AI_BASE_URL, then https://api.typesafe.ai/v1. |
headers | Additional request headers. |
fetch | Custom fetch implementation. |
Use TYPESAFE_AI_BASE_URL to route the default typeSafeAi instance through a
trusted proxy or self-hosted TypeSafe-compatible server:
TYPESAFE_AI_BASE_URL=http://127.0.0.1:8766/v1An explicit baseURL passed to createTypeSafeAi takes precedence over the
environment variable. The configured server receives the API key and decision
state and must implement the TypeSafe POST /systemone API.
Decision
import { typeSafeAi } from '@ai-sdk/typesafe-ai';
import { experimental_decide } from 'ai';
const result = await experimental_decide({
model: typeSafeAi.decisionModel('jev-latest'),
state: {
message: 'I was charged twice. Please refund the duplicate.',
},
questions: {
department: {
type: 'choice',
instructions: 'Which team should handle this?',
criteria: {
billing: { includes: ['Charges', 'Invoices', 'Refunds'] },
technical: ['Bugs', 'Outages'],
other: null,
},
},
severity: {
type: 'score',
instructions: 'How severe is the issue?',
criteria: ['Cosmetic', 'Workaround exists', 'Blocking; no workaround'],
},
requestsRefund: {
type: 'boolean',
instructions: 'Is the customer requesting money back?',
},
},
});
console.log(result.answers.department.choice);
console.log(result.answers.severity.score);
console.log(result.answers.requestsRefund.probability);
console.log(result.usage);Use jev-latest or another TypeSafe model ID. The response includes the actual
model ID, such as the resolved version behind the alias. Use model instances,
custom aliases, or registered providers. With typeSafeAi explicitly configured
as globalThis.AI_SDK_DEFAULT_PROVIDER, you can pass model: 'jev-latest'
directly. See model aliases and registries.
The provider includes these model IDs and aliases:
| Model ID | Description |
|---|---|
jev-1.13.0 | Jev 1.13 release. |
jev-1.14.0 | Jev 1.14 release. |
jev-latest | Alias for the latest stable release. |
jev-preview | Alias for the latest release, including previews. |
| Question | Native TypeSafe primitive | Limits and semantics |
|---|---|---|
choice | Choice | 1–255 options; selected option and full probabilities. |
score | Score | 2–10 ordered rubric levels; fractional score and full probabilities. |
box | Box | Four normalized image edges; requires Jev 1.14 or later and an image. |
boolean | Noul | Required model-estimated probability of true. |
State, instructions, and descriptions accept JSON objects and arrays in addition
to strings. Descriptions may be null. Boolean true/false criteria are optional.
The SDK uses the neutral name boolean and maps it to TypeSafe's noul field.
Box questions
Box questions locate one target in an image and take no criteria:
questions: {
target: { type: 'box', instructions: 'The cat in the image' },
}Send an image in state as shown below. The answer has
type: 'box' and a box object with top, bottom, left, and right.
Each edge is a fraction in [0, 1], measured from the top-left corner;
left < right and top < bottom. Confidence is available at
result.providerMetadata?.typesafe?.confidence, keyed by question ID.
This feature is experimental, and a box is returned even when the object is absent. Ask a Boolean question alongside it to check visibility. See TypeSafe's Box documentation.
Long context
By default, Jev allows 64k tokens per request (state plus all questions) and 32k
tokens per question (state plus one question). Set longContext: true to raise
both limits to 262k tokens:
providerOptions: {
typesafe: {
longContext: true,
},
},The provider sends TypeSafe-Danger-Zone-Long-Context: true only when this
option is enabled. It defaults to false. Both Jev 1.13 and 1.14 support it.
TypeSafe charges the same per-token price for long context, but describes it
as slower and less accurate than staying within the default limits. Enable it
only when needed; the provider does not estimate token counts or enable it
automatically. See TypeSafe's context documentation.
The TypeSafeAiDecisionModelOptions type is exported from @ai-sdk/typesafe-ai
for typing request-level options.
Image input
Pass PNG or JPEG images as file parts alongside text or JSON state:
import { readFileSync } from 'node:fs';
const result = await experimental_decide({
model: typeSafeAi.decisionModel('jev-latest'),
state: [
{ type: 'text', text: 'Inspect the animal in this picture.' },
{
type: 'file',
mediaType: 'image/png',
data: readFileSync('cat.png'),
},
],
questions: {
cat: { type: 'boolean', instructions: 'Does the image show a cat?' },
},
});The provider uploads images as binary files in a multipart request and automatically adds
TypeSafe-Danger-Zone-Images: true when images are present. TypeSafe currently
considers image support experimental. Only PNG and JPEG are supported; other
file formats are rejected. See TypeSafe's image documentation
for image size and dimension limits.
Explicit references are optional. Set providerOptions.typesafe.reference on a
file part to choose its asset reference. When omitted, the provider generates a
random reference. If the state already contains the reference, the provider
sends the state as written, so JSON state stays an object. Otherwise it inserts
the reference at the file's position in the state.
TypeSafe sends another copy of the image for every occurrence of its reference in state, instructions, or criteria. To ask several questions about one image, put it in the state once and refer to it by path in backticks, as shown below, rather than repeating the reference in each question.
Set providerOptions.typesafe.insertReference to false to send an image only
where you reference it in a question's instructions or criteria, for example a
known picture to compare against. This requires an explicit reference. Set it
to true to insert the reference even when the state already contains it.
const result = await experimental_decide({
model: typeSafeAi.decisionModel('jev-latest'),
state: [
{ type: 'json', value: { photo: '[found-dog]' } },
{
type: 'file',
mediaType: 'image/jpeg',
data: readFileSync('found-dog.jpg'),
providerOptions: { typesafe: { reference: '[found-dog]' } },
},
{
type: 'file',
mediaType: 'image/jpeg',
data: readFileSync('lost-dog-poster.jpg'),
providerOptions: {
typesafe: { reference: '[poster]', insertReference: false },
},
},
],
questions: {
sameDog: {
type: 'boolean',
instructions: 'Is the dog in `photo` the same dog as in [poster]?',
},
},
});References must be nonempty, contain more than whitespace, and be unique across
images in the request. TypeSafe substitutes references anywhere in string
values, so choose distinctive names such as [animal-photo] to avoid accidental
matches. The file's filename does not affect its reference.
The TypeSafeAiFilePartProviderOptions type is exported from
@ai-sdk/typesafe-ai for typing file-level options.
Rounded values and confidence
TypeSafe returns scores and probabilities rounded to two decimal places.
result.rounding reports this precision so the SDK can account for rounding when
checking distribution sums and weighted means. The returned values are preserved
unchanged; rounding means probabilities may not add up to exactly one.
Confidence is a separate TypeSafe statistic, available under
result.providerMetadata.typesafe.confidence[questionId] for Choice and Score
answers. Boolean probability always means P(true), not confidence in either
outcome. Choose decision thresholds in application code.
Errors and retries
Provider failures use APICallError, including validation and authentication
errors. Core retries transient failures such as 429 and 529 according to
maxRetries (default: 2). abortSignal, request headers, and custom fetch are
forwarded. There is no provider-side retry loop.
Unknown request-level entries under providerOptions.typesafe produce
unsupported-option warnings. Language,
embedding, and image model factories throw NoSuchModelError.
Decision models support workflow serialization. A custom fetch function is not serialized; restored models use the default fetch implementation.