Last updated: September 2026

Jev Choice vs Score vs Noul

Every question you can ask Jev is one of three types. Choice picks one option out of up to 255. Score places the state on an ordered scale of 2 to 10 levels and returns a probability-weighted float. Noul answers a yes or no statement with a single float from 0 to 1.

Below: the exact request and response JSON for each, when to reach for which, and the gotchas. The examples are social media judgments, because that is the domain these pages cover. Every shape here matches TypeSafe's primitives docs.

The three at a glance

Type
Asks
criteria
Answer fields
Choice
Which one of these options?
A map of option key to description. Up to 255 options.
choice, probabilities, confidence
Score
Which level on this ordered scale?
An ordered array of 2 to 10 level descriptions.
score, legend, probabilities, confidence
Noul
Is this statement true?
Optional object with true and false keys.
noul only. No confidence field.

All three go to the same endpoint, POST https://api.typesafe.ai/v1/systemone, in one request body of { state, model, questions }. You can mix types freely inside questions.

Choice

What it is. One option out of a fixed set. You declare the set as criteria, a map of option key to description, and the answer comes back as one of your keys plus a probability for every option.

When to use it. Unordered categories. Routing a reply to a queue, tagging a post by topic, picking which of your accounts should answer. Reach for it whenever the answer is a name your code will switch on.

choice-request.json
{
  "state": "Been using this for three weeks and the scheduler silently dropped two posts. Anyone else seeing that?",
  "model": "jev-1.13.0",
  "questions": {
    "reply_intent": {
      "type": "choice",
      "instructions": "What does this reply want from the account owner",
      "criteria": {
        "bug_report": "Describes something that is broken or behaved unexpectedly",
        "question": "Asks for information or help",
        "praise": "Positive feedback with nothing requested",
        "promotion": "Promotes the replier's own product, link or account",
        "troll": "Insult or provocation with no substance"
      }
    }
  }
}
choice-response.json
{
  "model": "jev-1.13.0",
  "answers": {
    "reply_intent": {
      "type": "choice",
      "choice": "bug_report",
      "probabilities": {
        "bug_report": 0.871,
        "question": 0.104,
        "troll": 0.020,
        "praise": 0.003,
        "promotion": 0.002
      },
      "confidence": 0.744
    }
  },
  "usage": { "input_tokens": 96, "output_tokens": 12 }
}

Gotchas.

  • criteria is a map, not an array. There is no options field. If you copied an options: [...] example from a launch write-up, it will not validate.
  • Descriptions can be null. {"calm": null, "frustrated": null, "angry": null} is valid when the key says everything. Write a description the moment two options could overlap.
  • 255 options is the ceiling. Past that, do two stages: a Choice over groups, then a Choice inside the winning group.
  • probabilities covers every option and sums to 1.0. Read it. A top answer at 0.44 with the runner-up at 0.42 is a coin flip with a label on it.
  • The key is the contract. choice returns your key verbatim, so name keys the way your switch statement wants them.
  • confidence is derived. It is a statistic computed from the distribution you already have, not an independent second opinion.

Score

What it is. A position on an ordered scale. You declare the levels as criteria, an ordered array of 2 to 10 descriptions, and the answer is a float: the probability-weighted mean of the level indices.

When to use it. Anything with a rubric. Hook strength, clarity, how far a post is likely to travel, how angry a reply is. Also use it any time you want to rank a batch, because a float sorts and a category does not.

score-request.json
{
  "state": { "draft_post": "I deleted 90% of our code and revenue went up.", "platform": "X (Twitter)" },
  "model": "jev-1.13.0",
  "questions": {
    "hook": {
      "type": "score",
      "instructions": "How well does the opening line stop someone mid scroll?",
      "criteria": [
        "No hook. Opens with context, throat clearing, or a greeting.",
        "Functional. States the subject plainly without creating a reason to continue.",
        "Strong. Creates tension, a gap, or a surprise in the first line."
      ]
    }
  }
}
score-response.json
{
  "model": "jev-1.13.0",
  "answers": {
    "hook": {
      "type": "score",
      "score": 1.87,
      "legend": {
        "0": "No hook. Opens with context, throat clearing, or a greeting.",
        "1": "Functional. States the subject plainly without creating a reason to continue.",
        "2": "Strong. Creates tension, a gap, or a surprise in the first line."
      },
      "probabilities": { "0": 0.01, "1": 0.11, "2": 0.88 },
      "confidence": 0.86
    }
  },
  "usage": { "input_tokens": 84, "output_tokens": 6 }
}

The float is not a rounding of the top level. It is the weighted mean of the indices: 0 x 0.01 + 1 x 0.11 + 2 x 0.88 = 1.87. TypeSafe's own docs work the same sum on a different example.

Gotchas.

  • criteria is an ordered array, and there is no min or max. The scale runs from index 0 to index length minus 1. Write the levels in ascending order and phrase them so a higher index means more of the thing.
  • 2 levels minimum, 10 maximum. Three to five is the useful range. Ten levels that a human cannot tell apart produce a float that looks precise and is not.
  • A score is for ranking and thresholding, not measuring. 1.5 does not mean halfway between level 1 and level 2 in meaning. It means the distribution straddled them.
  • legend keys are strings. To get the level text a draft landed on, index with String(Math.round(score)).
  • A level can be an object, not just a string. TypeSafe's advanced primitives docs allow structure, so a level can carry examples alongside its description. The legend then returns that same object, so read whichever shape you sent.
  • Every Score returns confidence. That includes a two-level Score, which is how you get a confidence number on a yes or no question that a Noul will not give you.

Noul

What it is. A yes or no statement, answered with one float from 0 to 1. You write instructions as a statement rather than a question, and the float is the probability that the statement is true of the state.

When to use it. Flags. Is this engagement bait, does this contain a slur, does this read as AI written, is this off topic for the account. Nouls are cheap to add, so a feed filter usually runs several at once.

noul-request.json
{
  "state": { "draft_post": "Drop a 🔥 if you agree and I'll DM you the template. Follow for part 2.", "platform": "X (Twitter)" },
  "model": "jev-1.13.0",
  "questions": {
    "slop": {
      "type": "noul",
      "instructions": "This reads like it was generated by an AI rather than written by a person.",
      "criteria": {
        "true": "Uniformly long sentences, triadic lists, words like delve or landscape.",
        "false": "Uneven rhythm, specific detail, an identifiable voice."
      }
    },
    "bait": {
      "type": "noul",
      "instructions": "This post explicitly asks the reader for likes, reposts, replies, follows, or bookmarks."
    }
  }
}
noul-response.json
{
  "model": "jev-1.13.0",
  "answers": {
    "slop": { "type": "noul", "noul": 0.21 },
    "bait": { "type": "noul", "noul": 0.97 }
  },
  "usage": { "input_tokens": 118, "output_tokens": 4 }
}

A Noul answer has no confidence field

This is the single most common error in third-party writing about Jev. A Noul returns noul and nothing else. There is no confidence, no probabilities map, no legend. Reading answers.bait.confidence gets you undefined. The probability itself is the confidence signal: 0.97 and 0.03 are both confident, and 0.5 is the model saying it cannot tell. Source: docs.typesafe.ai/primitives/noul.

Gotchas.

  • Write a statement, not a question. "This post asks for engagement" beats "Does this post ask for engagement?" A statement makes it obvious which direction high means.
  • criteria is optional and takes true and false keys. Use it when the statement is fuzzy, which is most of the time. Put the boundary cases in it.
  • You set the threshold, and it is not 0.5. Pick it from your own labelled examples, and keep a dead band in the middle where nothing acts automatically.
  • Threshold per consequence. Blurring a post and deleting a post should not share a cutoff.
  • Avoid negations. "This is not off topic" is two negations deep by the time your code reads it, and TypeSafe documents that indirection degrades accuracy.

Which primitive to reach for

What you want
Use
Why
Exactly one label from a fixed set of unordered categories
Choice
You get a probability for every option, so you can see when two categories are close.
A position on a scale where the levels have an order
Score
The returned float is a probability-weighted mean, so it ranks and thresholds cleanly.
A yes or no with a probability attached
Noul
One float from 0 to 1. Threshold it in your own code.
A yes or no that also carries a confidence field
A two-level Score
A Noul has no confidence field. A Score with two levels does, because every Score returns one.
To rank 40 drafts against each other
Score
Run the same Score over each draft and sort on the float. Do not ask one question to compare them.
To pick from more than 255 options
Two stages of Choice
Choice caps at 255 options. TypeSafe used a two-stage approach for its own Wikiracing demo.
To count how many items match something
Neither. Code.
TypeSafe documents counting as unreliable and says to iterate in code over the candidates.

All three in one request

TypeSafe documents that all declared questions are evaluated in the same parallel pass, so mixing types adds no extra round trip. They also share one copy of the state, which is why TypeSafe's fan-out pattern exists: a cookbook that batched 13 questions over one document measured 12.2x cheaper and 10.0x faster than asking them one at a time, with no change in the answers.

This is the shape the free post scorer uses. Seven questions, one request: one Score for reach strength on a five-level rubric, three Scores for hook, clarity and specificity on three levels each, and three Nouls for AI-slop, engagement bait and toxicity. All seven questions are answered in the same parallel pass.

scoreDraft.ts
import { TypeSafeClient, noul, score } from '@typesafe-ai/sdk';

const client = new TypeSafeClient({ timeout: 4000 });

const { answers, usage } = await client.systemOne({
  state: { draft_post: text, platform: 'X (Twitter)' },
  questions: {
    strength: score('How far will this post travel compared to a typical post from the same author?', STRENGTH_LEVELS),
    hook: score('How well does the opening line stop someone mid scroll?', HOOK_LEVELS),
    clarity: score('How easily does a reader with no context understand this?', CLARITY_LEVELS),
    specificity: score('How concrete is this?', SPECIFICITY_LEVELS),
    slop: noul('This reads like it was generated by an AI rather than written by a person.'),
    bait: noul('This post explicitly asks the reader for likes, reposts, replies, follows, or bookmarks.'),
    toxicity: noul('This text contains slurs, harassment, sexual content, or abuse targeted at a person or group.'),
  },
});

answers.strength.score;          // 2.41
answers.strength.confidence;     // 0.63
answers.strength.probabilities;  // { "0": 0.03, "1": 0.14, ... }
answers.hook.legend['2'];        // the rubric text the draft landed on
answers.bait.noul;               // 0.04
answers.bait.confidence;         // undefined. Nouls do not have one.

Two practical notes from running this. Pin the model string: log jev-1.13.0 rather than jev-latest, or the thresholds you tuned drift the day the alias moves. And keep the state small. TypeSafe documents that irrelevant context degrades accuracy, so send the draft and the platform, not the whole account history.

Seven mistakes worth knowing before you write the schema

Mistake
What is actually true
Using an options array on a Choice
There is no options field. Choice takes criteria, a map of option key to description. Several widely-shared launch articles publish an options array that the API does not accept.
Using min and max on a Score
There is no min or max. Score takes criteria, an ordered array of 2 to 10 level descriptions, and the range is implied by the array length.
Reading confidence off a Noul
A Noul answer contains only noul. Reading answers.x.confidence on a Noul gets you undefined. Only Choice and Score return confidence.
Treating a Score as a measurement
A score of 1.5 does not mean halfway between level 1 and level 2 in meaning. It means the distribution sat between them. Use a score to rank or to threshold, not to measure magnitude.
Indexing the legend with a number
Legend keys are strings. Index with String(Math.round(score)), not with the float and not with a number key.
Phrasing a Noul so that true means the good outcome sometimes and the bad outcome other times
Phrase every Noul as a statement whose truth is the thing you are looking for, so a high value always means yes. TypeSafe warns that contradictory instructions and criteria degrade answers.
Expecting one question to see another question's answer
Questions in a request are independent. Question A's answer never becomes context for question B, and no arithmetic relationship between two answers is guaranteed.

The wider version of the last point is on TypeSafe's model jaggedness page, and the practical consequences are in can Jev hallucinate. For the request ceilings that decide how you batch, see Jev rate limits and context window.

See seven questions answered at once

Will it go viral? runs four Scores and three Nouls over any X draft and shows the confidence band that the Scores return. Free, no signup. When the judgment is made, the OpenTweet API publishes to X, Bluesky and LinkedIn in one call.