From e8ff0c1e28a4ce185ccedbde3ebf50faddca2028 Mon Sep 17 00:00:00 2001 From: Brandon Thomas Date: Thu, 4 Dec 2025 12:57:30 -0500 Subject: [PATCH] seedream 4.5 fal wrapper --- ...edance_seedream_v4p5_edit_image_webhook.rs | 142 ++++++++++++++++++ .../src/requests/webhook/image/edit/mod.rs | 1 + ...nce_seedream_v4p5_text_to_image_webhook.rs | 134 +++++++++++++++++ .../src/requests/webhook/image/text/mod.rs | 1 + .../bytedance_seedream_v4p5_edit_image.rs | 12 +- .../bytedance_seedream_v4p5_text_to_image.rs | 8 +- 6 files changed, 286 insertions(+), 12 deletions(-) create mode 100644 crates/api_clients/fal_client/src/requests/webhook/image/edit/enqueue_bytedance_seedream_v4p5_edit_image_webhook.rs create mode 100644 crates/api_clients/fal_client/src/requests/webhook/image/text/enqueue_bytedance_seedream_v4p5_text_to_image_webhook.rs diff --git a/crates/api_clients/fal_client/src/requests/webhook/image/edit/enqueue_bytedance_seedream_v4p5_edit_image_webhook.rs b/crates/api_clients/fal_client/src/requests/webhook/image/edit/enqueue_bytedance_seedream_v4p5_edit_image_webhook.rs new file mode 100644 index 0000000000..558971422f --- /dev/null +++ b/crates/api_clients/fal_client/src/requests/webhook/image/edit/enqueue_bytedance_seedream_v4p5_edit_image_webhook.rs @@ -0,0 +1,142 @@ +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::bytedance::seedream::bytedance_seedream_v4p5_edit_image::{bytedance_seedream_v4p5_edit_image, BytedanceSeedreamV4p5EditImageInput}; +use fal::webhook::WebhookResponse; +use reqwest::IntoUrl; + +pub struct EnqueueBytedanceSeedreamV4p5EditImageArgs<'a, R: IntoUrl> { + // Request required + pub prompt: &'a str, + pub image_urls: Vec, + + // Optional args + pub num_images: Option, + pub max_images: Option, + pub image_size: Option, + + // Fulfillment + pub webhook_url: R, + pub api_key: &'a FalApiKey, +} + +#[derive(Copy, Clone, Debug)] +pub enum EnqueueBytedanceSeedreamV4p5EditImageNumImages { + One, + Two, + Three, + Four, +} + +#[derive(Copy, Clone, Debug)] +pub enum EnqueueBytedanceSeedreamV4p5EditImageMaxImages { + One, + Two, + Three, + Four, +} + +#[derive(Copy, Clone, Debug)] +pub enum EnqueueBytedanceSeedreamV4p5EditImageSize { + // Square + Square, + SquareHd, + // Tall + PortraitFourThree, + PortraitSixteenNine, + // Wide + LandscapeFourThree, + LandscapeSixteenNine, + // Auto + Auto2k, + Auto4k, +} + +pub async fn enqueue_bytedance_seedream_v4p5_edit_image_webhook( + args: EnqueueBytedanceSeedreamV4p5EditImageArgs<'_, R> +) -> Result { + + let num_images = args.num_images + .map(|num_images| match num_images { + EnqueueBytedanceSeedreamV4p5EditImageNumImages::One => 1, + EnqueueBytedanceSeedreamV4p5EditImageNumImages::Two => 2, + EnqueueBytedanceSeedreamV4p5EditImageNumImages::Three => 3, + EnqueueBytedanceSeedreamV4p5EditImageNumImages::Four => 4, + }); + + let max_images = args.max_images + .map(|num_images| match num_images { + EnqueueBytedanceSeedreamV4p5EditImageMaxImages::One => 1, + EnqueueBytedanceSeedreamV4p5EditImageMaxImages::Two => 2, + EnqueueBytedanceSeedreamV4p5EditImageMaxImages::Three => 3, + EnqueueBytedanceSeedreamV4p5EditImageMaxImages::Four => 4, + }); + + let image_size = args.image_size + .map(|image_size| match image_size { + EnqueueBytedanceSeedreamV4p5EditImageSize::Square => "square", + EnqueueBytedanceSeedreamV4p5EditImageSize::SquareHd => "square_hd", + EnqueueBytedanceSeedreamV4p5EditImageSize::PortraitFourThree => "portrait_4_3", + EnqueueBytedanceSeedreamV4p5EditImageSize::PortraitSixteenNine => "portrait_16_9", + EnqueueBytedanceSeedreamV4p5EditImageSize::LandscapeFourThree => "landscape_4_3", + EnqueueBytedanceSeedreamV4p5EditImageSize::LandscapeSixteenNine => "landscape_16_9", + EnqueueBytedanceSeedreamV4p5EditImageSize::Auto2k => "auto_2K", + EnqueueBytedanceSeedreamV4p5EditImageSize::Auto4k => "auto_4K", + }) + .map(|resolution| resolution.to_string()); + + let request = BytedanceSeedreamV4p5EditImageInput { + prompt: args.prompt.to_string(), + image_urls: args.image_urls, + // Optionals + num_images, + max_images, + image_size, + // Constants + enable_safety_checker: Some(false), + seed: None, + }; + + let result = bytedance_seedream_v4p5_edit_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::edit::enqueue_bytedance_seedream_v4p5_edit_image_webhook::{enqueue_bytedance_seedream_v4p5_edit_image_webhook, EnqueueBytedanceSeedreamV4p5EditImageArgs, EnqueueBytedanceSeedreamV4p5EditImageMaxImages, EnqueueBytedanceSeedreamV4p5EditImageNumImages, EnqueueBytedanceSeedreamV4p5EditImageSize}; + use errors::AnyhowResult; + use std::fs::read_to_string; + use test_data::web::image_urls::{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 secret = read_to_string("/home/bt/Artcraft/credentials/fal_api_key.txt")?; + + let api_key = FalApiKey::from_str(&secret); + + let args = EnqueueBytedanceSeedreamV4p5EditImageArgs { + image_urls: vec![ + GHOST_IMAGE_URL.to_string(), + TREX_SKELETON_IMAGE_URL.to_string(), + ], + prompt: "add the ghost to the image of the t-rex skeleton, make it look spooky but friendly", + num_images: Some(EnqueueBytedanceSeedreamV4p5EditImageNumImages::Two), + max_images: Some(EnqueueBytedanceSeedreamV4p5EditImageMaxImages::Two), + image_size: Some(EnqueueBytedanceSeedreamV4p5EditImageSize::Auto2k), + api_key: &api_key, + webhook_url: "https://example.com/webhook", + }; + + let result = enqueue_bytedance_seedream_v4p5_edit_image_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 87217cb516..bd0f2a6971 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 @@ -1,4 +1,5 @@ pub mod enqueue_bytedance_seedream_v4_edit_image_webhook; +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; diff --git a/crates/api_clients/fal_client/src/requests/webhook/image/text/enqueue_bytedance_seedream_v4p5_text_to_image_webhook.rs b/crates/api_clients/fal_client/src/requests/webhook/image/text/enqueue_bytedance_seedream_v4p5_text_to_image_webhook.rs new file mode 100644 index 0000000000..b47b2133bc --- /dev/null +++ b/crates/api_clients/fal_client/src/requests/webhook/image/text/enqueue_bytedance_seedream_v4p5_text_to_image_webhook.rs @@ -0,0 +1,134 @@ +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::bytedance::seedream::bytedance_seedream_v4p5_text_to_image::{bytedance_seedream_v4p5_text_to_image, BytedanceSeedreamV4p5TextToImageInput}; +use fal::webhook::WebhookResponse; +use reqwest::IntoUrl; + +pub struct EnqueueBytedanceSeedreamV4p5TextToImageArgs<'a, R: IntoUrl> { + // Request required + pub prompt: &'a str, + + // Optional args + pub num_images: Option, + pub max_images: Option, + pub image_size: Option, + + // Fulfillment + pub webhook_url: R, + pub api_key: &'a FalApiKey, +} + +#[derive(Copy, Clone, Debug)] +pub enum EnqueueBytedanceSeedreamV4p5TextToImageNumImages { + One, + Two, + Three, + Four, +} + +#[derive(Copy, Clone, Debug)] +pub enum EnqueueBytedanceSeedreamV4p5TextToImageMaxImages { + One, + Two, + Three, + Four, +} + +#[derive(Copy, Clone, Debug)] +pub enum EnqueueBytedanceSeedreamV4p5TextToImageSize { + // Square + Square, + SquareHd, + // Tall + PortraitFourThree, + PortraitSixteenNine, + // Wide + LandscapeFourThree, + LandscapeSixteenNine, + // Auto + Auto2k, + Auto4k, +} + +pub async fn enqueue_bytedance_seedream_v4p5_text_to_image_webhook( + args: EnqueueBytedanceSeedreamV4p5TextToImageArgs<'_, R> +) -> Result { + + let num_images = args.num_images + .map(|num_images| match num_images { + EnqueueBytedanceSeedreamV4p5TextToImageNumImages::One => 1, + EnqueueBytedanceSeedreamV4p5TextToImageNumImages::Two => 2, + EnqueueBytedanceSeedreamV4p5TextToImageNumImages::Three => 3, + EnqueueBytedanceSeedreamV4p5TextToImageNumImages::Four => 4, + }); + + let max_images = args.max_images + .map(|num_images| match num_images { + EnqueueBytedanceSeedreamV4p5TextToImageMaxImages::One => 1, + EnqueueBytedanceSeedreamV4p5TextToImageMaxImages::Two => 2, + EnqueueBytedanceSeedreamV4p5TextToImageMaxImages::Three => 3, + EnqueueBytedanceSeedreamV4p5TextToImageMaxImages::Four => 4, + }); + + let image_size = args.image_size + .map(|image_size| match image_size { + EnqueueBytedanceSeedreamV4p5TextToImageSize::Square => "square", + EnqueueBytedanceSeedreamV4p5TextToImageSize::SquareHd => "square_hd", + EnqueueBytedanceSeedreamV4p5TextToImageSize::PortraitFourThree => "portrait_4_3", + EnqueueBytedanceSeedreamV4p5TextToImageSize::PortraitSixteenNine => "portrait_16_9", + EnqueueBytedanceSeedreamV4p5TextToImageSize::LandscapeFourThree => "landscape_4_3", + EnqueueBytedanceSeedreamV4p5TextToImageSize::LandscapeSixteenNine => "landscape_16_9", + EnqueueBytedanceSeedreamV4p5TextToImageSize::Auto2k => "auto_2K", + EnqueueBytedanceSeedreamV4p5TextToImageSize::Auto4k => "auto_4K", + }) + .map(|resolution| resolution.to_string()); + + let request = BytedanceSeedreamV4p5TextToImageInput { + prompt: args.prompt.to_string(), + // Optionals + num_images, + max_images, + image_size, + // Constants + enable_safety_checker: Some(false), + seed: None, + }; + + let result = bytedance_seedream_v4p5_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_bytedance_seedream_v4p5_text_to_image_webhook::{enqueue_bytedance_seedream_v4p5_text_to_image_webhook, EnqueueBytedanceSeedreamV4p5TextToImageArgs, EnqueueBytedanceSeedreamV4p5TextToImageMaxImages, EnqueueBytedanceSeedreamV4p5TextToImageNumImages, EnqueueBytedanceSeedreamV4p5TextToImageSize}; + 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("/home/bt/Artcraft/credentials/fal_api_key.txt")?; + + let api_key = FalApiKey::from_str(&secret); + + let args = EnqueueBytedanceSeedreamV4p5TextToImageArgs { + prompt: "an anime girl is riding a t-rex in the forest", + num_images: Some(EnqueueBytedanceSeedreamV4p5TextToImageNumImages::Two), + max_images: Some(EnqueueBytedanceSeedreamV4p5TextToImageMaxImages::Two), + image_size: Some(EnqueueBytedanceSeedreamV4p5TextToImageSize::LandscapeSixteenNine), + api_key: &api_key, + webhook_url: "https://example.com/webhook", + }; + + let result = enqueue_bytedance_seedream_v4p5_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 d62eeca98b..7bff55e3b3 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,2 +1,3 @@ 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 diff --git a/crates/vendor/fal-rs/src/endpoints/fal_ai/bytedance/seedream/bytedance_seedream_v4p5_edit_image.rs b/crates/vendor/fal-rs/src/endpoints/fal_ai/bytedance/seedream/bytedance_seedream_v4p5_edit_image.rs index 0041a576e7..e1791856ba 100644 --- a/crates/vendor/fal-rs/src/endpoints/fal_ai/bytedance/seedream/bytedance_seedream_v4p5_edit_image.rs +++ b/crates/vendor/fal-rs/src/endpoints/fal_ai/bytedance/seedream/bytedance_seedream_v4p5_edit_image.rs @@ -1,7 +1,7 @@ use crate::prelude::{Deserialize, FalRequest, Serialize}; #[derive(Debug, Serialize, Deserialize, Default)] -pub struct BytedanceSeedream4p5EditImageInput { +pub struct BytedanceSeedreamV4p5EditImageInput { pub prompt: String, pub image_urls: Vec, @@ -28,17 +28,13 @@ pub struct BytedanceSeedream4p5EditImageInput { #[serde(skip_serializing_if = "Option::is_none")] pub seed: Option, - /// Possible enum values: standard, fast (default "standard") - #[serde(skip_serializing_if = "Option::is_none")] - pub enhance_prompt_mode: Option, - /// Defaults to true #[serde(skip_serializing_if = "Option::is_none")] pub enable_safety_checker: Option, } #[derive(Debug, Serialize, Deserialize)] -pub struct BytedanceSeedream4p5EditImageOutput { +pub struct BytedanceSeedreamV4p5EditImageOutput { pub images: Vec, } @@ -49,7 +45,7 @@ pub struct ImageFile { } pub fn bytedance_seedream_v4p5_edit_image( - params: BytedanceSeedream4p5EditImageInput, -) -> FalRequest { + params: BytedanceSeedreamV4p5EditImageInput, +) -> FalRequest { FalRequest::new("fal-ai/bytedance/seedream/v4.5/edit", params) } diff --git a/crates/vendor/fal-rs/src/endpoints/fal_ai/bytedance/seedream/bytedance_seedream_v4p5_text_to_image.rs b/crates/vendor/fal-rs/src/endpoints/fal_ai/bytedance/seedream/bytedance_seedream_v4p5_text_to_image.rs index ca8808f3e1..8e72cab789 100644 --- a/crates/vendor/fal-rs/src/endpoints/fal_ai/bytedance/seedream/bytedance_seedream_v4p5_text_to_image.rs +++ b/crates/vendor/fal-rs/src/endpoints/fal_ai/bytedance/seedream/bytedance_seedream_v4p5_text_to_image.rs @@ -1,7 +1,7 @@ use crate::prelude::{Deserialize, FalRequest, Serialize}; #[derive(Debug, Serialize, Deserialize, Default)] -pub struct BytedanceSeedream4p5TextToImageInput { +pub struct BytedanceSeedreamV4p5TextToImageInput { pub prompt: String, /// Supports a custom height/width (which we didn't implement) OR an enum value - @@ -32,7 +32,7 @@ pub struct BytedanceSeedream4p5TextToImageInput { } #[derive(Debug, Serialize, Deserialize)] -pub struct BytedanceSeedream4p5TextToImageOutput { +pub struct BytedanceSeedreamV4p5TextToImageOutput { pub images: Vec, } @@ -43,7 +43,7 @@ pub struct ImageFile { } pub fn bytedance_seedream_v4p5_text_to_image( - params: BytedanceSeedream4p5TextToImageInput, -) -> FalRequest { + params: BytedanceSeedreamV4p5TextToImageInput, +) -> FalRequest { FalRequest::new("fal-ai/bytedance/seedream/v4.5/text-to-image", params) }