Config-driven provider resolution for the Native agentic loop.
Resolves a provider name (or nil for cascade) into a fully-configured
{module, model, provider_opts} tuple. Provider definitions are loaded
from TOML at startup and stored in LlmCore.Config.Store.
Resolution Logic
- If the caller reports the requested model is loaded on the local Appliance
(
appliance_has_model: true) and an Appliance-module provider is listed in the cascade (or is the fallback), that provider is used. With no Appliance provider configured the flag has no effect. - Explicit provider (
llm_provider: "my_provider") → look up Definition by name/alias - Model routing (
modelmatches a pattern) → look up Definition for matched provider; an unusable target falls through to the cascade - Cascade → walk the configured
[native]cascadelist, find the first usable provider Definition. With no cascade configured, the:fallbackoption (the routing default, passed by the caller) is the only member. - Nothing matches →
{:error, :no_provider}
A cascade member is usable only if its Definition is enabled, can run the native
loop (provider_kind: :module, not a CLI provider), and — when it declares an auth
credential — that credential resolves. Anything else is skipped, and skipped/2
reports why. llm_core ships no cascade of its own; see LlmCore.LLM.Native.Config.
Adding a new provider
Add a [providers.my_provider] section to llm_core.toml. Zero code changes.
Summary
Functions
The alias of the first usable cascade member (or fallback) backed by the local
LlmCore.LLM.Appliance module, or nil.
Returns an ordered list of candidates — primary first, then cascade fallbacks.
Walk the cascade and return the first available provider.
Returns the default model for a provider alias from config.
Resolve a provider for the given model string.
Resolve a provider by explicit name (e.g. "my_provider" from native:my_provider syntax).
Match a lowercased model string against routing patterns. First match wins.
Explains which cascade members are not usable and why.
Types
@type config() :: map()
@type provider_opts() :: keyword()
@type resolution() :: {:ok, {module(), String.t(), provider_opts()}} | {:error, :no_provider}
Functions
The alias of the first usable cascade member (or fallback) backed by the local
LlmCore.LLM.Appliance module, or nil.
Callers use it to decide whether probing the appliance for a loaded model is worthwhile at all: with no Appliance provider configured, nothing is probed.
@spec candidates(String.t() | nil, config(), keyword()) :: [ {module(), String.t(), provider_opts()} ]
Returns an ordered list of candidates — primary first, then cascade fallbacks.
The primary is what resolve/3 would pick. Fallbacks are the remaining
cascade providers with their own default models (NOT the originally requested
model — a model id one provider serves usually can't run on another).
The primary module is never duplicated in the tail. Callers walk the list and attempt each candidate in order, falling through on runtime failure.
Returns [] when nothing resolves.
@spec cascade_pick(config(), String.t() | nil, map()) :: resolution()
Walk the cascade and return the first available provider.
Returns the default model for a provider alias from config.
@spec resolve(String.t() | nil, config(), keyword()) :: resolution()
Resolve a provider for the given model string.
Returns {:ok, {module, model, provider_opts}} or {:error, :no_provider}.
The appliance_has_model flag lets the caller indicate whether the model
is available locally (checked externally via Appliance discovery).
@spec resolve_provider(String.t()) :: resolution()
Resolve a provider by explicit name (e.g. "my_provider" from native:my_provider syntax).
This is the direct routing path — no cascade, no model routing.
Returns {:ok, {module, model, provider_opts}} or {:error, :no_provider}.
Match a lowercased model string against routing patterns. First match wins.
Explains which cascade members are not usable and why.
Accepts the same :fallback option as candidates/3. Reasons: :no_provider
(no such definition), :disabled, :not_native (a CLI provider cannot run the
native loop), :no_credentials (the definition's auth credential is unset).