From ced17552c1df57753b5000ffb06a8ce0ab1897d5 Mon Sep 17 00:00:00 2001 From: Brandon Thomas Date: Thu, 16 Apr 2026 00:02:35 -0400 Subject: [PATCH] artcraft_router for seedance2fast + kinovi --- ...ideo_cost_seedance2pro_seedance2p0_fast.rs | 129 ++++++++++++++++++ .../generate_video/cost/seedance2pro/mod.rs | 1 + .../execute_seedance2pro_seedance2p0.rs | 6 +- .../execute_seedance2pro_seedance2p0_fast.rs | 57 ++++++++ .../execute/seedance2pro/mod.rs | 1 + .../generate_video/generate_video_request.rs | 2 + .../generate_video/plan/seedance2pro/mod.rs | 1 + ...ate_video_seedance2pro_seedance2p0_fast.rs | 27 ++++ .../generate_video/video_generation_plan.rs | 10 ++ .../test_data/images/safe_blue_square.png | Bin 0 -> 179 bytes .../test_data/images/safe_gradient_512.png | Bin 0 -> 94588 bytes .../video/omni_gen_video_generate_handler.rs | 1 + 12 files changed, 232 insertions(+), 3 deletions(-) create mode 100644 crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/cost/seedance2pro/estimate_video_cost_seedance2pro_seedance2p0_fast.rs create mode 100644 crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/execute/seedance2pro/execute_seedance2pro_seedance2p0_fast.rs create mode 100644 crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/plan/seedance2pro/plan_generate_video_seedance2pro_seedance2p0_fast.rs create mode 100644 crates/api_clients/worldlabs_api_client/test_data/images/safe_blue_square.png create mode 100644 crates/api_clients/worldlabs_api_client/test_data/images/safe_gradient_512.png diff --git a/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/cost/seedance2pro/estimate_video_cost_seedance2pro_seedance2p0_fast.rs b/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/cost/seedance2pro/estimate_video_cost_seedance2pro_seedance2p0_fast.rs new file mode 100644 index 0000000000..dc19144d43 --- /dev/null +++ b/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/cost/seedance2pro/estimate_video_cost_seedance2pro_seedance2p0_fast.rs @@ -0,0 +1,129 @@ +use seedance2pro_client::creds::seedance2pro_session::Seedance2ProSession; +use seedance2pro_client::requests::generate_video::generate_video::{GenerateVideoArgs, ModelType, Resolution}; + +use crate::generate::generate_video::plan::seedance2pro::plan_generate_video_seedance2pro_seedance2p0::PlanSeedance2proSeedance2p0; +use crate::generate::generate_video::video_generation_cost_estimate::VideoGenerationCostEstimate; + +pub(crate) fn estimate_video_cost_seedance2pro_seedance2p0_fast( + plan: &PlanSeedance2proSeedance2p0, +) -> VideoGenerationCostEstimate { + // A dummy session is sufficient — cost estimation does not make any network calls. + let dummy_session = Seedance2ProSession::from_cookies_string(String::new()); + + let args = GenerateVideoArgs { + session: &dummy_session, + model_type: ModelType::Seedance2Fast, // <-- Fast, not Pro + prompt: String::new(), + resolution: Resolution::Square1x1, // Resolution does not affect cost + duration_seconds: plan.duration_seconds, + batch_count: plan.batch_count, + start_frame_url: None, + end_frame_url: None, + reference_image_urls: None, + reference_video_urls: None, + reference_audio_urls: None, + character_ids: None, + use_face_blur_hack: None, + host_override: None, + }; + + let cost_in_credits = args.estimate_credits(); + let cost_in_usd_cents = args.estimate_cost_in_usd_cents(); + + VideoGenerationCostEstimate { + cost_in_credits: Some(cost_in_credits as u64), + cost_in_usd_cents: Some(cost_in_usd_cents), + is_free: false, + is_unlimited: false, + is_rate_limited: false, + has_watermark: false, + } +} + +#[cfg(test)] +mod tests { + use crate::api::common_video_model::CommonVideoModel; + use crate::api::provider::Provider; + use crate::client::request_mismatch_mitigation_strategy::RequestMismatchMitigationStrategy; + use crate::generate::generate_video::generate_video_request::GenerateVideoRequest; + + fn estimate_usd_cents(duration_seconds: u16, video_batch_count: u16) -> u64 { + let request = GenerateVideoRequest { + model: CommonVideoModel::Seedance2p0Fast, + provider: Provider::Seedance2Pro, + prompt: None, + negative_prompt: None, + start_frame: None, + end_frame: None, + reference_images: None, + reference_videos: None, + reference_audio: None, + reference_character_tokens: None, + resolution: None, + aspect_ratio: None, + duration_seconds: Some(duration_seconds), + video_batch_count: Some(video_batch_count), + generate_audio: None, + request_mismatch_mitigation_strategy: RequestMismatchMitigationStrategy::PayMoreUpgrade, + idempotency_token: None, + }; + request.build() + .expect("build should succeed") + .estimate_costs() + .cost_in_usd_cents + .expect("cost_in_usd_cents should be present") + } + + #[test] + fn cost_is_present() { + let cents = estimate_usd_cents(5, 1); + assert!(cents > 0); + } + + #[test] + fn cost_batch_1() { + // Seedance2Fast: 28 credits/sec, 220 credits/$1 (with rounding) + assert_eq!(estimate_usd_cents(4, 1), 51); + assert_eq!(estimate_usd_cents(5, 1), 64); + assert_eq!(estimate_usd_cents(10, 1), 127); + assert_eq!(estimate_usd_cents(15, 1), 191); + } + + #[test] + fn cost_batch_2() { + assert_eq!(estimate_usd_cents(5, 2), 127); + } + + #[test] + fn cost_batch_4() { + assert_eq!(estimate_usd_cents(5, 4), 255); + } + + #[test] + fn fast_is_cheaper_than_pro() { + let fast = estimate_usd_cents(5, 1); + + let pro_request = GenerateVideoRequest { + model: CommonVideoModel::Seedance2p0, + provider: Provider::Seedance2Pro, + prompt: None, + negative_prompt: None, + start_frame: None, + end_frame: None, + reference_images: None, + reference_videos: None, + reference_audio: None, + reference_character_tokens: None, + resolution: None, + aspect_ratio: None, + duration_seconds: Some(5), + video_batch_count: Some(1), + generate_audio: None, + request_mismatch_mitigation_strategy: RequestMismatchMitigationStrategy::PayMoreUpgrade, + idempotency_token: None, + }; + let pro = pro_request.build().unwrap().estimate_costs().cost_in_usd_cents.unwrap(); + + assert!(fast < pro, "Fast ({}) should be cheaper than Pro ({})", fast, pro); + } +} diff --git a/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/cost/seedance2pro/mod.rs b/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/cost/seedance2pro/mod.rs index d29759b40c..7bdb334f14 100644 --- a/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/cost/seedance2pro/mod.rs +++ b/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/cost/seedance2pro/mod.rs @@ -1 +1,2 @@ pub mod estimate_video_cost_seedance2pro_seedance2p0; +pub mod estimate_video_cost_seedance2pro_seedance2p0_fast; diff --git a/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/execute/seedance2pro/execute_seedance2pro_seedance2p0.rs b/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/execute/seedance2pro/execute_seedance2pro_seedance2p0.rs index fe72bfe644..43702873ee 100644 --- a/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/execute/seedance2pro/execute_seedance2pro_seedance2p0.rs +++ b/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/execute/seedance2pro/execute_seedance2pro_seedance2p0.rs @@ -60,7 +60,7 @@ pub async fn execute_seedance2pro_seedance2p0( } /// Downloads a file from a source URL and re-uploads it to seedance2pro CDN. -async fn upload_to_seedance2pro( +pub(crate) async fn upload_to_seedance2pro( session: &seedance2pro_client::creds::seedance2pro_session::Seedance2ProSession, source_url: &str, ) -> Result { @@ -89,7 +89,7 @@ async fn upload_to_seedance2pro( Ok(upload_response.public_url) } -async fn upload_optional_url( +pub(crate) async fn upload_optional_url( session: &seedance2pro_client::creds::seedance2pro_session::Seedance2ProSession, url: Option<&str>, ) -> Result, ArtcraftRouterError> { @@ -99,7 +99,7 @@ async fn upload_optional_url( } } -async fn upload_optional_url_list( +pub(crate) async fn upload_optional_url_list( session: &seedance2pro_client::creds::seedance2pro_session::Seedance2ProSession, urls: Option<&[String]>, ) -> Result>, ArtcraftRouterError> { diff --git a/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/execute/seedance2pro/execute_seedance2pro_seedance2p0_fast.rs b/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/execute/seedance2pro/execute_seedance2pro_seedance2p0_fast.rs new file mode 100644 index 0000000000..8ff46cca6a --- /dev/null +++ b/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/execute/seedance2pro/execute_seedance2pro_seedance2p0_fast.rs @@ -0,0 +1,57 @@ +use crate::client::router_seedance2pro_client::RouterSeedance2ProClient; +use crate::errors::artcraft_router_error::ArtcraftRouterError; +use crate::errors::provider_error::ProviderError; +use crate::generate::generate_video::generate_video_response::{ + GenerateVideoResponse, Seedance2proVideoResponsePayload, +}; +use crate::generate::generate_video::plan::seedance2pro::plan_generate_video_seedance2pro_seedance2p0::PlanSeedance2proSeedance2p0; +use crate::generate::generate_video::execute::seedance2pro::execute_seedance2pro_seedance2p0::{ + upload_optional_url, upload_optional_url_list, +}; +use seedance2pro_client::requests::generate_video::generate_video::{ + generate_video, GenerateVideoArgs, ModelType, +}; + +/// Execute Seedance 2.0 Fast via the Seedance2Pro/Kinovi provider. +/// +/// Identical to the Seedance 2.0 Pro executor except it uses +/// `ModelType::Seedance2Fast` instead of `ModelType::Seedance2Pro`. +pub async fn execute_seedance2pro_seedance2p0_fast( + plan: &PlanSeedance2proSeedance2p0, + seedance2pro_client: &RouterSeedance2ProClient, +) -> Result { + let session = &seedance2pro_client.session; + + // Upload media files to seedance2pro CDN + let start_frame_url = upload_optional_url(session, plan.start_frame_url.as_deref()).await?; + let end_frame_url = upload_optional_url(session, plan.end_frame_url.as_deref()).await?; + let reference_image_urls = upload_optional_url_list(session, plan.reference_image_urls.as_deref()).await?; + let reference_video_urls = upload_optional_url_list(session, plan.reference_video_urls.as_deref()).await?; + let reference_audio_urls = upload_optional_url_list(session, plan.reference_audio_urls.as_deref()).await?; + + let args = GenerateVideoArgs { + session, + model_type: ModelType::Seedance2Fast, // <-- Fast, not Pro + prompt: plan.prompt.clone().unwrap_or_default(), + resolution: plan.resolution, + duration_seconds: plan.duration_seconds, + batch_count: plan.batch_count, + start_frame_url, + end_frame_url, + reference_image_urls, + reference_video_urls, + reference_audio_urls, + character_ids: None, + use_face_blur_hack: None, + host_override: None, + }; + + let response = generate_video(args) + .await + .map_err(|err| ArtcraftRouterError::Provider(ProviderError::Seedance2Pro(err)))?; + + Ok(GenerateVideoResponse::Seedance2Pro(Seedance2proVideoResponsePayload { + order_id: response.order_id, + task_id: response.task_id, + })) +} diff --git a/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/execute/seedance2pro/mod.rs b/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/execute/seedance2pro/mod.rs index 98193dacca..35e435c85d 100644 --- a/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/execute/seedance2pro/mod.rs +++ b/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/execute/seedance2pro/mod.rs @@ -1 +1,2 @@ pub mod execute_seedance2pro_seedance2p0; +pub mod execute_seedance2pro_seedance2p0_fast; diff --git a/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/generate_video_request.rs b/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/generate_video_request.rs index d4f466aa1f..1f927552d3 100644 --- a/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/generate_video_request.rs +++ b/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/generate_video_request.rs @@ -44,6 +44,7 @@ use crate::generate::generate_video::plan::fal::plan_generate_video_fal_veo_3p1: use crate::generate::generate_video::plan::fal::plan_generate_video_fal_veo_3p1_fast::plan_generate_video_fal_veo_3p1_fast; use crate::generate::generate_video::plan::muapi::plan_generate_video_muapi_seedance2p0::plan_generate_video_muapi_seedance2p0; use crate::generate::generate_video::plan::seedance2pro::plan_generate_video_seedance2pro_seedance2p0::plan_generate_video_seedance2pro_seedance2p0; +use crate::generate::generate_video::plan::seedance2pro::plan_generate_video_seedance2pro_seedance2p0_fast::plan_generate_video_seedance2pro_seedance2p0_fast; use crate::generate::generate_video::video_generation_plan::VideoGenerationPlan; /// Plan to either (1) generate a video or (2) determine how much it costs to generate that video. @@ -174,6 +175,7 @@ impl<'a> GenerateVideoRequest<'a> { fn build_seedance2pro(&self) -> Result, ArtcraftRouterError> { match self.model { CommonVideoModel::Seedance2p0 => plan_generate_video_seedance2pro_seedance2p0(self), + CommonVideoModel::Seedance2p0Fast => plan_generate_video_seedance2pro_seedance2p0_fast(self), _ => Err(ArtcraftRouterError::UnsupportedModel(format!("{:?}", self.model))), } } diff --git a/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/plan/seedance2pro/mod.rs b/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/plan/seedance2pro/mod.rs index 19d854ae8a..3413f094c5 100644 --- a/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/plan/seedance2pro/mod.rs +++ b/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/plan/seedance2pro/mod.rs @@ -1 +1,2 @@ pub mod plan_generate_video_seedance2pro_seedance2p0; +pub mod plan_generate_video_seedance2pro_seedance2p0_fast; diff --git a/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/plan/seedance2pro/plan_generate_video_seedance2pro_seedance2p0_fast.rs b/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/plan/seedance2pro/plan_generate_video_seedance2pro_seedance2p0_fast.rs new file mode 100644 index 0000000000..8b4ba233cb --- /dev/null +++ b/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/plan/seedance2pro/plan_generate_video_seedance2pro_seedance2p0_fast.rs @@ -0,0 +1,27 @@ +use crate::errors::artcraft_router_error::ArtcraftRouterError; +use crate::generate::generate_video::generate_video_request::GenerateVideoRequest; +use crate::generate::generate_video::plan::seedance2pro::plan_generate_video_seedance2pro_seedance2p0::{ + PlanSeedance2proSeedance2p0, plan_generate_video_seedance2pro_seedance2p0, +}; +use crate::generate::generate_video::video_generation_plan::VideoGenerationPlan; + +/// Plan for Seedance 2.0 Fast via Seedance2Pro provider. +/// +/// Uses the same plan structure as Seedance 2.0 Pro (same resolution, duration, +/// batch count, and media reference handling). The only difference is the model +/// type used at execution time. +pub fn plan_generate_video_seedance2pro_seedance2p0_fast<'a>( + request: &'a GenerateVideoRequest<'a>, +) -> Result, ArtcraftRouterError> { + // Reuse the Pro plan builder — it produces a PlanSeedance2proSeedance2p0. + // We wrap it in the Fast variant so execution dispatches to the Fast executor. + let pro_plan = plan_generate_video_seedance2pro_seedance2p0(request)?; + + // Extract the inner plan struct and re-wrap it. + match pro_plan { + VideoGenerationPlan::Seedance2proSeedance2p0(plan) => { + Ok(VideoGenerationPlan::Seedance2proSeedance2p0Fast(plan)) + } + _ => unreachable!("plan_generate_video_seedance2pro_seedance2p0 always returns Seedance2proSeedance2p0"), + } +} diff --git a/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/video_generation_plan.rs b/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/video_generation_plan.rs index 6df6a39bd7..de132b2c28 100644 --- a/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/video_generation_plan.rs +++ b/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/video_generation_plan.rs @@ -35,6 +35,7 @@ use crate::generate::generate_video::cost::fal::estimate_video_cost_fal_veo_3p1: use crate::generate::generate_video::cost::fal::estimate_video_cost_fal_veo_3p1_fast::estimate_video_cost_fal_veo_3p1_fast; use crate::generate::generate_video::cost::muapi::estimate_video_cost_muapi_seedance2p0::estimate_video_cost_muapi_seedance2p0; use crate::generate::generate_video::cost::seedance2pro::estimate_video_cost_seedance2pro_seedance2p0::estimate_video_cost_seedance2pro_seedance2p0; +use crate::generate::generate_video::cost::seedance2pro::estimate_video_cost_seedance2pro_seedance2p0_fast::estimate_video_cost_seedance2pro_seedance2p0_fast; use crate::generate::generate_video::execute::artcraft::generate_video_artcraft_kling_1_6_pro::execute_artcraft_kling_1_6_pro; use crate::generate::generate_video::execute::artcraft::generate_video_artcraft_kling_2_1_master::execute_artcraft_kling_2_1_master; use crate::generate::generate_video::execute::artcraft::generate_video_artcraft_kling_2_1_pro::execute_artcraft_kling_2_1_pro; @@ -70,6 +71,7 @@ use crate::generate::generate_video::execute::fal::generate_video_fal_veo_3p1::e use crate::generate::generate_video::execute::fal::generate_video_fal_veo_3p1_fast::execute_fal_veo_3p1_fast; use crate::generate::generate_video::execute::muapi::execute_muapi_seedance2p0::execute_muapi_seedance2p0; use crate::generate::generate_video::execute::seedance2pro::execute_seedance2pro_seedance2p0::execute_seedance2pro_seedance2p0; +use crate::generate::generate_video::execute::seedance2pro::execute_seedance2pro_seedance2p0_fast::execute_seedance2pro_seedance2p0_fast; use crate::generate::generate_video::generate_video_response::GenerateVideoResponse; use crate::generate::generate_video::plan::artcraft::plan_generate_video_artcraft_kling_1_6_pro::PlanArtcraftKling16Pro; use crate::generate::generate_video::plan::artcraft::plan_generate_video_artcraft_kling_2_1_master::PlanArtcraftKling21Master; @@ -129,6 +131,7 @@ pub enum VideoGenerationPlan<'a> { ArtcraftVeo3p1Fast(PlanArtcraftVeo3p1Fast<'a>), MuapiSeedance2p0(PlanMuapiSeedance2p0), Seedance2proSeedance2p0(PlanSeedance2proSeedance2p0), + Seedance2proSeedance2p0Fast(PlanSeedance2proSeedance2p0), FalVeo2(PlanFalVeo2), FalVeo3(PlanFalVeo3), FalVeo3Fast(PlanFalVeo3Fast), @@ -229,6 +232,10 @@ impl<'a> VideoGenerationPlan<'a> { let seedance2pro_client = client.get_seedance2pro_client_ref()?; execute_seedance2pro_seedance2p0(plan, seedance2pro_client).await } + VideoGenerationPlan::Seedance2proSeedance2p0Fast(plan) => { + let seedance2pro_client = client.get_seedance2pro_client_ref()?; + execute_seedance2pro_seedance2p0_fast(plan, seedance2pro_client).await + } VideoGenerationPlan::FalVeo2(plan) => { let fal_client = client.get_fal_client_ref()?; execute_fal_veo_2(plan, fal_client).await @@ -335,6 +342,9 @@ impl<'a> VideoGenerationPlan<'a> { VideoGenerationPlan::Seedance2proSeedance2p0(plan) => { estimate_video_cost_seedance2pro_seedance2p0(plan) } + VideoGenerationPlan::Seedance2proSeedance2p0Fast(plan) => { + estimate_video_cost_seedance2pro_seedance2p0_fast(plan) + } VideoGenerationPlan::FalVeo2(plan) => estimate_video_cost_fal_veo_2(plan), VideoGenerationPlan::FalVeo3(plan) => estimate_video_cost_fal_veo_3(plan), VideoGenerationPlan::FalVeo3Fast(plan) => estimate_video_cost_fal_veo_3_fast(plan), diff --git a/crates/api_clients/worldlabs_api_client/test_data/images/safe_blue_square.png b/crates/api_clients/worldlabs_api_client/test_data/images/safe_blue_square.png new file mode 100644 index 0000000000000000000000000000000000000000..e06ac19715ab458efb99654996196f5a64a6db58 GIT binary patch literal 179 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1SD0tpLGJMDo+>3kcv5P&j)fcD2N<5Ab!m! z&1jn9AB&cQOn1`Wel9)C%A4WH9Ce{V%wz!-nKosNn_nv~JrQ@e66gvBPgg&ebxsLQ E0P!0)9{>OV literal 0 HcmV?d00001 diff --git a/crates/api_clients/worldlabs_api_client/test_data/images/safe_gradient_512.png b/crates/api_clients/worldlabs_api_client/test_data/images/safe_gradient_512.png new file mode 100644 index 0000000000000000000000000000000000000000..66917cdeb3b21a8e45dafb6e84996b8a0364a0c1 GIT binary patch literal 94588 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 literal 0 HcmV?d00001 diff --git a/crates/service/web/storyteller_web/src/http_server/endpoints/omni_gen/generate/video/omni_gen_video_generate_handler.rs b/crates/service/web/storyteller_web/src/http_server/endpoints/omni_gen/generate/video/omni_gen_video_generate_handler.rs index f859303072..e61f532f42 100644 --- a/crates/service/web/storyteller_web/src/http_server/endpoints/omni_gen/generate/video/omni_gen_video_generate_handler.rs +++ b/crates/service/web/storyteller_web/src/http_server/endpoints/omni_gen/generate/video/omni_gen_video_generate_handler.rs @@ -159,6 +159,7 @@ pub async fn omni_gen_video_generate_handler( let execution_provider = match request.model { Some(CommonVideoModel::Seedance2p0) => Provider::Seedance2Pro, + Some(CommonVideoModel::Seedance2p0Fast) => Provider::Seedance2Pro, _ => Provider::Fal, };