diff --git a/crates/api_clients/fal_client/src/requests/webhook/image/edit/enqueue_gpt_image_1p5_edit_image_webhook.rs b/crates/api_clients/fal_client/src/requests/webhook/image/edit/enqueue_gpt_image_1p5_edit_image_webhook.rs new file mode 100644 index 0000000000..67aeb028aa --- /dev/null +++ b/crates/api_clients/fal_client/src/requests/webhook/image/edit/enqueue_gpt_image_1p5_edit_image_webhook.rs @@ -0,0 +1,182 @@ +use crate::creds::fal_api_key::FalApiKey; +use crate::error::classify_fal_error::classify_fal_error; +use crate::error::fal_error_plus::FalErrorPlus; +use fal::endpoints::fal_ai::gpt_image::gpt_image_1p5_image_edit::{gpt_image_1p5_image_edit, GptImage1p5ImageEditInput}; +use fal::webhook::WebhookResponse; +use reqwest::IntoUrl; + +pub struct EnqueueGptImage1p5EditImageArgs<'a, R: IntoUrl> { + // Request required + pub prompt: &'a str, + pub image_urls: Vec, + pub num_images: EnqueueGptImage1p5EditImageNumImages, + + // Optional args + pub mask_image_url: Option, + pub image_size: Option, + pub background: Option, + pub quality: Option, + pub input_fidelity: Option, + pub output_format: Option, + + // Fulfillment + pub webhook_url: R, + pub api_key: &'a FalApiKey, +} + +#[derive(Copy, Clone, Debug)] +pub enum EnqueueGptImage1p5EditImageNumImages { + One, + Two, + Three, + Four, +} + +#[derive(Copy, Clone, Debug)] +pub enum EnqueueGptImage1p5EditImageSize { + /// 1024x1024 + Square, + /// 1536x1024 + Wide, + /// 1024x1536 + Tall, +} + +#[derive(Copy, Clone, Debug)] +pub enum EnqueueGptImage1p5EditImageBackground { + Auto, + Transparent, + Opaque, +} + +#[derive(Copy, Clone, Debug)] +pub enum EnqueueGptImage1p5EditImageQuality { + Low, + Medium, + High, +} + +#[derive(Copy, Clone, Debug)] +pub enum EnqueueGptImage1p5EditImageInputFidelity { + Low, + High, +} + +#[derive(Copy, Clone, Debug)] +pub enum EnqueueGptImage1p5EditImageOutputFormat { + Jpeg, + Png, + Webp, +} + +pub async fn enqueue_gpt_image_1p5_image_edit_webhook( + args: EnqueueGptImage1p5EditImageArgs<'_, R> +) -> Result { + + let num_images = match args.num_images { + EnqueueGptImage1p5EditImageNumImages::One => 1, + EnqueueGptImage1p5EditImageNumImages::Two => 2, + EnqueueGptImage1p5EditImageNumImages::Three => 3, + EnqueueGptImage1p5EditImageNumImages::Four => 4, + }; + + let image_size = args.image_size + .map(|s| match s { + EnqueueGptImage1p5EditImageSize::Square => "1024x1024", + EnqueueGptImage1p5EditImageSize::Wide => "1536x1024", + EnqueueGptImage1p5EditImageSize::Tall => "1024x1536", + }) + .map(|resolution| resolution.to_string()); + + let background = args.background + .map(|s| match s { + EnqueueGptImage1p5EditImageBackground::Auto => "auto", + EnqueueGptImage1p5EditImageBackground::Transparent => "transparent", + EnqueueGptImage1p5EditImageBackground::Opaque => "opaque", + }) + .map(|aspect_ratio| aspect_ratio.to_string()); + + let quality = args.quality + .map(|s| match s { + EnqueueGptImage1p5EditImageQuality::Low => "low", + EnqueueGptImage1p5EditImageQuality::Medium => "medium", + EnqueueGptImage1p5EditImageQuality::High => "high", + }) + .map(|aspect_ratio| aspect_ratio.to_string()); + + let input_fidelity = args.input_fidelity + .map(|s| match s { + EnqueueGptImage1p5EditImageInputFidelity::Low => "low", + EnqueueGptImage1p5EditImageInputFidelity::High => "high", + }) + .map(|aspect_ratio| aspect_ratio.to_string()); + + let output_format = args.output_format + .map(|s| match s { + EnqueueGptImage1p5EditImageOutputFormat::Jpeg => "jpeg", + EnqueueGptImage1p5EditImageOutputFormat::Png => "png", + EnqueueGptImage1p5EditImageOutputFormat::Webp => "webp", + }) + .map(|aspect_ratio| aspect_ratio.to_string()) + .unwrap_or_else(|| "png".to_string()); + + let request = GptImage1p5ImageEditInput { + prompt: args.prompt.to_string(), + image_urls: args.image_urls, + num_images: Some(num_images), + output_format: Some(output_format), + // Optionals + mask_image_url: args.mask_image_url, + image_size, + background, + quality, + input_fidelity, + }; + + let result = gpt_image_1p5_image_edit(request) + .with_api_key(&args.api_key.0) + .queue_webhook(args.webhook_url) + .await; + + result.map_err(|err| classify_fal_error(err)) +} + +#[cfg(test)] +mod tests { + use crate::creds::fal_api_key::FalApiKey; + use crate::requests::webhook::image::edit::enqueue_gpt_image_1p5_edit_image_webhook::{enqueue_gpt_image_1p5_image_edit_webhook, EnqueueGptImage1p5EditImageArgs, EnqueueGptImage1p5EditImageNumImages}; + use errors::AnyhowResult; + use std::fs::read_to_string; + use test_data::web::image_urls::{ERNEST_SCARED_STUPID_IMAGE_URL, GHOST_IMAGE_URL, TREX_SKELETON_IMAGE_URL}; + + #[tokio::test] + #[ignore] + async fn test() -> AnyhowResult<()> { + // XXX: Don't commit secrets! + let secret = read_to_string("/Users/bt/Artcraft/credentials/fal_api_key.txt")?; + + let api_key = FalApiKey::from_str(&secret); + + let args = EnqueueGptImage1p5EditImageArgs { + image_urls: vec![ + GHOST_IMAGE_URL.to_string(), + TREX_SKELETON_IMAGE_URL.to_string(), + ERNEST_SCARED_STUPID_IMAGE_URL.to_string(), + ], + prompt: "add the ghost and scared man to the image of the t-rex skeleton, make it look spooky but friendly", + num_images: EnqueueGptImage1p5EditImageNumImages::Two, + mask_image_url: None, + image_size: None, + background: None, + quality: None, + input_fidelity: None, + api_key: &api_key, + webhook_url: "https://example.com/webhook", + output_format: None, + }; + + let result = enqueue_gpt_image_1p5_image_edit_webhook(args).await?; + + Ok(()) + } +} diff --git a/crates/api_clients/fal_client/src/requests/webhook/image/edit/mod.rs b/crates/api_clients/fal_client/src/requests/webhook/image/edit/mod.rs index bd0f2a6971..e58f936c13 100644 --- a/crates/api_clients/fal_client/src/requests/webhook/image/edit/mod.rs +++ b/crates/api_clients/fal_client/src/requests/webhook/image/edit/mod.rs @@ -3,6 +3,7 @@ pub mod enqueue_bytedance_seedream_v4p5_edit_image_webhook; pub mod enqueue_flux_pro_kontext_max_edit_webhook; pub mod enqueue_gemini_25_flash_edit_webhook; pub mod enqueue_gpt_image_1_edit_image_webhook; +pub mod enqueue_gpt_image_1p5_edit_image_webhook; pub mod enqueue_nano_banana_pro_edit_image_webhook; pub mod enqueue_qwen_image_edit_webhook; pub mod enqueue_seededit_v3_edit_webhook; \ No newline at end of file diff --git a/crates/api_clients/fal_client/src/requests/webhook/image/text/enqueue_gpt_image_1p5_text_to_image_webhook.rs b/crates/api_clients/fal_client/src/requests/webhook/image/text/enqueue_gpt_image_1p5_text_to_image_webhook.rs new file mode 100644 index 0000000000..57dd7717f9 --- /dev/null +++ b/crates/api_clients/fal_client/src/requests/webhook/image/text/enqueue_gpt_image_1p5_text_to_image_webhook.rs @@ -0,0 +1,164 @@ +use crate::creds::fal_api_key::FalApiKey; +use crate::error::classify_fal_error::classify_fal_error; +use crate::error::fal_error_plus::FalErrorPlus; +use fal::endpoints::fal_ai::gpt_image::gpt_image_1p5_text_to_image::{gpt_image_1p5_text_to_image, GptImage1p5TextToImageInput}; +use fal::webhook::WebhookResponse; +use reqwest::IntoUrl; + +pub struct EnqueueGptImage1p5TextToImageArgs<'a, R: IntoUrl> { + // Request required + pub prompt: &'a str, + pub num_images: EnqueueGptImage1p5TextToImageNumImages, + + // Optional args + pub image_size: Option, + pub background: Option, + pub quality: Option, + pub output_format: Option, + + // Fulfillment + pub webhook_url: R, + pub api_key: &'a FalApiKey, +} + +#[derive(Copy, Clone, Debug)] +pub enum EnqueueGptImage1p5TextToImageNumImages { + One, + Two, + Three, + Four, +} + +#[derive(Copy, Clone, Debug)] +pub enum EnqueueGptImage1p5TextToImageSize { + /// 1024x1024 + Square, + /// 1536x1024 + Wide, + /// 1024x1536 + Tall, +} + +#[derive(Copy, Clone, Debug)] +pub enum EnqueueGptImage1p5TextToImageBackground { + Auto, + Transparent, + Opaque, +} + +#[derive(Copy, Clone, Debug)] +pub enum EnqueueGptImage1p5TextToImageQuality { + Low, + Medium, + High, +} + +#[derive(Copy, Clone, Debug)] +pub enum EnqueueGptImage1p5TextToImageOutputFormat { + Jpeg, + Png, + Webp, +} + + +pub async fn enqueue_gpt_image_1p5_text_to_image_webhook( + args: EnqueueGptImage1p5TextToImageArgs<'_, R> +) -> Result { + + + let num_images = match args.num_images { + EnqueueGptImage1p5TextToImageNumImages::One => 1, + EnqueueGptImage1p5TextToImageNumImages::Two => 2, + EnqueueGptImage1p5TextToImageNumImages::Three => 3, + EnqueueGptImage1p5TextToImageNumImages::Four => 4, + }; + + let image_size = args.image_size + .map(|s| match s { + EnqueueGptImage1p5TextToImageSize::Square => "1024x1024", + EnqueueGptImage1p5TextToImageSize::Wide => "1536x1024", + EnqueueGptImage1p5TextToImageSize::Tall => "1024x1536", + }) + .map(|resolution| resolution.to_string()); + + let background = args.background + .map(|s| match s { + EnqueueGptImage1p5TextToImageBackground::Auto => "auto", + EnqueueGptImage1p5TextToImageBackground::Transparent => "transparent", + EnqueueGptImage1p5TextToImageBackground::Opaque => "opaque", + }) + .map(|aspect_ratio| aspect_ratio.to_string()); + + let quality = args.quality + .map(|s| match s { + EnqueueGptImage1p5TextToImageQuality::Low => "low", + EnqueueGptImage1p5TextToImageQuality::Medium => "medium", + EnqueueGptImage1p5TextToImageQuality::High => "high", + }) + .map(|aspect_ratio| aspect_ratio.to_string()); + + let output_format = args.output_format + .map(|s| match s { + EnqueueGptImage1p5TextToImageOutputFormat::Jpeg => "jpeg", + EnqueueGptImage1p5TextToImageOutputFormat::Png => "png", + EnqueueGptImage1p5TextToImageOutputFormat::Webp => "webp", + }) + .map(|aspect_ratio| aspect_ratio.to_string()) + .unwrap_or_else(|| "png".to_string()); + + let num_images = match args.num_images { + EnqueueGptImage1p5TextToImageNumImages::One => 1, + EnqueueGptImage1p5TextToImageNumImages::Two => 2, + EnqueueGptImage1p5TextToImageNumImages::Three => 3, + EnqueueGptImage1p5TextToImageNumImages::Four => 4, + }; + + let request = GptImage1p5TextToImageInput { + prompt: args.prompt.to_string(), + num_images: Some(num_images), + output_format: Some(output_format), + // Optionals + image_size, + background, + quality, + }; + + let result = gpt_image_1p5_text_to_image(request) + .with_api_key(&args.api_key.0) + .queue_webhook(args.webhook_url) + .await; + + result.map_err(|err| classify_fal_error(err)) +} + +#[cfg(test)] +mod tests { + use crate::creds::fal_api_key::FalApiKey; + use crate::requests::webhook::image::text::enqueue_gpt_image_1p5_text_to_image_webhook::{enqueue_gpt_image_1p5_text_to_image_webhook, EnqueueGptImage1p5TextToImageArgs, EnqueueGptImage1p5TextToImageNumImages}; + use errors::AnyhowResult; + use std::fs::read_to_string; + + #[tokio::test] + #[ignore] + async fn test() -> AnyhowResult<()> { + // XXX: Don't commit secrets! + let secret = read_to_string("/Users/bt/Artcraft/credentials/fal_api_key.txt")?; + + let api_key = FalApiKey::from_str(&secret); + + let args = EnqueueGptImage1p5TextToImageArgs { + prompt: "an anime girl riding on the back of a t-rex", + num_images: EnqueueGptImage1p5TextToImageNumImages::Two, + image_size: None, + background: None, + quality: None, + api_key: &api_key, + webhook_url: "https://example.com/webhook", + output_format: None, + }; + + let result = enqueue_gpt_image_1p5_text_to_image_webhook(args).await?; + + Ok(()) + } +} diff --git a/crates/api_clients/fal_client/src/requests/webhook/image/text/mod.rs b/crates/api_clients/fal_client/src/requests/webhook/image/text/mod.rs index 7bff55e3b3..b35e5a0ecb 100644 --- a/crates/api_clients/fal_client/src/requests/webhook/image/text/mod.rs +++ b/crates/api_clients/fal_client/src/requests/webhook/image/text/mod.rs @@ -1,3 +1,4 @@ pub mod enqueue_bytedance_seedream_v4_text_to_image_webhook; pub mod enqueue_bytedance_seedream_v4p5_text_to_image_webhook; -pub mod enqueue_nano_banana_pro_text_to_image_webhook; \ No newline at end of file +pub mod enqueue_nano_banana_pro_text_to_image_webhook; +pub mod enqueue_gpt_image_1p5_text_to_image_webhook; \ No newline at end of file diff --git a/crates/vendor/fal-rs/src/endpoints/fal_ai/gpt_image/gpt_image_1p5_image_edit.rs b/crates/vendor/fal-rs/src/endpoints/fal_ai/gpt_image/gpt_image_1p5_image_edit.rs index 19db4d1aec..241e2d1911 100644 --- a/crates/vendor/fal-rs/src/endpoints/fal_ai/gpt_image/gpt_image_1p5_image_edit.rs +++ b/crates/vendor/fal-rs/src/endpoints/fal_ai/gpt_image/gpt_image_1p5_image_edit.rs @@ -28,7 +28,7 @@ pub struct GptImage1p5ImageEditInput { /// 1 - 4 #[serde(skip_serializing_if = "Option::is_none")] - pub number_of_images: Option, + pub num_images: Option, /// Possible enum values: jpeg, png, webp #[serde(skip_serializing_if = "Option::is_none")] diff --git a/crates/vendor/fal-rs/src/endpoints/fal_ai/gpt_image/gpt_image_1p5_text_to_image.rs b/crates/vendor/fal-rs/src/endpoints/fal_ai/gpt_image/gpt_image_1p5_text_to_image.rs index 7163bdba54..ababf73e4f 100644 --- a/crates/vendor/fal-rs/src/endpoints/fal_ai/gpt_image/gpt_image_1p5_text_to_image.rs +++ b/crates/vendor/fal-rs/src/endpoints/fal_ai/gpt_image/gpt_image_1p5_text_to_image.rs @@ -18,7 +18,7 @@ pub struct GptImage1p5TextToImageInput { /// 1 - 4 #[serde(skip_serializing_if = "Option::is_none")] - pub number_of_images: Option, + pub num_images: Option, /// Possible enum values: jpeg, png, webp #[serde(skip_serializing_if = "Option::is_none")] @@ -36,7 +36,7 @@ pub struct GptImage1p5TextToImageOutput { pub images: Vec, } -pub fn nano_banana_pro_text_to_image( +pub fn gpt_image_1p5_text_to_image( params: GptImage1p5TextToImageInput, ) -> FalRequest { FalRequest::new("fal-ai/gpt-image-1.5", params)