Decisions

Ask a model a fixed set of questions about a piece of text and get back typed answers with probabilities, instead of prose you have to parse.

POST/v1/decisions

A decision model answers a fixed set of questions about a piece of text and returns typed answers with probabilities. Use it where you would otherwise ask a chat model for JSON and hope it complies: routing a ticket to a team, scoring urgency, flagging whether a conversation is blocked.

The difference from /v1/chat/completions is that the shape of the answer is guaranteed. You declare the questions, so there is nothing to parse and nothing to retry when the model wraps its JSON in an apology.

Decision models only work on this endpoint. Sending jev-1.13 to /v1/chat/completions returns a 400, and sending a chat model here returns a 400 as well.

Questions

state is the text being judged. questions is a map of your own names to a question. Three types, and the criteria field differs between them:

typeask itcriteriayou get back
choicepick one of several labelsa map of label → what it meanschoice + probabilities
scoreplace it on an ordered scalean ordered array, low to highscore + legend
noulhow likely is this truenonenoul, between 0 and 1

The names are yours: answers comes back keyed by exactly the names you sent.

Example

import Tchavi from '@tchavi/sdk';

const client = new Tchavi({ apiKey: 'YOUR_API_KEY' });

const result = await client.decisions.create({
  model: 'jev-1.13',
  state: "Customer writes: 'My API key stopped working after I rotated it.'",
  questions: {
    team: {
      type: 'choice',
      instructions: 'Which team should handle this?',
      criteria: { billing: 'payments or credits', technical: 'API, keys' },
    },
    urgency: {
      type: 'score',
      instructions: 'How urgent is this?',
      criteria: ['not urgent', 'normal', 'urgent', 'critical'],
    },
    blocked: {
      type: 'noul',
      instructions: 'Is the customer unable to use the product?',
    },
  },
});

// Answers are discriminated on `type`.
if (result.answers.team.type === 'choice') {
  console.log(result.answers.team.choice, result.answers.team.probabilities);
}
if (result.answers.blocked.type === 'noul') {
  console.log('blocked likelihood:', result.answers.blocked.noul);
}

The response

JSON
{
  "model": "jev-1.13.0",
  "answers": {
    "team": {
      "type": "choice",
      "choice": "technical",
      "confidence": 1,
      "probabilities": { "billing": 0, "technical": 1 }
    },
    "urgency": {
      "type": "score",
      "score": 1.67,
      "confidence": 0.65,
      "legend": { "0": "not urgent", "1": "normal", "2": "urgent", "3": "critical" },
      "probabilities": { "0": 0, "1": 0.34, "2": 0.65, "3": 0.01 }
    },
    "blocked": { "type": "noul", "noul": 0.76 }
  },
  "usage": { "input_tokens": 382, "output_tokens": 62 }
}

A score is continuous, not an index: 1.67 sits between normal and urgent and leans urgent. legend maps the positions back to the labels you sent, so you can round or threshold however your workflow needs.

confidence is the model's own certainty. On choice it is the winning label's probability; a low one is your signal to escalate to a human rather than act on the answer.

Credits

A decision response is the provider's own body, so the cost is in the headers rather than in the JSON:

X-Credits-Used: 0.001
X-Credits-Remaining: 115397.775

Decisions are priced per token like any other model, and they are cheap — the example above costs a thousandth of a credit. That is the point: a decision model is meant to run on every message, not on the ones you have already decided are worth a large model.

On this page