| name | ai-core/adapter-configuration |
| description | Provider adapter selection and configuration: openaiText, anthropicText, geminiText, ollamaText, grokText, groqText, openRouterText. Per-model type safety with modelOptions, reasoning/thinking configuration, runtime adapter switching, extendAdapter() for custom models, createModel(). API key env vars: OPENAI_API_KEY, ANTHROPIC_API_KEY, GOOGLE_API_KEY/GEMINI_API_KEY, XAI_API_KEY, GROQ_API_KEY, OPENROUTER_API_KEY, OLLAMA_HOST.
|
| type | sub-skill |
| library | tanstack-ai |
| library_version | 0.10.0 |
| sources | ["TanStack/ai:docs/adapters/openai.md","TanStack/ai:docs/adapters/anthropic.md","TanStack/ai:docs/adapters/gemini.md","TanStack/ai:docs/adapters/ollama.md","TanStack/ai:docs/advanced/per-model-type-safety.md","TanStack/ai:docs/advanced/runtime-adapter-switching.md","TanStack/ai:docs/advanced/extend-adapter.md"] |
Adapter Configuration
Dependency: This skill builds on ai-core. Read it first for critical rules.
Before implementing: Ask the user which provider and model they want.
Then fetch the latest available models from the provider's source code
(check the adapter's model metadata file, e.g. packages/typescript/ai-openai/src/model-meta.ts)
or from the provider's API/docs to recommend the most current model.
The model lists in this skill and its reference files may be outdated.
Always verify against the source before recommending a specific model.
Setup
Create an adapter and use it with chat():
import { chat, toServerSentEventsResponse } from '@tanstack/ai'
import { openaiText } from '@tanstack/ai-openai'
const stream = chat({
adapter: openaiText('gpt-5.2'),
messages,
temperature: 0.7,
maxTokens: 1000,
})
return toServerSentEventsResponse(stream)
The adapter factory function takes the model name as a string literal and an
optional config object (API key, base URL, etc.). The model name is passed
into the factory, not into chat().
Core Patterns
1. Adapter Selection
Each provider has a dedicated package with tree-shakeable adapter factories.
The text adapter is the primary one for chat/completions:
| Provider | Package | Factory | Env Var |
|---|
| OpenAI | @tanstack/ai-openai | openaiText | OPENAI_API_KEY |
| Anthropic | @tanstack/ai-anthropic | anthropicText | ANTHROPIC_API_KEY |
| Gemini | @tanstack/ai-gemini | geminiText | GOOGLE_API_KEY or GEMINI_API_KEY |
| Grok (xAI) | @tanstack/ai-grok | grokText | XAI_API_KEY |
| Groq | @tanstack/ai-groq | groqText | GROQ_API_KEY |
| OpenRouter | @tanstack/ai-openrouter | openRouterText | OPENROUTER_API_KEY |
| Ollama | @tanstack/ai-ollama | ollamaText | OLLAMA_HOST (default: http://localhost:11434) |
import { openaiText } from '@tanstack/ai-openai'
import { anthropicText } from '@tanstack/ai-anthropic'
import { geminiText } from '@tanstack/ai-gemini'
import { grokText } from '@tanstack/ai-grok'
import { groqText } from '@tanstack/ai-groq'
import { openRouterText } from '@tanstack/ai-openrouter'
import { ollamaText } from '@tanstack/ai-ollama'
const adapter = openaiText('gpt-5.2')
const adapter2 = anthropicText('claude-sonnet-4-6')
const adapter3 = geminiText('gemini-2.5-pro')
const adapter4 = grokText('grok-4')
const adapter5 = groqText('llama-3.3-70b-versatile')
const adapter6 = openRouterText('anthropic/claude-sonnet-4')
const adapter7 = ollamaText('llama3.3')
const adapterWithKey = openaiText('gpt-5.2', {
apiKey: 'sk-...',
})
2. Runtime Adapter Switching
Use an adapter factory map to switch providers dynamically based on user
input or configuration:
import { chat, toServerSentEventsResponse } from '@tanstack/ai'
import type { TextAdapter } from '@tanstack/ai/adapters'
import { openaiText } from '@tanstack/ai-openai'
import { anthropicText } from '@tanstack/ai-anthropic'
import { geminiText } from '@tanstack/ai-gemini'
const adapters: Record<string, () => TextAdapter> = {
'openai/gpt-5.2': () => openaiText('gpt-5.2'),
'anthropic/claude-sonnet-4-6': () => anthropicText('claude-sonnet-4-6'),
'gemini/gemini-2.5-pro': () => geminiText('gemini-2.5-pro'),
}
export function handleChat(providerModel: string, messages: Array<any>) {
const createAdapter = adapters[providerModel]
if (!createAdapter) {
throw new Error(`Unknown provider/model: ${providerModel}`)
}
const stream = chat({
adapter: createAdapter(),
messages,
})
return toServerSentEventsResponse(stream)
}
3. Configuring Reasoning / Thinking
Different providers expose reasoning/thinking through their modelOptions:
import { chat } from '@tanstack/ai'
import { openaiText } from '@tanstack/ai-openai'
import { anthropicText } from '@tanstack/ai-anthropic'
import { geminiText } from '@tanstack/ai-gemini'
const openaiStream = chat({
adapter: openaiText('gpt-5.2'),
messages,
modelOptions: {
reasoning: {
effort: 'high',
summary: 'auto',
},
},
})
const anthropicStream = chat({
adapter: anthropicText('claude-sonnet-4-6'),
messages,
maxTokens: 16000,
modelOptions: {
thinking: {
type: 'enabled',
budget_tokens: 8000,
},
},
})
const adaptiveStream = chat({
adapter: anthropicText('claude-sonnet-4-6'),
messages,
maxTokens: 16000,
modelOptions: {
thinking: {
type: 'adaptive',
},
effort: 'high',
},
})
const geminiStream = chat({
adapter: geminiText('gemini-2.5-pro'),
messages,
modelOptions: {
thinkingConfig: {
includeThoughts: true,
thinkingBudget: 4096,
},
},
})
4. Extending Adapters with Custom Models
Use extendAdapter() and createModel() to add custom or fine-tuned models
while preserving type safety for the original models:
import { extendAdapter, createModel } from '@tanstack/ai'
import { openaiText } from '@tanstack/ai-openai'
const customModels = [
createModel('ft:gpt-5.2:my-org:custom-model:abc123', ['text', 'image']),
createModel('my-local-proxy-model', ['text']),
] as const
const myOpenai = extendAdapter(openaiText, customModels)
const gpt5 = myOpenai('gpt-5.2')
const custom = myOpenai('ft:gpt-5.2:my-org:custom-model:abc123')
At runtime, extendAdapter simply passes through to the original factory.
The _customModels parameter is only used for type inference.
Common Mistakes
a. HIGH: Confusing legacy monolithic with tree-shakeable adapter
The legacy openai() (and anthropic(), etc.) monolithic adapters are
deprecated. They take the model in chat(), not in the factory.
import { openai } from '@tanstack/ai-openai'
chat({ adapter: openai(), model: 'gpt-5.2', messages })
import { openaiText } from '@tanstack/ai-openai'
chat({ adapter: openaiText('gpt-5.2'), messages })
Source: docs/migration/migration.md
b. MEDIUM: Wrong API key environment variable name
Each provider uses a specific env var name. Using the wrong one causes a
runtime error:
| Provider | Correct Env Var | Common Mistake |
|---|
| OpenAI | OPENAI_API_KEY | |
| Anthropic | ANTHROPIC_API_KEY | |
| Gemini | GOOGLE_API_KEY or GEMINI_API_KEY | GOOGLE_GENAI_API_KEY (does not work) |
| Grok (xAI) | XAI_API_KEY | GROK_API_KEY (does not work) |
| Groq | GROQ_API_KEY | |
| OpenRouter | OPENROUTER_API_KEY | |
| Ollama | OLLAMA_HOST | No API key needed, just the host URL (default: http://localhost:11434) |
Source: adapter source code (utils/client.ts in each adapter package).
References
Detailed per-adapter reference files:
Tension
HIGH Tension: Type safety vs. quick prototyping -- Per-model type safety
requires specific model string literals. Quick prototyping wants dynamic
selection with string variables. Agents optimizing for quick setup silently
lose type safety. If model names come from user input or config files, use
extendAdapter() to add custom names.
Cross-References
- See also:
ai-core/chat-experience/SKILL.md -- Adapter choice affects chat setup
- See also:
ai-core/structured-outputs/SKILL.md -- outputSchema handles provider differences transparently