# `PhoenixKitAI.Provider`
[🔗](https://github.com/BeamLabEU/phoenix_kit_ai/blob/0.23.1/lib/phoenix_kit_ai/provider.ex#L1)

The seam between the public image API and one provider's HTTP shape.

Consumers never see a provider: they call `PhoenixKitAI.process_image/4`,
`PhoenixKitAI.edit_image/4` or `PhoenixKitAI.generate_image/3` with an
endpoint, and the endpoint's provider picks the adapter. Switching a
module from OpenRouter to xAI, OpenAI or a provider added later is a
change of endpoint, not of code.

An adapter implements this behaviour and is selected by the endpoint's
base provider key (`PhoenixKitAI.Endpoint.base_provider/1`):

| provider key | adapter |
|---|---|
| `"openrouter"` | `PhoenixKitAI.Providers.OpenRouter` — the unified Images API |
| `"xai"` | `PhoenixKitAI.Providers.XAI` — JSON `/images/edits` |
| `"openai"` | `PhoenixKitAI.Providers.OpenAI` — multipart `/images/edits` |
| anything else | `PhoenixKitAI.Providers.OpenAICompatible` — chat completions with image parts |

Vision (`describe` / `compare`) goes through the adapter's optional
`vision/3`, falling back to the chat-completions implementation.

A host adds or replaces adapters without touching this module:

    config :phoenix_kit_ai, provider_adapters: %{"fal" => MyApp.FalAdapter}

The key is the Integrations provider key the endpoint carries; the
value is a module implementing this behaviour.

## Contract

Every adapter receives already-normalised inputs: reference images as
data URLs (bytes inlined) or http(s) URLs, and options as a map with
the canonical keys from `PhoenixKitAI.Images` (`:aspect_ratio`,
`:resolution`, `:size`, `:quality`, `:background`, `:output_format`,
`:output_compression`, `:n`, `:seed`, `:response_format`), plus
`:model` (an override of the endpoint's model), `:transport` (an
adapter-specific hint) and `:provider_options` (a map passed through
to the provider untouched). It returns the uniform image result or one
of the module's error atoms/tuples.

# `image`

```elixir
@type image() :: %{
  :data =&gt; binary() | nil,
  :url =&gt; String.t() | nil,
  :content_type =&gt; String.t() | nil,
  optional(:width) =&gt; pos_integer(),
  optional(:height) =&gt; pos_integer()
}
```

# `image_result`

```elixir
@type image_result() :: %{
  :images =&gt; [image()],
  :text =&gt; String.t() | nil,
  :usage =&gt; map(),
  :latency_ms =&gt; non_neg_integer(),
  :model =&gt; String.t() | nil,
  optional(:warnings) =&gt; [term()]
}
```

# `options`

```elixir
@type options() :: %{optional(atom()) =&gt; term()}
```

# `image_edit`

```elixir
@callback image_edit(PhoenixKitAI.Endpoint.t(), String.t(), [String.t()], options()) ::
  {:ok, image_result()} | {:error, term()}
```

Image-in, image-out: edit `refs` (first = the image to change) per `prompt`.

# `image_edit_options`
*optional* 

```elixir
@callback image_edit_options(PhoenixKitAI.Endpoint.t(), options()) :: [atom()]
```

The request option names `image_edit/4` actually sends for these
options. Optional — defaults to `image_options/1`. An adapter whose
edit transport sends less than its generation one (a chat-completions
edit carries only an aspect ratio) implements this, so
`PhoenixKitAI.Images.process/4` drops the rest with a warning and picks
the operations' fallback wording instead of sending them nowhere.

# `image_generate`

```elixir
@callback image_generate(PhoenixKitAI.Endpoint.t(), String.t(), options()) ::
  {:ok, image_result()} | {:error, term()}
```

Text-to-image.

# `image_models`

```elixir
@callback image_models(PhoenixKitAI.Endpoint.t()) ::
  {:ok, [PhoenixKitAI.Images.ImageModel.t()]} | {:error, term()}
```

The provider's image models with their constraints, when it publishes them.

# `image_options`

```elixir
@callback image_options(PhoenixKitAI.Endpoint.t()) :: [atom()]
```

Option names the adapter can send when no per-model listing exists.

# `vision`
*optional* 

```elixir
@callback vision(PhoenixKitAI.Endpoint.t(), [map()], options()) ::
  {:ok, map()} | {:error, term()}
```

Vision: a chat-shaped question about images. Optional — adapters that
do not implement it get `PhoenixKitAI.Providers.OpenAICompatible.vision/3`
(chat completions with `image_url` parts), which is right for every
OpenAI-shaped API. A provider with its own vision shape implements this.

# `adapters`

```elixir
@spec adapters() :: %{required(String.t()) =&gt; module()}
```

Provider key → adapter module, built-ins under the host's `:provider_adapters`.

# `default`

```elixir
@spec default() :: module()
```

The adapter used when a provider has no dedicated one.

# `edit_options`

```elixir
@spec edit_options(module(), PhoenixKitAI.Endpoint.t(), options()) :: [atom()]
```

The request options `adapter`'s `image_edit/4` sends for `options`: its
`image_edit_options/2` when it has one, `image_options/1` otherwise.

# `for_endpoint`

```elixir
@spec for_endpoint(PhoenixKitAI.Endpoint.t() | map()) :: module()
```

The adapter for an endpoint's provider (the default one when unknown).

# `for_provider`

```elixir
@spec for_provider(String.t()) :: module()
```

The adapter for a provider key.

---

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