Skip to main content

Decision models

A decision model evaluates information and returns values your application can act on: a category, a position on a scale, or the probability that a statement is true. Jev is TypeSafe's decision model, available through the MindsHub Inference API. TypeSafe calls this class of model System One: models designed for focused judgments that fit inside a larger software workflow.

For example, a delivery report might need three judgments: which team should handle it, whether the customer requested a replacement, and how much damage is described. Jev can answer all three in one request. Your application then routes the report and decides whether to ask for more information.

Start with the runnable example if you prefer to learn by doing. It sends all three question types with your existing MindsHub key. No TypeSafe account or SDK is required.

From information to a decision

Every request has three ingredients:

IngredientWhat you provideDelivery-report example
stateThe information to inspectThe customer's report and relevant delivery details
questionsNamed judgments, each with a type and instructions or criteriateam, replacement, damage
modelA MindsHub decision-model aliasjev

The response puts each result under the same name in answers. Reading answers.team.choice gives the selected team directly. You do not extract JSON from a generated chat message.

The sequence is simple: collect the relevant facts → ask Jev → inspect the answer and uncertainty → run your application logic. The endpoint returns judgments; it does not call your tools, change records, or execute the action named by an answer.

When to use a decision model

Your application needs…Use…Example
A semantic judgment with a defined answer shapeDecisionsPick a queue, assess relevance, flag whether a message asks for follow-up
Generated content or a flexible JSON structureA chat model, optionally with structured outputDraft a reply, summarise a document, extract a variable-length list
An exact calculation or a known ruleOrdinary codeCompare dates, add invoice amounts, enforce permissions

Structured output constrains a chat model's generated response to your schema. Decisions gives you three specific question types with probabilities as part of their answer contract. Choose based on the result your code needs.

You can use both in a workflow: let Jev identify the relevant queue, then use a chat model to draft a response using that queue's instructions.

Pick the question type

Noul: how likely is “yes”?

Use "type": "noul" for one yes/no proposition. The name is part of the API; the returned noul value is a number between 0 and 1.

{
"type": "noul",
"instructions": "Does the report explicitly ask for a replacement item?"
}

A value near 1 supports “yes”; near 0 supports “no”. Around 0.5, neither answer has a clear lead. 0.5 does not mean half a replacement, medium damage, or a Boolean false. There is no separate confidence field for this type.

Keep the number until your code applies a threshold. For example, you might treat values at least 0.9 as a request for replacement, at most 0.1 as no request, and the rest as needing clarification. These are example thresholds to evaluate on your own reports, not service guarantees.

If the boundary needs explanation, supply criteria.true and criteria.false; see the request reference.

Choice: which category fits?

Use "type": "choice" when the result must be one of a set of named options. criteria maps the names your code uses to descriptions of when they apply.

{
"type": "choice",
"instructions": "Which team should review this delivery report?",
"criteria": {
"packaging": "Damage to the packaging, with the item itself intact.",
"product": "Damage to the item itself.",
"other": "A different issue, or not enough information to identify one."
}
}

Read the selected name from choice, each option's probability from probabilities, and the distribution's certainty from confidence. Keep option names stable if you persist them or use them as routing keys.

Include an explicit fallback when real inputs may fit none of your business categories. A model can still select a category on an ambiguous report; your confidence policy determines whether to use it.

Choice selects one option. For several independent labels that may all apply, ask several noul questions in the same request.

Score: where does it sit on a scale?

Use "type": "score" for one ordered dimension. Each entry in criteria describes a level, and its array position assigns the number: 0, 1, 2, and so on.

{
"type": "score",
"instructions": "How much damage does the report describe?",
"criteria": [
"Both packaging and item are undamaged.",
"Packaging is damaged; the item is intact and usable.",
"The item is damaged and cannot be used normally."
]
}

Here the returned score lies between 0 and 2. It is the probability-weighted average of the level numbers, so it can be fractional. For example, probabilities {"0": 0.1, "1": 0.6, "2": 0.3} produce 0 × 0.1 + 1 × 0.6 + 2 × 0.3 = 1.2.

Read legend to map the numbers back to your descriptions. Both legend and probabilities use string keys in JSON, such as "1".

Keep the score as a number when sorting reports. If your code requires one category, prefer a choice question or define a conversion rule explicitly. A score is not a measured damage percentage, a dollar amount, or an array index you can use without conversion.

Probability and confidence

For choice and score, probabilities describes the distribution across the options or levels. confidence is a separate value from 0 to 1 derived by TypeSafe from that distribution. It is not necessarily the selected option's probability, and it is not a guarantee of correctness. Use the returned value rather than substituting the largest probability. See TypeSafe's confidence explanation.

Record both when evaluating an integration. Two answers with a similar score can distribute probability differently; a routing policy that looks only at the score loses that information. Noul has no separate confidence value: use the probability of the proposition itself.

Here is a routing policy for the endpoint example. It chooses a destination but performs no external action:

# `result` is the parsed response from the quick example.
team = result["answers"]["team"]
minimum_confidence = 0.8 # Illustrative; tune against labelled reports.

if team["confidence"] < minimum_confidence or team["choice"] == "other":
destination = "manual_review"
else:
destination = {
"packaging": "packaging_queue",
"product": "product_queue",
}[team["choice"]]

print(destination)

An unclear answer is a useful outcome. Keep a review or clarification path instead of forcing every report into automatic handling. Measure incorrect automatic routes and the proportion sent for review when choosing your threshold.

Give each question the context it needs

state can be a string, a JSON object, or an array. An object helps label several pieces of related information:

{
"report": "The outer box arrived torn. The item inside is undamaged and works normally.",
"delivery": {"reference": "B-204", "status": "delivered"},
"previous_contact": "No earlier report for this delivery."
}

This is one state, not a batch of independent evaluations. An array is also one shared state. All questions in a request see that state. Send separate requests for unrelated reports, or identify the exact item each question refers to.

Question names such as damage are identifiers for your code; TypeSafe does not use those names as model instructions. Put the actual question in instructions and the distinctions in criteria. Write each description so it can be understood on its own: “the item cannot be used normally” is clearer than “worse than above”.

Questions are evaluated independently. A question cannot read another answer in the same request. If a second judgment depends on the first result, make a second call with the relevant result included in its state. If it only depends on the same original facts, put both questions in the first call.

Jev currently accepts text and structured JSON, not images, audio, or video. Sending an image URL in a field does not make it a vision request. Extract the text or observations you need first. See the provider's state guide.

Design and evaluate your questions

  1. Define the output your code needs. Choose a category, a yes/no proposition, or one ordered dimension before writing the request.
  2. Make distinctions explicit. Include boundary cases in the instructions or criteria. An instruction such as “is this bad?” leaves your application's rule undefined.
  3. Separate judgments. Ask about damage and replacement intent separately. Your code can combine them later without hiding how each contributed.
  4. Test representative inputs. Include straightforward cases, missing facts, conflicting statements, out-of-category reports, and the languages you expect to receive. Inspect wrong answers as well as low-confidence ones.
  5. Version the evaluation. Record your question definitions, application thresholds, requested alias, returned model version, answers, and expected outcomes. Re-run the same cases before changing any of those inputs.

Keep exact arithmetic, date comparisons, access checks, and action execution in code. Jev's structured answer does not make an arbitrary calculation exact. User-supplied state can also contain misleading instructions; a model answer should never grant permissions. TypeSafe documents these limitations for Jev 1.13.

For request fields, examples, billing, limits, and retry behavior, continue to the Decisions endpoint. For the provider's terminology and background, see TypeSafe's System One introduction.