Skip to main content

ai-core-adapter-configuration

Provider adapter selection and configuration: openaiText, anthropicText, geminiText, ollamaText, grokText, groqText, openRouterText, bedrockText, byteplusText, openaiCompatible. Per-model type safety with modelOptions, reasoning/thinking configuration, runtime adapter switching, extendAdapter() for custom models, createModel(). Generic OpenAI-compatible providers (DeepSeek, Together, Fireworks, etc.) via openaiCompatible({ baseURL, apiKey, models }) from @tanstack/ai-openai/compatible. 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, BEDROCK_API_KEY (or AWS_BEARER_TOKEN_BEDROCK). BytePlus needs TWO keys: ARK_API_KEY (ModelArk — chat/video/image) and BYTEPLUS_VOICE_API_KEY (Seed Speech — TTS/transcription); neither is a fallback for the other.

Source facts

Repository
TanStack/ai
Last source activity
October 15, 2026 at 09:43
Detected SKILL.md language
English
Stars
3,199
Forks
366

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

File Explorer
9 files

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
ai-core/adapter-configuration
description
Provider adapter selection and configuration: openaiText, anthropicText, geminiText, ollamaText, grokText, groqText, openRouterText, bedrockText, byteplusText, openaiCompatible. Per-model type safety with modelOptions, reasoning/thinking configuration, runtime adapter switching, extendAdapter() for custom models, createModel(). Generic OpenAI-compatible providers (DeepSeek, Together, Fireworks, etc.) via openaiCompatible({ baseURL, apiKey, models }) from @tanstack/ai-openai/compatible. 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, BEDROCK_API_KEY (or AWS_BEARER_TOKEN_BEDROCK). BytePlus needs TWO keys: ARK_API_KEY (ModelArk — chat/video/image) and BYTEPLUS_VOICE_API_KEY (Seed Speech — TTS/transcription); neither is a fallback for the other.
type
sub-skill
library
tanstack-ai
library_version
0.42.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/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()`: ```typescript import { chat, toServerSentEventsResponse } from '@tanstack/ai' import { openaiText } from '@tanstack/ai-openai' export async function POST(request: Request) { const { messages } = await request.json() const stream = chat({ adapter: openaiText('gpt-5.2'), messages, modelOptions: { temperature: 0.7, max_output_tokens: 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()`. Sampling options (`temperature`, token limits, `top_p`/`topP`, etc.) live inside `modelOptions` using each provider's native key — they are **not** top-level options on `chat()`. See the per-provider table in [Configuring Sampling](#5-configuring-sampling) below. ## 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`) | | Bedrock | `@tanstack/ai-bedrock` | `bedrockText` | `BEDROCK_API_KEY` or `AWS_BEARER_TOKEN_BEDROCK` | | BytePlus | `@tanstack/ai-byteplus` | `byteplusText` | `ARK_API_KEY` (falls back to `BYTEPLUS_API_KEY`) | | OpenAI-compatible | `@tanstack/ai-openai/compatible` | `openaiCompatible` / `openaiCompatibleText` | provider-specific (passed via `apiKey`) | | Cloudflare | `@tanstack/ai-cloudflare` | `cloudflareText` | `CLOUDFLARE_ACCOUNT_ID` + `CLOUDFLARE_API_TOKEN`, or `{ binding: env.AI }` in a Worker | > **BytePlus uses two keys.** `byteplusText` / `byteplusVideo` / > `byteplusImage` read `ARK_API_KEY` (ModelArk, `Authorization: Bearer`), but > `byteplusSpeech` / `byteplusTranscription` are a separate product and read > **`BYTEPLUS_VOICE_API_KEY`** (Seed Speech, `X-Api-Key`). Ark keys are also > region-isolated — the default base URL is the ap-southeast endpoint. ```typescript // Each factory takes model as first arg, optional config as second import { openaiText, createOpenaiChat } 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' import { bedrockText } from '@tanstack/ai-bedrock' import { byteplusText } from '@tanstack/ai-byteplus' // Model string is passed to the factory, NOT to chat() 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.6') const adapter5 = groqText('llama-3.3-70b-versatile') const adapter6 = openRouterText('anthropic/claude-sonnet-4') const adapter7 = ollamaText('llama3.3:latest') const adapter8 = bedrockText('us.anthropic.claude-3-7-sonnet-20250219-v1:0') const adapter9 = byteplusText('seed-2-0-lite-260428') // Optional: pass an explicit API key via the create* sibling // (the plain factory reads it from the environment) const adapterWithKey = createOpenaiChat('gpt-5.2', 'sk-...') ``` `@tanstack/ai-bedrock` (Amazon Bedrock) branches on `config.api`: - `bedrockText(model)` or `bedrockText(model, { api: 'converse' })` (the default) — Bedrock's native Converse API via `@aws-sdk/client-bedrock-runtime` (adapter name `bedrock-converse`). Reaches the broad catalog: Claude, Nova, Llama, Mistral, DeepSeek, and more. - `bedrockText(model, { api: 'chat' })` — OpenAI-compatible Chat Completions endpoint (adapter name `bedrock`). Open-weight models only (gpt-oss, DeepSeek V3.x, Gemma, Qwen, etc.). Does NOT reach Claude, Nova, or Llama. - `bedrockText(model, { api: 'responses' })` — OpenAI-compatible Responses API, mantle-only (adapter name `bedrock-responses`). Currently gpt-oss and Gemma 4. Use `createBedrockText(model, apiKey, config?)` to pass the key explicitly. Auth resolves from `BEDROCK_API_KEY` / `AWS_BEARER_TOKEN_BEDROCK`, or SigV4 via the standard AWS credential chain (no extra packages needed — handled by `@aws-sdk/client-bedrock-runtime`). ### 2. Runtime Adapter Switching Use an adapter factory map to switch providers dynamically based on user input or configuration: ```typescript import { chat, toServerSentEventsResponse } from '@tanstack/ai' import type { ModelMessage } from '@tanstack/ai' import { openaiText } from '@tanstack/ai-openai' import { anthropicText } from '@tanstack/ai-anthropic' import { geminiText } from '@tanstack/ai-gemini' // Define a map of provider+model to adapter factory calls const adapters = { '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'), } function isKnownProviderModel(key: string): key is keyof typeof adapters { return key in adapters } export function handleChat( providerModel: string, messages: Array<ModelMessage>, ) { if (!isKnownProviderModel(providerModel)) { throw new Error(`Unknown provider/model: ${providerModel}`) } const stream = chat({ adapter: adapters[providerModel](), messages, }) return toServerSentEventsResponse(stream) } ``` ### 3. Configuring Reasoning / Thinking Set reasoning/thinking with the `reasoning` option on `chat()`. It is the same on every provider, and the types take only the levels of the selected model: ```typescript import { chat } from '@tanstack/ai' import { openaiText } from '@tanstack/ai-openai' import { anthropicText } from '@tanstack/ai-anthropic' import { geminiText } from '@tanstack/ai-gemini' const messages = [ { role: 'user' as const, content: 'Plan a database migration.' }, ] // OpenAI: the level goes out as reasoning.effort, with a summary const openaiStream = chat({ adapter: openaiText('gpt-5.2'), messages, reasoning: { level: 'high', summary: true }, }) // Anthropic: extended thinking with a token budget const anthropicStream = chat({ adapter: anthropicText('claude-sonnet-4-6'), messages, modelOptions: { max_tokens: 16000 }, reasoning: { level: 'high', budgetTokens: 8000 }, }) // Anthropic: adaptive thinking (Sonnet 5, Fable 5, Opus 4.7+) — the level // goes out as output_config.effort const adaptiveStream = chat({ adapter: anthropicText('claude-sonnet-5'), messages, modelOptions: { max_tokens: 16000 }, reasoning: 'high', // 'low' | 'medium' | 'high' | 'xhigh' | 'max' }) // Gemini: a thinking budget on Gemini 2.5, a thinking level on Gemini 3 const geminiStream = chat({ adapter: geminiText('gemini-2.5-pro'), messages, reasoning: { level: 'high', budgetTokens: 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: ```typescript import { extendAdapter, createModel } from '@tanstack/ai' import { openaiText } from '@tanstack/ai-openai' // Define custom models const customModels = [ createModel('ft:gpt-5.2:my-org:custom-model:abc123', ['text', 'image']), createModel('my-local-proxy-model', ['text']), ] as const // Create extended factory - original models still fully typed const myOpenai = extendAdapter(openaiText, customModels) // Use original models - full type inference preserved const gpt5 = myOpenai('gpt-5.2') // Use custom models - accepted by the type system const custom = myOpenai('ft:gpt-5.2:my-org:custom-model:abc123') // Type error: 'nonexistent-model' is not a valid model // myOpenai('nonexistent-model') ``` At runtime, `extendAdapter` simply passes through to the original factory. The `_customModels` parameter is only used for type inference. ### 5. Configuring Sampling Sampling controls (`temperature`, token limits, nucleus sampling) are passed inside `modelOptions` using each provider's **native** key. They are not top-level fields on `chat()`/`ai()`/`generate()`. ```typescript import { chat } from '@tanstack/ai' import { openaiText } from '@tanstack/ai-openai' import { anthropicText } from '@tanstack/ai-anthropic' import { geminiText } from '@tanstack/ai-gemini' import { ollamaText } from '@tanstack/ai-ollama' const messages = [{ role: 'user' as const, content: 'Hello' }] // OpenAI — native keys chat({ adapter: openaiText('gpt-5.2'), messages, modelOptions: { temperature: 0.7, top_p: 0.9, max_output_tokens: 1000 }, }) // Anthropic chat({ adapter: anthropicText('claude-sonnet-4-6'), messages, modelOptions: { temperature: 0.7, top_p: 0.9, max_tokens: 1000 }, }) // Gemini — camelCase chat({ adapter: geminiText('gemini-2.5-pro'), messages, modelOptions: { temperature: 0.7, topP: 0.9, maxOutputTokens: 1000 }, }) // Ollama — NESTED under modelOptions.options // (use the `family:tag` id — a bare `llama3.3` falls back to untyped options) chat({ adapter: ollamaText('llama3.3:latest'), messages, modelOptions: { options: { temperature: 0.7, top_p: 0.9, num_predict: 1000 }, }, }) ``` Per-provider sampling keys (all live inside `modelOptions`): | Provider | Temperature | Nucleus | Max output tokens | | ----------------- | ------------- | ------- | ----------------------------------------- |
View on GitHub
This SKILL.md is very large, so SkillsMP previews the first section here. View on GitHub