Configuration¶
All configuration lives in the extension configuration: Admin Tools → Settings → Extension Configuration → nt_ai.
You can also override values per environment via the standard TYPO3 mechanism in config/system/settings.php:
For secrets, use environment-variable placeholders directly in the extension configuration form:
provider.anthropic.apiKey = %env(NT_AI_ANTHROPIC_KEY)%
provider.openai.apiKey = %env(NT_AI_OPENAI_KEY)%
nt-ai resolves these at runtime via getenv().
Provider settings¶
provider.default selects which provider is used when none is specified at call time. One of: anthropic, openai, gemini, deepseek, mistral, groq, ollama.
Anthropic Claude¶
| Setting | Type | Default | Description |
|---|---|---|---|
provider.anthropic.apiKey |
string | (empty) | API key from console.anthropic.com. Required to use Claude. Supports %env(VAR)% placeholders. |
provider.anthropic.model |
string | claude-sonnet-4-5 |
Model identifier. Examples: claude-opus-4-5, claude-haiku-4-5. Vision is supported on all Claude 3+ and 4+ models. |
provider.anthropic.baseUrl |
string | https://api.anthropic.com/v1 |
Override only if you proxy Anthropic through your own infrastructure. |
OpenAI¶
| Setting | Type | Default | Description |
|---|---|---|---|
provider.openai.apiKey |
string | (empty) | API key from platform.openai.com. Required to use OpenAI. |
provider.openai.model |
string | gpt-4o-mini |
Examples: gpt-4o, gpt-4o-mini, gpt-4-turbo. Use a vision model (gpt-4o, gpt-4o-mini) if you want alt texts to work. |
provider.openai.baseUrl |
string | https://api.openai.com/v1 |
Override for Azure OpenAI, OpenRouter, LM Studio, or any other OpenAI-API-compatible endpoint. |
Google Gemini¶
| Setting | Type | Default | Description |
|---|---|---|---|
provider.gemini.apiKey |
string | (empty) | API key from aistudio.google.com. Required to use Gemini. |
provider.gemini.model |
string | gemini-2.0-flash |
Examples: gemini-2.5-pro, gemini-2.0-flash, gemini-1.5-flash. All listed models support vision. |
provider.gemini.baseUrl |
string | https://generativelanguage.googleapis.com/v1beta |
Override for proxy setups. |
DeepSeek¶
| Setting | Type | Default | Description |
|---|---|---|---|
provider.deepseek.apiKey |
string | (empty) | API key from platform.deepseek.com. Required to use DeepSeek. |
provider.deepseek.model |
string | deepseek-chat |
deepseek-chat = DeepSeek-V3 (general), deepseek-reasoner = R1 (chain-of-thought). Neither model supports vision. |
provider.deepseek.baseUrl |
string | https://api.deepseek.com/v1 |
Override for proxy setups. |
Mistral AI¶
| Setting | Type | Default | Description |
|---|---|---|---|
provider.mistral.apiKey |
string | (empty) | API key from console.mistral.ai. |
provider.mistral.model |
string | mistral-small-latest |
Examples: mistral-large-latest, pixtral-large-latest (vision). Vision is only available on pixtral-* models. |
provider.mistral.baseUrl |
string | https://api.mistral.ai/v1 |
Override for proxy setups. |
Groq¶
| Setting | Type | Default | Description |
|---|---|---|---|
provider.groq.apiKey |
string | (empty) | API key from console.groq.com. |
provider.groq.model |
string | llama-3.3-70b-versatile |
Examples: llama-3.1-8b-instant, gemma2-9b-it. Groq does not support vision. |
provider.groq.baseUrl |
string | https://api.groq.com/openai/v1 |
Override for proxy setups. |
Ollama (local)¶
| Setting | Type | Default | Description |
|---|---|---|---|
provider.ollama.baseUrl |
string | http://localhost:11434 |
URL of your Ollama instance. Use the in-container URL if you run TYPO3 and Ollama in separate containers (e.g. http://host.docker.internal:11434). |
provider.ollama.model |
string | llama3.2 |
Model name as installed locally. For vision features, install a multimodal model first: ollama pull llama3.2-vision |
Behavior¶
| Setting | Type | Default | Description |
|---|---|---|---|
behavior.timeout |
integer (seconds) | 60 |
Hard timeout for any AI request. Increase for very slow local models. |
behavior.maxTokens |
integer | 2048 |
Maximum tokens generated per response. Applies to all providers. |
behavior.logRequests |
boolean | off | When on, every AI call is logged via the standard TYPO3 logger. Useful for debugging, but may write sensitive prompt content to log files. Leave off in production. |
behavior.defaultLanguage |
string (ISO 639-1) | en |
Used when no language is specified at call time. Affects content generation, translations, and alt texts. |
Alt-text customization¶
These settings shape the alt texts the AI produces so they match your site's voice and accessibility policy. The settings apply to every alt-text generation, including batch CLI runs and the "Generate alt text" buttons.
| Setting | Type | Default | Description |
|---|---|---|---|
altText.customInstructions |
multi-line text | (empty) | Free-form additional rules for the AI. One instruction per line. Appended verbatim to the system prompt. |
altText.maxLength |
integer | 125 |
Soft cap on alt text length in characters. Set to 0 to disable. The AI is instructed to stay under the limit, and the output is also hard-cut on word boundaries if the model exceeds it. WCAG/screen-reader best practice is around 125 characters. |
altText.style |
enum | concise |
concise — one short sentence. descriptive — 1–2 sentences. detailed — 2–3 sentences, suitable for charts and content images. |
altText.forbidImagePrefix |
boolean | on | Strips openings like "Image of…", "Picture showing…", "Bild von…" from the prompt and defensively from the response. Screen readers already announce "Graphic", making these prefixes redundant. |
altText.includeDetectedText |
boolean | on | The AI transcribes visible text inside the image (signs, screenshots, labels, product packaging). Important for accessibility — text in images is otherwise lost to screen reader users. |
altText.tone |
string | neutral, factual |
Free-form tone description. Examples: friendly, casual (blogs), marketing, enthusiastic (landing pages), technical, precise (documentation). |
altText.brandContext |
string (max 500 chars) | (empty) | A short note about your site or brand sent with every alt-text request. Useful for context-sensitive descriptions. |
Building a strict alt-text policy — German museum site
altText.style = descriptive
altText.maxLength = 200
altText.tone = neutral, sachlich
altText.brandContext = Museum für moderne Kunst. Bildbeschreibungen für ein Fachpublikum, Künstlernamen nur wenn klar erkennbar.
altText.customInstructions:
Bei Kunstwerken: Stil, Material und ungefähre Epoche nennen.
Personen nur als "Besucher", "Mitarbeiter" o.ä. bezeichnen.
Texttafeln und Beschriftungen wörtlich übernehmen.
Audit settings¶
| Setting | Type | Default | Description |
|---|---|---|---|
audit.enabled |
boolean | on | Master switch for the Accessibility Audit backend module. |
audit.autoOnSave |
boolean | off | When on, an audit is automatically triggered every time a page is saved in the backend. Slows down the save action because the page is fetched over HTTP. |
audit.autoCooldown |
integer (seconds) | 120 |
Minimum gap between auto-audits for the same page. Prevents audit storms when editors rapid-save. |
audit.aiRules |
boolean | on | Whether to run AI-powered audit rules. When off, only deterministic rule-based checks run. AI rules are also automatically skipped when no provider is configured. |
audit.retentionDays |
integer (days) | 90 |
Audit reports older than this are eligible for deletion via the standard TYPO3 garbage-collection task. 0 disables. |
audit.maxReportsPerPage |
integer | 10 |
Maximum number of reports kept per page. Older reports are pruned automatically after each new audit run. |
audit.explainPrompt |
multi-line text | (built-in) | Custom prompt template for AI-generated finding explanations. Available placeholders: {ruleId}, {severity}, {message}, {wcagReference}, {snippet}. |
Accessibility statement¶
Fields for the "Erklärung" (Statement) tab in the audit module, which turns audit data into a draft accessibility statement (see user guide → "Generate an accessibility statement").
| Setting | Type | Default | Description |
|---|---|---|---|
declaration.targetPageUid |
integer | 0 |
Target page the statement is published to. 0 = the page with slug /barrierefreiheit automatically. |
declaration.organisation |
string | (empty) | Organisation name used in the statement. Empty = website title from the site configuration. |
declaration.feedbackContact |
text | (empty) | Contact for accessibility feedback (name, email, phone). Mandatory field of the statement. |
declaration.enforcementBody |
text | (empty) | Competent enforcement / conciliation body (for companies under the BFSG usually the market surveillance authority of the federal states). |
declaration.enforcementUrl |
string | (empty) | Link to the competent body (optional). |
declaration.measures |
text | (built-in) | Measures already implemented, one per line or separated by \|. Installed sibling extensions (nt-lingua) and the nt-ai audit are appended automatically. |
Publishing Quality Gate¶
Warns about open accessibility findings when publishing a page, or blocks publishing until critical issues are fixed. The gate uses the page's latest stored audit report, so keep audit.autoOnSave enabled to keep it current. The loop is: fix → save (re-audit) → report updates → publishing passes.
| Setting | Type | Default | Description |
|---|---|---|---|
audit.qualityGate |
options (off/warn/block) |
off |
warn: flash warning on publish. block: prevents publishing (page stays hidden) until the gate passes. |
audit.qualityGateSeverity |
options (error/warning) |
error |
Lowest severity that trips the gate. error = errors only, warning = errors and warnings. |
audit.qualityGateScope |
options (onPublish/onSave) |
onPublish |
onPublish: only when making the page visible (hidden=0). onSave: on every save of a non-hidden page. |
audit.qualityGateAdminBypass |
boolean | on | Administrators may publish despite findings (they only get a warning). |
For pipelines: nt_ai:audit --fail-on=error (or warning) returns a non-zero exit code when findings exist — usable as a CI/CD gate.
PDF accessibility (upload gate)¶
Checks PDFs on backend upload for accessibility (tagged PDF, text layer, document language, title, alternative texts …) and warns or blocks depending on the mode. Regardless of the mode, every backend PDF upload stores a report for the Media → PDF Accessibility module. Frontend form uploads are exempt. Rule details: user guide.
| Setting | Type | Default | Description |
|---|---|---|---|
pdf.uploadGate |
options (off/warn/block) |
off |
warn: the file is stored and the editor gets a persistent warning listing the findings. block: the upload is rejected with a message including the findings — no file is created. |
pdf.uploadGateSeverity |
options (error/warning) |
warning |
Severity threshold. warning = errors and warnings (e.g. missing document title), error = critical errors only. |
pdf.uploadGateAdminBypass |
boolean | on | Administrators may upload despite findings (they only get the warning instead of a block). |
For inventory checks and pipelines: nt_ai:pdf-audit [--limit N] [--force] [--fail-on=error|warning]
— pure PHP (no system binaries required); unchanged files are skipped.
Token tracking and cost estimation¶
| Setting | Type | Default | Description |
|---|---|---|---|
tokenTracking.enabled |
boolean | off | When on, every AI call is recorded in tx_ntai_token_usage. Required for the Token Usage dashboard widgets. |
tokenTracking.currency |
options | USD |
Display currency for estimated costs. USD → ~$0.19, EUR → ~0,18 €. |
tokenTracking.eurRate |
float | 0.92 |
USD → EUR conversion rate. Only used when currency is set to EUR. |
tokenTracking.customPricing |
text (JSON) | (empty) | Override or extend the built-in model price table. Prices in USD per 1,000,000 tokens. Prefix matching applies — "gpt-4o" matches gpt-4o-2024-08-06. |
{
"my-fine-tuned-gpt4o": {"input": 4.00, "output": 12.00},
"custom-ollama-model": {"input": 0.00, "output": 0.00}
}
Built-in price table (as of 2026-07)¶
| Model prefix | Provider | Input $/1M | Output $/1M |
|---|---|---|---|
gpt-4o-mini |
OpenAI | 0.15 | 0.60 |
gpt-4o |
OpenAI | 2.50 | 10.00 |
claude-haiku-4 |
Anthropic | 0.80 | 4.00 |
claude-sonnet-4 |
Anthropic | 3.00 | 15.00 |
claude-opus-4 |
Anthropic | 15.00 | 75.00 |
gemini-2.0-flash |
0.10 | 0.40 | |
gemini-2.5-pro |
1.25 | 10.00 | |
deepseek-chat |
DeepSeek | 0.27 | 1.10 |
mistral-small |
Mistral AI | 0.20 | 0.60 |
llama-3.3-70b |
Groq | 0.59 | 0.79 |
The full table is in EXT:nt_ai/Configuration/ModelPricing.php. Unknown models are shown as a warning in the dashboard widget. TTS models (tts-1, tts-1-hd, gpt-4o-mini-tts) are listed there priced per 1M characters.
Read-aloud (TTS)¶
Engine, rate and read scope are chosen in the site settings (category "Vorlesen (TTS)"). The following apply to the cloud engine only and live in the extension configuration (tab "Vorlesen (TTS)"):
| Setting | Type | Default | Description |
|---|---|---|---|
tts.cloudProvider |
options | openai |
Premium voice provider. API key = that provider's key (tab "Provider settings"). |
tts.voice |
string | alloy |
Provider voice (OpenAI: alloy, echo, fable, onyx, nova, shimmer). |
tts.model |
string | tts-1 |
TTS model (OpenAI: tts-1, tts-1-hd, gpt-4o-mini-tts). |
tts.rateLimit |
integer | 30 |
Max new cloud requests per IP per minute (bot/mass-usage protection). 0 = unlimited; cache hits don't count. |
The cloud endpoint /nt-ai/tts only answers when the site engine is cloud; it rejects cross-site requests and is rate-limited. Generated audio is cached server-side, so repeat reads of the same text are free. See User Guide → Read-aloud.
Lighthouse monitoring¶
Public URLs required
The PSI API can only analyse publicly accessible URLs. Use this feature on staging or production servers, not on local DDEV instances — Google cannot reach *.ddev.site URLs.
| Setting | Type | Default | Description |
|---|---|---|---|
lighthouse.apiKey |
string | (empty) | Google API key from console.cloud.google.com (enable the "PageSpeed Insights API"). Without a key, requests are rate-limited to ~25 per 100 seconds. For production use, always configure a key. Supports %env(NT_AI_LIGHTHOUSE_KEY)%. |
lighthouse.strategy |
options | mobile |
Device profile for Lighthouse. mobile (default), desktop, or both (runs two PSI calls per page — doubles quota usage). |
# All pages, mobile strategy (reads setting from Extension Configuration).
vendor/bin/typo3 nt_ai:lighthouse
# Page 42 and full subtree, desktop only.
vendor/bin/typo3 nt_ai:lighthouse 42 99 --strategy=desktop
# Page 1 children, run both strategies.
vendor/bin/typo3 nt_ai:lighthouse 1 1 --strategy=both
Results are stored in tx_ntai_lighthouse_report as append-only history. The Lighthouse-Scores dashboard widget shows the average scores across all analysed pages.
Score threshold alerting¶
| Setting | Type | Default | Description |
|---|---|---|---|
alerting.enabled |
boolean | off | Master switch. No email is ever sent when this is off. |
alerting.recipient |
string | (empty) | One or more email addresses, comma-separated. Example: webmaster@example.com, seo@example.com. |
alerting.thresholdGeo |
integer (0–100) | 0 |
Alert when the GEO/SEO AI score for any page falls below this value. 0 disables alerting for this category. |
alerting.thresholdPerformance |
integer (0–100) | 0 |
Threshold for the Performance AI score. |
alerting.thresholdSemantics |
integer (0–100) | 0 |
Threshold for the Semantics AI score. |
alerting.thresholdKeywords |
integer (0–100) | 0 |
Threshold for the Keywords AI score. |
alerting.thresholdAccessibility |
integer (0–100) | 0 |
Threshold for the Barrierefreiheit AI score. |
alerting.thresholdLighthousePerformance |
integer (0–100) | 0 |
Threshold for the Lighthouse Performance score. |
alerting.thresholdLighthouseAccessibility |
integer (0–100) | 0 |
Threshold for the Lighthouse Accessibility score. |
alerting.thresholdLighthouseBestPractices |
integer (0–100) | 0 |
Threshold for the Lighthouse Best Practices score. |
alerting.thresholdLighthouseSeo |
integer (0–100) | 0 |
Threshold for the Lighthouse SEO score. |
Recommended Scheduler setup — run nt_ai:alert-check after the analysis commands so it reads fresh data: