Model Resolution
How RubyLLM turns a model name into a concrete model and provider, step by step, covering aliases, the registry, provider preference, and unlisted models.
Table of contents
- Overview
- The Resolution Procedure
- Aliases
- Resolving Without a Provider
- Resolving With a Provider
- Models the Registry Doesn’t List
- When Resolution Fails
- Next Steps
After reading this guide, you will know:
- The exact steps RubyLLM follows to turn a model name into a model and a provider.
- How aliases map a friendly name to a provider’s versioned ID.
- How RubyLLM picks a provider when you don’t name one.
- How provider-specific resolution works, including Bedrock regions and inference profiles.
- How to use a model the registry doesn’t list, and what happens when resolution fails.
Overview
Every entry point that takes a model: argument runs the same resolution. RubyLLM.chat, RubyLLM.embed, and RubyLLM.paint all hand the name to the registry, which returns two things: a RubyLLM::Model describing the model, and the provider instance that will serve it.
chat = RubyLLM.chat(model: "claude-sonnet-4-5")
chat.model.id # => "claude-sonnet-4-5-20250929" (resolved from the alias)
chat.model.provider # => "anthropic" (inferred from the registry)
You gave a name; RubyLLM resolved it to a concrete model and chose a provider. The rest of this guide is the procedure behind that.
The Resolution Procedure
Resolution takes a name and an optional provider, and runs in this order:
- Did you name a provider? If you passed
provider:, resolution is scoped to that provider. If not, RubyLLM searches every provider and chooses one by preference. - Resolve aliases. The name is looked up in the alias table and rewritten to the provider’s versioned ID when a match exists.
- Look up the registry. RubyLLM finds the model whose ID (or pre-alias name) matches in the model registry.
- Bypass the registry for unlisted models. With
assume_model_exists: true(or a local provider), RubyLLM skips the lookup and trusts the name you gave. - Resolve the provider. The provider comes from the matched model’s
providerfield, or from theprovider:you named.
If no model matches and you didn’t ask RubyLLM to assume it exists, resolution raises RubyLLM::ModelNotFoundError.
Aliases
An alias is a stable, friendly name that maps to a provider’s exact, versioned model ID. Aliases live in aliases.json and are shaped as friendly-name => { provider => versioned-id }:
{
"claude-haiku-4-5": {
"anthropic": "claude-haiku-4-5-20251001",
"bedrock": "anthropic.claude-haiku-4-5-20251001-v1:0",
"vertexai": "claude-haiku-4-5",
"azure": "claude-haiku-4-5-20251001"
}
}
When you name a provider, RubyLLM uses that provider’s entry. Without a provider, it uses the first entry listed for the alias:
RubyLLM.chat(model: "claude-haiku-4-5") # resolves to anthropic's claude-haiku-4-5-20251001
RubyLLM.chat(model: "claude-haiku-4-5", provider: :bedrock) # resolves to us.anthropic.claude-haiku-4-5-20251001-v1:0
A name that isn’t in the alias table is passed through unchanged, so exact model IDs always work.
Resolving Without a Provider
When you don’t name a provider, the same name can exist across several services, so RubyLLM has to choose one. It collects every registry model whose ID matches either the name you gave or its alias-resolved ID, then picks the most preferred provider.
Provider preference puts first-party providers ahead of the aggregators that resell their models:
| Rank | Providers |
|---|---|
| First-party | openai, anthropic, gemini, deepseek, mistral, perplexity, xai |
| Cloud platforms | vertexai, bedrock |
| Aggregators and local | openrouter, azure, ollama, gpustack |
Preference decides the winner even when another provider has a more literal match. claude-3-5-haiku is an exact ID on Vertex AI and an alias on Anthropic, but Anthropic outranks Vertex AI, so a bare claude-3-5-haiku resolves to Anthropic:
RubyLLM.chat(model: "claude-3-5-haiku").model.provider # => "anthropic"
To pin a different provider, name it:
RubyLLM.chat(model: "claude-3-5-haiku", provider: :vertexai)
Provider preference only breaks ties between providers that both carry the model. It never makes RubyLLM use a provider you haven’t configured a key for, because a chat still needs that provider’s credentials to run.
Resolving With a Provider
When you pass provider:, RubyLLM resolves the alias for that provider, then finds the model whose ID matches the resolved ID (falling back to the raw name) for that provider only.
RubyLLM.embed("hello", model: "text-embedding-3-small", provider: :openai)
Bedrock Regions and Inference Profiles
Bedrock adds one more step. Its model IDs carry a region prefix and may require an inference profile, so before the registry lookup RubyLLM rewrites the ID for your configured bedrock_region:
RubyLLM.configure { |config| config.bedrock_region = "us-east-1" }
chat = RubyLLM.chat(model: "claude-haiku-4-5", provider: :bedrock)
chat.model.id # => "us.anthropic.claude-haiku-4-5-20251001-v1:0" (region prefix applied)
RubyLLM only applies the prefix when a matching regional model exists in the registry, and normalizes the inference-profile form from the model’s metadata. See Custom Endpoints and Unlisted Models for routing the same model through a different provider.
Models the Registry Doesn’t List
New releases, custom fine-tunes, and private deployments won’t be in the registry. Set assume_model_exists: true to skip the registry lookup and use the name as-is. You must name a provider, since there is no registry entry to infer one from:
chat = RubyLLM.chat(
model: "my-custom-deployment",
provider: :openai,
assume_model_exists: true
)
RubyLLM then builds a synthetic RubyLLM::Model with assumed capabilities (function_calling, streaming, vision, structured_output) and a metadata warning, because it can’t know the real ones:
chat.model.capabilities # => ["function_calling", "streaming", "vision", "structured_output"]
chat.model.metadata # => { warning: "Assuming model exists, capabilities may not be accurate" }
You are responsible for using only the features the model actually supports. The same flag works on RubyLLM.embed, RubyLLM.paint, and with_model.
Local providers like Ollama and GPUStack assume models exist automatically. You can pull and run any model name without registering it first, and you don’t need to pass the flag.
When Resolution Fails
If nothing matches and you didn’t assume existence, RubyLLM raises RubyLLM::ModelNotFoundError with guidance to refresh the registry:
RubyLLM.chat(model: "gpt-7-ultra")
# => RubyLLM::ModelNotFoundError: Unknown model: "gpt-7-ultra".
# If the model exists at the provider, refresh the registry with
# `RubyLLM.models.refresh!`.
A real but newly released model usually means your registry is stale. Refresh it from the published catalog and your configured providers:
RubyLLM.models.refresh!
See Working with Models for refreshing in applications and Rails. If the model genuinely isn’t in any catalog, use assume_model_exists: true instead.
Next Steps
- Working with Models - explore, filter, and inspect the registry.
- Custom Endpoints and Unlisted Models - point a provider at a custom host and use unlisted models.
- Custom Providers and Protocols - teach RubyLLM about a service it doesn’t know.