# `Omni.Model`
[🔗](https://github.com/aaronrussell/omni/blob/v1.2.1/lib/omni/model.ex#L1)

A data struct describing a specific LLM.

Models carry identity, capabilities, and pricing information. They are loaded
from JSON data files in `priv/models/` at startup rather than defined as
individual modules. The `:provider` and `:dialect` fields hold direct module
references, making the struct self-contained for callback dispatch — given a
`%Model{}`, Omni knows where to send the request and how to format it.

## Struct fields

  * `:id` — the provider's string identifier (e.g. `"claude-sonnet-4-5-20250514"`)
  * `:name` — human-readable display name (e.g. `"Claude Sonnet 4.5"`)
  * `:provider` — the provider module (e.g. `Omni.Providers.Anthropic`)
  * `:dialect` — the dialect module (e.g. `Omni.Dialects.AnthropicMessages`)
  * `:context_size` — maximum input tokens the model accepts
  * `:max_output_tokens` — maximum tokens the model can generate
  * `:reasoning` — whether the model supports extended thinking
  * `:input_modalities` — supported input types (`:text`, `:image`, `:pdf`)
  * `:output_modalities` — supported output types (`:text`)
  * `:input_cost` — USD cost per million input tokens
  * `:output_cost` — USD cost per million output tokens
  * `:cache_read_cost` — USD cost per million cached input tokens (read)
  * `:cache_write_cost` — USD cost per million cached input tokens (write)
  * `:release_date` — date the model was released (from models.dev)

## Looking up models

Most users access models through the top-level API with `{provider_id,
model_id}` tuples — Omni resolves them automatically:

    {:ok, response} = Omni.generate_text({:anthropic, "claude-sonnet-4-5-20250514"}, "Hello!")

To inspect a model's capabilities or browse what's available:

    {:ok, model} = Omni.get_model(:anthropic, "claude-sonnet-4-5-20250514")
    model.context_size  #=> 200000

    {:ok, models} = Omni.list_models(:openai)
    Enum.map(models, & &1.id)

## Custom models

When a model isn't in the built-in JSON data (new releases, fine-tunes,
self-hosted endpoints), create and register it manually:

    model = Omni.Model.new(
      id: "my-fine-tune",
      name: "My Fine-Tune",
      provider: Omni.Providers.OpenAI,
      dialect: Omni.Dialects.OpenAICompletions,
      context_size: 128_000,
      max_output_tokens: 16_384,
      input_cost: 2.0,
      output_cost: 8.0
    )

    Omni.put_model(:openai, model)

The model is now discoverable via `Omni.get_model/2` and `Omni.list_models/1`,
and can be used directly as a struct with `generate_text/3` and `stream_text/3`.

# `ref`

```elixir
@type ref() :: {atom(), String.t()}
```

A model reference as `{provider_id, model_id}`.

The provider ID is an atom identifying a loaded provider (e.g. `:anthropic`,
`:openai`, `:google`). The model ID is the provider's string identifier
for the model (e.g. `"claude-sonnet-4-5-20250514"`).

# `t`

```elixir
@type t() :: %Omni.Model{
  cache_read_cost: number(),
  cache_write_cost: number(),
  context_size: non_neg_integer(),
  dialect: module(),
  id: String.t(),
  input_cost: number(),
  input_modalities: [atom()],
  max_output_tokens: non_neg_integer(),
  name: String.t(),
  output_cost: number(),
  output_modalities: [atom()],
  provider: module(),
  reasoning: boolean(),
  release_date: Date.t() | nil
}
```

An LLM model descriptor.

The `:provider` and `:dialect` fields are module references used for callback
dispatch. Cost fields are per million tokens. Modalities are filtered to the
supported set (see `new/1`).

# `get`

```elixir
@spec get(atom(), String.t()) :: {:ok, t()} | {:error, term()}
```

Looks up a model by provider ID and model ID.

Returns `{:ok, model}` if found, or an error tuple identifying what's missing.

# `list`

```elixir
@spec list(atom()) :: {:ok, [t()]} | {:error, term()}
```

Returns all models for a provider, or an error if the provider is unknown.

# `new`

```elixir
@spec new(Enumerable.t()) :: t()
```

Creates a new model struct from a keyword list or map.

Normalizes modalities to the supported set — unsupported values are silently
dropped, and an empty list defaults to `[:text]`. Does not validate field
values; validation happens at the API boundary. See the "Custom models"
section in the moduledoc for a full example.

# `put`

```elixir
@spec put(atom(), t()) :: :ok
```

Registers a model under the given provider ID.

Makes the model discoverable via `get/2` and `list/1`. If a model with the
same ID already exists for that provider, it is replaced. See the "Custom
models" section in the moduledoc for a full example.

# `to_ref`

```elixir
@spec to_ref(t()) :: ref()
```

Converts a model back to its `{provider_id, model_id}` lookup reference.

    {:ok, model} = Omni.get_model(:anthropic, "claude-sonnet-4-5-20250514")
    Model.to_ref(model)
    #=> {:anthropic, "claude-sonnet-4-5-20250514"}

---

*Consult [api-reference.md](api-reference.md) for complete listing*
