skip to content

Porting tool_choice "required" from OpenAI to Mistral — which value do you use?

level: middleimportance: must knowfreq 58%

answer

  1. one value string differs from OpenAI
  2. not "required" here
  3. auto is the default with tools present
  4. the forcing mode has a shorter name
  5. none keeps schemas, bans calls

basics

~20 s

Use "any". Mistral's documented tool_choice values are auto (the default when tools are present), any (the model must call a tool), and none (tools stay declared but unused); "required" is OpenAI's spelling for the any mode.

solid answer

~40 s

Mistral's chat completions API takes `tool_choice` with the documented values `"auto"`, `"any"` and `"none"`, plus an object naming a specific function when you want to pin the call. `"auto"` is the default once you pass a `tools` array and lets the model decide per request. `"any"` is the forcing mode — the reply is guaranteed to be a tool call, which is what OpenAI spells `"required"`; that rename is the concrete gotcha when you port code between the two OpenAI-shaped APIs. `"none"` keeps the schemas in the prompt (so they still cost input tokens) but bars the model from calling them, which is how you get a plain-language answer without rebuilding the request. Do not assume the OpenAI spelling is honoured just because the rest of the envelope matches.

go deeper

for a junior

Remember the three strings — auto, any, none — and that auto is what you get by default once tools are attached. Say that any is Mistral's name for forcing a call.

for a middle

Explain each mode's effect on the response, call out that OpenAI spells the forcing mode "required", and note that a specific function can be pinned by passing an object instead of a string.

for a senior

Show the operational side: tool_choice is per request, so forcing is a first-hop switch you flip back to auto; none still bills the schemas; forcing on uncovered inputs manufactures confident wrong calls.

for a principal

Own the portability decision — when an abstraction layer normalizes these values across OpenAI-shaped vendors, where the mapping table lives, and how you keep a silent rename from shipping as a production behaviour change.

## Why this is the classic Mistral porting bug Mistral's chat completions endpoint is deliberately OpenAI-shaped: same `messages` array, same `tools` array of `{"type": "function", "function": {name, description, parameters}}`, same `tool_calls` on the way back. That similarity is exactly what makes the `tool_choice` vocabulary dangerous — you copy a working OpenAI request, change the base URL and the model id, and every field looks familiar. But the forcing mode has a different name, and "OpenAI-compatible" never means "every value string is identical". ## The four settings **`"auto"`** — the default whenever `tools` is present. The model decides, per request, whether to answer in prose or emit `tool_calls`. This is what you want for a general assistant, and it is what you must return to after a forced turn. **`"any"`** — the model *must* return a tool call. Use it when the tool is the point of the request: a structured-extraction step where prose is never an acceptable output, a router that must select one of N handlers, a first hop that must always fetch data before reasoning. The consequence is that a response under `"any"` never terminates a loop on its own — there is no path to a final text answer while the setting is in force. **`"none"`** — the tools stay in the request and therefore still occupy input tokens, but the model is barred from calling them and answers in text. This is the setting for a summarize-what-you-found turn at the end of an agent loop, or for a debugging comparison where you want the same prompt with and without tool access. Note the difference from simply omitting `tools`: with `"none"` the schemas are still visible to the model and still billed; with the array removed the model does not know the tools exist at all. **A specific-function object** — passing an object that names one declared function pins the call to that function. It is the narrowest form of forcing: not "call something" but "call this one". Useful when your application already knows which tool is required and only needs the model to fill in the arguments from natural language. ## The mapping table you should carry in your head | Intent | Mistral | OpenAI | |---|---|---| | Model decides | `auto` | `auto` | | Must call some tool | **`any`** | **`required`** | | Must not call | `none` | `none` | | Must call one named tool | object naming the function | object naming the function | Only one row differs, and it is the row people copy most often. ## Operational consequences of forcing Forcing changes the termination properties of your loop. Under `"auto"`, the loop ends naturally when the model decides it has enough information and returns `finish_reason: "stop"`. Under `"any"`, that never happens: every round trip yields another call. The correct pattern is per-request rather than per-conversation — set `"any"` on the specific request where a call is mandatory, then rebuild the next request with `"auto"` (or `"none"` if you want a guaranteed final prose turn). Since `tool_choice` is a request-level parameter, not conversation state, this costs you nothing but the discipline of not hard-coding it in a client wrapper. Forcing also interacts with quality. A model compelled to call a tool when no tool actually fits will pick the closest match and invent plausible arguments rather than say "I cannot help with that". If your tool surface does not cover the whole input distribution, `"auto"` plus a well-described fallback tool is usually safer than `"any"`. ## Cost notes Tool schemas are prompt content. Whatever `tool_choice` you set, every declared tool's name, description and JSON Schema is serialized into the input and counted in `usage.prompt_tokens` on each request. Setting `"none"` suppresses calling, not billing. If a long agent loop only needs three of your twenty tools after the first hop, pruning the `tools` array for later requests saves real money; flipping `tool_choice` does not. ## What a strong answer sounds like Name the values, name the rename against OpenAI, and then say something operational: that `"any"` is a per-request switch you flip back, that `"none"` still costs tokens, and that pinning a specific function is available when you already know the target. Candidates who only recite three strings without the porting trap or the termination consequence sound like they have read the docs but not run the loop.

  • Does tool_choice "none" save you the token cost of your tool schemas?
    No. The `tools` array is serialized into the prompt and counted in input tokens regardless of `tool_choice`; `"none"` only forbids the model from emitting calls. To actually cut that cost you must shrink or drop the `tools` array itself for that request — which is a legitimate optimisation on later hops of a loop where only a couple of tools remain relevant.
  • You need arguments for one known function extracted from free text. Which setting and why?
    Pin it: pass a `tool_choice` object naming that function. `"any"` would guarantee a call but not which one, so with several tools declared you would have to handle a wrong pick. Pinning turns the model into a schema-filling parser for a known target, which is both more deterministic and easier to validate downstream.
  • What happens to answer quality when you force "any" on inputs your tools do not cover?
    The model cannot decline, so it picks the least-bad tool and fabricates plausible arguments. You get confident, wrong calls instead of a refusal. Either keep `"auto"` and describe the tools well enough that abstention is natural, or declare an explicit escape-hatch tool such as one that records that no tool applies, so forcing still has a correct answer available.

saying these in an interview costs you the question

  • Assumes OpenAI's "required" is the Mistral value
  • Thinks tool_choice "none" removes schema token cost
  • Hard-codes "any" for a whole conversation
  • Believes "auto" means the model always calls a tool
  • Says forcing improves accuracy on out-of-scope inputs

context