Docs

This page isn't translated yet

next

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. locked entries 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 /generate and /generate-image endpoints (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
  }
}
  • groq is Groq's hosted OpenAI-compatible endpoint — the base URL is preset (overridable) and the model defaults to openai/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 with quota for cost-capped setups.
  • image: false disables the image surfaces while keeping text generation.
  • When image is omitted and text runs on openai, 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 via generateText (the source image is sent as an input part and the edited image comes back in the response files) — the AI SDK's generateImage doesn't support Gemini image editing.
  • Field-level compose buttons are injected into all eligible collections (system collections like users, forms, form-submissions and redirects are always excluded, as are identifier-ish fields like slug, email or url). Use compose.collections / compose.excludeFields to narrow further.
  • reasoningEffort (ai.text, and per-entry in ai.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 into providerOptions.openai.reasoningEffort, which the AI SDK maps onto the Responses API's reasoning.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.