ZTS Docs
AI

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:

export const ENABLED_AI_PROVIDERS = [
  "openai",
  "anthropic",
  "gemini",
  "mistral",
  "llama",
];

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 patternTypePurpose
aiProviderEnabled{providerId}booleanUser toggled this provider on
aiProviderKey{providerId}string?User's API key for that provider

Schema: packages/shared/src/types/user-preferences.ts

Helpers:

import { getProviderEnabledKey, getProviderApiKeyKey } from "@zts/shared/config/ai-providers";
 
getProviderEnabledKey("openai");  // "aiProviderEnabledopenai"
getProviderApiKeyKey("openai");   // "aiProviderKeyopenai"

Which providers appear in chat

listConfiguredAiProviders() (packages/shared/src/ai/list-configured-providers.ts) returns providers where:

  1. The provider is in ENABLED_AI_PROVIDERS, and
  2. aiProviderEnabled{id} is true, and
  3. aiProviderKey{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:

LevelBehavior
standardDefault — no extra reasoning budget
low / medium / highProvider-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:

import { resolveUserAiProvider } from "@zts/ai";
 
const resolved = resolveUserAiProvider(preferences, {
  providerId: "openai",  // optional — from chat UI
  modelId: "gpt-4o",     // optional
});
// → { providerId, modelId, apiKey } or an error

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

  1. Add an entry to AI_PROVIDERS in packages/shared/src/config/ai-providers.ts.
  2. Add the provider id to ENABLED_AI_PROVIDERS when ready to ship.
  3. Wire the SDK factory in packages/ai/src/create-language-model.ts (import the matching @ai-sdk/* package).
  4. Add models to PROVIDER_MODEL_CATALOG in packages/shared/src/ai/model-catalog.ts.
  5. Add the @ai-sdk/* dependency to packages/ai/package.json and run pnpm 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:

NEXT_PUBLIC_ENABLE_CHAT_PAGE=true

On this page