# Model Specs
Model specs are how ReqLLM knows which model to call and what metadata is needed to route the request correctly.
That can be as simple as a registry lookup like `"openai:gpt-4o"`, or as explicit as a full `%LLMDB.Model{}` that carries the provider, model ID, base URL, and any extra routing metadata needed for a model that is not in the registry yet.
This guide covers both paths.
## Start with LLMDB
ReqLLM uses [`llm_db`](https://hex.pm/packages/llm_db) as its model registry. The easiest human-readable reference for that registry is [LLMDB.xyz](https://llmdb.xyz).
Use [LLMDB.xyz](https://llmdb.xyz) when you want to:
- look up the exact provider and model ID to pass to ReqLLM
- inspect current model variants and versioned releases
- confirm whether a date-stamped release ID already exists in the registry
- copy an exact `provider:model` spec instead of guessing
ReqLLM and LLMDB intentionally lean into exact, versioned model IDs when providers publish them. That is why you will often see date-based or version-stamped IDs such as:
- `anthropic:claude-3-5-sonnet-20240620`
- `openai:gpt-4o-mini-2024-07-18`
- `google_vertex:claude-sonnet-4-5@20250929`
That strategy matters for developer experience:
- exact IDs are reproducible and easier to debug
- aliases can still resolve to a current canonical model through LLMDB
- moving from one dated release to another is an explicit choice instead of an accidental drift
If the model is already on [LLMDB.xyz](https://llmdb.xyz), prefer using that exact spec first.
## What A Model Spec Is
A model spec is the complete input ReqLLM uses to resolve a model.
In practice, ReqLLM supports four forms:
### 1. String Specs
The most common path. Strings resolve through LLMDB.
```elixir
"anthropic:claude-haiku-4-5"
"openai:gpt-4o-mini-2024-07-18"
"google_vertex:claude-sonnet-4-5@20250929"
```
### 2. Tuple Specs
Tuples also resolve through LLMDB, but let you keep the provider and model ID split.
```elixir
{:anthropic, "claude-haiku-4-5", max_tokens: 512}
{:openai, id: "gpt-4o"}
```
### 3. `%LLMDB.Model{}`
This is the canonical explicit model contract in ReqLLM.
If you already have a `%LLMDB.Model{}`, ReqLLM uses it directly instead of looking up the model in the registry.
```elixir
model =
LLMDB.Model.new!(%{
provider: :openai,
id: "gpt-6-mini",
base_url: "http://localhost:8000/v1"
})
ReqLLM.generate_text!(model, "Hello")
```
### 4. Plain Maps
ReqLLM accepts plain maps for backwards compatibility and convenience.
They are treated as full model specs and normalized into an enriched `%LLMDB.Model{}`.
```elixir
ReqLLM.generate_text!(
%{provider: :openai, id: "gpt-6-mini", base_url: "http://localhost:8000/v1"},
"Hello"
)
```
The clearer path is to normalize first with `ReqLLM.model!/1`.
## How ReqLLM Resolves Model Specs
ReqLLM has two distinct resolution paths:
### Registry Path
Strings and tuples resolve through LLMDB.
```elixir
{:ok, model} = ReqLLM.model("openai:gpt-4o")
{:ok, model} = ReqLLM.model({:anthropic, "claude-haiku-4-5"})
```
This path is best when:
- the model already exists in LLMDB
- you want aliases and canonical version resolution
- you want shared metadata like pricing, capabilities, and limits
### Full Model Specification Path
`%LLMDB.Model{}` values and plain maps bypass registry lookup. They are self-contained model specs.
This path is best when:
- a new model exists but is not in LLMDB yet
- you are testing a local or proxied deployment
- you need per-model `base_url` metadata
- you are working with a private or experimental model ID
This is the key point: you do not need the model to exist in LLMDB before ReqLLM can use it, as long as you provide a complete enough model spec.
## Recommended Workflow
### Use a string when the model is in LLMDB
```elixir
ReqLLM.generate_text!("openai:gpt-4o", "Hello")
```
### Pin an exact release when reproducibility matters
```elixir
ReqLLM.generate_text!("anthropic:claude-3-5-sonnet-20240620", "Hello")
```
### Use `ReqLLM.model!/1` when the model is not in LLMDB yet
```elixir
model =
ReqLLM.model!(%{
provider: :openai,
id: "gpt-6-mini",
base_url: "http://localhost:8000/v1"
})
ReqLLM.generate_text!(model, "Hello")
```
### Keep the resulting `%LLMDB.Model{}` if you are going to reuse it
```elixir
model =
ReqLLM.model!(%{
provider: :openai,
id: "gpt-6-mini",
base_url: "http://localhost:8000/v1"
})
ReqLLM.generate_text!(model, "Write a haiku")
ReqLLM.stream_text!(model, "Write a poem")
```
## Minimum Required Fields
For the full model specification path, the minimum required fields are:
- `provider`
- `id` or `model`
ReqLLM then enriches and normalizes the spec where possible.
## Common Fields
These are the fields you will use most often when building a full model spec.
### `provider`
The provider atom, such as `:openai`, `:anthropic`, `:google`, `:google_vertex`, `:azure`, or `:openrouter`.
This is required.
### `id`
The ReqLLM model ID.
For most cases this is also the API model ID. This is required unless you provide `model` instead.
### `model`
An alternate way to provide the model ID. ReqLLM normalizes `id` and `model` so either can seed the spec.
### `provider_model_id`
The provider-facing model ID when it should differ from `id`.
This is useful when:
- you want a friendlier local `id`
- you want to preserve an alias in `id` but send a different provider model ID
- a provider needs a specific wire ID that differs from your local identifier
If it can be derived, ReqLLM and LLMDB will fill it in for you.
### `base_url`
Per-model endpoint metadata.
This is especially useful for:
- local OpenAI-compatible servers
- proxies
- Azure resource endpoints
- provider-compatible gateways
### `capabilities`, `limits`, `modalities`, `pricing`, `cost`
Optional metadata that can improve validation, usage reporting, and capability checks.
You do not need to provide all of these just to make a request.
### `extra`
Provider-specific metadata that does not belong in the common top-level fields.
The main advanced cases today are:
- `extra.family` for certain Google Vertex MaaS models
- `extra.wire.protocol` when you need to force a specific OpenAI-compatible wire protocol
## Examples
### Standard Catalog Model
```elixir
ReqLLM.generate_text!("openai:gpt-4o", "Hello")
```
### Exact Dated Release
```elixir
ReqLLM.generate_text!("openai:gpt-4o-mini-2024-07-18", "Hello")
```
### Local OpenAI-Compatible Server
```elixir
model =
ReqLLM.model!(%{
provider: :openai,
id: "qwen3-32b",
base_url: "http://localhost:8000/v1"
})
ReqLLM.generate_text!(model, "Explain supervision trees")
```
### Azure
Azure often benefits from the full model specification path because the Azure resource URL is model metadata.
`deployment` is still a request option.
```elixir
model =
ReqLLM.model!(%{
provider: :azure,
id: "gpt-4o",
base_url: "https://my-resource.openai.azure.com/openai"
})
ReqLLM.generate_text!(
model,
"Hello",
deployment: "my-gpt4-deployment"
)
```
### Google Vertex MaaS
Some Google Vertex MaaS and OpenAI-compatible model IDs need an explicit family hint if the family cannot be inferred from the ID alone.
```elixir
model =
ReqLLM.model!(%{
provider: :google_vertex,
id: "zai-org/glm-4.7-maas",
extra: %{family: "glm"}
})
ReqLLM.generate_text!(model, "Hello")
```
## Advanced: Local `%LLMDB.Model{}`
The canonical advanced path is an explicit `%LLMDB.Model{}`.
If you want full control, build it directly with `LLMDB.Model.new!/1`.
```elixir
model =
LLMDB.Model.new!(%{
provider: :openai,
id: "gpt-6-mini",
provider_model_id: "gpt-6-mini",
base_url: "http://localhost:8000/v1",
capabilities: %{chat: true},
limits: %{context: 200_000, output: 8_192}
})
ReqLLM.generate_text!(model, "Hello")
```
This is useful when:
- you want to construct and store reusable model definitions yourself
- you want a fully explicit contract with no ambiguity about the final struct
- you are integrating ReqLLM into a system that already manages model metadata
Plain maps are still accepted, but conceptually they are convenience input for constructing `%LLMDB.Model{}` values.
## Validation And Errors
ReqLLM intentionally hard-fails early for malformed full model specs.
Common failures include:
- missing `provider`
- missing `id` or `model`
- provider string that does not correspond to a registered provider
- provider-specific routing metadata that cannot be inferred
Provider-specific examples:
- Azure still needs `base_url`
- Google Vertex MaaS models may need `extra.family`
This is an advanced workflow, so explicit errors are preferred over silent fallback behavior.
## Useful Helpers
### Resolve a model spec
```elixir
{:ok, model} = ReqLLM.model("openai:gpt-4o")
model = ReqLLM.model!(%{provider: :openai, id: "gpt-6-mini"})
```
### Browse models in the registry
```elixir
models = LLMDB.models(:openai)
specs = Enum.map(models, &LLMDB.Model.spec/1)
```
### Resolve directly through LLMDB
```elixir
{:ok, model} = LLMDB.model("openai:gpt-4o")
```
## When To Update LLMDB Instead
The full model specification path is the fastest way to use a model that is missing from the registry.
You should still update LLMDB or add registry metadata when you want:
- the model to be discoverable on [LLMDB.xyz](https://llmdb.xyz)
- shared, reusable metadata for the team
- compatibility tooling such as `mix mc`
- richer cost, capability, and limit metadata everywhere
Use the registry for shared catalog quality.
Use full model specs when you need to move immediately.