Decide

decide() returns a typed decision for one call. Chat completions stay chat completions. Pass using to say which of the three you want. The response repeats using and model.

The three choices

usingYou also passWhat runs
savanothing elseTokand's hosted Sava model, on Tokand's routing
your-modelmodel: "gpt-4.1"A typed decision on your model
tokand-modelmodel: "tokand/…"The same kind of decision, on that Tokand router model. Not Sava.

Calls

const ai = withTokand(openai, { apiKey: process.env.TOKAND_KEY })

await ai.decide({
  using: "sava",
  state: { messages },
  questions,
})

await ai.decide({
  using: "your-model",
  model: "gpt-4.1",
  tier: "measured",
  state: { messages },
  questions,
})

await ai.decide({
  using: "tokand-model",
  model: "tokand/qwen",
  state: { messages },
  questions,
})

What comes back

level is L0 unless you set it to L1, L2, or auto.

{
  "using": "your-model",
  "model": "gpt-4.1",
  "level": "L0",
  "tier": "measured",
  "tokey": { "tokens": { "raw_in": 840, "filtered_in": 210 } },
  "decisions": []
}

Token saver

Omit tier and the state is sent unchanged. Set it to shrink state.messages before the decision runs. The tiers are the same as chat: safe, measured, aggressive, and aggressive+. Questions are not shrunk. The response repeats tier.

await ai.decide({
  using: "your-model",
  model: "gpt-4.1",
  tier: "measured",
  state: { messages },
  questions,
})

When it fails

decide() does not fall through to chat.completions.create. If the choice you named cannot run, the call throws DecideError with the same using and model. Sava rejects a model id. your-model rejects a tokand/ id. tokand-model requires one.