Decision model providers

A DecisionService can be answered by a decision model like Jev, by a chat model prompted to decide, or by your own code. Decision models are configured the same way chat models are. Put a provider starter on the classpath and it registers one DecisionService bean per model in its catalog, named after the model. Defaults and roles pick a model by name.

SourceHow a model becomes availableExample name
Catalog starterThe starter loads models/<provider>-models.yml and defines a bean per entry. Inject one by type or with @Qualifier("<model>").jev-latest, clef
DecisionModelProvider beanThe registry asks the provider the first time a default, role or named lookup uses a name no bean holdsA chat model name, such as gpt-4.1-mini
Application beanA singleton DecisionService or ClassificationService bean registers under its bean name. A @Lazy or scoped bean is left out.triage

Selection

Defaults and roles take a model name or a bean name. Spring aliases are supported in defaults, roles and default candidates; direct named(name) lookup uses the registry name. This example makes the catalog model jev-latest the decision default and binds the review and routing roles to the chat model gpt-4.1-mini.

embabel:
  agent:
    platform:
      models:
        typesafe:
          api-key: $\{TYPESAFE_API_KEY}
      decisions:
        capture-content: false
  models:
    decision:
      # A catalog model the TypeSafe starter registers as a bean named after the model.
      default: jev-latest
      roles:
        support-triage: jev-latest
        # A chat model name builds a prompted decision service on first use.
        dice-revision-review: gpt-4.1-mini
    classification:
      roles:
        dice-revision: gpt-4.1-mini
        ticket-routing: gpt-4.1-mini

A decision service also classifies, so a classification default or role can name one. A classification-only service cannot be a decision default or role. Startup fails when a default or role names neither a registered bean nor a model a provider serves, and the error names the property. A missing or ambiguous default fails at lookup, when defaultService() is called. describes the default rule and the registry snapshot.

Provider models

DecisionModelProvider supplies services by name for models that are not beans. LlmDecisionServiceFactory is the provider for chat models. It serves every chat model the ModelProvider lists and builds a prompted decision service for the one you name.

MemberContract
providerProvider name. Registry error messages list each provider’s models under it.
modelNames()Names the provider can serve now. The registry checks a default or role against this list only when no bean holds the name.
decisionServiceNamed(name)The service for a name, or null when the provider does not serve it

The registry asks providers in order and keeps the first service returned, so each name is built once. A registered bean wins over a provider model with the same name. A provider model is a family default only when configuration names it. A provider builds services that use the deployment’s own credentials. Per-user keys go through using(service).

In Kotlin, add providers to the builder. ChatModels here is a test provider that serves gpt-x.

val registry = DecisionServiceRegistry.builder()
    .register("jev", jev)
    .provider(ChatModels(listOf("gpt-x")))
    .decisionDefault("gpt-x")
    .classificationRole("routing", "gpt-x")
    .build()

shows the same registry built in Java. In Spring, every DecisionModelProvider bean is added to the registry in bean order. A service a provider created stays cached for the life of the registry.

Credentials

A catalog starter reads its key the same way the chat model starters do, from the environment variable first and then the api-key property. TypeSafe uses TYPESAFE_API_KEY and embabel.agent.platform.models.typesafe.api-key; TypeSafeModelsConfig requires one at startup even when application beans replace every catalog model service. A custom TypeSafeModelFactory is a separate path: providing one makes the TypeSafe starter configuration back off, so the starter key check does not run. Cloudflare uses CLOUDFLARE_API_TOKEN and embabel.agent.platform.models.cloudflare.api-key, plus an account id, and startup fails without them. For a key supplied at run time, TypeSafeModelFactory, CloudflareModelFactory and every other SystemOneModelFactory implement ByokFactory<DecisionService>. buildValidated() checks the key with the provider and returns a service, which you pass to using(service). See , and .

Was this page helpful?

Share