AI providers
Provider catalog, user API keys, models, and intelligence levels.
AI provider keys are per-user, stored in user preferences — not in server environment variables. Each signed-in user enables providers and pastes their own keys in Settings → AI.
Provider catalog
The full list of known providers lives in packages/shared/src/config/ai-providers.ts as AI_PROVIDERS. Each entry has an id, display name, and capability flags (tool usage, vision, image generation, etc.).
Only a subset is wired in the backend:
The settings UI and chat provider picker only offer providers in ENABLED_AI_PROVIDERS. Additional entries in AI_PROVIDERS (xAI, Groq, Cohere, Azure, Bedrock, …) are catalog placeholders for future wiring.
User preference keys
For every provider in AI_PROVIDERS, the preference schema dynamically adds:
| Key pattern | Type | Purpose |
|---|---|---|
aiProviderEnabled{providerId} | boolean | User toggled this provider on |
aiProviderKey{providerId} | string? | User's API key for that provider |
Schema: packages/shared/src/types/user-preferences.ts
Helpers:
Which providers appear in chat
listConfiguredAiProviders() (packages/shared/src/ai/list-configured-providers.ts) returns providers where:
- The provider is in
ENABLED_AI_PROVIDERS, and aiProviderEnabled{id}istrue, andaiProviderKey{id}is non-empty.
The web hook useConfiguredAiProviders wraps this for the chat header dropdown.
Model catalog
Per-provider models, labels, and supported intelligence levels are defined in packages/shared/src/ai/model-catalog.ts (PROVIDER_MODEL_CATALOG).
Intelligence controls extended thinking / reasoning where the provider supports it:
| Level | Behavior |
|---|---|
standard | Default — no extra reasoning budget |
low / medium / high | Provider-specific thinking options |
Implementation: packages/ai/src/provider-options.ts maps levels to OpenAI reasoning models, Anthropic thinking budgets, Gemini thinkingConfig, etc. Invalid combinations are clamped to standard via resolveIntelligenceForModel().
Resolving a provider at runtime
resolveUserAiProvider() in @zts/ai picks the provider from an explicit request or the first enabled + keyed provider in preference order:
createLanguageModel() then instantiates the Vercel AI SDK model for that provider + key.
Settings UI
apps/web/src/components/settings/SettingsTabAI/settings-tab-ai.tsx renders toggles and key inputs filtered by ENABLED_AI_PROVIDERS.
Keys are never logged or returned to other users; they are read server-side only in /api/chat.
Adding a new provider
- Add an entry to
AI_PROVIDERSinpackages/shared/src/config/ai-providers.ts. - Add the provider
idtoENABLED_AI_PROVIDERSwhen ready to ship. - Wire the SDK factory in
packages/ai/src/create-language-model.ts(import the matching@ai-sdk/*package). - Add models to
PROVIDER_MODEL_CATALOGinpackages/shared/src/ai/model-catalog.ts. - Add the
@ai-sdk/*dependency topackages/ai/package.jsonand runpnpm install.
Preference keys are generated automatically from AI_PROVIDERS — no manual schema edits needed.
Environment variables
There are no OPENAI_API_KEY-style vars in .env.example for chat. Users supply keys via the UI.
Server env still needs DATABASE_URL, BETTER_AUTH_SECRET, etc. for sessions and persisted chats. Feature flag:
Related
- AI overview
- Chat — how resolved providers reach the UI
- Agents — model passed into
ToolLoopAgent