TanStack
Migration

Moving reasoning into chat()

Your modelOptions has a reasoning field, like reasoning: { effort }, thinking, or thinkingConfig, and now it does not type-check. That field moved to one option on chat(): reasoning. It takes a level (off to max) or { level, summary, budgetTokens }.

Move it

  1. Delete the reasoning field from modelOptions.
  2. Set reasoning on chat().
ts
// Before
chat({
  adapter: openaiText("gpt-6.1-sol"),
  messages,
  modelOptions: { reasoning: { effort: "high", summary: "auto" } },
});
ts
import { chat } from "@tanstack/ai";
import { openaiText } from "@tanstack/ai-openai";

const messages = [{ role: "user" as const, content: "Hello" }];

// After
chat({
  adapter: openaiText("gpt-6.1-sol"),
  messages,
  reasoning: "high",
});

The thinking text streams by default. Want it hidden? Pass reasoning: { level: "high", summary: false }.

Adapter by adapter

AdapterOld modelOptions fieldNow
openaiText, openaiChatCompletions, azureOpenaiTextreasoning: { effort, summary }reasoning: "high"
anthropicTextthinking, effort, output_config.effortreasoning: "high", or { level, budgetTokens } on budget models
geminiTextthinkingConfigreasoning: "high", or { level, budgetTokens } on Gemini 2.5
geminiTextInteractionsgeneration_config.thinking_level, thinking_summariesreasoning: "high"
grokText, openRouterTextreasoning: { effort }reasoning: "high"
vercelGatewayText, lovableTextreasoning, include_reasoningreasoning: "high"
bedrockTextreasoning_effort (chat), reasoning (Responses)reasoning: "high"
groqText, llmGatewayText, cloudflareTextreasoning_effortreasoning: "high"
byteplusTextthinking, reasoning_effortreasoning: "high", or "off"
ollamaTextthinkreasoning: "high", or "off"
codexTextmodelReasoningEffortreasoning: "high"

"high" is an example. Use any level that your model has. The types show which ones.

Budgets

An Anthropic or Gemini 2.5 budget moves to budgetTokens:

ts
// Before
modelOptions: { thinking: { type: "enabled", budget_tokens: 8000 } }
ts
import { chat } from "@tanstack/ai";
import { anthropicText } from "@tanstack/ai-anthropic";

const messages = [{ role: "user" as const, content: "Hello" }];

// After
chat({
  adapter: anthropicText("claude-haiku-4-5"),
  messages,
  reasoning: { level: "high", budgetTokens: 8000 },
});

The adapter raises max_tokens when it is below the budget.

Azure

azureOpenaiText and createAzureOpenaiText read the levels of the OpenAI model name that you pass, not of your deployment name:

ts
import { chat } from "@tanstack/ai";
import { createAzureOpenaiText } from "@tanstack/ai-openai";

const messages = [{ role: "user" as const, content: "Hello" }];

chat({
  adapter: createAzureOpenaiText("gpt-6.1-sol", "azure-key", {
    resourceName: "my-resource",
    deploymentName: "prod-chat",
  }),
  messages,
  reasoning: "high",
});

Pass only a deployment name as the model, and the adapter has no levels for it. Then reasoning is a type error. Pass the OpenAI model name and set deploymentName.

Codex

modelReasoningEffort stays on the codexText config. It is the default for a call without reasoning.

Removed types

  • @tanstack/ai-openai: OpenAIReasoningOptions, OpenAIReasoningOptionsWithConcise.
  • @tanstack/ai-anthropic: AnthropicThinkingOptions, AnthropicAdaptiveThinkingOptions, AnthropicAdaptiveOnlyThinkingOptions, AnthropicAdaptiveOrDisabledThinkingOptions, AnthropicEffortOptions, AnthropicOutputConfigOptions.
  • @tanstack/ai-gemini: GeminiThinkingOptions.
  • @tanstack/ai-grok: GrokReasoning, GrokReasoningEffort, GrokBuildProviderOptions.
  • @tanstack/ai-openrouter: ReasoningOptions. @tanstack/ai-byteplus: BytePlusThinkingOption, BytePlusReasoningEffort.

Done. Your reasoning now works the same on every provider. See Reasoning for levels, budgets, and middleware.