---
title: "Settings"
manual: "LLM Foundation"
version: "0.42"
permalink: "https://docs.typo3.org/permalink/netresearch/nr-llm:configuration-settings@0.42"
source: "Configuration/Settings.rst"
rendered: "2026-11-01T11:38:21+00:00"
---

# Settings {#configuration-settings}

<a id="configuration-typoscript"></a>

## Provider configuration {#configuration-providers}

Providers, models and configurations are **database-backed** and managed
in the LLM backend module — not via TypoScript. nr-llm does **not** read
`plugin.tx_nrllm` TypoScript settings; any such constants/setup have no
effect (this is true for both classic TypoScript templates and site sets).

To make the generic `chat()` / `complete()` entry points work without
pinning a provider per call, set up a default configuration:

1.  Open the **LLM** backend module.
1.  Create a **Provider** (e.g. OpenAI) and store its API key as an
    nr-vault identifier — see [API key protection](https://docs.typo3.org/permalink/netresearch/nr-llm:configuration-security-api-keys@0.42).
1.  Create a **Model** for that provider.
1.  Create a **Configuration** bundling the model, then mark it
    **active** and **default**.

The **Setup Wizard** in the module walks through these steps.

Without an active default configuration, generic calls throw
*"No provider specified and no default provider configured"*.

## Background worker {#configuration-worker}

A queued run reads its provider key in a process without a logged-in backend
user ([ADR-234](https://docs.typo3.org/permalink/netresearch/nr-llm:adr-234@0.42)). The setting is in the extension configuration of
`nr_llm`, section *worker*.

-   **worker.technicalBeUserUid**

    -   *Type:* integer
    -   *Default:* 0

    The uid of the backend user whose grants on a vault secret count when a queued
    run reads a provider key on the command line. `nrllm:agent:work`, a Messenger
    consumer and the reaper all do. The user needs read access to the secret, by
    ownership or through a group, and nothing else.

    `0` applies the nr_vault CLI rule, which refuses unless `allowCliAccess` is
    on. Every run then fails on its first model call. A user who is disabled,
    deleted or unknown fails the run. A web request keeps its logged-in user.

    Everything else a run does in nr_vault happens as this user too. A delegated MCP
    tool stores a credential for the run's actor, which this user cannot do without
    the rights of that actor, so a queued run with such a tool fails.

## Filling empty fields on save {#configuration-field-fill}

An installation can have empty fields written by AI after a save
([ADR-237](https://docs.typo3.org/permalink/netresearch/nr-llm:adr-237@0.42)). The settings are in the extension configuration of
`nr_llm`, section *fieldFill*. Nothing happens until `fieldFill.fields` is set.

-   **fieldFill.fields**

    -   *Type:* string
    -   *Default:* (empty)

    Comma-separated `table.field` entries, for example
    `pages.description,pages.abstract`. Only plain text fields count: a rich text
    field, a code editor, a slug or a relation stays out, and so do accounts and
    `sys_` tables.

-   **fieldFill.maxLength**

    -   *Type:* integer
    -   *Default:* 300

    The longest text written, in characters. The TCA `max` of the field applies
    when it is lower. The model is asked for a tenth less, and a longer answer is
    not written.

When an editor changes and saves a record with such a field empty, a background
run is queued and the save ends. A save that changes nothing queues nothing. A worker has to run ([Background tasks from the command line and the scheduler](https://docs.typo3.org/permalink/netresearch/nr-llm:administration-agent-runs-background@0.42)). It writes
the text as the user who saved, and only while the field is still empty. A failed
run writes nothing, and **Agent Runs** shows what became of each answer.

Nothing is queued for a field the save itself emptied, for a page with hardly any
text, for `_cli_` or an import, for a copy or a translation made by TYPO3, or
for a user who may not use the default configuration. The record's text is sent
to the provider of the default configuration.

## Vision editor actions {#configuration-vision}

The editor actions **Set alternative text** and
**Update file metadata** send the image of the file to the model
([ADR-235](https://docs.typo3.org/permalink/netresearch/nr-llm:adr-235@0.42)). The settings are in the extension configuration of
`nr_llm`, section *vision*.

-   **vision.maxImageEdge**

    -   *Type:* integer
    -   *Default:* 1024

    The longest edge in pixels of the image sent to the model. TYPO3's file
    processing scales it and keeps the result in the processed folder. An image
    that cannot be brought to this size is not sent, and the action is not
    available for it. Core remembers a failed attempt: an image tried while the
    image processor was broken stays unavailable until the processed files are
    cleared in **Admin Tools > Maintenance**.

-   **vision.excludedFolders**

    -   *Type:* string
    -   *Default:* (empty)

    Comma-separated combined folder identifiers, for example
    `1:/private/, 1:/hr/`. No image from these folders or their subfolders
    reaches a model.

-   **vision.\<field>.enabled**

    -   *Type:* boolean
    -   *Default:* 1

    For `alternative`, `title` and `description`. Whether the editor action
    fills this field. An action with no field left is not offered. This does not
    switch off the tool: the assistant may still use it, which the
    **Tools** module governs.

-   **vision.\<field>.maxLength**

    -   *Type:* integer
    -   *Default:* 0

    The longest value in characters. It applies to every writer of the field,
    the assistant included, and it cannot lift a narrower TCA `max`. The model
    is asked for a tenth less, and a longer value is refused on the approval card.
    `0` sets no limit of its own.

-   **vision.\<field>.prompt**

    -   *Type:* string
    -   *Default:* (empty)

    What the model is told to write for this field. Empty uses the shipped text.

The action is not offered when the default configuration's model declares
capabilities without `vision`. A model with an empty capability field passes. A
configuration that selects its model by criteria has to require `vision` in its
criteria.
The prompt asks for the language of the backend user.

## Environment variables {#configuration-environment}

**.env**

```bash
# TYPO3 encryption key (used for API key encryption)
TYPO3_CONF_VARS__SYS__encryptionKey=your-key
```

## Security {#configuration-security}

### API key protection {#configuration-security-api-keys}

1.  **Encrypted storage** — API keys are stored as
    vault identifiers (UUIDs) via the
    [nr-vault](https://github.com/netresearch/t3x-nr-vault)
    extension, which uses envelope encryption.
    nr-llm never stores raw API keys.
1.  **Database security** — the database only contains
    vault UUIDs, not secrets. Ensure backups are
    encrypted regardless.
1.  **Backend access** — restrict the LLM module to
    authorized administrators.
1.  **Key rotation** — re-encrypt via nr-vault's
    key rotation mechanism.

### Input sanitization {#configuration-security-input}

Sanitize user input before sending to providers:

**Example: Sanitizing user input**

```php
// Strip markup and control characters from free-text input before it is
// sent to a provider. (GeneralUtility::removeXSS() was removed from the
// TYPO3 core and must not be used.)
$sanitizedInput = trim(strip_tags($userInput));

$response = $adapter->chatCompletion([
    ['role' => 'user', 'content' => $sanitizedInput],
]);
```

### Output handling {#configuration-security-output}

Treat LLM responses as untrusted content:

**Example: Escaping output**

```php
$response = $adapter->chatCompletion([
    ['role' => 'user', 'content' => $prompt],
]);

$safeOutput = htmlspecialchars(
    $response->content, ENT_QUOTES, 'UTF-8'
);
```

## Logging {#configuration-logging}

**config/system/additional.php**

```php
use Psr\Log\LogLevel;
use TYPO3\CMS\Core\Log\Writer\FileWriter;

$GLOBALS['TYPO3_CONF_VARS']['LOG']
    ['Netresearch']['NrLlm'] = [
    'writerConfiguration' => [
        LogLevel::DEBUG => [
            FileWriter::class => [
                'logFileInfix' => 'nr_llm',
            ],
        ],
    ],
];
```

Log files: `var/log/typo3_nr_llm_*.log`

## Caching {#configuration-caching}

The extension uses TYPO3's caching framework with
cache identifier `nrllm_responses`.

**No cache backend is specified** — TYPO3 automatically
uses the instance's default cache backend. If your
instance has Redis, Valkey, or Memcached configured,
nr-llm uses it transparently with zero configuration.

-   **Cache identifier**: `nrllm_responses`
-   **Cache group**: `nrllm`
-   **Default TTL**: 3600 seconds (1 hour)
-   **Embeddings TTL**: 86400 seconds (24 hours)

To override the backend for this cache specifically:

**config/system/additional.php**

```php
use TYPO3\CMS\Core\Cache\Backend\RedisBackend;

$GLOBALS['TYPO3_CONF_VARS']['SYS']['caching']
    ['cacheConfigurations']['nrllm_responses']
    ['backend'] = RedisBackend::class;
```

Clear cache:

```bash
vendor/bin/typo3 cache:flush --group=nrllm
```
