AI Providers

Configure Anthropic, OpenAI, Google, and xAI — model aliases, auto-detection, fallback chains, and provider capabilities

Kit supports four AI providers through a unified interface. Each provider implements the same BaseProvider abstract class — switching providers requires only changing an environment variable. This page covers provider setup, model configuration, auto-detection, fallback chains, and capabilities.

Quick Setup

Available Providers

API Key: OPENAI_API_KEY Default Model: gpt-5-nano Base URL Override: OPENAI_BASE_URL
ModelContextInput (per 1M)Output (per 1M)Best For
gpt-5.2400K$1.75$14.00Latest flagship reasoning
gpt-5400K$1.25$10.00Coding and agentic tasks
gpt-5-mini400K$0.25$2.00Well-defined tasks
gpt-5-nano400K$0.05$0.40Default — fast, cheapest
gpt-4.11M$2.00$8.00Complex tasks, coding
gpt-4.1-mini1M$0.40$1.60Efficient coding
o3200K$2.00$8.00Deep reasoning
o4-mini200K$1.10$4.40Reasoning on a budget
OpenAI is required for RAG embeddings (text-embedding-3-small), even when using another provider for chat.

Auto-Detection

When AI_PROVIDER is not set, Kit automatically selects the provider based on which API keys are available. The detection order is: Anthropic → OpenAI → Google → xAI.
src/lib/ai/config.ts — Provider Auto-Detection
export function getActiveProvider(): AIProvider | null {
  // If provider is explicitly set, use it (AI_API_KEY can serve as its key)
  if (aiConfig.AI_PROVIDER) {
    return aiConfig.AI_PROVIDER
  }

  // Auto-detect based on available API keys (Anthropic preferred)
  if (aiConfig.ANTHROPIC_API_KEY) return 'anthropic'
  if (aiConfig.OPENAI_API_KEY) return 'openai'
  if (aiConfig.GOOGLE_AI_API_KEY) return 'google'
  if (aiConfig.XAI_API_KEY) return 'xai'

  return null
}
To force a specific provider, set AI_PROVIDER explicitly:
bash
# Force Anthropic even if OpenAI key is also present
AI_PROVIDER=anthropic

Model Aliases

Kit provides human-friendly aliases for model names. Use claude instead of claude-sonnet-4-5-20250929, or gpt4 instead of gpt-4.1:
src/lib/ai/config.ts — Model Aliases
export const MODEL_ALIASES: Record<string, string> = {
  // OpenAI aliases
  gpt5: 'gpt-5',
  'gpt5.2': 'gpt-5.2',
  'gpt5-mini': 'gpt-5-mini',
  'gpt5-nano': 'gpt-5-nano',
  gpt4: 'gpt-4.1',
  'gpt4-mini': 'gpt-4.1-mini',
  'gpt-4': 'gpt-4.1',
  o3: 'o3',
  'o4-mini': 'o4-mini',

  // Anthropic aliases
  claude: 'claude-sonnet-4-5-20250929',
  'claude-opus': 'claude-opus-4-6-20260205',
  'claude-sonnet': 'claude-sonnet-4-5-20250929',
  'claude-haiku': 'claude-haiku-4-5-20251001',
  opus: 'claude-opus-4-6-20260205',
  sonnet: 'claude-sonnet-4-5-20250929',
  haiku: 'claude-haiku-4-5-20251001',

  // Google aliases
  gemini: 'gemini-2.5-flash',
  'gemini-pro': 'gemini-2.5-pro',
  'gemini-flash': 'gemini-2.5-flash',
  'gemini-lite': 'gemini-2.5-flash-lite',
  'gemini-3': 'gemini-3-flash-preview',

  // xAI aliases
  grok: 'grok-4-1-fast-reasoning',
  'grok-code': 'grok-code-fast-1',
  'grok-fast': 'grok-4-fast-reasoning',
  'grok-4.1': 'grok-4-1-fast-reasoning',
}

Provider Factory

The provider factory creates and caches provider instances. It validates configuration with Zod, creates the correct provider class, and caches instances by a SHA-256 hash of the API key:
src/lib/ai/provider-factory.ts — Factory with Cache
export function createAIProvider(config: ProviderConfig): BaseProvider {
  // Validate configuration
  const validation = ProviderConfigSchema.safeParse(config)

  if (!validation.success) {
    const errors = validation.error.issues.map((err) => ({
      field: err.path.join('.'),
      message: err.message,
    }))
    throw new ValidationError('Invalid provider configuration', errors)
  }

  const validatedConfig = validation.data

  // Check cache
  const cacheKey = createCacheKey(validatedConfig)
  const cached = providerCache.get(cacheKey)
  if (cached) {
    return cached
  }

  // Create new provider instance
  let provider: BaseProvider

  switch (validatedConfig.provider) {
    case 'openai':
      provider = new OpenAIProvider(validatedConfig)
      break

    case 'anthropic':
      provider = new AnthropicProvider(validatedConfig)
      break

    case 'google':
      provider = new GoogleProvider(validatedConfig)
      break

    case 'xai':
      provider = new XAIProvider(validatedConfig)
      break

    default:
      // This should never happen due to validation, but TypeScript needs it
      throw new InvalidProviderError(validatedConfig.provider)
  }

  // Cache the provider instance
  providerCache.set(cacheKey, provider)

  return provider
}
Key behaviors:
  • Validation first — Zod schema validates provider, API key, and model before creating an instance
  • Cache by config hash — Same provider + API key + model returns the cached instance
  • Switch statement — Maps provider string to concrete class (OpenAI, Anthropic, Google, xAI)

Fallback Chains

When the preferred provider is unavailable, Kit falls back to the best available alternative using a scoring algorithm. Each provider is scored based on the request's requirements:
src/lib/ai/provider-factory.ts — Provider Selection
export interface ProviderRequirements {
  needsVision?: boolean
  needsEmbeddings?: boolean
  needsFunctions?: boolean
  needsLargeContext?: boolean
  preferCheap?: boolean
  preferFast?: boolean
}

/**
 * Select the best provider based on requirements
 */
export function selectProvider(
  requirements: ProviderRequirements = {}
): AIProvider {
  const available = getAvailableProviders()

  if (available.length === 0) {
    throw new ValidationError('No AI providers configured', [
      {
        field: 'API_KEYS',
        message: 'At least one provider API key must be set',
      },
    ])
  }

  // Score each provider based on requirements
  const scores: Record<AIProvider, number> = {
    openai: 0,
    anthropic: 0,
    google: 0,
    xai: 0,
  }

  for (const provider of available) {
    let score = 10 // Base score for being available

    if (requirements.needsVision) {
      if (
        provider === 'openai' ||
        provider === 'anthropic' ||
        provider === 'google'
      ) {
        score += 5
      }
    }

    if (requirements.needsEmbeddings) {
      if (provider === 'openai' || provider === 'google') {
        score += 5
      }
    }

    if (requirements.needsFunctions) {
      // All providers support functions
      score += 1
    }

    if (requirements.needsLargeContext) {
      if (provider === 'anthropic' || provider === 'google') {
        score += 5 // Both have 1M+ context windows
      }
    }

    if (requirements.preferCheap) {
      if (provider === 'google') {
        score += 10 // Gemini Flash is very cheap
      } else if (provider === 'openai') {
        score += 5 // GPT-4o-mini is cheap
      }
    }

    if (requirements.preferFast) {
      if (provider === 'openai' || provider === 'google') {
        score += 5
      }
    }

    scores[provider] = score
  }

  // Find provider with highest score
  let bestProvider: AIProvider = available[0]
  let bestScore = scores[bestProvider]

  for (const provider of available) {
    if (scores[provider] > bestScore) {
      bestProvider = provider
      bestScore = scores[provider]
    }
  }

  return bestProvider
}
The scoring system considers:
RequirementTop ScorersScore Bonus
needsVisionOpenAI, Anthropic, Google+5
needsEmbeddingsOpenAI, Google+5
needsLargeContextAnthropic, Google (1M+)+5
preferCheapGoogle (+10), OpenAI (+5)+5 to +10
preferFastOpenAI, Google+5
Every available provider gets a base score of 10. The provider with the highest total score is selected.

Provider Capabilities

Not all providers support all features. The capability matrix helps Kit make informed fallback decisions:
CapabilityOpenAIAnthropicGooglexAI
StreamingYesYesYesYes
Functions/ToolsYesYesYesYes
VisionYesYesYesNo
EmbeddingsYesNoYesNo
System MessagesYesYesYesYes
Max Context1M200K (1M beta)1M2M

Reasoning Model Handling

GPT-5 family and o-series models are reasoning models with special parameter constraints. The isReasoningModel flag on ModelInfo controls runtime behavior:
ModelReasoningTemperatureDefault maxTokens
GPT-5, GPT-5.2, GPT-5 Mini, GPT-5 NanoYesUnsupported16,384
o3, o4-miniYesUnsupported16,384
GPT-4.1No0.7 (default)1,000
Key constraints:
  • Temperature is unsupported — passing it to reasoning models triggers an SDK warning and may degrade behavior. Kit omits temperature entirely for reasoning models.
  • maxTokens covers BOTH internal reasoning AND visible output — reasoning models use most of the token budget for internal chain-of-thought. The default of 1,000 is far too low; Kit uses 16,384 for reasoning models.
  • Symptom of too-low maxTokens: finishReason: 'length' with 0 output tokens → empty response to the user.
typescript
// Reasoning-aware parameter handling (openai.ts)
const isReasoning = modelInfo?.isReasoningModel === true
streamText({
  ...(isReasoning ? {} : { temperature: options.temperature ?? 0.7 }),
  maxTokens: options.maxTokens ?? (isReasoning ? 16384 : 1000),
})

Streaming Response Diagnostics

streamText() from the Vercel AI SDK returns lazy Promises that resolve after the stream ends. Kit reads these for diagnostics:
PropertyTypePurpose
result.finishReasonPromise<string>Why the stream ended ('stop', 'length', 'content_filter')
result.usagePromise<object>Token counts (promptTokens, completionTokens)
result.warningsWarning[]Unsupported parameters, model deprecations
finishReason values:
ValueMeaningAction
'stop'Normal completionNone
'length'Token budget exhaustedIncrease maxTokens
'content_filter'Provider blocked the responseReview content policy
Kit logs a warning when the stream completes with 0 content chunks or a non-'stop' finish reason. This diagnostic data is essential for debugging empty AI responses.

Custom Base URLs

Each provider supports a custom base URL for proxies, self-hosted models, or alternative endpoints:
VariableDefaultPurpose
OPENAI_BASE_URLhttps://api.openai.com/v1OpenAI API proxy or compatible endpoint
ANTHROPIC_BASE_URLhttps://api.anthropic.comAnthropic API proxy
GOOGLE_AI_BASE_URLhttps://generativelanguage.googleapis.com/v1Google AI proxy
XAI_BASE_URLhttps://api.x.ai/v1xAI API proxy
OPENAI_ORG_IDOpenAI organization ID for billing

Error Handling and Retries

All providers share the same retry logic from BaseProvider. Failed requests are retried with exponential backoff:
src/lib/ai/providers/base-provider.ts — Retry Configuration
export abstract class BaseProvider {
  protected readonly provider: AIProvider
  protected readonly apiKey: string
  protected readonly baseURL?: string
  protected readonly timeout: number
  protected readonly retryConfig: RetryConfig
  protected defaultModel: string
  protected defaultTemperature: number
  protected defaultMaxTokens: number

  constructor(config: ProviderConfig) {
    this.provider = config.provider
    this.apiKey = config.apiKey
    this.baseURL = config.baseURL
    this.timeout = config.timeout ?? 30000 // 30 seconds default
    this.defaultModel = config.model ?? this.getDefaultModel()
    this.defaultTemperature = config.defaultTemperature ?? 0.7
    this.defaultMaxTokens = config.defaultMaxTokens ?? 1000

    this.retryConfig = {
      maxRetries: config.maxRetries ?? 3,
      baseDelay: 1000,
      maxDelay: 10000,
      backoffFactor: 2,
    }
  }
The retry behavior:
  • Max retries: 3 (configurable per provider)
  • Base delay: 1,000ms
  • Max delay: 10,000ms (cap)
  • Backoff factor: 2x (1s → 2s → 4s)
  • Jitter: 0-30% random variation to avoid thundering herd
  • Timeout: 30 seconds per request (configurable)
Only retryable errors trigger retries — network timeouts and 5xx responses. Client errors (400, 401, 403) fail immediately.
The error class hierarchy provides structured error information:
Error ClassWhenRetryable
AIProviderErrorBase class for all provider errorsVaries
NetworkErrorConnection failures, DNS errorsYes
TimeoutErrorRequest exceeds timeoutYes
ValidationErrorInvalid config or requestNo
InvalidProviderErrorUnknown provider stringNo