The Vercel AI Chatbot supports multiple AI model providers through Vercel AI Gateway. You can add custom models, configure model-specific features, and create specialized models for different tasks.
Model configuration
All available chat models are defined in lib/ai/models.ts:
export const DEFAULT_CHAT_MODEL = "openai/gpt-4.1-mini" ;
export type ChatModel = {
id : string ;
name : string ;
provider : string ;
description : string ;
};
export const chatModels : ChatModel [] = [
// Anthropic
{
id: "anthropic/claude-haiku-4.5" ,
name: "Claude Haiku 4.5" ,
provider: "anthropic" ,
description: "Fast and affordable, great for everyday tasks" ,
},
// OpenAI
{
id: "openai/gpt-4.1-mini" ,
name: "GPT-4.1 Mini" ,
provider: "openai" ,
description: "Fast and cost-effective for simple tasks" ,
},
// Google
{
id: "google/gemini-2.5-flash-lite" ,
name: "Gemini 2.5 Flash Lite" ,
provider: "google" ,
description: "Ultra fast and affordable" ,
},
// Reasoning models
{
id: "anthropic/claude-3.7-sonnet-thinking" ,
name: "Claude 3.7 Sonnet" ,
provider: "reasoning" ,
description: "Extended thinking for complex problems" ,
},
];
Model IDs follow the Vercel AI Gateway format: provider/model-name
"anthropic/claude-haiku-4.5"
"anthropic/claude-3.7-sonnet-thinking"
Adding a custom model
Add model to configuration
Add your model to the chatModels array in lib/ai/models.ts: export const chatModels : ChatModel [] = [
// ... existing models
{
id: "openai/gpt-5-turbo" ,
name: "GPT-5 Turbo" ,
provider: "openai" ,
description: "Latest OpenAI model with improved reasoning" ,
},
];
Verify model availability
The model will automatically appear in the model selector UI. The allowedModelIds Set is generated from the chatModels array: export const allowedModelIds = new Set ( chatModels . map (( m ) => m . id ));
Configure provider settings (optional)
If your model requires special configuration, modify lib/ai/providers.ts: export function getLanguageModel ( modelId : string ) {
// Add custom logic for your model
if ( modelId === "openai/gpt-5-turbo" ) {
return gateway . languageModel ( modelId , {
// Custom configuration
});
}
return gateway . languageModel ( modelId );
}
Model providers
The chatbot uses Vercel AI Gateway to route requests to different model providers:
import { gateway } from "@ai-sdk/gateway" ;
import {
customProvider ,
extractReasoningMiddleware ,
wrapLanguageModel ,
} from "ai" ;
export function getLanguageModel ( modelId : string ) {
const isReasoningModel =
modelId . endsWith ( "-thinking" ) ||
( modelId . includes ( "reasoning" ) && ! modelId . includes ( "non-reasoning" ));
if ( isReasoningModel ) {
const gatewayModelId = modelId . replace ( /-thinking $ / , "" );
return wrapLanguageModel ({
model: gateway . languageModel ( gatewayModelId ),
middleware: extractReasoningMiddleware ({ tagName: "thinking" }),
});
}
return gateway . languageModel ( modelId );
}
Reasoning models
Models with extended thinking capabilities are detected automatically and configured with reasoning middleware:
Models ending in -thinking
Models containing reasoning (but not non-reasoning)
These models extract reasoning traces with the <thinking> tag.
Specialized models
The chatbot uses different models for specific tasks:
Title generation
export function getTitleModel () {
return gateway . languageModel ( "google/gemini-2.5-flash-lite" );
}
Used in app/(chat)/actions.ts to generate chat titles from user messages.
Artifact generation
export function getArtifactModel () {
return gateway . languageModel ( "anthropic/claude-haiku-4.5" );
}
Used for creating and updating documents, generating suggestions, and other artifact-related tasks.
Use faster, more affordable models for simple tasks like title generation and suggestions to reduce costs while maintaining quality.
Model grouping
Models are grouped by provider for display in the UI:
export const modelsByProvider = chatModels . reduce (
( acc , model ) => {
if ( ! acc [ model . provider ]) {
acc [ model . provider ] = [];
}
acc [ model . provider ]. push ( model );
return acc ;
},
{} as Record < string , ChatModel []>
);
This creates groups like:
anthropic: Claude models
openai: GPT models
google: Gemini models
reasoning: Extended thinking models
xai: Grok models
Reasoning model configuration
Reasoning models receive special configuration in the chat API:
app/(chat)/api/chat/route.ts
const isReasoningModel =
selectedChatModel . endsWith ( "-thinking" ) ||
( selectedChatModel . includes ( "reasoning" ) &&
! selectedChatModel . includes ( "non-reasoning" ));
const result = streamText ({
model: getLanguageModel ( selectedChatModel ),
system: systemPrompt ({ selectedChatModel , requestHints }),
messages: modelMessages ,
experimental_activeTools: isReasoningModel
? [] // Disable tools for reasoning models
: [ "getWeather" , "createDocument" , "updateDocument" , "requestSuggestions" ],
providerOptions: isReasoningModel
? {
anthropic: {
thinking: { type: "enabled" , budgetTokens: 10_000 },
},
}
: undefined ,
});
Reasoning configuration options
Budget tokens Set the maximum tokens allocated for reasoning traces (default: 10,000)
Tool availability Tools are disabled for reasoning models to prevent distraction during deep analysis
Reasoning extraction The extractReasoningMiddleware extracts <thinking> tags and exposes them separately from the final response
Test environment models
For testing, you can use mock models:
export const myProvider = isTestEnvironment
? (() => {
const {
artifactModel ,
chatModel ,
reasoningModel ,
titleModel ,
} = require ( "./models.mock" );
return customProvider ({
languageModels: {
"chat-model" : chatModel ,
"chat-model-reasoning" : reasoningModel ,
"title-model" : titleModel ,
"artifact-model" : artifactModel ,
},
});
})()
: null ;
Provider-specific features
Anthropic extended thinking
Claude models with -thinking suffix support extended reasoning:
const result = streamText ({
model: getLanguageModel ( "anthropic/claude-3.7-sonnet-thinking" ),
providerOptions: {
anthropic: {
thinking: {
type: "enabled" ,
budgetTokens: 10_000 , // Adjust based on complexity
},
},
},
});
Streaming reasoning to UI
Reasoning traces are streamed separately from the main response:
app/(chat)/api/chat/route.ts
dataStream . merge (
result . toUIMessageStream ({ sendReasoning: isReasoningModel })
);
The UI displays reasoning in a collapsible section using the MessageReasoning component.
Model selection UI
Users can switch models using the ModelSelector component:
components/ai-elements/model-selector.tsx
import { chatModels , modelsByProvider } from "@/lib/ai/models" ;
export function ModelSelector () {
return (
< Select >
{ Object . entries ( modelsByProvider ). map (([ provider , models ]) => (
< SelectGroup key = { provider } >
< SelectLabel > { provider } </ SelectLabel >
{ models . map (( model ) => (
< SelectItem key = { model . id } value = { model . id } >
{ model . name }
< span className = "text-muted-foreground" > { model . description } </ span >
</ SelectItem >
)) }
</ SelectGroup >
)) }
</ Select >
);
}
Best practices
Choose appropriate models : Use smaller, faster models (like Haiku or Flash) for simple tasks and larger models (like Sonnet or Pro) for complex reasoning.
Cost optimization : Configure different models for different use cases:
Title generation: Fast, cheap models
Document creation: Balanced models
Complex reasoning: Premium models
Test thoroughly : Always test new models in a development environment before deploying to production. Different models may have different capabilities and limitations.