aimux_core/image_model.rs
1//! The `ImageModel` trait — the provider-facing interface for image generation.
2//!
3//! Aligned with Vercel AI SDK `ImageModelV4`
4//! (`reference/ai/packages/provider/src/image-model/v4/`).
5
6use async_trait::async_trait;
7use serde::{Deserialize, Serialize};
8use ts_rs::TS;
9
10use crate::error::AiMuxError;
11use crate::shared::{
12 AbortSignal, AspectRatio, SharedHeaders, SharedProviderMetadata, SharedProviderOptions, Size,
13 Warning,
14};
15
16/// An image file used for image editing or variation generation.
17///
18/// Aligned with V4 `ImageModelV4File`.
19#[derive(Debug, Clone, Serialize, Deserialize, TS)]
20#[ts(export)]
21pub enum ImageFile {
22 /// Inline file data (base64 or binary) with an explicit media type.
23 File {
24 /// IANA media type, e.g. `"image/png"`.
25 media_type: String,
26 /// File data as base64 string or binary bytes.
27 data: ImageFileData,
28 },
29 /// A URL pointing to the image.
30 Url {
31 /// The URL of the image file.
32 url: String,
33 },
34}
35
36/// File payload for an [`ImageFile::File`]: base64 string or raw bytes.
37#[derive(Debug, Clone, Serialize, Deserialize, TS)]
38#[ts(export)]
39pub enum ImageFileData {
40 /// Base64-encoded string.
41 Base64(String),
42 /// Raw binary bytes.
43 Binary(Vec<u8>),
44}
45
46/// The generated images, returned as a homogeneous collection.
47///
48/// The TS result type is `Array<string> | Array<Uint8Array>` — i.e. either all
49/// base64 strings or all binary blobs, never mixed. This enum models that
50/// union. Providers should return data without unnecessary conversion: if the
51/// upstream API returns base64, return [`ImageOutputs::Base64`]; if binary,
52/// return [`ImageOutputs::Binary`].
53#[derive(Debug, Clone, Serialize, Deserialize, TS)]
54#[ts(export)]
55pub enum ImageOutputs {
56 /// Images as base64-encoded strings.
57 Base64(Vec<String>),
58 /// Images as raw binary bytes.
59 Binary(Vec<Vec<u8>>),
60}
61
62/// Token usage for an image generation call (if the provider reports it).
63///
64/// Aligned with V4 `ImageModelV4Usage`.
65#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
66#[ts(export)]
67pub struct ImageUsage {
68 /// Number of input (prompt) tokens used.
69 pub input_tokens: Option<u32>,
70 /// Number of output tokens used, if reported.
71 pub output_tokens: Option<u32>,
72 /// Total tokens as reported by the provider.
73 pub total_tokens: Option<u32>,
74}
75
76/// Options passed to [`ImageModel::do_generate`].
77///
78/// Aligned with V4 `ImageModelV4CallOptions`.
79#[derive(Debug, Clone, Serialize, Deserialize, TS)]
80#[ts(export)]
81pub struct ImageCallOptions {
82 /// Prompt for the image generation. `None` for operations (e.g. upscaling)
83 /// that do not require a prompt.
84 pub prompt: Option<String>,
85
86 /// Number of images to generate.
87 pub n: u32,
88
89 /// Size of the images, in `{width}x{height}` format.
90 /// `None` uses the provider's default size.
91 pub size: Option<Size>,
92
93 /// Aspect ratio of the images, in `{width}:{height}` format.
94 /// `None` uses the provider's default aspect ratio.
95 pub aspect_ratio: Option<AspectRatio>,
96
97 /// Seed for deterministic generation. `None` uses the provider's default.
98 #[ts(type = "number | null")]
99 pub seed: Option<u64>,
100
101 /// Images for image editing or variation generation.
102 pub files: Option<Vec<ImageFile>>,
103
104 /// Mask image for inpainting operations.
105 pub mask: Option<ImageFile>,
106
107 /// Additional provider-specific options, keyed by provider name.
108 pub provider_options: SharedProviderOptions,
109
110 /// Abort signal for cancelling the operation.
111 #[serde(skip)]
112 #[ts(skip)]
113 pub abort_signal: Option<AbortSignal>,
114
115 /// Additional HTTP headers to send with the request.
116 pub headers: Option<SharedHeaders>,
117}
118
119impl ImageCallOptions {
120 /// Create options with a prompt and `n = 1`, all other fields defaulted.
121 pub fn new(prompt: impl Into<String>) -> Self {
122 Self {
123 prompt: Some(prompt.into()),
124 n: 1,
125 size: None,
126 aspect_ratio: None,
127 seed: None,
128 files: None,
129 mask: None,
130 provider_options: SharedProviderOptions::new(),
131 abort_signal: None,
132 headers: None,
133 }
134 }
135}
136
137/// The result of [`ImageModel::do_generate`].
138///
139/// Aligned with V4 `ImageModelV4Result`.
140#[derive(Debug, Clone, Serialize, Deserialize, TS)]
141#[ts(export)]
142pub struct ImageResult {
143 /// Generated images (base64 strings or binary data).
144 pub images: ImageOutputs,
145
146 /// Warnings for the call, e.g. unsupported features.
147 pub warnings: Vec<Warning>,
148
149 /// Additional provider-specific metadata, keyed by provider name.
150 pub provider_metadata: Option<SharedProviderMetadata>,
151
152 /// Response information for telemetry and debugging.
153 pub response: ImageResponse,
154
155 /// Optional token usage for the call (if the provider reports it).
156 pub usage: Option<ImageUsage>,
157}
158
159/// Response information for an image generation call.
160#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
161#[ts(export)]
162pub struct ImageResponse {
163 /// Timestamp for the start of the generated response (ISO 8601 string).
164 pub timestamp: Option<String>,
165 /// The ID of the model that was used to generate the response.
166 pub model_id: Option<String>,
167 /// Response headers.
168 pub headers: Option<SharedHeaders>,
169}
170
171/// The unified image generation model trait (provider-facing).
172///
173/// Aligned with V4 `ImageModelV4`.
174#[async_trait]
175pub trait ImageModel: Send + Sync {
176 /// Specification version (always `"v4"`).
177 fn specification_version(&self) -> &'static str {
178 "v4"
179 }
180
181 /// Provider name, e.g. `"openai"`.
182 fn provider(&self) -> &str;
183
184 /// Provider-specific model ID, e.g. `"dall-e-3"`.
185 fn model_id(&self) -> &str;
186
187 /// Limit of how many images can be generated in a single API call.
188 ///
189 /// `None` means there is no fixed limit. The TS spec allows this to be a
190 /// function of `modelId`; in Rust the provider resolves it to a plain
191 /// `Option<u32>`.
192 fn max_images_per_call(&self) -> Option<u32>;
193
194 /// Generate an array of images.
195 ///
196 /// Naming: the `do_` prefix prevents accidental direct usage by users.
197 async fn do_generate(&self, options: &ImageCallOptions) -> Result<ImageResult, AiMuxError>;
198}