How to Map Reasoning Parameters Between Models
Reasoning parameters for OpenAI, Claude, and Gemini: how to map effort and budget, detect ignored settings, and test routing behavior.

The reasoning mode cannot be described on a single scale from "fast" to "smart." Some APIs accept an effort level, others take a numeric budget, some decide for themselves whether to think at all, and others silently map your parameter to the nearest supported value. If you hide all of this behind a "Thinking: High" switch, your team will get unpredictable bills, odd delays, and false confidence that the setting is working.
A reliable approach has two parts: an explicit conversion table and regression tests. The table says which field is valid for a particular model and what it means. The tests answer a more uncomfortable question: did the actual behavior change after the API politely returned 200?
One label does not mean one mechanism
reasoning.effort, thinkingBudget, thinkingLevel, budget_tokens, and effort control different things. You cannot safely reduce them to a reasoning_level field without losing meaning.
For OpenAI, reasoning.effort sets the intensity of reasoning. It is not a contract for a fixed number of reasoning tokens. Current OpenAI guidance for the Responses API may include none, low, medium, high, xhigh, and max, but the available set depends on the model. Even when two models accept the same word, high, they do not have to spend the same amount of time or use the same number of tokens.
With Anthropic, distinguish between the older manual budget and adaptive mode. thinking: {"type":"enabled", "budget_tokens": N} sets an internal reasoning budget for models that still support this mode. Claude documentation explicitly says that budget_tokens must be less than max_tokens, and actual usage may be below the specified budget. On newer models, manual budgeting is gradually being phased out: Anthropic recommends thinking: {"type":"adaptive"} together with effort, while some models reject manual mode with a 400 error.
With Google, the distinction is especially clear. Gemini 2.5 uses a numeric thinkingBudget; on some models, 0 disables thinking and -1 enables dynamic budget selection. Gemini 3 uses thinkingLevel, and Google separately warns that a numeric budget does not provide precise control in newer generations, even if the API still accepts it.
There is another layer: the gateway. It may accept a unified object, choose the provider's native field, round a level, or reject an unsupported combination. That is useful, but you still need to know exactly what happened after the conversion.
A conversion table should store semantics, not just field names
A working table should not be a list of attractive mappings such as high = 8192. It must record the scope of applicability: model family, API, allowed values, control precision, whether the mode can be disabled, and the observable signal in the response.
Here is a template that is convenient to keep next to the routing code. Model IDs change faster than architectural rules, so tie each entry to the exact model ID and model-card version.
| Family and mode | Native setting | What it controls | Can it be disabled? | Control precision | What to check in the response |
|---|---|---|---|---|---|
| OpenAI reasoning through the Responses API | reasoning.effort | Desired intensity of internal work | Only if the model accepts none | Low, it is a level, not a budget | usage, latency, quality on tests |
| Claude with manual thinking | thinking.type=enabled, budget_tokens | Upper target for thinking tokens | Yes, by omitting or disabling thinking if the version supports it | Medium, the model may not use the entire budget | usage.output_tokens_details.thinking_tokens |
| Claude with adaptive thinking | thinking.type=adaptive, effort | Depth of work, while the model decides whether to think | Depends on the model version | Low for tokens, medium for behavior | usage, tool calls, latency |
| Gemini 2.5 | thinkingBudget | Numeric target for thinking | Depends on the specific model | Medium, but it is not guaranteed usage | usage metadata, latency, test score |
| Gemini 3 | thinkingLevel | Discrete thinking level | Not possible on some models | Low, Google determines usage | level in the request, usage, test score |
| Unified gateway | reasoning.effort or reasoning.max_tokens | Request to convert into a native mode | Only if the model and provider allow it | Depends on the final route | selected model, effective config, usage |
The last row is especially important. A unified field does not make different mechanisms identical. It gives your application layer a common language, but the routing layer must preserve which native parameter was sent next and which restrictions were applied.
OpenRouter documentation makes this clear: reasoning.effort is mapped to Google's thinkingLevel, while reasoning.max_tokens may be passed as thinkingBudget. It also notes that for Gemini 3, a numeric budget may still be converted internally into a level, while Google determines actual consumption. This is an honest example of normalization: the API does not promise precision that the provider does not have.
First separate budget, effort, and output limit
Teams regularly confuse three limits and then look for the problem in the model.
The reasoning budget applies to internal work. In classic Claude, this is budget_tokens. It gives the model room to think, but does not say how much final text it should return.
The effort level expresses a preference, not arithmetic. high for OpenAI or Claude means the model may spend more computation and tokens on a difficult task. On a simple request, the difference between medium and high may be barely noticeable. In a tool-using task, it may appear in the number of calls, repeated checks, and the length of the process.
The total output limit restricts everything the model generates in the response. Names vary: max_output_tokens, max_tokens, and max_completion_tokens. In some APIs, reasoning and final text compete for the same ceiling. If you provide 2,000 tokens and request a large thinking budget, the model is not required to leave enough room for a useful answer.
This is not a theoretical detail. Claude documentation says that budget_tokens must be less than max_tokens. It also counts thinking as part of output tokens in usage, while output_tokens_details.thinking_tokens shows the breakdown for observability only. Billing is based on total output_tokens, not on the neat shortened trace you saw in the response.
The practical rule is simple: set the financial ceiling through the total limit and your application's request limits. Let the reasoning mode choose the depth of work within that ceiling. Do not use effort as a cost safety switch.
Normalize requests in two layers
Client code needs a small, stable contract. The router needs a second, more detailed contract with rules for a specific model. Combining them in one object is inconvenient: either every service must carry the names of all providers, or you lose important restrictions.
The first layer can look like this:
{
"reasoning": {
"mode": "enabled",
"intent": "balanced",
"max_reasoning_tokens": null,
"allow_fallback": false
},
"max_output_tokens": 6000
}
Here, intent must not pretend to be a native parameter. It is your internal policy: fast, balanced, or deep. The max_reasoning_tokens field makes sense only where the model has a numeric budget. allow_fallback forces you to decide explicitly whether lowering a deep request to medium is acceptable.
The second layer turns the policy into an exact request and records the conversion result:
{
"requested": {
"intent": "deep",
"max_reasoning_tokens": 12000
},
"effective": {
"provider": "google",
"parameter": "thinkingLevel",
"value": "high",
"budget_applied": false,
"reason": "Модель принимает уровни, а не точный бюджет"
}
}
You do not have to send this object to the user. Write it to the trace log and attach it to experiment data. Otherwise, a month later you will see a quality drop and have no way to tell whether the model, provider, conversion rule, or request itself changed.
For an OpenAI-compatible client, a nonstandard object is often passed through extra_body so the SDK does not discard the field before sending it:
from openai import OpenAI
client = OpenAI(
base_url="https://api.airouter.kz/v1",
api_key="${AI_ROUTER_API_KEY}"
)
response = client.chat.completions.create(
model="provider/model-id",
messages=[
{"role": "user", "content": "Реши задачу и верни только JSON по схеме."}
],
max_tokens=6000,
extra_body={
"reasoning": {
"effort": "high",
"exclude": True
}
}
)
AI Router lets you keep the OpenAI SDK and replace base_url with api.airouter.kz, but transport compatibility does not make every parameter universal. Before releasing a new model, your adapter should obtain its capabilities and decide what to do with unsupported fields.
Paired tests catch silent ignoring
A check like "we sent high and got 200" is useless. Silent ignoring appears not in the status code, but in the fact that modes produce statistically indistinguishable results on tasks where depth should change the solution process.
Build a small permanent test set. Do not use only competition mathematics: it often tests one ability and poorly reflects production. Include tasks from your application, with machine-checkable results.
A suitable minimal set includes:
- extracting fields from an ambiguous contract with a verifiable schema;
- an SQL task with small tables and a known answer;
- fixing a defect in a short repository with tests;
- a tool call where the first result is intentionally incomplete;
- a calculation with a trap involving units or calendar rules.
Run the same sample in at least two modes, such as low and high. Shuffle the run order so random load does not coincide with one level. Repeat identical requests several times when possible: generation is nondeterministic, and one answer may simply be better by chance.
Store more than the quality score. For every attempt, record the exact model ID, provider, normalized request body, time to first byte, total latency, usage, finish reason, number of tool calls, and final validation results. If the model returns billed thinking tokens, store them separately from visible reasoning text.
A simple warning signal looks like this: low and high have the same median latency, the same usage, and the same pass rate on tasks where a stronger mode would normally perform additional checks. This is not proof of ignoring, but it is enough reason to inspect the trace and compare the effective config.
Make parameter degradation a separate contract
A quality check answers the question, "Is there an effect?" A contract test answers, "What effect is acceptable?" These are different tests, and you need both.
For every entry in the conversion table, define the expected behavior. Examples:
| Situation | Expectation | Error caught |
|---|---|---|
deep for a model with thinkingLevel | The effective config records a valid level | The gateway sent a nonexistent field or failed to record the conversion |
| Numeric budget for Gemini 3 | The log states that an exact budget is not guaranteed | The team assumed that 12,000 means exactly 12,000 thinking tokens |
reasoning: none for a model with mandatory thinking | An explicit error or a predeclared fallback | The mode is silently enabled even though the application considered it disabled |
Manual budget_tokens for a new Claude model | A compatibility error or a switch to adaptive policy | Production receives a 400 after the model version changes |
high with a small total limit | A warning or configuration rejection | The final answer is cut off because there are not enough output tokens |
Do not hide these rules in conditional statements scattered throughout the code. Keep them in one capability registry. In a mature implementation, an entry might look like this:
model: google/gemini-family-version
reasoning:
enabled: true
required: false
controls:
- kind: effort
accepted: [minimal, low, medium, high]
maps_to: thinkingLevel
- kind: budget
accepted: false
disable:
supported: false
observability:
usage_breakdown: provider-dependent
fallback:
xhigh: high
max: high
The registry does not have to be a manually written encyclopedia. Some capabilities can be obtained from a model catalog API, but catalog data does not replace testing. The catalog says that a field is allowed. It does not guarantee that a particular route, region, provider version, or tool configuration will produce the expected effect.
Do not use one mode for chat, RAG, and agents
The most common bad recommendation is: enable high everywhere if quality matters. It seems safe because it removes the need to choose. In practice, it masks problems in task design and makes an expensive model analyze requests that need fast retrieval, a strict schema, or a good reranker.
For a short chat over a known knowledge base, first check retrieval, source citations, and response limits. High reasoning effort will not fix a document that never reached the context.
For extracting data from invoices or forms, a strict JSON schema, a validator, and retries only for invalid fields matter more. Deep reasoning can help with ambiguity, but it does not replace format validation.
For an agent with tools, the effort mode affects more than text. Claude documentation describes effort as controlling total token usage, including call arguments and tool work. A higher level may mean more attempts, a broader plan, and more requests to your systems. Measure not only the answer, but also the number of actions, the risk of side effects, and the cost of each completed task.
For complex code, migration planning, or incident investigation, a deep mode is often justified. Enable it through task routing, not a global setting. A service that creates a short user notification and a service that proposes a database rollback plan should not receive the same default policy.
Visible reasoning is not suitable for accounting or audits
Developers like to use reasoning text as proof that a model "thought." This is unreliable for two reasons.
First, some models do not return internal reasoning tokens at all. For example, OpenAI o-series models do not have to expose them through unified interfaces. Second, Anthropic may show a shortened thinking trace while billing for the full internal work. Extended thinking documentation notes that billed output and the visible amount of thinking may differ; for observability, use usage.output_tokens_details.thinking_tokens when the specific response provides it.
For audits, store facts that can be compared across providers:
- the policy ID that selected the mode;
- the requested config and effective config;
- the exact model ID and provider route;
- input size, output size, and the available usage breakdown;
- latency, validator result, and tool traces.
Do not record hidden reasoning in logs just out of curiosity. In banking, healthcare, or government systems, that text can easily become another sensitive-data set that must be classified, access-controlled, retained, and deleted according to policy. For investigating a failure, a reproducible input, prompt version, configuration, and verifiable result are usually more useful.
Choose a mode by measured boundaries, not by its name
There is no correct default reasoning setting for an entire company. There is a specific point after which extra time and cost stop producing a noticeable improvement in your result.
Start with three policies: fast, balanced, and deep. For each one, define the allowed modes by model family, total output limit, and task set. Then build a simple table with pass rate, median latency, median output usage, number of tool calls, and the cost of a successfully completed task. If deep improves quality only on code migration tasks, route only that class of requests there.
Do not automatically transfer a discovered mapping to a new model. Moving from thinkingBudget to thinkingLevel, replacing Claude manual thinking with adaptive thinking, or updating an OpenAI model changes the meaning of the old levels. First update the registry entry, then run contract and paired tests, and only after that change the production route.
A good conversion table does not promise that different models think alike. It honestly records where control is precise, where it is approximate, and where the parameter cannot be disabled. That honesty protects you from silent regressions that would otherwise surface only after a bill or an incident.
Frequently asked questions
Can reasoning effort be treated as an exact token limit?
No. reasoning.effort usually sets the desired intensity of the model's work, not a number of hidden tokens. For OpenAI and modern Claude models, it is a behavioral signal, so two identical requests may require different amounts of internal work. If you need a cost ceiling, limit total output and measure actual usage.
Why does the API return 200 even though the reasoning parameter had no effect?
Because APIs often accept unknown or inapplicable fields for compatibility. A gateway may normalize the parameter, map it to the nearest supported level, or remove it before calling the provider. A 200 status only proves that the request was processed, not that the setting changed the model's behavior.
How can I check whether a model is actually using thinking?
First check the model metadata, then run a paired test on tasks where additional reasoning should noticeably change the result. Compare output-token usage, latency, validation pass rate, and response shape. One successful example proves nothing.
How does thinkingBudget differ from thinkingLevel in Gemini?
For Gemini 2.5, thinkingBudget and thinkingLevel are not equivalent: the first sets a numeric budget, while the second belongs to newer families. For Gemini 3, Google recommends the minimal, low, medium, and high levels; a numeric budget may be accepted only for backward compatibility. Do not move configuration between generations without a separate test.
Should I use budget_tokens with new Claude models?
On newer Claude models, manual budget_tokens is no longer a universal way to control reasoning. For some versions, Anthropic recommends adaptive thinking together with effort, while some models reject a manual budget with a 400 error. Check the documentation for the exact model ID, not for the Claude family as a whole.
Is high always better than medium?
No. Logical complexity, tool use, context length, and the required response format matter more than the level name. Start with the regular mode, then increase effort only for scenarios where tests show a meaningful quality improvement.
How do I avoid leaving too few tokens for the final answer?
Set max_output_tokens or the equivalent total output limit separately. The reasoning budget often counts toward this limit or competes with the final answer for it. If the model has too few output tokens, it may spend them on internal work and cut off the useful answer.
Can I log the chain of thought for auditing?
Do not treat the hidden chain of thought as a required audit log. Some providers do not return it, some provide only a shortened version, and for some models the visible text does not match the billed internal usage. For observability, store settings, usage, latency, validation results, and the model identifier.
How can I send a nonstandard reasoning parameter through the OpenAI SDK?
Pass the setting through extra_body or a similar SDK mechanism so the library does not discard an unfamiliar field. Save the exact request JSON in a test environment and compare it with what the gateway receives. When changing models, do not automatically carry the field over unless the model card declares support for it.
Can I change providers without rewriting the client?
Yes, if your code uses an OpenAI-compatible client and you do not rely on provider-specific fields without checking them. AI Router lets you change base_url to api.airouter.kz and continue using familiar SDKs, code, and prompts. You still need to validate the meaning of each reasoning parameter for the selected model.