JEV Decisions (TypeSafe)
JEV is TypeSafe's decision model. You give it a piece of context (state) and a set of typed questions (questions), and a single call returns structured answers: every answer comes with a probability distribution instead of free-form text you have to parse yourself. It suits judgment and classification work such as ticket routing, intent detection, content moderation, and sentiment scoring.
Create a decision
POST https://ciyuanx.io/inference/jev/api/alpha/decisions
Note
This endpoint does not live under the /v1 compatibility layer, and its request body differs from Chat Completions, so you cannot call it with the OpenAI SDK — send a plain HTTP request as shown below. The alpha in the path means the API is still early and fields may change.
curl https://ciyuanx.io/inference/jev/api/alpha/decisions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_KEY" \
-d '{
"model": "jev",
"state": "Help! My payouts have been failing for 3 days.",
"questions": {
"is_urgent": {
"type": "noul",
"instructions": "Does this message convey urgency?",
"criteria": {
"true": "Explicitly time-sensitive",
"false": "No urgency expressed"
}
},
"department": {
"type": "choice",
"instructions": "Which team should handle this?",
"criteria": {
"billing": "Payments, invoicing, refunds",
"technical": "Bugs, outages, integrations",
"sales": "Pricing, upgrades, new accounts"
}
},
"frustration": {
"type": "score",
"instructions": "How frustrated is the customer?",
"criteria": ["Calm", "Frustrated", "Very angry"]
}
}
}'import requests
response = requests.post(
"https://ciyuanx.io/inference/jev/api/alpha/decisions",
headers={
"Content-Type": "application/json",
"Authorization": "Bearer YOUR_API_KEY",
},
json={
"model": "jev",
"state": "Help! My payouts have been failing for 3 days.",
"questions": {
"is_urgent": {
"type": "noul",
"instructions": "Does this message convey urgency?",
"criteria": {
"true": "Explicitly time-sensitive",
"false": "No urgency expressed",
},
},
"department": {
"type": "choice",
"instructions": "Which team should handle this?",
"criteria": {
"billing": "Payments, invoicing, refunds",
"technical": "Bugs, outages, integrations",
"sales": "Pricing, upgrades, new accounts",
},
},
"frustration": {
"type": "score",
"instructions": "How frustrated is the customer?",
"criteria": ["Calm", "Frustrated", "Very angry"],
},
},
},
)
answers = response.json()["answers"]
print(answers["department"]["choice"]) # billing
print(answers["frustration"]["score"]) # 1.04
print(answers["is_urgent"]["noul"]) # 0.95const response = await fetch(
"https://ciyuanx.io/inference/jev/api/alpha/decisions",
{
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: "Bearer YOUR_API_KEY",
},
body: JSON.stringify({
model: "jev",
state: "Help! My payouts have been failing for 3 days.",
questions: {
is_urgent: {
type: "noul",
instructions: "Does this message convey urgency?",
criteria: {
true: "Explicitly time-sensitive",
false: "No urgency expressed",
},
},
department: {
type: "choice",
instructions: "Which team should handle this?",
criteria: {
billing: "Payments, invoicing, refunds",
technical: "Bugs, outages, integrations",
sales: "Pricing, upgrades, new accounts",
},
},
frustration: {
type: "score",
instructions: "How frustrated is the customer?",
criteria: ["Calm", "Frustrated", "Very angry"],
},
},
}),
},
);
const { answers } = await response.json();
console.log(answers.department.choice); // billing
console.log(answers.frustration.score); // 1.04
console.log(answers.is_urgent.noul); // 0.95Request parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
model | string | Yes | Model ID; use jev. The response reports the exact version that served the request |
state | string | Yes | The context to judge, such as a user message, an email, or a conversation transcript |
questions | object | Yes | The set of questions. You choose the keys, and answers come back under those same keys |
Each question in questions has this shape:
| Field | Type | Required | Description |
|---|---|---|---|
type | string | Yes | Question type: noul, choice, or score |
instructions | string | Yes | Plain-language description of what this question asks |
criteria | object or array | Yes | The judging criteria; the shape depends on type |
Question types
noul — yes/no judgment
criteria uses the keys true and false to describe when the condition holds and when it does not.
"is_urgent": {
"type": "noul",
"instructions": "Does this message convey urgency?",
"criteria": {
"true": "Explicitly time-sensitive",
"false": "No urgency expressed"
}
}The answer returns a single noul field: a probability from 0 to 1 that the condition holds (0.95 means "almost certainly urgent"). This type returns no confidence — the probability itself carries the strength.
choice — single selection
criteria maps each option to what that option means, and the model picks one.
"department": {
"type": "choice",
"instructions": "Which team should handle this?",
"criteria": {
"billing": "Payments, invoicing, refunds",
"technical": "Bugs, outages, integrations",
"sales": "Pricing, upgrades, new accounts"
}
}The answer returns the selected choice, the probabilities for every option, and a confidence.
score — graded scoring
criteria is an ordered array where the index is the score: item 0 scores 0, item 1 scores 1, and so on.
"frustration": {
"type": "score",
"instructions": "How frustrated is the customer?",
"criteria": ["Calm", "Frustrated", "Very angry"]
}The answer returns a continuous score, a legend mapping indices back to the grade labels, the probabilities for each grade, and a confidence. The score is the probability-weighted expected value, so it usually falls between two grades: in the example, 0 × 0 + 1 × 0.96 + 2 × 0.04 = 1.04.
Response
{
"model": "typesafe/jev-1.13-20260917",
"answers": {
"department": {
"type": "choice",
"choice": "billing",
"probabilities": {
"technical": 0.11,
"sales": 0,
"billing": 0.89
},
"confidence": 0.83
},
"frustration": {
"type": "score",
"score": 1.04,
"legend": {
"0": "Calm",
"1": "Frustrated",
"2": "Very angry"
},
"probabilities": {
"0": 0,
"1": 0.96,
"2": 0.04
},
"confidence": 0.95
},
"is_urgent": {
"type": "noul",
"noul": 0.95
}
},
"usage": {
"input_tokens": 427,
"output_tokens": 73,
"cost": 0.000017934
},
"id": "gen-dec-1789948229-FmHnDQNsDfVnwWsYaUP1",
"provider": "TypeSafe"
}Response fields
Top-level fields:
| Field | Type | Description |
|---|---|---|
model | string | The model version that served the request, e.g. typesafe/jev-1.13-20260917 |
answers | object | The answers, keyed exactly like questions in the request |
usage | object | Token usage and cost for this call |
id | string | Unique request ID; include it when contacting support |
provider | string | The upstream provider, here TypeSafe |
Answer object fields, by type:
| Field | Present for | Type | Description |
|---|---|---|---|
type | all | string | Matches the type you asked with |
noul | noul | number | Probability from 0 to 1 that the condition holds |
choice | choice | string | The selected option key |
score | score | number | Probability-weighted score, continuous |
legend | score | object | Maps each index to its grade label |
probabilities | choice, score | object | Probability per option / grade, summing to roughly 1 |
confidence | choice, score | number | The model's confidence in the answer, 0 to 1; reported separately from probabilities and not equal to the highest probability |
usage fields:
| Field | Type | Description |
|---|---|---|
input_tokens | integer | Input tokens (the state plus every question definition) |
output_tokens | integer | Output tokens |
cost | number | Cost of this call, in USD |
Best practices
Put the judgment in the criteria
- Let
instructionssay what to judge andcriteriasay where each outcome begins and ends — sharpening the boundaries beats lengthening theinstructions - Question keys become answer keys, so use meaningful names like
is_urgentordepartmentand read values straight out of the response
Route on the probabilities
- Don't look at
choicealone; checkconfidenceandprobabilitiestoo, and send anything below your threshold to a human scoreis continuous, so take the argmax ofprobabilitiesor round it yourself when you need a discrete grade
Batch your questions
- Put every question about the same
statein one request: thestateis sent and billed once, which costs fewer tokens than splitting it across calls
Tip
Answers come back keyed, and their order is not guaranteed to match the order you asked in (the example response above is reordered). Read by key rather than relying on order.