ALLM.ImageAdapter behaviour (allm v0.4.0)

Copy Markdown View Source

Image-generation provider adapter contract.

Layer B — runtime. Implementations take an ALLM.ImageRequest plus a keyword opts list (resolved at the call site by ALLM.generate_image/3) and return either {:ok, %ALLM.ImageResponse{}} or {:error, %ALLM.Error.ImageAdapterError{}}.

Minimum impl skeleton

defmodule MyImageProvider do
  @behaviour ALLM.ImageAdapter

  @impl true
  def generate(%ALLM.ImageRequest{operation: op} = req, opts) do
    if op in supported_operations do
      # Translate request → HTTP body, fire via Req, translate
      # response → %ALLM.ImageResponse{}.
      {:ok, %ALLM.ImageResponse{}}
    else
      {:error, %ALLM.Error.ImageAdapterError{reason: :unsupported_operation, metadata: %{operation: op}}}
    end
  end

  @impl true
  def supported_operations, do: [:generate]
end

HTTP transport guidance

Use Req for non-streaming image calls. Image generation is a request/response shape — there is no streaming counterpart.

Invariants

  1. generate/2 is synchronous: it returns only after the HTTP response has been read in full and any binary image bytes resolved.
  2. generate/2 never raises for HTTP-shaped failures. Network failures, 4xx, and 5xx all convert to {:error, %ALLM.Error.ImageAdapterError{reason: ..., ...}}. Only programmer errors (invalid request shape reaching the adapter, which the validator should have caught) may raise.
  3. generate/2 MUST honor opts[:request_timeout] if provided. Exceeding the timeout produces {:error, %ImageAdapterError{reason: :timeout}}.
  4. generate/2 MUST return {:error, %ImageAdapterError{reason: :unsupported_operation, metadata: %{operation: op}}} BEFORE any HTTP I/O when request.operation not in supported_operations. This is the entry-point gate; per-model gating (e.g., dall-e-3 only supports :generate) is the adapter's internal concern.
  5. generate/2 MUST preserve opts[:request_id] onto response.request_id when the response shape allows. When opts[:request_id] is absent, the adapter is free to populate response.request_id from a provider-supplied id (e.g., x-request-id HTTP header).
  6. generate/2 MUST round-trip request.metadata onto response.metadata UNCHANGED when the adapter has no use for it. (Library treats request/response metadata as opaque.)
  7. prepare_request/2 (optional) returns an unfired Req.Request configured exactly as generate/2 would fire it. Callers may mutate the returned request before firing.

Summary

Callbacks

Execute an image request against the provider synchronously.

Escape hatch: return a configured but unfired Req.Request that the caller can further customize (headers, retries, middleware) before firing.

Return the closed list of operations the adapter can perform.

Callbacks

generate(t, keyword)

@callback generate(
  ALLM.ImageRequest.t(),
  keyword()
) :: {:ok, ALLM.ImageResponse.t()} | {:error, ALLM.Error.ImageAdapterError.t()}

Execute an image request against the provider synchronously.

Returns {:ok, %ALLM.ImageResponse{}} on success, or {:error, %ALLM.Error.ImageAdapterError{}} on every failure shape. See ALLM.Error.ImageAdapterError for the closed reason enum and the per-reason recovery table.

prepare_request(t, keyword)

(optional)
@callback prepare_request(
  ALLM.ImageRequest.t(),
  keyword()
) :: {:ok, Req.Request.t()} | {:error, ALLM.Error.ImageAdapterError.t()}

Escape hatch: return a configured but unfired Req.Request that the caller can further customize (headers, retries, middleware) before firing.

Optional. When unimplemented, callers must dispatch to generate/2 directly.

supported_operations()

@callback supported_operations() :: [ALLM.ImageRequest.operation()]

Return the closed list of operations the adapter can perform.

Per-module (one list for the adapter), NOT per-call-with-model-arg. Per-model gating (e.g., dall-e-3 is generate-only while gpt-image-1 does generate+edit) is the adapter's internal concern, asserted separately in the adapter's own tests.

The conformance suite asserts that requests whose :operation is not in this list are rejected with {:error, %ALLM.Error.ImageAdapterError{reason: :unsupported_operation, metadata: %{operation: op}}} BEFORE any HTTP I/O.