Skip to content

Images API

Validated in-memory image supplied to a multimodal LLM request.

Image bytes are deliberately kept in memory and are never fetched from a URL by the library. This gives every provider the same input contract and avoids surprising network access or provider-specific URL semantics.

Source code in majordomo_llm/base.py
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
@dataclass(frozen=True)
class ImageInput:
    """Validated in-memory image supplied to a multimodal LLM request.

    Image bytes are deliberately kept in memory and are never fetched from a
    URL by the library. This gives every provider the same input contract and
    avoids surprising network access or provider-specific URL semantics.
    """

    data: bytes
    media_type: str

    def __post_init__(self) -> None:
        if not self.data:
            raise ValueError("ImageInput.data must not be empty")
        if self.media_type not in SUPPORTED_IMAGE_MEDIA_TYPES:
            supported = ", ".join(sorted(SUPPORTED_IMAGE_MEDIA_TYPES))
            raise ValueError(
                f"Unsupported image media type '{self.media_type}'. Supported: {supported}"
            )

Bases: ABC

Common async interface for image generation and editing providers.

Source code in majordomo_llm/image.py
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
class ImageModel(ABC):
    """Common async interface for image generation and editing providers."""

    def __init__(
        self,
        *,
        provider: str,
        model: str,
        text_input_cost: float,
        image_input_cost: float,
        text_output_cost: float,
        image_output_cost: float,
        api_key: str | None = None,
        api_key_alias: str | None = None,
        hook_pipeline: "ImageHookPipeline | None" = None,
    ) -> None:
        self.provider = provider
        self.model = model
        self.text_input_cost = text_input_cost
        self.image_input_cost = image_input_cost
        self.text_output_cost = text_output_cost
        self.image_output_cost = image_output_cost
        self.api_key_hash = _hash_api_key(api_key) if api_key else None
        self.api_key_alias = api_key_alias
        self.hook_pipeline = hook_pipeline

    def get_full_model_name(self) -> str:
        return f"{self.provider}:{self.model}"

    def _calculate_costs(self, usage: ImageUsage) -> tuple[float, float, float]:
        input_cost = (
            usage.text_input_tokens * self.text_input_cost
            + usage.image_input_tokens * self.image_input_cost
        ) / TOKENS_PER_MILLION
        output_cost = (
            usage.text_output_tokens * self.text_output_cost
            + usage.image_output_tokens * self.image_output_cost
        ) / TOKENS_PER_MILLION
        return input_cost, output_cost, input_cost + output_cost

    async def generate(
        self,
        prompt: str,
        *,
        count: int = 1,
        aspect_ratio: ImageAspectRatio = "1:1",
        image_size: ImageSize = "1K",
        output_format: ImageFormat = "jpeg",
        quality: ImageQuality = "auto",
        background: ImageBackground = "auto",
        extra_headers: dict[str, str] | None = None,
        caller_metadata: dict[str, Any] | None = None,
    ) -> ImageResponse:
        """Generate one or more images from a text prompt."""
        request = ImageHookRequest(
            operation="generate",
            prompt=prompt,
            count=count,
            aspect_ratio=aspect_ratio,
            image_size=image_size,
            output_format=output_format,
            quality=quality,
            background=background,
        )
        return await self._execute_request(request, extra_headers, caller_metadata)

    async def edit(
        self,
        prompt: str,
        images: tuple[ImageInput, ...],
        *,
        mask: ImageInput | None = None,
        count: int = 1,
        aspect_ratio: ImageAspectRatio = "1:1",
        image_size: ImageSize = "1K",
        output_format: ImageFormat = "jpeg",
        quality: ImageQuality = "auto",
        background: ImageBackground = "auto",
        extra_headers: dict[str, str] | None = None,
        caller_metadata: dict[str, Any] | None = None,
    ) -> ImageResponse:
        """Edit one or more reference images using a text prompt."""
        request = ImageHookRequest(
            operation="edit",
            prompt=prompt,
            images=images,
            mask=mask,
            count=count,
            aspect_ratio=aspect_ratio,
            image_size=image_size,
            output_format=output_format,
            quality=quality,
            background=background,
        )
        return await self._execute_request(request, extra_headers, caller_metadata)

    async def _execute_request(
        self,
        request: ImageHookRequest,
        extra_headers: dict[str, str] | None,
        caller_metadata: dict[str, Any] | None,
    ) -> ImageResponse:
        if self.hook_pipeline is None:
            return await self._dispatch_request(request, extra_headers, caller_metadata)

        async def call(modified: ImageHookRequest) -> ImageResponse:
            return await self._dispatch_request(modified, extra_headers, caller_metadata)

        return await self.hook_pipeline.run(request, call, caller_metadata=caller_metadata)

    async def _dispatch_request(
        self,
        request: ImageHookRequest,
        extra_headers: dict[str, str] | None,
        caller_metadata: dict[str, Any] | None,
    ) -> ImageResponse:
        if request.operation == "generate":
            return await self._generate_impl(
                request.prompt,
                count=request.count,
                aspect_ratio=request.aspect_ratio,
                image_size=request.image_size,
                output_format=request.output_format,
                quality=request.quality,
                background=request.background,
                extra_headers=extra_headers,
                caller_metadata=caller_metadata,
            )
        return await self._edit_impl(
            request.prompt,
            request.images,
            mask=request.mask,
            count=request.count,
            aspect_ratio=request.aspect_ratio,
            image_size=request.image_size,
            output_format=request.output_format,
            quality=request.quality,
            background=request.background,
            extra_headers=extra_headers,
            caller_metadata=caller_metadata,
        )

    @abstractmethod
    async def _generate_impl(
        self,
        prompt: str,
        *,
        count: int,
        aspect_ratio: ImageAspectRatio,
        image_size: ImageSize,
        output_format: ImageFormat,
        quality: ImageQuality,
        background: ImageBackground,
        extra_headers: dict[str, str] | None,
        caller_metadata: dict[str, Any] | None,
    ) -> ImageResponse: ...

    @abstractmethod
    async def _edit_impl(
        self,
        prompt: str,
        images: tuple[ImageInput, ...],
        *,
        mask: ImageInput | None,
        count: int,
        aspect_ratio: ImageAspectRatio,
        image_size: ImageSize,
        output_format: ImageFormat,
        quality: ImageQuality,
        background: ImageBackground,
        extra_headers: dict[str, str] | None,
        caller_metadata: dict[str, Any] | None,
    ) -> ImageResponse: ...

edit async

edit(prompt, images, *, mask=None, count=1, aspect_ratio='1:1', image_size='1K', output_format='jpeg', quality='auto', background='auto', extra_headers=None, caller_metadata=None)

Edit one or more reference images using a text prompt.

Source code in majordomo_llm/image.py
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
async def edit(
    self,
    prompt: str,
    images: tuple[ImageInput, ...],
    *,
    mask: ImageInput | None = None,
    count: int = 1,
    aspect_ratio: ImageAspectRatio = "1:1",
    image_size: ImageSize = "1K",
    output_format: ImageFormat = "jpeg",
    quality: ImageQuality = "auto",
    background: ImageBackground = "auto",
    extra_headers: dict[str, str] | None = None,
    caller_metadata: dict[str, Any] | None = None,
) -> ImageResponse:
    """Edit one or more reference images using a text prompt."""
    request = ImageHookRequest(
        operation="edit",
        prompt=prompt,
        images=images,
        mask=mask,
        count=count,
        aspect_ratio=aspect_ratio,
        image_size=image_size,
        output_format=output_format,
        quality=quality,
        background=background,
    )
    return await self._execute_request(request, extra_headers, caller_metadata)

generate async

generate(prompt, *, count=1, aspect_ratio='1:1', image_size='1K', output_format='jpeg', quality='auto', background='auto', extra_headers=None, caller_metadata=None)

Generate one or more images from a text prompt.

Source code in majordomo_llm/image.py
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
async def generate(
    self,
    prompt: str,
    *,
    count: int = 1,
    aspect_ratio: ImageAspectRatio = "1:1",
    image_size: ImageSize = "1K",
    output_format: ImageFormat = "jpeg",
    quality: ImageQuality = "auto",
    background: ImageBackground = "auto",
    extra_headers: dict[str, str] | None = None,
    caller_metadata: dict[str, Any] | None = None,
) -> ImageResponse:
    """Generate one or more images from a text prompt."""
    request = ImageHookRequest(
        operation="generate",
        prompt=prompt,
        count=count,
        aspect_ratio=aspect_ratio,
        image_size=image_size,
        output_format=output_format,
        quality=quality,
        background=background,
    )
    return await self._execute_request(request, extra_headers, caller_metadata)

One generated image returned as decoded bytes.

Source code in majordomo_llm/image.py
19
20
21
22
23
24
25
26
27
28
29
@dataclass(frozen=True)
class GeneratedImage:
    """One generated image returned as decoded bytes."""

    data: bytes
    media_type: str
    revised_prompt: str | None = None

    def __post_init__(self) -> None:
        if not self.data:
            raise ValueError("GeneratedImage.data must not be empty")

Modality-specific usage reported by an image provider.

Source code in majordomo_llm/image.py
32
33
34
35
36
37
38
39
@dataclass(frozen=True)
class ImageUsage:
    """Modality-specific usage reported by an image provider."""

    text_input_tokens: int = 0
    image_input_tokens: int = 0
    text_output_tokens: int = 0
    image_output_tokens: int = 0

Generated images with usage, pricing, and latency metadata.

Source code in majordomo_llm/image.py
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
@dataclass(frozen=True)
class ImageResponse:
    """Generated images with usage, pricing, and latency metadata."""

    images: tuple[GeneratedImage, ...]
    usage: ImageUsage
    input_cost: float
    output_cost: float
    total_cost: float
    response_time: float
    provider: str
    model: str

    def __post_init__(self) -> None:
        if not self.images:
            raise ValueError("ImageResponse.images must not be empty")

Create an image-generation model from bundled configuration.

Source code in majordomo_llm/factory.py
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
def get_image_instance(
    provider: str,
    model: str,
    *,
    api_key: str | None = None,
    api_key_alias: str | None = None,
    hook_pipeline: ImageHookPipeline | None = None,
    base_url: str | None = None,
    default_headers: dict[str, str] | None = None,
) -> ImageModel:
    """Create an image-generation model from bundled configuration."""
    provider_config = IMAGE_CONFIG.get(provider)
    if provider_config is None:
        available = ", ".join(IMAGE_CONFIG.keys())
        raise ConfigurationError(f"Unknown image provider '{provider}'. Available: {available}")
    attributes = provider_config.get("models", {}).get(model)
    if attributes is None:
        available = ", ".join(provider_config.get("models", {}).keys())
        raise ConfigurationError(
            f"Unknown image model '{model}' for provider '{provider}'. Available: {available}"
        )
    cls = _IMAGE_PROVIDER_CLASSES.get(provider)
    if cls is None:
        raise ConfigurationError(f"Image provider '{provider}' has no implementation")

    required_costs = (
        "text_input_cost",
        "image_input_cost",
        "text_output_cost",
        "image_output_cost",
    )
    missing = [name for name in required_costs if name not in attributes]
    if missing:
        raise ConfigurationError(
            f"Image model '{provider}/{model}' is missing pricing fields: {', '.join(missing)}"
        )
    image_cls: Any = cls
    return cast(
        ImageModel,
        image_cls(
            model=model,
            text_input_cost=attributes["text_input_cost"],
            image_input_cost=attributes["image_input_cost"],
            text_output_cost=attributes["text_output_cost"],
            image_output_cost=attributes["image_output_cost"],
            api_key=api_key,
            api_key_alias=api_key_alias,
            hook_pipeline=hook_pipeline,
            base_url=base_url,
            default_headers=default_headers,
        ),
    )

Return providers with configured image-generation models.

Source code in majordomo_llm/factory.py
181
182
183
def get_supported_image_providers() -> list[str]:
    """Return providers with configured image-generation models."""
    return list(IMAGE_CONFIG.keys())

Return image-generation models configured for a provider.

Source code in majordomo_llm/factory.py
186
187
188
189
190
191
192
def get_supported_image_models(provider: str) -> list[str]:
    """Return image-generation models configured for a provider."""
    provider_config = IMAGE_CONFIG.get(provider)
    if provider_config is None:
        available = ", ".join(IMAGE_CONFIG.keys())
        raise ConfigurationError(f"Unknown image provider '{provider}'. Available: {available}")
    return list(provider_config.get("models", {}).keys())