TemprTempr

Reasoning that survives a model switch

  • Gateway
  • Reasoning
A conversation moving from Claude to DeepSeek and back, with each model's reasoning kept in its own shape

Asking a model to think is one half of reasoning. The other half is what comes back: the model's reasoning, which arrives alongside its answer, and which some models need to see again on the next turn.

Our earlier post covered the first half, one setting for how much a model thinks. This one is about the second half, and about a conversation that starts on one model and continues on another. That conversation shouldn't break.

Four shapes for the same thing

Providers don't agree on how to return reasoning:

  • DeepSeek, Kimi, MiMo and GLM put it in a reasoning_content field on the message.
  • Groq and Cerebras put it in a bare reasoning field.
  • Claude returns thinking blocks, each with a signature, and sometimes a redacted_thinking block instead.
  • OpenAI keeps reasoning on its side, and hands it out in encrypted form if you ask.

If your client reads one of these, it misses the others. That's annoying when you only want to show the reasoning. It matters more in an agent loop, because some models need their earlier reasoning back when they call a tool. Claude needs its thinking blocks on a tool-calling turn. DeepSeek, Z.AI, Moonshot and Xiaomi MiMo models need their reasoning back as reasoning_content across tool calls. Drop it, and the model carries on without its earlier reasoning.

Then the conversation moves. You start a task on one model, switch to another halfway through, or a fallback chain sends one turn somewhere else. The history now holds reasoning in the first provider's shape, and the next provider may not accept it.

One shape out, the provider's own kept

On /v1/chat/completions, the Tempr Gateway returns a model's reasoning as reasoning text and reasoning_details on the message, or on delta when streaming. That's the shape OpenRouter returns, and it's the same for every model.

Where a provider has its own field, such as reasoning_content, that field comes back too. A client written for DeepSeek's API keeps working, and a client written for OpenRouter's shape works on DeepSeek.

json
{
  "role": "assistant",
  "content": null,
  "reasoning": "The test fails because the fixture is stale...",
  "reasoning_details": [ ... ],
  "reasoning_content": "The test fails because the fixture is stale...",
  "tool_calls": [ ... ]
}

To keep reasoning across a tool call, send the assistant message back with its reasoning_details unchanged. For Claude, Tempr turns them back into Claude's thinking blocks.

OpenAI's newest models are a special case. Tempr doesn't store responses, so when it bridges a request to OpenAI's Responses API it asks for the reasoning in encrypted form. That also comes back in reasoning_details, and goes back to OpenAI on the next request. The post on OpenAI's newest models explains the bridge.

Claude's signatures, kept intact

Claude Code and other clients built on Anthropic's SDK speak the Messages API, /v1/messages. Claude models on Anthropic pass straight through, so their thinking blocks are Anthropic's own.

Some Claude requests are translated rather than passed through: Claude on AWS Bedrock, and Anthropic models when a request asks for response caching. On those, Tempr keeps Claude's signature on each thinking block, and redacted thinking comes back as a redacted_thinking block. On Claude 4.7 and later the thinking text is empty, because Anthropic leaves it out by default, but the signature still holds the reasoning, so Tempr returns the block anyway. Send the blocks back unchanged, as Claude Code does, and Claude gets them on the next turn.

If Claude on Bedrock rejects a signature sent back in a later turn, Tempr retries once without that thinking instead of returning the error, as it does for Anthropic.

Other labs' reasoning as thinking blocks

When a Messages API request runs on a model outside Claude, such as DeepSeek, Z.AI, Moonshot or Xiaomi MiMo, its reasoning comes back as a thinking block ahead of the text and tool calls, with thinking_delta events when streaming. Claude Code sees reasoning the way it expects to.

These models don't sign their thinking, so Tempr gives the block the signature tempr:reasoning. Send it back unchanged and the model gets its text back as reasoning_content, which it needs across tool calls.

If a later turn goes to Claude, Tempr leaves those blocks out, because Claude would reject a signature it didn't write. Claude's own blocks stay. So a Claude Code session can move from Claude to DeepSeek and back without the history becoming something either side refuses.

Providers that reject what others send

Some providers are stricter than others about what an assistant turn can carry, and a conversation that has been on another model brings fields with it.

  • Fireworks AI rejects some of the reasoning fields an earlier turn can carry. Tempr turns reasoning into reasoning_content and removes reasoning_details before sending the request.
  • Mistral rejects them too, so Tempr removes reasoning, reasoning_details and reasoning_content from earlier turns.
  • Cerebras rejects fields such as prompt-cache markers, which a conversation that started on another model can carry. Tempr removes them before the request is sent.

In each case the request goes through instead of failing on history the new provider didn't write.

What doesn't carry over

Not every model's reasoning can travel. On the Messages API, a model that doesn't take its earlier reasoning back as reasoning_content doesn't get it. Its thinking is readable in the answer, but it isn't sent back, and the model answers the next turn reasoning afresh rather than failing.

And reasoning doesn't cross between labs. When a Messages API conversation moves from DeepSeek to Claude, Claude doesn't see DeepSeek's thinking, only the answers and tool calls. That's the price of a turn that works.

The details, field by field, are in the Reasoning and Messages API sections of the docs.