Say your coding agent is 200,000 tokens into a session and hits a hard bug. You want high reasoning effort for this one turn. On most APIs, raising it rewrites the start of the prompt, so the next request reprocesses all 200,000 tokens at the uncached rate. That's why most agents pick one effort level and never change it.
OpenAI and Anthropic now document a way to change effort mid-conversation and keep the cached prefix. OpenRouter supports it across all three of its APIs. Here's how each one works, and where the other providers stand.
Why a normal effort change misses the cache
Prompt caching matches the rendered prompt from the first token forward. The first difference ends the match, and everything after it is processed again at the uncached rate.
Effort looks like a request setting, but it affects what the model sees. OpenAI's prompt caching guide lists reasoning.effort as a setting that "can change model-side reasoning instructions" and says changing it "can rewrite instructions in the hidden system instructions." Those instructions come before your system prompt and your messages. Change them and the whole conversation is a new prefix.
Anthropic is more definite. Its prompt caching docs say changing output_config.effort "always invalidates message blocks," and on some models the tool and system caches go with it. The one exception is setting effort explicitly to the model's default, which is the same as omitting it.
The fix is to put the change in the history
Both providers solved it the same way. Leave the request-level effort alone and add an item to the conversation at the point where the new effort should start. The items before it stay byte-for-byte the same, so the cached prefix still matches. The new effort applies from that point on, until another update changes it.
OpenAI
On the GPT-6 family, add a configuration_update item before the next user message and keep the top-level reasoning.effort at its original value.
{
"model": "gpt-6-astra",
"reasoning": { "effort": "low" },
"previous_response_id": "resp_...",
"input": [
{ "type": "configuration_update", "reasoning": { "effort": "high" } },
{
"role": "user",
"content": "Analyze the failure modes and propose rollback steps."
}
]
}
The reasoning guide says this "preserves the original prompt prefix for prompt caching." A few rules come with it.
- Configuration updates only work on GPT-6 models in standard, single-agent mode, and they only change reasoning effort.
- If you manage history yourself instead of using
previous_response_id, replay every update in its original position on every later request. Dropping or moving one changes the prefix. - The API rejects two adjacent updates, and updates can't be combined with automatic compaction, automatic truncation, or the standalone compaction endpoint.
- The response's
reasoning.effortstill reports the request-level value. There is no field that tells you which effort the update applied.
Anthropic
Claude Fable 5.1, Mythos 5.1, Opus 5.5, and Opus 5 accept a per-message effort change behind the mid-conversation-output-config-2026-07-01 beta header. The update is a system message with empty content and an output_config.
{
"model": "claude-fable-5-1",
"max_tokens": 4096,
"output_config": { "effort": "high" },
"messages": [
{
"role": "user",
"content": "Plan a migration from SQLite to PostgreSQL in three short steps."
},
{
"role": "assistant",
"content": "1. Export the SQLite data. 2. Create the PostgreSQL schema. 3. Import the data and verify row counts."
},
{ "role": "system", "content": [], "output_config": { "effort": "low" } },
{ "role": "user", "content": "Summarize the plan in one sentence." }
]
}
The new level takes effect from the next user turn. Models without support, including Claude Fable 5, return a 400. Anthropic also gives a second reason to prefer this form on Fable 5.1. A top-level change "steers the model less reliably," because the model's earlier replies were written at the old level and it tends to stay consistent with them.
OpenRouter
OpenRouter's reasoning guide states the problem more directly than either provider. Raising request-level effort for one hard turn and lowering it on the next "changes the request prefix and invalidates the prompt cache." It accepts the update in the native shape for each of its three APIs. That includes Chat Completions, where the content-less system message is an OpenRouter extension because OpenAI's own Chat Completions API has no equivalent. Requests with an update are routed only to endpoints that support it, and a model without support returns a 400 instead of silently dropping the update.
Where each provider stands
I only count a provider as supported when its docs connect an effort change to cache reuse. A separate request parameter plus prefix caching is not enough. OpenAI's effort setting is also a separate request parameter, and it still rewrites hidden instructions.
| Provider | Effort control | Mid-conversation change keeps the cache? |
|---|---|---|
| OpenAI | reasoning.effort plus configuration_update item (Responses API) | Yes, documented, on GPT-6 models. Changing the top-level value can break it. |
| Anthropic | output_config.effort plus per-message system update (beta) | Yes, documented, on Fable 5.1, Mythos 5.1, Opus 5.5, and Opus 5. A top-level change restarts the cache. |
| OpenRouter | reasoning.effort plus an in-history update on all three APIs | Yes, documented, for models whose provider supports it. A request-level change invalidates it. |
| xAI | reasoning_effort request parameter | Not documented. The caching guide covers message edits and missing reasoning content, not effort. |
| DeepSeek | reasoning_effort / reasoning.effort | Not documented. A hit requires the request to fully match a cache prefix unit. |
| Meta Muse Spark | reasoning_effort request parameter | Not documented. The caching guide describes prefix matching and doesn't mention effort. |
| Google Gemini | thinkingLevel / thinkingBudget in generation config | Not documented, and there are user reports of implicit caching misbehaving with thinking budgets. |
| Z.ai GLM | thinking.type, toggled per turn | Not documented. Turn-level thinking switches thinking on or off per request but makes no cache claim. |
| Mistral, Kimi, Qwen | Request-level reasoning_effort or enable_thinking | Not documented. |
"Not documented" doesn't mean it breaks. xAI, DeepSeek, and Meta all describe caching as a match on the leading tokens, and a request parameter might never touch those tokens. It might also inject hidden instructions the way OpenAI's does. The docs don't say either way, so the only answer is to measure it.
Gemini deserves extra care. One forum report describes cached-token counts disappearing once a thinking budget is set. It isn't about changing the budget mid-session, but I wouldn't assume Gemini keeps the cache across thinking changes without testing it.
Switching models is still a cold start
None of this carries over to switching models. The cache holds the model's own internal state for the prefix, so a different model can't reuse it, even one from the same family. OpenAI lists model first among the settings that affect the cached prefix. Changing effort on one model is the cheap way to spend less on easy steps. Routing easy steps to a smaller model costs a full uncached pass the first time each model sees the history.
Check it on your own traffic
The cached input count is in each response's usage. On OpenAI it is usage.input_tokens_details.cached_tokens, and on Anthropic it is usage.cache_read_input_tokens. Run the same conversation twice, once at a fixed effort and once with effort changes, and compare the cached share of input.
I did this for the OpenAI path. Over 42 live requests on GPT-6 Astra with alternating fixed and adaptive trials, the cached share of input was 0.869 at fixed effort and 0.868 with effort changing mid-session. That is a small sample, and cache hits still vary with routing and eviction, but I saw no extra cache loss from changing effort.