ADR-066: Criteria-mode configurations resolve in the service layer
- Status
-
Accepted
- Date
-
2026-07-15
- Authors
-
Netresearch DTT GmbH
Context
ADR-055 and ADR-056 introduced
LlmConfiguration records with two selection modes: fixed (a
direct model_uid relation) and criteria (a stored
ModelSelectionCriteria JSON; model_uid = 0, the concrete model
chosen at call time). ModelSelectionService::resolveModel() was the
resolver — but it had no production caller. Every
*ForConfiguration() entry point (embedForConfiguration,
chatWithConfiguration, chatWithToolsForConfiguration,
completeWithConfiguration, streamChatWithConfiguration) obtained
its adapter through getAdapterFromConfiguration(), which read
$configuration->getLlmModel() — a plain getter — directly. For a
criteria-mode record that relation is null, so the call threw
ProviderException: Configuration "…" has no model assigned.
This surfaced live: nr_ai_search's embeddingConfiguration /
chatConfiguration presets are criteria-mode, so the entire retrieval
path failed the moment it reached the provider adapter.
Decision
getAdapterFromConfiguration() — the single adapter choke point for
every *ForConfiguration() path — resolves the model through
ModelSelectionService::resolveModel($configuration), which returns
the directly configured model unchanged for fixed mode and selects
from the stored criteria for criteria mode. It still throws the same
ProviderException (code 1735300100) when resolution yields no
model.
The resolved model is not written back onto the configuration. The
configuration is a repository-managed Extbase entity; calling
setLlmModel() would mark it dirty and Extbase would persist
model_uid at request end, silently converting a criteria-mode record
into a fixed-mode one. Per-model cost analytics for criteria configs
(UsageMiddleware reads getLlmModel() directly) therefore remain a
separate, non-destructive follow-up.
Consequences
- Criteria-mode configurations work across embed / chat / tools / complete / stream without the caller pre-resolving a model.
ModelSelectionServiceInterfaceis a trailing, nullable constructor dependency ofLlmServiceManager(autowired); when absent the method falls back to the raw getter, so existing 5-argument constructions keep compiling.- Cost analytics for criteria-mode configs fall back to the provider-reported model id (no per-model DB pricing) until the follow-up threads the resolved model through the call metadata.
See PR #372.