diff --git a/crates/api_clients/artcraft/artcraft_router/src/generate/generate_image/cost/fal/estimate_image_cost_fal_nano_banana_2.rs b/crates/api_clients/artcraft/artcraft_router/src/generate/generate_image/cost/fal/estimate_image_cost_fal_nano_banana_2.rs new file mode 100644 index 0000000000..855c17a75c --- /dev/null +++ b/crates/api_clients/artcraft/artcraft_router/src/generate/generate_image/cost/fal/estimate_image_cost_fal_nano_banana_2.rs @@ -0,0 +1,34 @@ +use crate::generate::generate_image::image_generation_cost_estimate::ImageGenerationCostEstimate; +use crate::generate::generate_image::plan::fal::plan_generate_image_fal_nano_banana_2::{ + FalNb2NumImages, FalNb2Resolution, PlanFalNanaBanana2, +}; + +pub(crate) fn estimate_image_cost_fal_nano_banana_2( + plan: &PlanFalNanaBanana2<'_>, +) -> ImageGenerationCostEstimate { + // Pricing: $0.08/image at 0.5K, $0.15/image at 1K or 2K, $0.30/image at 4K. + // (Stored as USD cents.) + let cost_per_image: u64 = match plan.resolution { + Some(FalNb2Resolution::HalfK) => 8, + Some(FalNb2Resolution::FourK) => 30, + _ => 15, + }; + + let num_images: u64 = match plan.num_images { + FalNb2NumImages::One => 1, + FalNb2NumImages::Two => 2, + FalNb2NumImages::Three => 3, + FalNb2NumImages::Four => 4, + }; + + let cost_in_usd_cents = cost_per_image * num_images; + + ImageGenerationCostEstimate { + cost_in_credits: Some(cost_in_usd_cents), + cost_in_usd_cents: Some(cost_in_usd_cents), + is_free: false, + is_unlimited: false, + is_rate_limited: false, + has_watermark: false, + } +} diff --git a/crates/api_clients/artcraft/artcraft_router/src/generate/generate_image/cost/fal/mod.rs b/crates/api_clients/artcraft/artcraft_router/src/generate/generate_image/cost/fal/mod.rs index ef3d0a420c..955164d0f0 100644 --- a/crates/api_clients/artcraft/artcraft_router/src/generate/generate_image/cost/fal/mod.rs +++ b/crates/api_clients/artcraft/artcraft_router/src/generate/generate_image/cost/fal/mod.rs @@ -1 +1,2 @@ +pub(crate) mod estimate_image_cost_fal_nano_banana_2; pub(crate) mod estimate_image_cost_fal_nano_banana_pro; diff --git a/crates/api_clients/artcraft/artcraft_router/src/generate/generate_image/execute/fal/generate_image_fal_nano_banana_2.rs b/crates/api_clients/artcraft/artcraft_router/src/generate/generate_image/execute/fal/generate_image_fal_nano_banana_2.rs new file mode 100644 index 0000000000..e9891b9eb0 --- /dev/null +++ b/crates/api_clients/artcraft/artcraft_router/src/generate/generate_image/execute/fal/generate_image_fal_nano_banana_2.rs @@ -0,0 +1,92 @@ +use crate::client::router_fal_client::RouterFalClient; +use crate::errors::artcraft_router_error::ArtcraftRouterError; +use crate::errors::provider_error::ProviderError; +use crate::generate::generate_image::generate_image_response::{ + FalImageResponsePayload, GenerateImageResponse, +}; +use crate::generate::generate_image::plan::fal::plan_generate_image_fal_nano_banana_2::{ + FalNb2NumImages, FalNb2Resolution, PlanFalNanaBanana2, +}; +use fal_client::requests::webhook::image::edit::enqueue_nano_banana_2_edit_image_webhook::{ + enqueue_nano_banana_2_edit_image_webhook, EnqueueNanoBanana2EditImageArgs, + EnqueueNanoBanana2EditImageNumImages, EnqueueNanoBanana2EditImageResolution, +}; +use fal_client::requests::webhook::image::text::enqueue_nano_banana_2_text_to_image_webhook::{ + enqueue_nano_banana_2_text_to_image_webhook, EnqueueNanoBanana2TextToImageArgs, + EnqueueNanoBanana2TextToImageNumImages, EnqueueNanoBanana2TextToImageResolution, +}; + +pub async fn execute_fal_nano_banana_2( + plan: &PlanFalNanaBanana2<'_>, + fal_client: &RouterFalClient, +) -> Result { + let webhook_response = if plan.image_urls.is_empty() { + // Text-to-image mode + let args = EnqueueNanoBanana2TextToImageArgs { + prompt: plan.prompt.unwrap_or(""), + num_images: to_t2i_num_images(plan.num_images), + resolution: plan.resolution.map(to_t2i_resolution), + aspect_ratio: plan.t2i_aspect_ratio, + webhook_url: fal_client.webhook_url.as_str(), + api_key: &fal_client.api_key, + }; + enqueue_nano_banana_2_text_to_image_webhook(args) + .await + .map_err(|e| ArtcraftRouterError::Provider(ProviderError::Fal(e)))? + } else { + // Image-edit mode + let args = EnqueueNanoBanana2EditImageArgs { + prompt: plan.prompt.unwrap_or(""), + image_urls: plan.image_urls.clone(), + num_images: to_edit_num_images(plan.num_images), + resolution: plan.resolution.map(to_edit_resolution), + aspect_ratio: plan.edit_aspect_ratio, + webhook_url: fal_client.webhook_url.as_str(), + api_key: &fal_client.api_key, + }; + enqueue_nano_banana_2_edit_image_webhook(args) + .await + .map_err(|e| ArtcraftRouterError::Provider(ProviderError::Fal(e)))? + }; + + Ok(GenerateImageResponse::Fal(FalImageResponsePayload { + request_id: webhook_response.request_id, + gateway_request_id: webhook_response.gateway_request_id, + })) +} + +fn to_t2i_num_images(n: FalNb2NumImages) -> EnqueueNanoBanana2TextToImageNumImages { + match n { + FalNb2NumImages::One => EnqueueNanoBanana2TextToImageNumImages::One, + FalNb2NumImages::Two => EnqueueNanoBanana2TextToImageNumImages::Two, + FalNb2NumImages::Three => EnqueueNanoBanana2TextToImageNumImages::Three, + FalNb2NumImages::Four => EnqueueNanoBanana2TextToImageNumImages::Four, + } +} + +fn to_edit_num_images(n: FalNb2NumImages) -> EnqueueNanoBanana2EditImageNumImages { + match n { + FalNb2NumImages::One => EnqueueNanoBanana2EditImageNumImages::One, + FalNb2NumImages::Two => EnqueueNanoBanana2EditImageNumImages::Two, + FalNb2NumImages::Three => EnqueueNanoBanana2EditImageNumImages::Three, + FalNb2NumImages::Four => EnqueueNanoBanana2EditImageNumImages::Four, + } +} + +fn to_t2i_resolution(r: FalNb2Resolution) -> EnqueueNanoBanana2TextToImageResolution { + match r { + FalNb2Resolution::HalfK => EnqueueNanoBanana2TextToImageResolution::HalfK, + FalNb2Resolution::OneK => EnqueueNanoBanana2TextToImageResolution::OneK, + FalNb2Resolution::TwoK => EnqueueNanoBanana2TextToImageResolution::TwoK, + FalNb2Resolution::FourK => EnqueueNanoBanana2TextToImageResolution::FourK, + } +} + +fn to_edit_resolution(r: FalNb2Resolution) -> EnqueueNanoBanana2EditImageResolution { + match r { + FalNb2Resolution::HalfK => EnqueueNanoBanana2EditImageResolution::HalfK, + FalNb2Resolution::OneK => EnqueueNanoBanana2EditImageResolution::OneK, + FalNb2Resolution::TwoK => EnqueueNanoBanana2EditImageResolution::TwoK, + FalNb2Resolution::FourK => EnqueueNanoBanana2EditImageResolution::FourK, + } +} diff --git a/crates/api_clients/artcraft/artcraft_router/src/generate/generate_image/execute/fal/mod.rs b/crates/api_clients/artcraft/artcraft_router/src/generate/generate_image/execute/fal/mod.rs index 3f02544188..2c341dfe8a 100644 --- a/crates/api_clients/artcraft/artcraft_router/src/generate/generate_image/execute/fal/mod.rs +++ b/crates/api_clients/artcraft/artcraft_router/src/generate/generate_image/execute/fal/mod.rs @@ -1 +1,2 @@ +pub mod generate_image_fal_nano_banana_2; pub mod generate_image_fal_nano_banana_pro; diff --git a/crates/api_clients/artcraft/artcraft_router/src/generate/generate_image/generate_image_request.rs b/crates/api_clients/artcraft/artcraft_router/src/generate/generate_image/generate_image_request.rs index 764356a52c..bc8cc10663 100644 --- a/crates/api_clients/artcraft/artcraft_router/src/generate/generate_image/generate_image_request.rs +++ b/crates/api_clients/artcraft/artcraft_router/src/generate/generate_image/generate_image_request.rs @@ -21,6 +21,7 @@ use crate::generate::generate_image::plan::artcraft::plan_generate_image_artcraf use crate::generate::generate_image::plan::artcraft::plan_generate_image_artcraft_seedream_4::plan_generate_image_artcraft_seedream_4; use crate::generate::generate_image::plan::artcraft::plan_generate_image_artcraft_seedream_4p5::plan_generate_image_artcraft_seedream_4p5; use crate::generate::generate_image::plan::artcraft::plan_generate_image_artcraft_seedream_5_lite::plan_generate_image_artcraft_seedream_5_lite; +use crate::generate::generate_image::plan::fal::plan_generate_image_fal_nano_banana_2::plan_generate_image_fal_nano_banana_2; use crate::generate::generate_image::plan::fal::plan_generate_image_fal_nano_banana_pro::plan_generate_image_fal_nano_banana_pro; #[derive(Debug)] @@ -100,6 +101,7 @@ impl<'a> GenerateImageRequest<'a> { fn build_fal(&self) -> Result, ArtcraftRouterError> { match self.model { + CommonImageModel::NanaBanana2 => plan_generate_image_fal_nano_banana_2(self), CommonImageModel::NanaBananaPro => plan_generate_image_fal_nano_banana_pro(self), _ => { Err(ArtcraftRouterError::Client(ClientError::ModelDoesNotSupportOption { diff --git a/crates/api_clients/artcraft/artcraft_router/src/generate/generate_image/image_generation_plan.rs b/crates/api_clients/artcraft/artcraft_router/src/generate/generate_image/image_generation_plan.rs index 34568f316e..1e2eabd4b9 100644 --- a/crates/api_clients/artcraft/artcraft_router/src/generate/generate_image/image_generation_plan.rs +++ b/crates/api_clients/artcraft/artcraft_router/src/generate/generate_image/image_generation_plan.rs @@ -13,6 +13,7 @@ use crate::generate::generate_image::cost::artcraft::estimate_image_cost_artcraf use crate::generate::generate_image::cost::artcraft::estimate_image_cost_artcraft_seedream_5_lite::estimate_image_cost_artcraft_seedream_5_lite; use crate::generate::generate_image::cost::artcraft::estimate_image_cost_artcraft_qwen_edit_2511_angles::estimate_image_cost_artcraft_qwen_edit_2511_angles; use crate::generate::generate_image::cost::artcraft::estimate_image_cost_artcraft_flux_2_lora_angles::estimate_image_cost_artcraft_flux_2_lora_angles; +use crate::generate::generate_image::cost::fal::estimate_image_cost_fal_nano_banana_2::estimate_image_cost_fal_nano_banana_2; use crate::generate::generate_image::cost::fal::estimate_image_cost_fal_nano_banana_pro::estimate_image_cost_fal_nano_banana_pro; use crate::generate::generate_image::execute::artcraft::generate_image_artcraft_flux_1_dev::execute_artcraft_flux_1_dev; use crate::generate::generate_image::execute::artcraft::generate_image_artcraft_flux_1_schnell::execute_artcraft_flux_1_schnell; @@ -27,6 +28,7 @@ use crate::generate::generate_image::execute::artcraft::generate_image_artcraft_ use crate::generate::generate_image::execute::artcraft::generate_image_artcraft_seedream_5_lite::execute_artcraft_seedream_5_lite; use crate::generate::generate_image::execute::artcraft::generate_image_artcraft_qwen_edit_2511_angles::execute_artcraft_qwen_edit_2511_angles; use crate::generate::generate_image::execute::artcraft::generate_image_artcraft_flux_2_lora_angles::execute_artcraft_flux_2_lora_angles; +use crate::generate::generate_image::execute::fal::generate_image_fal_nano_banana_2::execute_fal_nano_banana_2; use crate::generate::generate_image::execute::fal::generate_image_fal_nano_banana_pro::execute_fal_nano_banana_pro; use crate::generate::generate_image::generate_image_response::GenerateImageResponse; use crate::generate::generate_image::image_generation_cost_estimate::ImageGenerationCostEstimate; @@ -43,6 +45,7 @@ use crate::generate::generate_image::plan::artcraft::plan_generate_image_artcraf use crate::generate::generate_image::plan::artcraft::plan_generate_image_artcraft_seedream_5_lite::PlanArtcraftSeedream5Lite; use crate::generate::generate_image::plan::artcraft::plan_generate_image_artcraft_qwen_edit_2511_angles::PlanArtcraftQwenEdit2511Angles; use crate::generate::generate_image::plan::artcraft::plan_generate_image_artcraft_flux_2_lora_angles::PlanArtcraftFlux2LoraAngles; +use crate::generate::generate_image::plan::fal::plan_generate_image_fal_nano_banana_2::PlanFalNanaBanana2; use crate::generate::generate_image::plan::fal::plan_generate_image_fal_nano_banana_pro::PlanFalNanaBananaPro; #[derive(Debug)] @@ -60,6 +63,7 @@ pub enum ImageGenerationPlan<'a> { ArtcraftSeedream5Lite(PlanArtcraftSeedream5Lite<'a>), ArtcraftQwenEdit2511Angles(PlanArtcraftQwenEdit2511Angles<'a>), ArtcraftFlux2LoraAngles(PlanArtcraftFlux2LoraAngles<'a>), + FalNanaBanana2(PlanFalNanaBanana2<'a>), FalNanaBananaPro(PlanFalNanaBananaPro<'a>), } @@ -121,6 +125,10 @@ impl<'a> ImageGenerationPlan<'a> { let artcraft_client = client.get_artcraft_client_ref()?; execute_artcraft_flux_2_lora_angles(plan, artcraft_client).await } + ImageGenerationPlan::FalNanaBanana2(plan) => { + let fal_client = client.get_fal_client_ref()?; + execute_fal_nano_banana_2(plan, fal_client).await + } ImageGenerationPlan::FalNanaBananaPro(plan) => { let fal_client = client.get_fal_client_ref()?; execute_fal_nano_banana_pro(plan, fal_client).await @@ -169,6 +177,9 @@ impl<'a> ImageGenerationPlan<'a> { ImageGenerationPlan::ArtcraftFlux2LoraAngles(plan) => { estimate_image_cost_artcraft_flux_2_lora_angles(plan) } + ImageGenerationPlan::FalNanaBanana2(plan) => { + estimate_image_cost_fal_nano_banana_2(plan) + } ImageGenerationPlan::FalNanaBananaPro(plan) => { estimate_image_cost_fal_nano_banana_pro(plan) } diff --git a/crates/api_clients/artcraft/artcraft_router/src/generate/generate_image/plan/fal/mod.rs b/crates/api_clients/artcraft/artcraft_router/src/generate/generate_image/plan/fal/mod.rs index b0916422f1..fe718e48ed 100644 --- a/crates/api_clients/artcraft/artcraft_router/src/generate/generate_image/plan/fal/mod.rs +++ b/crates/api_clients/artcraft/artcraft_router/src/generate/generate_image/plan/fal/mod.rs @@ -1 +1,2 @@ +pub mod plan_generate_image_fal_nano_banana_2; pub mod plan_generate_image_fal_nano_banana_pro; diff --git a/crates/api_clients/artcraft/artcraft_router/src/generate/generate_image/plan/fal/plan_generate_image_fal_nano_banana_2.rs b/crates/api_clients/artcraft/artcraft_router/src/generate/generate_image/plan/fal/plan_generate_image_fal_nano_banana_2.rs new file mode 100644 index 0000000000..4f541661e4 --- /dev/null +++ b/crates/api_clients/artcraft/artcraft_router/src/generate/generate_image/plan/fal/plan_generate_image_fal_nano_banana_2.rs @@ -0,0 +1,202 @@ +use crate::api::common_aspect_ratio::CommonAspectRatio; +use crate::api::common_resolution::CommonResolution; +use crate::api::image_list_ref::ImageListRef; +use crate::client::request_mismatch_mitigation_strategy::RequestMismatchMitigationStrategy; +use crate::errors::artcraft_router_error::ArtcraftRouterError; +use crate::errors::client_error::ClientError; +use crate::generate::generate_image::generate_image_request::GenerateImageRequest; +use crate::generate::generate_image::image_generation_plan::ImageGenerationPlan; +use fal_client::requests::webhook::image::edit::enqueue_nano_banana_2_edit_image_webhook::EnqueueNanoBanana2EditImageAspectRatio; +use fal_client::requests::webhook::image::text::enqueue_nano_banana_2_text_to_image_webhook::EnqueueNanoBanana2TextToImageAspectRatio; + +/// Intermediate resolution type shared between t2i and edit enums. +#[derive(Debug, Clone, Copy)] +pub enum FalNb2Resolution { + HalfK, + OneK, + TwoK, + FourK, +} + +/// Intermediate num-images type shared between t2i and edit enums. +#[derive(Debug, Clone, Copy)] +pub enum FalNb2NumImages { + One, + Two, + Three, + Four, +} + +#[derive(Debug, Clone)] +pub struct PlanFalNanaBanana2<'a> { + pub prompt: Option<&'a str>, + /// Image URLs for editing. Empty vec = text-to-image mode. + pub image_urls: Vec, + /// Pre-resolved aspect ratio for text-to-image mode. + pub t2i_aspect_ratio: Option, + /// Pre-resolved aspect ratio for image-edit mode. + pub edit_aspect_ratio: Option, + /// Pre-resolved resolution (shared for both modes). + pub resolution: Option, + /// Pre-resolved number of images (1–4, shared for both modes). + pub num_images: FalNb2NumImages, +} + +pub fn plan_generate_image_fal_nano_banana_2<'a>( + request: &'a GenerateImageRequest<'a>, +) -> Result, ArtcraftRouterError> { + let strategy = request.request_mismatch_mitigation_strategy; + let is_edit_mode = request.image_inputs.is_some(); + let image_urls = resolve_image_list_ref(request.image_inputs)?; + let t2i_aspect_ratio = plan_t2i_aspect_ratio(request.aspect_ratio, strategy)?; + let edit_aspect_ratio = plan_edit_aspect_ratio(request.aspect_ratio, is_edit_mode, strategy)?; + let resolution = plan_resolution(request.resolution)?; + let num_images = plan_num_images(request.image_batch_count, strategy)?; + + Ok(ImageGenerationPlan::FalNanaBanana2(PlanFalNanaBanana2 { + prompt: request.prompt, + image_urls, + t2i_aspect_ratio, + edit_aspect_ratio, + resolution, + num_images, + })) +} + +fn resolve_image_list_ref( + image_list_ref: Option>, +) -> Result, ArtcraftRouterError> { + match image_list_ref { + None => Ok(vec![]), + Some(ImageListRef::Urls(urls)) => Ok(urls.clone()), + Some(ImageListRef::MediaFileTokens(_)) => { + Err(ArtcraftRouterError::Client(ClientError::FalOnlySupportsUrls)) + } + } +} + +// NB2 text-to-image supported aspect ratios: +// auto, 1:1, 5:4, 4:3, 3:2, 16:9, 21:9, 4:5, 3:4, 2:3, 9:16 +fn plan_t2i_aspect_ratio( + aspect_ratio: Option, + strategy: RequestMismatchMitigationStrategy, +) -> Result, ArtcraftRouterError> { + use EnqueueNanoBanana2TextToImageAspectRatio as T2iAr; + match aspect_ratio { + None => Ok(None), + + Some(CommonAspectRatio::Auto) + | Some(CommonAspectRatio::Auto2k) + | Some(CommonAspectRatio::Auto4k) => Ok(Some(T2iAr::Auto)), + + Some(CommonAspectRatio::Square) | Some(CommonAspectRatio::SquareHd) => Ok(Some(T2iAr::OneByOne)), + Some(CommonAspectRatio::WideFiveByFour) => Ok(Some(T2iAr::FiveByFour)), + Some(CommonAspectRatio::WideFourByThree) => Ok(Some(T2iAr::FourByThree)), + Some(CommonAspectRatio::WideThreeByTwo) => Ok(Some(T2iAr::ThreeByTwo)), + Some(CommonAspectRatio::WideSixteenByNine) | Some(CommonAspectRatio::Wide) => Ok(Some(T2iAr::SixteenByNine)), + Some(CommonAspectRatio::WideTwentyOneByNine) => Ok(Some(T2iAr::TwentyOneByNine)), + Some(CommonAspectRatio::TallFourByFive) => Ok(Some(T2iAr::FourByFive)), + Some(CommonAspectRatio::TallThreeByFour) => Ok(Some(T2iAr::ThreeByFour)), + Some(CommonAspectRatio::TallTwoByThree) => Ok(Some(T2iAr::TwoByThree)), + Some(CommonAspectRatio::TallNineBySixteen) | Some(CommonAspectRatio::Tall) => Ok(Some(T2iAr::NineBySixteen)), + + Some(unsupported) => match strategy { + RequestMismatchMitigationStrategy::ErrorOut => { + Err(ArtcraftRouterError::Client(ClientError::ModelDoesNotSupportOption { + field: "aspect_ratio", + value: format!("{:?}", unsupported), + })) + } + RequestMismatchMitigationStrategy::PayMoreUpgrade + | RequestMismatchMitigationStrategy::PayLessDowngrade => { + Ok(Some(T2iAr::NineBySixteen)) + } + }, + } +} + +// NB2 image-edit supported aspect ratios: same as t2i plus Auto +fn plan_edit_aspect_ratio( + aspect_ratio: Option, + is_edit_mode: bool, + strategy: RequestMismatchMitigationStrategy, +) -> Result, ArtcraftRouterError> { + use EnqueueNanoBanana2EditImageAspectRatio as EditAr; + match aspect_ratio { + None => Ok(None), + + Some(CommonAspectRatio::Auto) + | Some(CommonAspectRatio::Auto2k) + | Some(CommonAspectRatio::Auto4k) => { + if is_edit_mode { + Ok(Some(EditAr::Auto)) + } else { + Ok(Some(EditAr::OneByOne)) + } + } + + Some(CommonAspectRatio::Square) | Some(CommonAspectRatio::SquareHd) => Ok(Some(EditAr::OneByOne)), + Some(CommonAspectRatio::WideFiveByFour) => Ok(Some(EditAr::FiveByFour)), + Some(CommonAspectRatio::WideFourByThree) => Ok(Some(EditAr::FourByThree)), + Some(CommonAspectRatio::WideThreeByTwo) => Ok(Some(EditAr::ThreeByTwo)), + Some(CommonAspectRatio::WideSixteenByNine) | Some(CommonAspectRatio::Wide) => Ok(Some(EditAr::SixteenByNine)), + Some(CommonAspectRatio::WideTwentyOneByNine) => Ok(Some(EditAr::TwentyOneByNine)), + Some(CommonAspectRatio::TallFourByFive) => Ok(Some(EditAr::FourByFive)), + Some(CommonAspectRatio::TallThreeByFour) => Ok(Some(EditAr::ThreeByFour)), + Some(CommonAspectRatio::TallTwoByThree) => Ok(Some(EditAr::TwoByThree)), + Some(CommonAspectRatio::TallNineBySixteen) | Some(CommonAspectRatio::Tall) => Ok(Some(EditAr::NineBySixteen)), + + Some(unsupported) => match strategy { + RequestMismatchMitigationStrategy::ErrorOut => { + Err(ArtcraftRouterError::Client(ClientError::ModelDoesNotSupportOption { + field: "aspect_ratio", + value: format!("{:?}", unsupported), + })) + } + RequestMismatchMitigationStrategy::PayMoreUpgrade + | RequestMismatchMitigationStrategy::PayLessDowngrade => { + Ok(Some(EditAr::NineBySixteen)) + } + }, + } +} + +fn plan_resolution( + resolution: Option, +) -> Result, ArtcraftRouterError> { + match resolution { + None => Ok(None), + Some(CommonResolution::HalfK) => Ok(Some(FalNb2Resolution::HalfK)), + Some(CommonResolution::OneK) => Ok(Some(FalNb2Resolution::OneK)), + Some(CommonResolution::TwoK) => Ok(Some(FalNb2Resolution::TwoK)), + Some(CommonResolution::FourK) => Ok(Some(FalNb2Resolution::FourK)), + Some(CommonResolution::ThreeK) => Ok(Some(FalNb2Resolution::TwoK)), + Some(CommonResolution::FourEightyP) => Ok(Some(FalNb2Resolution::HalfK)), + Some(CommonResolution::SevenTwentyP) => Ok(Some(FalNb2Resolution::OneK)), + Some(CommonResolution::TenEightyP) => Ok(Some(FalNb2Resolution::OneK)), + } +} + +fn plan_num_images( + image_batch_count: Option, + strategy: RequestMismatchMitigationStrategy, +) -> Result { + let count = image_batch_count.unwrap_or(1); + match count { + 0 => Err(ArtcraftRouterError::Client(ClientError::UserRequestedZeroGenerations)), + 1 => Ok(FalNb2NumImages::One), + 2 => Ok(FalNb2NumImages::Two), + 3 => Ok(FalNb2NumImages::Three), + 4 => Ok(FalNb2NumImages::Four), + _ => match strategy { + RequestMismatchMitigationStrategy::ErrorOut => { + Err(ArtcraftRouterError::Client(ClientError::ModelDoesNotSupportOption { + field: "image_batch_count", + value: format!("{}", count), + })) + } + RequestMismatchMitigationStrategy::PayMoreUpgrade + | RequestMismatchMitigationStrategy::PayLessDowngrade => Ok(FalNb2NumImages::Four), + }, + } +}