PhoenixKitAI.Provider behaviour (PhoenixKitAI v0.23.0)

Copy Markdown View Source

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 keyadapter
"openrouter"PhoenixKitAI.Providers.OpenRouter — the unified Images API
"xai"PhoenixKitAI.Providers.XAI — JSON /images/edits
"openai"PhoenixKitAI.Providers.OpenAI — multipart /images/edits
anything elsePhoenixKitAI.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.

Summary

Callbacks

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

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.

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

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

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.

Functions

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

The adapter used when a provider has no dedicated one.

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

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

The adapter for a provider key.

Types

image()

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

image_result()

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

options()

@type options() :: %{optional(atom()) => term()}

Callbacks

image_edit(t, t, list, options)

@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(t, options)

(optional)
@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(t, t, options)

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

Text-to-image.

image_models(t)

@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(t)

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

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

vision(t, list, options)

(optional)
@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.

Functions

adapters()

@spec adapters() :: %{required(String.t()) => module()}

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

default()

@spec default() :: module()

The adapter used when a provider has no dedicated one.

edit_options(adapter, endpoint, options)

@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(arg1)

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

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

for_provider(key)

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

The adapter for a provider key.