Migrating from /preview/evaluation to /v1/systemone
TL;DR
POST /preview/evaluationis replaced byPOST /v1/systemone. Auth is unchanged.document→state, thepromptsarray → aquestionsmap,options/levels→ a unifiedcriteriaper type, theresponsesarray → ananswersmap, and the answer value fields are renamed (probability→noul,chosen→choice,expectation→score).confidenceis computed differently, so re-tune any thresholds.
This is a breaking change: the endpoint, the request shape, and the response shape all changed (raw/docs/migrating-to-v1.md). Auth is unchanged — keep sending Authorization: Bearer <API_KEY> and Content-Type: application/json.
At a glance
| Area | Preview | v1 |
|---|---|---|
| Endpoint | POST /preview/evaluation |
POST /v1/systemone |
| Questions | prompts array (each carries a key) |
questions map (the key is the id) |
| Descriptors | per-type: criteria, options, levels |
unified criteria per type |
| Answers | responses array (same order) |
answers map (keyed by your id) |
| noul value | probability |
noul |
| choice value | chosen |
choice |
| score value | expectation |
score |
choice probabilities |
array of { option, probability } |
map of option → probability |
score probabilities |
— (not returned) | map over levels (new in v1) |
| Confidence | old computation | new computation |
| Usage | usage.billing_units placeholder |
usage.input_tokens / usage.output_tokens |
| Input field | document |
state |
| Python SDK | typesafe-client |
typesafe-sdk (new package) |
(Table reproduced from raw/docs/migrating-to-v1.md.)
1. Endpoint rename
evaluation became systemone.
- POST https://api.typesafe.ai/preview/evaluation
+ POST https://api.typesafe.ai/v1/systemone
2. prompts array becomes a questions map
In preview, you sent a prompts array where each item carried its own key, and answers came back in the same order. In v1, you send a questions map: the map key is an id of your choice, and answers come back mapped to the same id.
// preview — POST /preview/evaluation
{
"model": "jev-latest",
"document": "Sample document",
"prompts": [
{ "key": "test_noul", "type": "noul", "instructions": "Test instructions" }
]
}
// v1 — POST /v1/systemone
{
"model": "jev-latest",
"state": "Sample document",
"questions": {
"test_noul": { "type": "noul", "instructions": "Test instructions" }
}
}
Migration consequence: any code that relied on positional ordering of responses must switch to key lookup. The keys are yours and are "not sent to the underlying model" (raw/docs/api.md).
3. Unified criteria per question type
Every question type now describes itself with a field named criteria. The shape differs by type:
| Type | Preview field | v1 criteria shape |
Notes (verbatim where quoted) |
|---|---|---|---|
| Noul | none | criteria?: { true?, false? } |
An optional object describing the two outcomes. "New feature in v1!" |
| Choice | options: array of { option, description? } |
criteria: { <option>: description } |
"A map of option labels to their descriptions; use null when an option needs no explanation." |
| Score | levels: array of { level, description } |
criteria: [description, ...] |
"A bare array of level descriptions; the level is the 0-indexed array position. ... You can no longer skip levels; it was an anti-pattern now addressed by design." |
Choice criteria
// preview
{
"key": "test_choice",
"type": "choice",
"instructions": "Test instructions",
"options": [
{
option: "Option A",
description: "Description A"
},
{
option: "Option B",
description: "Description B"
}
]
}
// v1 (within "questions")
"test_choice": {
"type": "choice",
"instructions": "Test instructions",
"criteria": {
"Option A": "Description A",
"Option B": "Description B"
}
}
Score criteria
// preview
{
"key": "test_score",
"type": "score",
"instructions": "Test instructions",
"levels": [
{ "level": 0, "description": "Poor" },
{ "level": 1, "description": "Good" },
{ "level": 2, "description": "Excellent" }
]
}
// v1 (within "questions")
"test_score": {
"type": "score",
"instructions": "Test instructions",
"criteria": ["Poor", "Good", "Excellent"]
}
4. responses array becomes an answers map
Preview returned a responses array in the same order as your prompts, with each item containing a key property. v1 returns an answers map keyed by the question id you chose.
// preview
{
"responses": [
{ "key": "test_noul", "type": "noul", "probability": 0.92 }
]
}
// v1
{
"answers": {
"test_noul": { "type": "noul", "noul": 0.92 }
}
}
The key property is gone from each answer — the map key carries it.
5. Renamed answer value fields
| Type | Preview field | v1 field |
|---|---|---|
| Noul | probability |
noul |
| Choice | chosen |
choice |
| Score | expectation |
score |
6. Choice probabilities becomes a map
A Choice answer's probabilities changed from an array of { option, probability } objects to a map of option → probability. Score answers now also include a probabilities map over their levels, which preview did not return at all.
// preview
"probabilities": [
{ "option": "billing", "probability": 0.08 },
{ "option": "technical", "probability": 0.85 }
]
// v1
"probabilities": { "billing": 0.08, "technical": 0.85 }
7. Confidence is a new computation
Verbatim: "The computation behind confidence changed to make the value reliable, so the value will differ from preview even for an identical evaluation. Any logic your integration uses based on confidence should be carefully re-evaluated."
v1 also returns the full probability distribution for both Choice and Score, so you can compute your own statistic. To reproduce preview's confidence, use a normalized-entropy computation (verbatim from raw/docs/migrating-to-v1.md):
import math
def normalized_entropy_confidence(probabilities: dict[str, float]) -> float:
"""Reproduce preview's confidence: 1 − normalized Shannon entropy of the
distribution. Returns 1.0 when all weight is on one outcome, 0.0 when the
distribution is perfectly uniform."""
ps = [p for p in probabilities.values() if p > 0]
n = len(probabilities)
if n <= 1:
return 1.0
entropy = -sum(p * math.log(p) for p in ps)
return 1.0 - entropy / math.log(n)
# choice: probabilities maps each option -> probability
# score: probabilities maps each level (string key) -> probability
answer = response.answers["department"]
my_confidence = normalized_entropy_confidence(answer.probabilities)
The source does not state what the new v1 computation is; see Confidence vs probability.
8. Usage reports token counts
Preview reported a placeholder usage.billing_units. v1 reports token counts instead: usage.input_tokens and usage.output_tokens.
- "usage": { "billing_units": 1 }
+ "usage": { "input_tokens": 312, "output_tokens": 48 }
Billing follows: input tokens are charged, output tokens are free. See Models, aliases, pricing, rate limits, context.
9. document becomes state
Verbatim: "The field that carries the content to evaluate is named state. Preview, and the first v1 releases, called it document. v1 now accepts only state; a request with document fails validation."
- "document": "Sample document",
+ "state": "Sample document",
The content itself is unchanged: a string, an object, or an array. See State: what you send Jev.
Note the three-stage history implied here: preview used document, the first v1 releases also used document, and current v1 accepts only state. A client pinned to an early v1 build still needs this change.
Full before/after example
Request
// preview — POST /preview/evaluation
{
"model": "jev-latest",
"document": "Hi, I've been trying to connect my Stripe account for 3 days and it keeps failing. I'm losing sales. Please help ASAP.",
"prompts": [
{
"key": "is_urgent",
"type": "noul",
"instructions": "Does this message convey urgency?"
},
{
"key": "department",
"type": "choice",
"instructions": "Which team should handle this?",
"options": [
{ "option": "billing", "description": "Payment/invoicing" },
{ "option": "technical", "description": "Bugs/integrations" },
{ "option": "sales", "description": "Pricing/upgrades" }
]
},
{
"key": "frustration",
"type": "score",
"instructions": "How frustrated is the customer?",
"levels": [
{ "level": 0, "description": "Calm, stating facts" },
{ "level": 1, "description": "Frustrated but civil" },
{ "level": 2, "description": "Very angry" }
]
}
]
}
// v1 — POST /v1/systemone
{
"model": "jev-latest",
"state": "Hi, I've been trying to connect my Stripe account for 3 days and it keeps failing. I'm losing sales. Please help ASAP.",
"questions": {
"is_urgent": {
"type": "noul",
"instructions": "Does this message convey urgency?"
},
"department": {
"type": "choice",
"instructions": "Which team should handle this?",
"criteria": {
"billing": "Payment/invoicing",
"technical": "Bugs/integrations",
"sales": "Pricing/upgrades"
}
},
"frustration": {
"type": "score",
"instructions": "How frustrated is the customer?",
"criteria": ["Calm, stating facts", "Frustrated but civil", "Very angry"]
}
}
}
Response
// preview response
{
"model": "jev-latest",
"responses": [
{ "key": "is_urgent", "type": "noul", "probability": 0.92 },
{
"key": "department",
"type": "choice",
"chosen": "technical",
"probabilities": [
{ "option": "billing", "probability": 0.08 },
{ "option": "technical", "probability": 0.85 },
{ "option": "sales", "probability": 0.07 }
],
"confidence": 0.82
},
{
"key": "frustration",
"type": "score",
"expectation": 1.6,
"confidence": 0.78
}
],
"usage": { "billing_units": 1 }
}
// v1 response
{
"model": "jev-latest",
"answers": {
"is_urgent": { "type": "noul", "noul": 0.92 },
"department": {
"type": "choice",
"choice": "technical",
"probabilities": { "billing": 0.08, "technical": 0.85, "sales": 0.07 },
"confidence": 0.82
},
"frustration": {
"type": "score",
"score": 1.6,
"legend": { "0": "Calm, stating facts", "1": "Frustrated but civil", "2": "Very angry" },
"probabilities": { "0": 0.05, "1": 0.3, "2": 0.65 },
"confidence": 0.78
}
},
"usage": { "input_tokens": 312, "output_tokens": 48 }
}
Note that v1 adds legend on Score answers, which preview did not return.
Python SDK
The v1 API is served by a new package, typesafe-sdk, installed from PyPI with pip install typesafe-sdk. Verbatim: "The previous typesafe-client package (every release, 0.1.x and 1.0.x) sends document and no longer works against the API." All of the API changes above apply, plus these client renames:
typesafe-client |
typesafe-sdk |
|---|---|
from typesafe_client import TypeSafeClient |
from typesafe_sdk import TypeSafeClient (and AsyncTypeSafeClient) |
client.evaluate(...) / client.evaluate_async(...) |
client.system_one(...) / await async_client.system_one(...) |
*Prompt dataclasses or *Question models |
Noul, Choice, Score from typesafe_sdk |
system_one(model, document, questions) |
system_one(state, questions); model is optional and defaults to jev-latest |
Choice question options |
Choice question criteria (map of option → description) |
Score question levels |
Score criteria (positional list of level descriptions) |
response[key], response.noul(key) / .choice(key) / .score(key) |
response.answers[key], or response.nouls[key] / .choices[key] / .scores[key] |
Noul .probability |
Noul .noul |
Choice .chosen |
Choice .choice |
Score .expectation |
Score .score |
(Table reproduced from raw/docs/migrating-to-v1.md.)
Migration checklist
- Change the URL to
https://api.typesafe.ai/v1/systemone. Auth headers stay the same. - Rename
document→state. - Convert
prompts[]to aquestionsmap, dropping each item'skeyinto the map key. - Convert Choice
options[]→criteriamap and Scorelevels[]→ orderedcriteriaarray; fill any skipped levels, since they are no longer expressible. - Optionally add Noul
criteria.true/criteria.false, new in v1. - Switch response reads from
responses[i]toanswers[key]. - Rename
probability→noul,chosen→choice,expectation→score. - Convert Choice
probabilitiesarray handling to map handling; start using Scoreprobabilitiesandlegend. - Re-tune every
confidencethreshold, or compute your own statistic fromprobabilities. - Replace
usage.billing_unitswithusage.input_tokens/usage.output_tokensin any cost accounting. - Python: uninstall
typesafe-client,pip install typesafe-sdk, and apply the rename table.
Related
- HTTP API: POST /v1/systemone and GET /v1/models — the v1 contract in full
- OpenAPI component schemas — v1 schemas field by field
- Python SDK: install, clients, system_one() — the
typesafe-sdkclient - Python SDK changelog — when each rename landed
- Confidence vs probability — what the new confidence means
- State: what you send Jev — allowed shapes for
state - Versions and timeline (models, SDKs, API, company) — API, model, and SDK versions over time
Sources
- raw/docs/migrating-to-v1.md (https://docs.typesafe.ai/migrating-to-v1)
- raw/docs/api.md (https://docs.typesafe.ai/api)