Per-provider MIME and size validation for ALLM.ImagePart content.
Layer B helper used by the OpenAI and (future) Anthropic vision pre-flight
to gate %ALLM.ImagePart{} content against per-provider accept-sets and a
shared 20-MB byte-size ceiling. See spec §35.6 and Phase 17 design §3.1.
Per-image validation (validate/2)
Resolves the part's %ALLM.Image{} to bytes via
ALLM.Image.to_binary/1 and applies two checks:
- MIME membership against the supplied accept-set.
byte_size/1of the resolved bytes against@max_bytes(20 MB).
URL sources ({:url, _}) skip the size check — the adapter does NOT
fetch the URL during pre-flight (Decision #1); size validation is
deferred to the provider. MIME enforcement still applies (the URL's
:mime_type is always nil per ALLM.Image.from_url/1, so a URL-source
%ImagePart{} is accepted regardless of accept-set today; future
divergence is captured by the per-provider arg).
Request-level pre-flight (validate_request/2)
Walks every %ALLM.ImagePart{} in request.messages, calls validate/2
for each, accumulates {[:content, msg_idx, part_idx], reason} field
errors. Returns :ok when no part fails, otherwise an
%ALLM.Error.ValidationError{reason: :invalid_message} with all
per-image errors in :errors. Adapters call this once in pre-flight;
per-provider divergence is isolated to accept_mimes/1.
Provider accept-sets
iex> ALLM.Providers.Support.ImageMime.accept_mimes(:openai)
["image/png", "image/jpeg", "image/webp", "image/gif"]
iex> ALLM.Providers.Support.ImageMime.accept_mimes(:anthropic)
["image/png", "image/jpeg", "image/webp", "image/gif"]Both providers' published 2026-04 accept-sets match; the per-provider arg keeps the seam open for future divergence.
Summary
Types
Per-image validation result.
Functions
Provider accept-set for image/* MIME types.
Validate a single %ImagePart{} against an accept-set and the 20-MB
size ceiling.
Walk every %ImagePart{} in request.messages, validate, and return
either :ok or a %ValidationError{reason: :invalid_message} carrying
per-image field errors.
Types
@type validate_result() :: :ok | {:error, {:unsupported_image_format, mime :: String.t() | nil} | {:image_too_large, byte_size :: non_neg_integer()} | :missing_mime_type}
Per-image validation result.
Functions
@spec accept_mimes(:openai | :anthropic) :: [String.t()]
Provider accept-set for image/* MIME types.
Examples
iex> ALLM.Providers.Support.ImageMime.accept_mimes(:openai)
["image/png", "image/jpeg", "image/webp", "image/gif"]
@spec validate(ALLM.ImagePart.t(), [String.t()]) :: validate_result()
Validate a single %ImagePart{} against an accept-set and the 20-MB
size ceiling.
Examples
iex> img = ALLM.Image.from_binary("hi", "image/png")
iex> part = ALLM.ImagePart.new(img)
iex> ALLM.Providers.Support.ImageMime.validate(part, ["image/png"])
:ok
iex> img = ALLM.Image.from_binary("hi", "image/svg+xml")
iex> part = ALLM.ImagePart.new(img)
iex> ALLM.Providers.Support.ImageMime.validate(part, ["image/png"])
{:error, {:unsupported_image_format, "image/svg+xml"}}
iex> img = ALLM.Image.from_url("https://example.com/x.png")
iex> part = ALLM.ImagePart.new(img)
iex> ALLM.Providers.Support.ImageMime.validate(part, ["image/png"])
:ok
@spec validate_request(ALLM.Request.t(), :openai | :anthropic) :: :ok | {:error, ALLM.Error.ValidationError.t()}
Walk every %ImagePart{} in request.messages, validate, and return
either :ok or a %ValidationError{reason: :invalid_message} carrying
per-image field errors.
Field paths: [:content, msg_idx, part_idx] per spec §35.6 / Phase 17
design §7.
Examples
iex> img = ALLM.Image.from_binary("hi", "image/png")
iex> req = ALLM.Request.new([%ALLM.Message{role: :user, content: [%ALLM.ImagePart{image: img}]}])
iex> ALLM.Providers.Support.ImageMime.validate_request(req, :openai)
:ok