Image Providers¶
Image generation is its own provider surface (RFC §25), not a mode of the conversational response. The model that holds a conversation is rarely one that draws, so an agent conversing through Anthropic draws through Gemini exactly as it transcribes through Deepgram.
ImageProvider ¶
Bases: ABC
Generates images from a prompt, decoupled from the conversation (RFC §25).
model_name
abstractmethod
property
¶
Model identifier (e.g. 'gpt-image-2', 'gemini-3-pro-image').
supports_editing
property
¶
Whether reference_images is honoured. False refuses them outright.
available_models
classmethod
¶
Offline metadata for the image models this provider can describe.
Deliberately not folded into
:meth:~roomkit.providers.ai.base.AIProvider.available_models. The two
catalogs are disjoint sets — gpt-image-2 is not a conversational
model and no chat id draws — so merging them saves no maintenance, the
entries being written once either way, while obliging every consumer of
the conversational catalog to filter out a class of models it can never
use. Entries carry :data:IMAGE_GEN_CAPABILITY for a consumer that
merges the lists on purpose.
The base returns an empty list; providers override it.
catalog_entry ¶
The offline :class:ModelInfo for the active model, if the catalog has it.
generate
abstractmethod
async
¶
Generate n images from prompt.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
prompt
|
str
|
What to draw. |
required |
size
|
str | None
|
Geometry as |
None
|
n
|
int
|
How many images. Must be at least 1. A provider whose API has no batch parameter issues concurrent calls — the vendor bills per image either way. |
1
|
reference_images
|
list[AIImagePart] | None
|
Images to edit or draw from. Non-empty makes this
an edit; a provider that reports |
None
|
Returns:
| Type | Description |
|---|---|
list[ImageResult]
|
Exactly |
list[ImageResult]
|
a caller discovers a short list by indexing it, in production. |
Raises:
| Type | Description |
|---|---|
ProviderError
|
The vendor call failed. |
ValueError
|
The request is one this provider cannot express. |
ImageResult ¶
Bases: BaseModel
One generated image.
Attributes:
| Name | Type | Description |
|---|---|---|
data |
str
|
The image as a |
mime_type |
str
|
Media type of the payload. Always equal to the one spelled
in :attr: |
revised_prompt |
str | None
|
The prompt as the model rewrote it, where the vendor
reports one — |
usage |
dict[str, Any]
|
Token counters for the call, disjoint by construction —
|
MockImageProvider ¶
MockImageProvider(images=None, *, model='mock-image', mime_type='image/png', supports_editing=True, revised_prompt=None)
Bases: ImageProvider
Image provider that returns canned images.
Attributes:
| Name | Type | Description |
|---|---|---|
calls |
list[tuple[str, str | None, int, list[AIImagePart]]]
|
Every |
parse_size ¶
Parse a "WIDTHxHEIGHT" size string into its two integers.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
size
|
str
|
Geometry as the :class: |
required |
Returns:
| Type | Description |
|---|---|
tuple[int, int]
|
|
Raises:
| Type | Description |
|---|---|
ValueError
|
If size is not two positive integers joined by |
to_data_uri ¶
Encode raw bytes as the data:<mime>;base64,<payload> URI RoomKit carries images in.
parse_data_uri ¶
Split a data: URI into its media type and its decoded bytes.
The counterpart of :func:to_data_uri, and the one place a payload is
validated: every provider that accepts an image has to reject a corrupt
one, and each doing it itself is how one of them ends up handing
malformed bytes to a vendor and reporting the rejection as a provider
failure rather than a caller error.
Whitespace — an encoder that wrapped its lines — and missing padding are repaired, so the bytes, and anything re-encoded from them, are canonical whatever the caller's URI carried. Anything else is refused: a character outside the alphabet, a length no padding can complete.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
url
|
str
|
The URI to split. |
required |
fallback_mime
|
str | None
|
Media type to use when the URI declares none. |
None
|
Returns:
| Type | Description |
|---|---|
tuple[str, bytes]
|
|
Raises:
| Type | Description |
|---|---|
ValueError
|
If url is not a |
sniff_mime_type ¶
The media type of raw image bytes, read from their magic number.
For providers whose API can answer with bytes but no declared type — an
:class:ImageResult must state the type its data URI carries, and
labelling a JPEG image/png because a fallback said so is a lie every
consumer of the URI then repeats. Only the formats image APIs actually
return are recognized; fallback answers for anything else.
Vendor implementations¶
OpenAIImageProvider ¶
Bases: ImageProvider
Image provider using the OpenAI Images API.
generate calls images.generate; a call carrying reference images
calls images.edit instead. That split is OpenAI's, not the caller's —
RFC §25.4 requires the provider to absorb it.
OpenAIImageConfig ¶
Bases: BaseModel
OpenAI image-generation provider configuration (RFC §25).
Separate from :class:OpenAIConfig because it configures a different
endpoint with a disjoint model lineup — sampling temperature, reasoning
effort and completion caps mean nothing to /v1/images, and an image
model means nothing to Chat Completions.
Attributes:
| Name | Type | Description |
|---|---|---|
api_key |
SecretStr
|
API key for authentication. |
base_url |
str | None
|
Custom base URL for an OpenAI-compatible images endpoint.
|
model |
str
|
Image model identifier (e.g. |
quality |
str | None
|
|
background |
str | None
|
|
output_format |
str | None
|
|
timeout |
float
|
HTTP request timeout in seconds. Higher than the chat default because a high-quality image routinely takes more than 30s. |
connect_timeout |
float
|
TCP connect timeout in seconds, kept apart from
|
max_retries |
int
|
SDK-level retry count. 0 because RoomKit's RetryPolicy handles retries at the right layer. |
default_headers
class-attribute
instance-attribute
¶
Extra HTTP headers sent on every request, passed to the SDK's
default_headers — same role as on :class:OpenAIConfig.
GeminiImageProvider ¶
Bases: ImageProvider
Image provider using the Gemini Interactions API.
GeminiImageConfig ¶
Bases: BaseModel
Google Gemini image-generation provider configuration (RFC §25).
Separate from :class:GeminiConfig because it configures a disjoint model
lineup — the *-image models the chat catalog explicitly excludes — and
a different set of knobs: geometry and output encoding rather than
temperature and thinking level.
Attributes:
| Name | Type | Description |
|---|---|---|
api_key |
SecretStr
|
API key for authentication. |
model |
str
|
Image model identifier (e.g. |
image_size |
str | None
|
Default resolution tier — |
output_mime_type |
str | None
|
|
timeout |
float
|
Read budget in seconds for one interaction. Higher than the chat default because an image is produced whole: nothing streams before it. |
connect_timeout |
float
|
TCP connect timeout in seconds, kept apart from
|
XAIImageProvider ¶
Bases: ImageProvider
Image provider using xAI's Grok Imagine images API.
supports_editing
property
¶
Whether the configured model takes reference images.
Read off the catalog's edit tag. An id the catalog does not know
(a model newer than the snapshot) defaults to True rather than
refusing a capability the current lineup mostly has — the same
permissive default the chat provider uses for vision.
available_models
classmethod
¶
Curated, offline catalog of Grok Imagine image models.
XAIImageConfig ¶
Bases: BaseModel
xAI (Grok Imagine) image-generation provider configuration (RFC §25).
Separate from :class:XAIConfig because it configures a different
endpoint with a disjoint model lineup — sampling temperature and reasoning
effort mean nothing to /v1/images, and an image model means nothing to
Chat Completions. Distinct from :class:XAIRealtimeConfig for the same
reason — same vendor, three protocols.
Attributes:
| Name | Type | Description |
|---|---|---|
api_key |
SecretStr
|
xAI API key for authentication. |
base_url |
str
|
xAI's API endpoint. Override only to point at a proxy. |
model |
str
|
Grok Imagine image model id — see
:mod: |
quality |
str | None
|
|
resolution |
str | None
|
Default resolution tier — |
timeout |
float
|
HTTP request timeout in seconds. Higher than the chat default because image synthesis routinely takes more than 30s. |
connect_timeout |
float
|
TCP connect timeout in seconds, kept apart from
|
max_retries |
int
|
SDK-level retry count. 0 because RoomKit's RetryPolicy handles retries at the right layer. |
OpenRouterImageProvider ¶
Bases: ImageProvider
Image provider using OpenRouter's Image API.
available_models
classmethod
¶
Curated, offline slice of OpenRouter image model slugs.
A small representative sample — OpenRouter's public
GET /api/v1/images/models is the discovery surface for the full,
always-current set, with each model's live parameter constraints.
OpenRouterImageConfig ¶
Bases: OpenAIImageConfig
OpenRouter image-generation provider configuration (RFC §25).
OpenRouter's Image API routes one request shape to every image model it
aggregates, so this subclasses :class:OpenAIImageConfig and inherits
its request fields (quality, background, output_format,
timeout, max_retries, default_headers) — the Image API accepts
each of them, forwarding what the routed model understands. Only the
endpoint and OpenRouter's app-attribution headers are added on top.
The endpoint is OpenRouter's own (POST {base_url}/images), not the
OpenAI images path — see
:class:~roomkit.providers.openrouter.image.OpenRouterImageProvider.
base_url
class-attribute
instance-attribute
¶
OpenRouter's API root. Override only to point at a self-hosted proxy.
model
instance-attribute
¶
OpenRouter image model slug — e.g. "google/gemini-3.1-flash-image"
or "x-ai/grok-imagine-image-2.0". Required (the value of OpenRouter is
choosing the model). Browse the live set at GET /api/v1/images/models.
site_url
class-attribute
instance-attribute
¶
Sent as the HTTP-Referer header — same attribution role as on
:class:OpenRouterConfig.
app_name
class-attribute
instance-attribute
¶
Sent as the X-Title header — same attribution role as on
:class:OpenRouterConfig.
AzureImageProvider ¶
Bases: OpenAIImageProvider
Image provider using the images endpoint of an Azure OpenAI resource.
Subclasses :class:OpenAIImageProvider the way the chat providers pair up
— Azure serves the same gpt-image-* lineup through the same SDK, so
request building, the generate/edit split, response mapping and usage
accounting are all inherited. Two things are genuinely Azure's: the
client (endpoint, key and API version instead of a bearer token), and the
catalog (none — deployments are user-named).
available_models
classmethod
¶
Azure exposes user-named deployments, not a fixed model catalog.
The reason :meth:AzureAIProvider.available_models gives holds here
too: deployment names are chosen per Azure resource, so there is no
meaningful offline list. Rates for the underlying gpt-image-*
models are Azure's own, not OpenAI's, so the OpenAI image catalog is
deliberately not inherited either.
AzureImageConfig ¶
Bases: BaseModel
Azure OpenAI image-generation provider configuration (RFC §25).
Configures the images endpoint of an Azure OpenAI resource — the same
gpt-image-* lineup :class:~roomkit.providers.openai.config.OpenAIImageConfig
reaches on openai.com, deployed under a name the resource owner chose.
Separate from :class:AzureAIConfig for the reason the OpenAI configs are
separate: a different endpoint, a disjoint model lineup, and none of the
chat request fields mean anything to it.
Attributes:
| Name | Type | Description |
|---|---|---|
api_key |
SecretStr
|
Azure API key for authentication. |
azure_endpoint |
str
|
Azure OpenAI resource endpoint URL. |
api_version |
str
|
Azure API version string. The default is the version
Azure's image-generation documentation currently requires; older
versions predate |
model |
str
|
Deployment name (no default — deployment names are chosen per Azure resource, so there is nothing sensible to guess). |
quality |
str | None
|
|
background |
str | None
|
|
output_format |
str | None
|
|
timeout |
float
|
HTTP request timeout in seconds. Higher than the chat default because a high-quality image routinely takes more than 30s. |
connect_timeout |
float
|
TCP connect timeout in seconds, kept apart from
|
max_retries |
int
|
SDK-level retry count. 0 because RoomKit's RetryPolicy handles retries at the right layer. |
Catalogs and pricing¶
Each provider ships an offline catalog through available_models(), disjoint
from the conversational catalog returned by AIProvider.available_models(): no
id draws and converses, so merging the two would only oblige every consumer of
the conversational list to filter out models it can never use. Entries carry
capabilities=["image_gen", "edit"] for a consumer that combines the lists on
purpose.
The usage counters an ImageResult reports — input_tokens,
input_image_tokens, output_tokens, output_image_tokens — are disjoint: a
token appears under exactly one of them, so summing them counts it once.
Pricing is separate from that report, and optional. The OpenAI and Gemini
lineups meter per token, with generated pixels on their own counter at a rate
an order of magnitude above text, so ModelPricing carries
image_input_per_million and image_output_per_million alongside the text
rates and cost_for() applies them. xAI and most of OpenRouter's lineup charge
a flat amount per image; their catalog entries carry no pricing rather than a
rate in the wrong unit, and OpenRouter reports the billed amount itself, which
its provider surfaces as ImageResult.usage["cost"].
entry = next(m for m in provider.available_models() if m.id == provider.model_name)
cost = entry.pricing.cost_for(result.usage)
See the Image Generation guide for the whole path, from a tool call to an image in a room.