AI configuration
Configuration reference, credentials, self-hosted endpoints and the model picker.
On this page
The AI assistant is configured entirely through the ai plugin option. This page covers self-hosted endpoints, offering several models, every option, and how credentials travel.
Self-hosted / custom endpoints (Ollama, vLLM, …)Link to this section
Any OpenAI-compatible endpoint works for text generation:
ai: {
text: {
provider: 'openai-compatible',
baseURL: 'http://my-server:11434/v1', // e.g. Ollama
model: 'gemma3',
// apiKey optional — self-hosted endpoints often run keyless
},
}Multiple models (model picker)Link to this section
Offer several models per kind and let editors choose one per generation. Pass a models array on ai.text and/or ai.image instead of (or alongside — the array wins) the flat fields:
ai: {
packageName: 'Systhema AI Pro',
text: {
models: [
{ provider: 'openai', model: 'gpt-5.5', apiKey: process.env.OPENAI_API_KEY, label: 'GPT-5.5' },
{ provider: 'openai', model: 'gpt-5-mini', apiKey: process.env.OPENAI_API_KEY, label: 'GPT-5 mini' },
// A showcase teaser — shown disabled with the hint, never usable:
{ provider: 'anthropic', model: 'claude-opus-4-8', label: 'Claude Opus 4.8', locked: true, upgradeHint: 'Upgrade to Max' },
],
},
image: {
models: [
{ provider: 'openai', model: 'gpt-image-2' }, // reuses the OpenAI text key
{ provider: 'google', model: 'gemini-3-pro-image', label: 'Nano Banana Pro', locked: true, upgradeHint: 'Upgrade to Max' },
],
},
quota: { credits: 5000 },
}- The first usable, unlocked entry is the default.
lockedentries skip credential validation — they're advertising what a higher plan unlocks. - Editors pick from an always-visible dropdown in the compose, lexical, page and image panels; the choice is a per-user preference (reconciled against the live config — a removed or locked model falls back to the default). The one-click SEO and alt-text triggers always use the default model.
- The dropdown is hidden when there's only one selectable entry, so single-model setups look exactly as before.
- In credit mode, each text model shows a relative credit hint (e.g.
2x compared to Default, computed from list prices) and each image model a per-image estimate at the chosen resolution (e.g.approx. 63 credits). Never USD. - The picked model id is validated server-side on the
/generateand/generate-imageendpoints (the one-click SEO and alt-text triggers carry no model id — they always use the default): a locked or unknown id is rejected with HTTP 400 — the picker is the trust boundary, not the client. - The single-model flat form is unchanged and fully backwards-compatible (internally it's wrapped as a one-entry list).
Configuration referenceLink to this section
ai?: false | {
enabled?: boolean
packageName?: string // optional plan label (e.g. "Systhema AI Pro"); shown on the usage widget + in the "limit reached" message
text?: {
provider?: 'openai' | 'anthropic' | 'google' | 'groq' | 'openai-compatible'
model?: string // default: per-provider; required for 'openai-compatible'
apiKey?: string // forward from your env; required except for 'openai-compatible'
baseURL?: string // required for 'openai-compatible' (setting it implies the provider)
reasoningEffort?: 'minimal' | 'low' | 'medium' | 'high' // OpenAI-only; default: the model's own default
models?: Array<{ // multi-model: when set, the flat fields above are ignored
provider?: 'openai' | 'anthropic' | 'google' | 'groq' | 'openai-compatible'
model?: string
apiKey?: string
baseURL?: string
label?: string // picker label; default: the model id
locked?: boolean // showcase-only upgrade teaser (disabled, no key needed)
upgradeHint?: string // hint shown beside a locked entry, e.g. 'Upgrade to Pro'
reasoningEffort?: 'minimal' | 'low' | 'medium' | 'high' // OpenAI-only, per-model
}>
}
image?: false | {
provider?: 'openai' | 'google'
model?: string // default: 'gpt-image-1' / 'imagen-4.0-fast-generate-001'
apiKey?: string // default: reuses text.apiKey when text runs on the same provider
models?: Array<{ // multi-model image models (same shape, no baseURL)
provider?: 'openai' | 'google'
model?: string
apiKey?: string
label?: string
locked?: boolean
upgradeHint?: string
}>
}
compose?: {
collections?: string[] // allowlist; default: all eligible collections
excludeFields?: string[] // extra field names to skip
}
languages?: string[] // translate targets; default: common European languages
quota?: {
text?: number | 'unlimited' // text generations per period; 0 = not included; default unlimited
image?: number | 'unlimited' // image generations per period
credits?: number | 'unlimited' // pooled credit budget per period; set this to switch to credit mode (supersedes text/image counts)
creditValue?: number // USD per credit (hidden); default 0.001 ⇒ a $5 budget is 5,000 credits
period?: {
interval?: 'month' | 'year' // default 'month'
every?: number // bill every N intervals (2 = bimonthly); default 1
startDay?: number // day of month the period starts (1–28); default 1
start?: string // ISO anchor date for multi-month/yearly cycles
}
}
pricing?: { // cost-estimation overrides (merged over built-in list prices)
text?: Record<string, { input: number; output: number }> // USD per 1M tokens
image?: Record<string, number> // USD per image
}
}groqis Groq's hosted OpenAI-compatible endpoint — the base URL is preset (overridable) and the model defaults toopenai/gpt-oss-120b(a model with full structured-output support, which layout and page generation rely on; Groq's Llama models accept only plain JSON mode). Its low list prices make it a good pairing withquotafor cost-capped setups.image: falsedisables the image surfaces while keeping text generation.- When
imageis omitted and text runs onopenai, image generation activates automatically reusing the text API key. - Prompt-based refinement of existing images works with OpenAI's gpt-image models and with Google's Gemini image models (the "Nano Banana" family —
gemini-2.5-flash-image,gemini-3-pro-image, …). Google's Imagen models are text-to-image only, so the refine action is hidden for those. Under the hood OpenAI refines via the images API while Gemini edits viagenerateText(the source image is sent as an input part and the edited image comes back in the response files) — the AI SDK'sgenerateImagedoesn't support Gemini image editing. - Field-level compose buttons are injected into all eligible collections (system collections like
users,forms,form-submissionsandredirectsare always excluded, as are identifier-ish fields likeslug,emailorurl). Usecompose.collections/compose.excludeFieldsto narrow further. reasoningEffort(ai.text, and per-entry inai.text.models) is an OpenAI-only hint — ignored by other providers — controlling reasoning depth for text generation:'minimal'and'low'trade depth for speed/cost,'high'spends more on harder prompts. Defaults to the model's own default when unset. It's threaded intoproviderOptions.openai.reasoningEffort, which the AI SDK maps onto the Responses API'sreasoning.effort.
CredentialsLink to this section
API keys travel exclusively through the plugin option — name your environment variables whatever fits your deployment and forward them in payload.config.ts. All values are server-only: keys live in the server-side config store and never reach the admin client.
If ai is set but no usable provider configuration is found (missing key, missing baseURL for an OpenAI-compatible endpoint), the feature disables itself with a console warning — the config never crashes.