1use std::future::Future;
11use std::sync::Arc;
12
13use serde::Deserialize;
14use serde::Serialize;
15use tokio_util::sync::CancellationToken;
16use url::Url;
17
18use crate::dynamic::BoxFuture;
19use crate::error::ProviderError;
20use crate::image_model::AspectRatio;
21use crate::image_model::ImageSize;
22use crate::json::JsonValue;
23use crate::language_model::ResponseMetadata;
24use crate::shared::FileData;
25use crate::shared::Headers;
26use crate::shared::MediaType;
27use crate::shared::ModelId;
28use crate::shared::ProviderId;
29use crate::shared::ProviderMetadata;
30use crate::shared::ProviderOptions;
31use crate::shared::Warning;
32
33pub trait VideoModel: Send + Sync + 'static {
35 fn provider(&self) -> &ProviderId;
37
38 fn model_id(&self) -> &ModelId;
40
41 fn max_videos_per_call(&self) -> Option<usize>;
43
44 fn supports_generate(&self) -> bool {
46 false
47 }
48
49 fn do_generate(
51 &self,
52 options: VideoOptions,
53 ) -> impl Future<Output = Result<VideoResult, ProviderError>> + Send {
54 let _ = options;
55 std::future::ready(Err(ProviderError::unsupported(
56 "synchronous video generation",
57 )))
58 }
59
60 fn supports_operations(&self) -> bool {
63 false
64 }
65
66 fn do_start(
68 &self,
69 options: VideoStartOptions,
70 ) -> impl Future<Output = Result<VideoStartResult, ProviderError>> + Send {
71 let _ = options;
72 std::future::ready(Err(ProviderError::unsupported(
73 "asynchronous video generation",
74 )))
75 }
76
77 fn do_status(
79 &self,
80 options: VideoStatusOptions,
81 ) -> impl Future<Output = Result<VideoStatusResult, ProviderError>> + Send {
82 let _ = options;
83 std::future::ready(Err(ProviderError::unsupported(
84 "asynchronous video generation",
85 )))
86 }
87
88 fn supports_webhook(&self) -> bool {
90 false
91 }
92
93 fn handle_webhook(
98 &self,
99 factory: WebhookFactory,
100 ) -> impl Future<Output = Result<WebhookHandle, ProviderError>> + Send {
101 factory()
102 }
103}
104
105#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
107#[serde(untagged)]
108pub enum VideoAspectRatio {
109 Ratio(AspectRatio),
111 #[serde(with = "adaptive")]
113 Adaptive,
114}
115
116mod adaptive {
117 pub(super) fn serialize<S: serde::Serializer>(serializer: S) -> Result<S::Ok, S::Error> {
118 serializer.serialize_str("adaptive")
119 }
120
121 pub(super) fn deserialize<'de, D: serde::Deserializer<'de>>(
122 deserializer: D,
123 ) -> Result<(), D::Error> {
124 let text = <std::borrow::Cow<'de, str> as serde::Deserialize>::deserialize(deserializer)?;
125 if text == "adaptive" {
126 Ok(())
127 } else {
128 Err(serde::de::Error::custom("expected `adaptive`"))
129 }
130 }
131}
132
133#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
135#[serde(rename_all = "snake_case")]
136#[non_exhaustive]
137pub enum FrameType {
138 FirstFrame,
140 LastFrame,
142}
143
144#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
146pub struct VideoFile {
147 pub data: FileData,
149 #[serde(default, skip_serializing_if = "Option::is_none")]
151 pub media_type: Option<MediaType>,
152 #[serde(default, skip_serializing_if = "Option::is_none")]
154 pub provider_options: Option<ProviderOptions>,
155}
156
157#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
159pub struct FrameImage {
160 pub image: VideoFile,
162 pub frame_type: FrameType,
164}
165
166#[derive(Debug, Clone)]
168pub struct VideoOptions {
169 pub prompt: Option<String>,
171 pub n: u32,
173 pub aspect_ratio: Option<VideoAspectRatio>,
175 pub resolution: Option<ImageSize>,
177 pub duration: Option<f64>,
179 pub fps: Option<u32>,
181 pub seed: Option<u64>,
183 pub image: Option<VideoFile>,
185 pub frame_images: Vec<FrameImage>,
187 pub input_references: Vec<VideoFile>,
189 pub generate_audio: Option<bool>,
191 pub provider_options: ProviderOptions,
193 pub headers: Headers,
195 pub cancellation: CancellationToken,
197}
198
199impl VideoOptions {
200 #[must_use]
202 pub fn new(prompt: impl Into<String>) -> Self {
203 Self {
204 prompt: Some(prompt.into()),
205 ..Self::default()
206 }
207 }
208}
209
210impl Default for VideoOptions {
211 fn default() -> Self {
212 Self {
213 prompt: None,
214 n: 1,
215 aspect_ratio: None,
216 resolution: None,
217 duration: None,
218 fps: None,
219 seed: None,
220 image: None,
221 frame_images: Vec::new(),
222 input_references: Vec::new(),
223 generate_audio: None,
224 provider_options: ProviderOptions::new(),
225 headers: Headers::new(),
226 cancellation: CancellationToken::new(),
227 }
228 }
229}
230
231#[derive(Debug, Clone)]
233pub struct VideoStartOptions {
234 pub options: VideoOptions,
236 pub webhook_url: Option<Url>,
238}
239
240#[derive(Debug, Clone)]
242pub struct VideoStatusOptions {
243 pub operation: JsonValue,
245 pub headers: Headers,
247 pub cancellation: CancellationToken,
249}
250
251#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
253pub struct VideoData {
254 pub data: FileData,
256 pub media_type: MediaType,
258}
259
260#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
262pub struct VideoResult {
263 pub videos: Vec<VideoData>,
265 #[serde(default)]
267 pub warnings: Vec<Warning>,
268 #[serde(default, skip_serializing_if = "Option::is_none")]
270 pub provider_metadata: Option<ProviderMetadata>,
271 #[serde(default)]
273 pub response: ResponseMetadata,
274}
275
276#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
278pub struct VideoStartResult {
279 pub operation: JsonValue,
281 #[serde(default)]
283 pub warnings: Vec<Warning>,
284 #[serde(default, skip_serializing_if = "Option::is_none")]
286 pub provider_metadata: Option<ProviderMetadata>,
287 #[serde(default)]
289 pub response: ResponseMetadata,
290}
291
292#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
294#[serde(tag = "status", rename_all = "lowercase")]
295#[non_exhaustive]
296pub enum VideoStatusResult {
297 Pending {
299 #[serde(default)]
301 warnings: Vec<Warning>,
302 #[serde(default, skip_serializing_if = "Option::is_none")]
304 provider_metadata: Option<ProviderMetadata>,
305 #[serde(default)]
307 response: ResponseMetadata,
308 },
309 Completed {
311 videos: Vec<VideoData>,
313 #[serde(default)]
315 warnings: Vec<Warning>,
316 #[serde(default, skip_serializing_if = "Option::is_none")]
318 provider_metadata: Option<ProviderMetadata>,
319 #[serde(default)]
321 response: ResponseMetadata,
322 },
323 Error {
325 error: String,
327 #[serde(default, skip_serializing_if = "Option::is_none")]
329 provider_metadata: Option<ProviderMetadata>,
330 #[serde(default)]
332 response: ResponseMetadata,
333 },
334}
335
336#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
338pub struct WebhookPayload {
339 pub headers: Headers,
341 pub body: JsonValue,
343}
344
345pub struct WebhookHandle {
348 pub url: Url,
350 pub received: BoxFuture<'static, Result<WebhookPayload, ProviderError>>,
352}
353
354impl std::fmt::Debug for WebhookHandle {
355 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
356 f.debug_struct("WebhookHandle")
357 .field("url", &self.url)
358 .field("received", &"<future>")
359 .finish()
360 }
361}
362
363pub type WebhookFactory =
365 Arc<dyn Fn() -> BoxFuture<'static, Result<WebhookHandle, ProviderError>> + Send + Sync>;