# `Agentic.LLM`

Top-level entry point for chat and embedding calls.

Wraps `Agentic.LLM.Provider` with two conveniences:

  * `chat/2` and `chat_tier/3` for chat completions
  * `embed/2` and `embed_tier/3` for vector embeddings

Both flavours route through the configured provider stack and
return canonical `%Response{}` / `{:ok, vectors, model_id}` shapes.

## Embed return shape

`embed/2` and `embed_tier/3` always return a list of vectors,
even when called with a single string input. The third tuple
element is the model id used to produce the vectors so callers
(e.g. recollect's reembed pipeline) can store provenance.

    {:ok, [vector, ...], "text-embedding-3-small"} | {:error, %Error{}}

# `embed_result`

```elixir
@type embed_result() ::
  {:ok, [[float()]], String.t()} | {:error, Agentic.LLM.Error.t()}
```

# `chat`

Chat completion via a provider module + canonical params.

# `chat_tier`

Chat completion via tier resolution with full failover.

Resolves every healthy route in `tier` from the ModelRouter and walks
them in priority order with error classification and health reporting.
Accepts an optional `llm_chat` callback (same shape as the loop callback)
for callers that want to customise dispatch (e.g. host app credential
injection). Falls back to direct provider dispatch when no callback is
provided. Returns `{:ok, %Response{}}` or `{:error, %Error{}}`.

## Options

  * `:llm_chat` — `(map() -> {:ok, response} | {:error, term()})` callback.
    When provided, each route is injected as `params["_route"]` and the
    callback is invoked. Otherwise direct `Provider.chat/3` is used.

# `embed`

```elixir
@spec embed(
  String.t() | [String.t()],
  keyword()
) :: embed_result()
```

Generate embeddings for one or more strings via an explicit provider.

Required opts:

  * `:provider` — provider id atom (e.g. `:openai`)
  * `:model`    — model id string (e.g. `"text-embedding-3-small"`)

# `embed_tier`

```elixir
@spec embed_tier(String.t() | [String.t()], atom(), keyword()) :: embed_result()
```

Generate embeddings via tier-based model resolution.

Tier resolution order:

  1. Explicit `opts[:model]` + `opts[:provider]` (skips Catalog)
  2. `Catalog.find(has: :embeddings, tier: tier)` → first match
  3. Fallback to first model with the `:embeddings` capability

---

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