Fallback chain 

A LlmConfiguration can carry an ordered list of other configuration identifiers to fall back to on retryable provider failures. The lookup happens transparently inside NetresearchNrLlmServiceLlmServiceManager::chatWithConfiguration() and completeWithConfiguration(). Callers see a regular completion response or a typed exception; they never need to reach into retry mechanics.

Configuring a chain 

The tx_nrllm_configuration.fallback_chain column stores a JSON object with a single key, configurationIdentifiers, whose value is the ordered array of target configuration identifiers:

Example payload stored in fallback_chain
{"configurationIdentifiers": ["claude-sonnet", "ollama-local"]}
Copied!

Editors paste that JSON into the Fallback Chain tab in the backend form. The order is the retry order. Identifiers are matched case-insensitively against tx_nrllm_configuration.identifier. Using an object (rather than a bare top-level array) leaves room for future sibling fields — e.g. per-link retry policy — without a schema break.

Retryable vs. non-retryable errors 

Fallback only triggers for errors the next provider might actually recover from:

Exception Retryable?
ProviderConnectionException (network, timeout, HTTP 5xx, retries exhausted) Yes
ProviderResponseException with code 429 (rate-limited by this provider) Yes
ProviderResponseException with any other 4xx (authentication, bad request, not found, …) No. Bubbles up. A different provider with the same input would fail the same way.
ProviderConfigurationException No. Misconfiguration is a human problem.
UnsupportedFeatureException No. Fallback won't make a text-only provider handle images.

When every configuration in the chain trips a retryable error, NetresearchNrLlmProviderExceptionFallbackChainExhaustedException is thrown. It carries the per-attempt errors so consumers can surface the full failure sequence.

Scope limits 

v1 is deliberately narrow:

  • No streaming. streamChatWithConfiguration() does not wrap the call. Once the first chunk has been yielded to the caller, mid-stream provider-switching would be detectable and surprising.
  • No recursion. A fallback configuration's own chain is ignored. This avoids cycles (a -> b -> a) and unbounded attempt trees.
  • Single primary-only chain is a no-op. If the configured chain contains only the primary's own identifier, the primary's original exception is rethrown verbatim rather than wrapped in FallbackChainExhaustedException.

Using the DTO directly 

For programmatic construction — e.g. a wizard that generates a configuration and also sets up fallback — use the NetresearchNrLlmDomainDTOFallbackChain value object:

EXT:my_ext/Classes/Service/Setup.php
use Netresearch\NrLlm\Domain\DTO\FallbackChain;

$chain = (new FallbackChain())
    ->withLink('claude-sonnet')
    ->withLink('ollama-local');

$configuration->setFallbackChainDTO($chain);
Copied!

The DTO trims and lowercases identifiers on entry, deduplicates them, and silently rejects empty strings and non-string entries read from malformed JSON. See ADR-021: Provider Fallback Chain for the full design rationale and the alternatives we ruled out.