diff --git a/crates/api_clients/artcraft/artcraft_api_defs/src/generate/video/generate_kling_1_6_pro_image_to_video.rs b/crates/api_clients/artcraft/artcraft_api_defs/src/generate/video/generate_kling_1_6_pro_image_to_video.rs index a678c5c325..767f225eb5 100644 --- a/crates/api_clients/artcraft/artcraft_api_defs/src/generate/video/generate_kling_1_6_pro_image_to_video.rs +++ b/crates/api_clients/artcraft/artcraft_api_defs/src/generate/video/generate_kling_1_6_pro_image_to_video.rs @@ -28,7 +28,7 @@ pub struct GenerateKling16ProImageToVideoRequest { pub duration: Option, } -#[derive(Serialize, Deserialize, ToSchema)] +#[derive(Copy, Clone, Debug, Serialize, Deserialize, ToSchema)] #[serde(rename_all = "snake_case")] pub enum GenerateKling16ProAspectRatio { /// 16:9 aspect ratio @@ -39,7 +39,7 @@ pub enum GenerateKling16ProAspectRatio { Square, } -#[derive(Serialize, Deserialize, ToSchema)] +#[derive(Copy, Clone, Debug, Serialize, Deserialize, ToSchema)] #[serde(rename_all = "snake_case")] pub enum GenerateKling16ProDuration { FiveSeconds, diff --git a/crates/api_clients/artcraft/artcraft_api_defs/src/generate/video/generate_kling_2_1_master_image_to_video.rs b/crates/api_clients/artcraft/artcraft_api_defs/src/generate/video/generate_kling_2_1_master_image_to_video.rs index 7cdeed6718..db39fec73a 100644 --- a/crates/api_clients/artcraft/artcraft_api_defs/src/generate/video/generate_kling_2_1_master_image_to_video.rs +++ b/crates/api_clients/artcraft/artcraft_api_defs/src/generate/video/generate_kling_2_1_master_image_to_video.rs @@ -24,7 +24,7 @@ pub struct GenerateKling21MasterImageToVideoRequest { pub duration: Option, } -#[derive(Serialize, Deserialize, ToSchema)] +#[derive(Copy, Clone, Debug, Serialize, Deserialize, ToSchema)] #[serde(rename_all = "snake_case")] pub enum GenerateKling21MasterAspectRatio { /// 16:9 aspect ratio @@ -35,7 +35,7 @@ pub enum GenerateKling21MasterAspectRatio { Square, } -#[derive(Serialize, Deserialize, ToSchema)] +#[derive(Copy, Clone, Debug, Serialize, Deserialize, ToSchema)] #[serde(rename_all = "snake_case")] pub enum GenerateKling21MasterDuration { FiveSeconds, diff --git a/crates/api_clients/artcraft/artcraft_api_defs/src/generate/video/generate_kling_2_1_pro_image_to_video.rs b/crates/api_clients/artcraft/artcraft_api_defs/src/generate/video/generate_kling_2_1_pro_image_to_video.rs index 677bc813cc..b5ef89677f 100644 --- a/crates/api_clients/artcraft/artcraft_api_defs/src/generate/video/generate_kling_2_1_pro_image_to_video.rs +++ b/crates/api_clients/artcraft/artcraft_api_defs/src/generate/video/generate_kling_2_1_pro_image_to_video.rs @@ -28,7 +28,7 @@ pub struct GenerateKling21ProImageToVideoRequest { pub duration: Option, } -#[derive(Serialize, Deserialize, ToSchema)] +#[derive(Copy, Clone, Debug, Serialize, Deserialize, ToSchema)] #[serde(rename_all = "snake_case")] pub enum GenerateKling21ProAspectRatio { /// 16:9 aspect ratio @@ -39,7 +39,7 @@ pub enum GenerateKling21ProAspectRatio { Square, } -#[derive(Serialize, Deserialize, ToSchema)] +#[derive(Copy, Clone, Debug, Serialize, Deserialize, ToSchema)] #[serde(rename_all = "snake_case")] pub enum GenerateKling21ProDuration { FiveSeconds, diff --git a/crates/api_clients/artcraft/artcraft_api_defs/src/generate/video/multi_function/kling_2_5_turbo_multi_function_video_gen.rs b/crates/api_clients/artcraft/artcraft_api_defs/src/generate/video/multi_function/kling_2_5_turbo_multi_function_video_gen.rs index 0caf69524d..ca33079410 100644 --- a/crates/api_clients/artcraft/artcraft_api_defs/src/generate/video/multi_function/kling_2_5_turbo_multi_function_video_gen.rs +++ b/crates/api_clients/artcraft/artcraft_api_defs/src/generate/video/multi_function/kling_2_5_turbo_multi_function_video_gen.rs @@ -41,14 +41,14 @@ pub struct Kling2p5TurboProMultiFunctionVideoGenRequest { pub aspect_ratio: Option, } -#[derive(Serialize, Deserialize, ToSchema)] +#[derive(Copy, Clone, Debug, Serialize, Deserialize, ToSchema)] #[serde(rename_all = "snake_case")] pub enum Kling2p5TurboProMultiFunctionVideoGenDuration { FiveSeconds, TenSeconds, } -#[derive(Serialize, Deserialize, ToSchema)] +#[derive(Copy, Clone, Debug, Serialize, Deserialize, ToSchema)] #[serde(rename_all = "snake_case")] pub enum Kling2p5TurboProMultiFunctionVideoGenAspectRatio { Square, diff --git a/crates/api_clients/artcraft/artcraft_api_defs/src/generate/video/multi_function/kling_2_6_multi_function_video_gen.rs b/crates/api_clients/artcraft/artcraft_api_defs/src/generate/video/multi_function/kling_2_6_multi_function_video_gen.rs index 164fa6de16..113edaca28 100644 --- a/crates/api_clients/artcraft/artcraft_api_defs/src/generate/video/multi_function/kling_2_6_multi_function_video_gen.rs +++ b/crates/api_clients/artcraft/artcraft_api_defs/src/generate/video/multi_function/kling_2_6_multi_function_video_gen.rs @@ -45,14 +45,14 @@ pub struct Kling2p6ProMultiFunctionVideoGenRequest { pub aspect_ratio: Option, } -#[derive(Serialize, Deserialize, ToSchema)] +#[derive(Copy, Clone, Debug, Serialize, Deserialize, ToSchema)] #[serde(rename_all = "snake_case")] pub enum Kling2p6ProMultiFunctionVideoGenDuration { FiveSeconds, TenSeconds, } -#[derive(Serialize, Deserialize, ToSchema)] +#[derive(Copy, Clone, Debug, Serialize, Deserialize, ToSchema)] #[serde(rename_all = "snake_case")] pub enum Kling2p6ProMultiFunctionVideoGenAspectRatio { Square, diff --git a/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/cost/artcraft/estimate_video_cost_artcraft_kling_1_6_pro.rs b/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/cost/artcraft/estimate_video_cost_artcraft_kling_1_6_pro.rs new file mode 100644 index 0000000000..288e42ab41 --- /dev/null +++ b/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/cost/artcraft/estimate_video_cost_artcraft_kling_1_6_pro.rs @@ -0,0 +1,18 @@ +use crate::generate::generate_video::plan::artcraft::plan_generate_video_artcraft_kling_1_6_pro::PlanArtcraftKling16Pro; +use crate::generate::generate_video::video_generation_cost_estimate::VideoGenerationCostEstimate; + +pub(crate) fn estimate_video_cost_artcraft_kling_1_6_pro( + plan: &PlanArtcraftKling16Pro<'_>, +) -> VideoGenerationCostEstimate { + // Mirrors fal_client kling_v1p6_pro: 5s = $0.475 (rounded up to 48¢), 10s = $0.95 (95¢). + let cost_in_usd_cents: u64 = if plan.is_ten_seconds() { 95 } else { 48 }; + + VideoGenerationCostEstimate { + 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_video/cost/artcraft/estimate_video_cost_artcraft_kling_2_1_master.rs b/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/cost/artcraft/estimate_video_cost_artcraft_kling_2_1_master.rs new file mode 100644 index 0000000000..d2394a7195 --- /dev/null +++ b/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/cost/artcraft/estimate_video_cost_artcraft_kling_2_1_master.rs @@ -0,0 +1,18 @@ +use crate::generate::generate_video::plan::artcraft::plan_generate_video_artcraft_kling_2_1_master::PlanArtcraftKling21Master; +use crate::generate::generate_video::video_generation_cost_estimate::VideoGenerationCostEstimate; + +pub(crate) fn estimate_video_cost_artcraft_kling_2_1_master( + plan: &PlanArtcraftKling21Master<'_>, +) -> VideoGenerationCostEstimate { + // Mirrors fal_client kling_v2p1_master: 5s = $1.40, 10s = $2.80. + let cost_in_usd_cents: u64 = if plan.is_ten_seconds() { 280 } else { 140 }; + + VideoGenerationCostEstimate { + 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_video/cost/artcraft/estimate_video_cost_artcraft_kling_2_1_pro.rs b/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/cost/artcraft/estimate_video_cost_artcraft_kling_2_1_pro.rs new file mode 100644 index 0000000000..207a30c985 --- /dev/null +++ b/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/cost/artcraft/estimate_video_cost_artcraft_kling_2_1_pro.rs @@ -0,0 +1,18 @@ +use crate::generate::generate_video::plan::artcraft::plan_generate_video_artcraft_kling_2_1_pro::PlanArtcraftKling21Pro; +use crate::generate::generate_video::video_generation_cost_estimate::VideoGenerationCostEstimate; + +pub(crate) fn estimate_video_cost_artcraft_kling_2_1_pro( + plan: &PlanArtcraftKling21Pro<'_>, +) -> VideoGenerationCostEstimate { + // Mirrors fal_client kling_v2p1_pro: 5s = $0.45, 10s = $0.90. + let cost_in_usd_cents: u64 = if plan.is_ten_seconds() { 90 } else { 45 }; + + VideoGenerationCostEstimate { + 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_video/cost/artcraft/estimate_video_cost_artcraft_kling_2_5_turbo_pro.rs b/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/cost/artcraft/estimate_video_cost_artcraft_kling_2_5_turbo_pro.rs new file mode 100644 index 0000000000..459169ce58 --- /dev/null +++ b/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/cost/artcraft/estimate_video_cost_artcraft_kling_2_5_turbo_pro.rs @@ -0,0 +1,18 @@ +use crate::generate::generate_video::plan::artcraft::plan_generate_video_artcraft_kling_2_5_turbo_pro::PlanArtcraftKling2p5TurboPro; +use crate::generate::generate_video::video_generation_cost_estimate::VideoGenerationCostEstimate; + +pub(crate) fn estimate_video_cost_artcraft_kling_2_5_turbo_pro( + plan: &PlanArtcraftKling2p5TurboPro<'_>, +) -> VideoGenerationCostEstimate { + // Mirrors fal_client kling_v2p5_turbo_pro: 5s = $0.35, 10s = $0.70. + let cost_in_usd_cents: u64 = if plan.is_ten_seconds() { 70 } else { 35 }; + + VideoGenerationCostEstimate { + 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_video/cost/artcraft/estimate_video_cost_artcraft_kling_2_6_pro.rs b/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/cost/artcraft/estimate_video_cost_artcraft_kling_2_6_pro.rs new file mode 100644 index 0000000000..35575b11aa --- /dev/null +++ b/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/cost/artcraft/estimate_video_cost_artcraft_kling_2_6_pro.rs @@ -0,0 +1,26 @@ +use crate::generate::generate_video::plan::artcraft::plan_generate_video_artcraft_kling_2_6_pro::PlanArtcraftKling2p6Pro; +use crate::generate::generate_video::video_generation_cost_estimate::VideoGenerationCostEstimate; + +pub(crate) fn estimate_video_cost_artcraft_kling_2_6_pro( + plan: &PlanArtcraftKling2p6Pro<'_>, +) -> VideoGenerationCostEstimate { + // Mirrors fal_client kling_v2p6_pro: + // audio off: $0.07/sec → 5s=35¢, 10s=70¢ + // audio on: $0.14/sec → 5s=70¢, 10s=140¢ + // Default duration is 5s; default generate_audio is true. + let cost_in_usd_cents: u64 = match (plan.generate_audio_for_cost(), plan.is_ten_seconds()) { + (false, false) => 35, + (false, true) => 70, + (true, false) => 70, + (true, true) => 140, + }; + + VideoGenerationCostEstimate { + 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_video/cost/artcraft/mod.rs b/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/cost/artcraft/mod.rs index d86dfe625d..6a5af69461 100644 --- a/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/cost/artcraft/mod.rs +++ b/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/cost/artcraft/mod.rs @@ -1,3 +1,8 @@ +pub mod estimate_video_cost_artcraft_kling_1_6_pro; +pub mod estimate_video_cost_artcraft_kling_2_1_master; +pub mod estimate_video_cost_artcraft_kling_2_1_pro; +pub mod estimate_video_cost_artcraft_kling_2_5_turbo_pro; +pub mod estimate_video_cost_artcraft_kling_2_6_pro; pub mod estimate_video_cost_artcraft_kling3p0_pro; pub mod estimate_video_cost_artcraft_kling3p0_standard; pub mod estimate_video_cost_artcraft_seedance1p5_pro; diff --git a/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/execute/artcraft/generate_video_artcraft_kling_1_6_pro.rs b/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/execute/artcraft/generate_video_artcraft_kling_1_6_pro.rs new file mode 100644 index 0000000000..6b7db0815a --- /dev/null +++ b/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/execute/artcraft/generate_video_artcraft_kling_1_6_pro.rs @@ -0,0 +1,37 @@ +use crate::client::router_artcraft_client::RouterArtcraftClient; +use crate::errors::artcraft_router_error::ArtcraftRouterError; +use crate::errors::provider_error::ProviderError; +use crate::generate::generate_video::generate_video_response::{ + ArtcraftVideoResponsePayload, GenerateVideoResponse, +}; +use crate::generate::generate_video::plan::artcraft::plan_generate_video_artcraft_kling_1_6_pro::PlanArtcraftKling16Pro; +use artcraft_api_defs::generate::video::generate_kling_1_6_pro_image_to_video::GenerateKling16ProImageToVideoRequest; +use artcraft_client::endpoints::generate::video::generate_kling_16_pro_image_to_video::generate_kling_16_pro_image_to_video; + +pub async fn execute_artcraft_kling_1_6_pro( + plan: &PlanArtcraftKling16Pro<'_>, + artcraft_client: &RouterArtcraftClient, +) -> Result { + let request = GenerateKling16ProImageToVideoRequest { + uuid_idempotency_token: plan.idempotency_token.clone(), + media_file_token: Some(plan.start_frame.to_owned()), + end_frame_image_media_token: plan.end_frame.map(|t| t.to_owned()), + prompt: plan.prompt.map(|p| p.to_string()), + aspect_ratio: plan.aspect_ratio, + duration: plan.duration, + }; + + let response = generate_kling_16_pro_image_to_video( + &artcraft_client.api_host, + Some(&artcraft_client.credentials), + request, + ) + .await + .map_err(|err| ArtcraftRouterError::Provider(ProviderError::Storyteller(err)))?; + + let all_tokens = vec![response.inference_job_token.clone()]; + Ok(GenerateVideoResponse::Artcraft(ArtcraftVideoResponsePayload { + inference_job_token: response.inference_job_token, + all_inference_job_tokens: all_tokens, + })) +} diff --git a/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/execute/artcraft/generate_video_artcraft_kling_2_1_master.rs b/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/execute/artcraft/generate_video_artcraft_kling_2_1_master.rs new file mode 100644 index 0000000000..f4ab4b03e8 --- /dev/null +++ b/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/execute/artcraft/generate_video_artcraft_kling_2_1_master.rs @@ -0,0 +1,36 @@ +use crate::client::router_artcraft_client::RouterArtcraftClient; +use crate::errors::artcraft_router_error::ArtcraftRouterError; +use crate::errors::provider_error::ProviderError; +use crate::generate::generate_video::generate_video_response::{ + ArtcraftVideoResponsePayload, GenerateVideoResponse, +}; +use crate::generate::generate_video::plan::artcraft::plan_generate_video_artcraft_kling_2_1_master::PlanArtcraftKling21Master; +use artcraft_api_defs::generate::video::generate_kling_2_1_master_image_to_video::GenerateKling21MasterImageToVideoRequest; +use artcraft_client::endpoints::generate::video::generate_kling_21_master_image_to_video::generate_kling_21_master_image_to_video; + +pub async fn execute_artcraft_kling_2_1_master( + plan: &PlanArtcraftKling21Master<'_>, + artcraft_client: &RouterArtcraftClient, +) -> Result { + let request = GenerateKling21MasterImageToVideoRequest { + uuid_idempotency_token: plan.idempotency_token.clone(), + media_file_token: Some(plan.start_frame.to_owned()), + prompt: plan.prompt.map(|p| p.to_string()), + aspect_ratio: plan.aspect_ratio, + duration: plan.duration, + }; + + let response = generate_kling_21_master_image_to_video( + &artcraft_client.api_host, + Some(&artcraft_client.credentials), + request, + ) + .await + .map_err(|err| ArtcraftRouterError::Provider(ProviderError::Storyteller(err)))?; + + let all_tokens = vec![response.inference_job_token.clone()]; + Ok(GenerateVideoResponse::Artcraft(ArtcraftVideoResponsePayload { + inference_job_token: response.inference_job_token, + all_inference_job_tokens: all_tokens, + })) +} diff --git a/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/execute/artcraft/generate_video_artcraft_kling_2_1_pro.rs b/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/execute/artcraft/generate_video_artcraft_kling_2_1_pro.rs new file mode 100644 index 0000000000..88ebc1a3bc --- /dev/null +++ b/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/execute/artcraft/generate_video_artcraft_kling_2_1_pro.rs @@ -0,0 +1,37 @@ +use crate::client::router_artcraft_client::RouterArtcraftClient; +use crate::errors::artcraft_router_error::ArtcraftRouterError; +use crate::errors::provider_error::ProviderError; +use crate::generate::generate_video::generate_video_response::{ + ArtcraftVideoResponsePayload, GenerateVideoResponse, +}; +use crate::generate::generate_video::plan::artcraft::plan_generate_video_artcraft_kling_2_1_pro::PlanArtcraftKling21Pro; +use artcraft_api_defs::generate::video::generate_kling_2_1_pro_image_to_video::GenerateKling21ProImageToVideoRequest; +use artcraft_client::endpoints::generate::video::generate_kling_21_pro_image_to_video::generate_kling_21_pro_image_to_video; + +pub async fn execute_artcraft_kling_2_1_pro( + plan: &PlanArtcraftKling21Pro<'_>, + artcraft_client: &RouterArtcraftClient, +) -> Result { + let request = GenerateKling21ProImageToVideoRequest { + uuid_idempotency_token: plan.idempotency_token.clone(), + media_file_token: Some(plan.start_frame.to_owned()), + end_frame_image_media_token: plan.end_frame.map(|t| t.to_owned()), + prompt: plan.prompt.map(|p| p.to_string()), + aspect_ratio: plan.aspect_ratio, + duration: plan.duration, + }; + + let response = generate_kling_21_pro_image_to_video( + &artcraft_client.api_host, + Some(&artcraft_client.credentials), + request, + ) + .await + .map_err(|err| ArtcraftRouterError::Provider(ProviderError::Storyteller(err)))?; + + let all_tokens = vec![response.inference_job_token.clone()]; + Ok(GenerateVideoResponse::Artcraft(ArtcraftVideoResponsePayload { + inference_job_token: response.inference_job_token, + all_inference_job_tokens: all_tokens, + })) +} diff --git a/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/execute/artcraft/generate_video_artcraft_kling_2_5_turbo_pro.rs b/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/execute/artcraft/generate_video_artcraft_kling_2_5_turbo_pro.rs new file mode 100644 index 0000000000..5a08c81651 --- /dev/null +++ b/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/execute/artcraft/generate_video_artcraft_kling_2_5_turbo_pro.rs @@ -0,0 +1,38 @@ +use crate::client::router_artcraft_client::RouterArtcraftClient; +use crate::errors::artcraft_router_error::ArtcraftRouterError; +use crate::errors::provider_error::ProviderError; +use crate::generate::generate_video::generate_video_response::{ + ArtcraftVideoResponsePayload, GenerateVideoResponse, +}; +use crate::generate::generate_video::plan::artcraft::plan_generate_video_artcraft_kling_2_5_turbo_pro::PlanArtcraftKling2p5TurboPro; +use artcraft_api_defs::generate::video::multi_function::kling_2_5_turbo_multi_function_video_gen::Kling2p5TurboProMultiFunctionVideoGenRequest; +use artcraft_client::endpoints::generate::video::multi_function::kling_2p5_turbo_pro_multi_function_video_gen::kling_2p5_turbo_pro_multi_function_video_gen; + +pub async fn execute_artcraft_kling_2_5_turbo_pro( + plan: &PlanArtcraftKling2p5TurboPro<'_>, + artcraft_client: &RouterArtcraftClient, +) -> Result { + let request = Kling2p5TurboProMultiFunctionVideoGenRequest { + uuid_idempotency_token: plan.idempotency_token.clone(), + prompt: plan.prompt.map(|p| p.to_string()), + negative_prompt: plan.negative_prompt.map(|p| p.to_string()), + start_frame_image_media_token: plan.start_frame.map(|t| t.to_owned()), + end_frame_image_media_token: plan.end_frame.map(|t| t.to_owned()), + duration: plan.duration, + aspect_ratio: plan.aspect_ratio, + }; + + let response = kling_2p5_turbo_pro_multi_function_video_gen( + &artcraft_client.api_host, + Some(&artcraft_client.credentials), + request, + ) + .await + .map_err(|err| ArtcraftRouterError::Provider(ProviderError::Storyteller(err)))?; + + let all_tokens = vec![response.inference_job_token.clone()]; + Ok(GenerateVideoResponse::Artcraft(ArtcraftVideoResponsePayload { + inference_job_token: response.inference_job_token, + all_inference_job_tokens: all_tokens, + })) +} diff --git a/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/execute/artcraft/generate_video_artcraft_kling_2_6_pro.rs b/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/execute/artcraft/generate_video_artcraft_kling_2_6_pro.rs new file mode 100644 index 0000000000..ac56a6d910 --- /dev/null +++ b/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/execute/artcraft/generate_video_artcraft_kling_2_6_pro.rs @@ -0,0 +1,38 @@ +use crate::client::router_artcraft_client::RouterArtcraftClient; +use crate::errors::artcraft_router_error::ArtcraftRouterError; +use crate::errors::provider_error::ProviderError; +use crate::generate::generate_video::generate_video_response::{ + ArtcraftVideoResponsePayload, GenerateVideoResponse, +}; +use crate::generate::generate_video::plan::artcraft::plan_generate_video_artcraft_kling_2_6_pro::PlanArtcraftKling2p6Pro; +use artcraft_api_defs::generate::video::multi_function::kling_2_6_multi_function_video_gen::Kling2p6ProMultiFunctionVideoGenRequest; +use artcraft_client::endpoints::generate::video::multi_function::kling_2p6_pro_multi_function_video_gen::kling_2p6_pro_multi_function_video_gen; + +pub async fn execute_artcraft_kling_2_6_pro( + plan: &PlanArtcraftKling2p6Pro<'_>, + artcraft_client: &RouterArtcraftClient, +) -> Result { + let request = Kling2p6ProMultiFunctionVideoGenRequest { + uuid_idempotency_token: plan.idempotency_token.clone(), + prompt: plan.prompt.map(|p| p.to_string()), + negative_prompt: plan.negative_prompt.map(|p| p.to_string()), + start_frame_image_media_token: plan.start_frame.map(|t| t.to_owned()), + duration: plan.duration, + generate_audio: plan.generate_audio, + aspect_ratio: plan.aspect_ratio, + }; + + let response = kling_2p6_pro_multi_function_video_gen( + &artcraft_client.api_host, + Some(&artcraft_client.credentials), + request, + ) + .await + .map_err(|err| ArtcraftRouterError::Provider(ProviderError::Storyteller(err)))?; + + let all_tokens = vec![response.inference_job_token.clone()]; + Ok(GenerateVideoResponse::Artcraft(ArtcraftVideoResponsePayload { + inference_job_token: response.inference_job_token, + all_inference_job_tokens: all_tokens, + })) +} diff --git a/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/execute/artcraft/mod.rs b/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/execute/artcraft/mod.rs index 216e98e89a..81a97885e0 100644 --- a/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/execute/artcraft/mod.rs +++ b/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/execute/artcraft/mod.rs @@ -1,3 +1,8 @@ +pub mod generate_video_artcraft_kling_1_6_pro; +pub mod generate_video_artcraft_kling_2_1_master; +pub mod generate_video_artcraft_kling_2_1_pro; +pub mod generate_video_artcraft_kling_2_5_turbo_pro; +pub mod generate_video_artcraft_kling_2_6_pro; pub mod generate_video_artcraft_kling3p0_pro; pub mod generate_video_artcraft_kling3p0_standard; pub mod generate_video_artcraft_seedance1p5_pro; 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 506326e0dd..1373fced1e 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 @@ -9,6 +9,11 @@ use crate::api::provider::Provider; use crate::api::video_list_ref::VideoListRef; use crate::client::request_mismatch_mitigation_strategy::RequestMismatchMitigationStrategy; use crate::errors::artcraft_router_error::ArtcraftRouterError; +use crate::generate::generate_video::plan::artcraft::plan_generate_video_artcraft_kling_1_6_pro::plan_generate_video_artcraft_kling_1_6_pro; +use crate::generate::generate_video::plan::artcraft::plan_generate_video_artcraft_kling_2_1_master::plan_generate_video_artcraft_kling_2_1_master; +use crate::generate::generate_video::plan::artcraft::plan_generate_video_artcraft_kling_2_1_pro::plan_generate_video_artcraft_kling_2_1_pro; +use crate::generate::generate_video::plan::artcraft::plan_generate_video_artcraft_kling_2_5_turbo_pro::plan_generate_video_artcraft_kling_2_5_turbo_pro; +use crate::generate::generate_video::plan::artcraft::plan_generate_video_artcraft_kling_2_6_pro::plan_generate_video_artcraft_kling_2_6_pro; use crate::generate::generate_video::plan::artcraft::plan_generate_video_artcraft_kling3p0_pro::plan_generate_video_artcraft_kling3p0_pro; use crate::generate::generate_video::plan::artcraft::plan_generate_video_artcraft_kling3p0_standard::plan_generate_video_artcraft_kling3p0_standard; use crate::generate::generate_video::plan::artcraft::plan_generate_video_artcraft_seedance1p5_pro::plan_generate_video_artcraft_seedance1p5_pro; @@ -102,6 +107,11 @@ impl<'a> GenerateVideoRequest<'a> { fn build_artcraft(&self) -> Result, ArtcraftRouterError> { match self.model { + CommonVideoModel::Kling16Pro => plan_generate_video_artcraft_kling_1_6_pro(self), + CommonVideoModel::Kling21Master => plan_generate_video_artcraft_kling_2_1_master(self), + CommonVideoModel::Kling21Pro => plan_generate_video_artcraft_kling_2_1_pro(self), + CommonVideoModel::Kling2p5TurboPro => plan_generate_video_artcraft_kling_2_5_turbo_pro(self), + CommonVideoModel::Kling2p6Pro => plan_generate_video_artcraft_kling_2_6_pro(self), CommonVideoModel::Kling3p0Pro => plan_generate_video_artcraft_kling3p0_pro(self), CommonVideoModel::Kling3p0Standard => plan_generate_video_artcraft_kling3p0_standard(self), CommonVideoModel::Seedance1p5Pro => plan_generate_video_artcraft_seedance1p5_pro(self), diff --git a/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/plan/artcraft/mod.rs b/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/plan/artcraft/mod.rs index 24df015bad..59a6bb17e5 100644 --- a/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/plan/artcraft/mod.rs +++ b/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/plan/artcraft/mod.rs @@ -1,3 +1,8 @@ +pub mod plan_generate_video_artcraft_kling_1_6_pro; +pub mod plan_generate_video_artcraft_kling_2_1_master; +pub mod plan_generate_video_artcraft_kling_2_1_pro; +pub mod plan_generate_video_artcraft_kling_2_5_turbo_pro; +pub mod plan_generate_video_artcraft_kling_2_6_pro; pub mod plan_generate_video_artcraft_kling3p0_pro; pub mod plan_generate_video_artcraft_kling3p0_standard; pub mod plan_generate_video_artcraft_seedance1p5_pro; diff --git a/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/plan/artcraft/plan_generate_video_artcraft_kling_1_6_pro.rs b/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/plan/artcraft/plan_generate_video_artcraft_kling_1_6_pro.rs new file mode 100644 index 0000000000..00ec5a92b3 --- /dev/null +++ b/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/plan/artcraft/plan_generate_video_artcraft_kling_1_6_pro.rs @@ -0,0 +1,136 @@ +use crate::api::common_aspect_ratio::CommonAspectRatio; +use crate::api::image_ref::ImageRef; +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_video::generate_video_request::GenerateVideoRequest; +use crate::generate::generate_video::video_generation_plan::VideoGenerationPlan; +use artcraft_api_defs::generate::video::generate_kling_1_6_pro_image_to_video::{ + GenerateKling16ProAspectRatio, GenerateKling16ProDuration, +}; +use tokens::tokens::media_files::MediaFileToken; + +#[derive(Debug, Clone)] +pub struct PlanArtcraftKling16Pro<'a> { + pub prompt: Option<&'a str>, + pub start_frame: &'a MediaFileToken, + pub end_frame: Option<&'a MediaFileToken>, + pub aspect_ratio: Option, + pub duration: Option, + pub idempotency_token: String, +} + +pub fn plan_generate_video_artcraft_kling_1_6_pro<'a>( + request: &'a GenerateVideoRequest<'a>, +) -> Result, ArtcraftRouterError> { + let strategy = request.request_mismatch_mitigation_strategy; + + let start_frame = match request.start_frame { + Some(ImageRef::MediaFileToken(t)) => t, + Some(ImageRef::Url(_)) => { + return Err(ArtcraftRouterError::Client(ClientError::ArtcraftOnlySupportsMediaTokens)) + } + None => { + return Err(ArtcraftRouterError::Client(ClientError::ModelDoesNotSupportOption { + field: "start_frame", + value: "Kling 1.6 Pro requires a starting frame".to_string(), + })) + } + }; + let end_frame = resolve_end_frame(request.end_frame)?; + + let aspect_ratio = plan_aspect_ratio(request.aspect_ratio, strategy)?; + let duration = plan_duration(request.duration_seconds, strategy)?; + + Ok(VideoGenerationPlan::ArtcraftKling16Pro(PlanArtcraftKling16Pro { + prompt: request.prompt, + start_frame, + end_frame, + aspect_ratio, + duration, + idempotency_token: request.get_or_generate_idempotency_token(), + })) +} + +fn resolve_end_frame<'a>( + image_ref: Option>, +) -> Result, ArtcraftRouterError> { + match image_ref { + None => Ok(None), + Some(ImageRef::MediaFileToken(t)) => Ok(Some(t)), + Some(ImageRef::Url(_)) => { + Err(ArtcraftRouterError::Client(ClientError::ArtcraftOnlySupportsMediaTokens)) + } + } +} + +fn plan_aspect_ratio( + aspect_ratio: Option, + strategy: RequestMismatchMitigationStrategy, +) -> Result, ArtcraftRouterError> { + use GenerateKling16ProAspectRatio as Ar; + match aspect_ratio { + None => Ok(None), + + Some(CommonAspectRatio::Square) | Some(CommonAspectRatio::SquareHd) => Ok(Some(Ar::Square)), + Some(CommonAspectRatio::WideSixteenByNine) | Some(CommonAspectRatio::Wide) => Ok(Some(Ar::WideSixteenNine)), + Some(CommonAspectRatio::TallNineBySixteen) | Some(CommonAspectRatio::Tall) => Ok(Some(Ar::TallNineSixteen)), + + Some(CommonAspectRatio::Auto) + | Some(CommonAspectRatio::Auto2k) + | Some(CommonAspectRatio::Auto4k) => Ok(Some(Ar::WideSixteenNine)), + + Some(unsupported) => match strategy { + RequestMismatchMitigationStrategy::ErrorOut => { + Err(ArtcraftRouterError::Client(ClientError::ModelDoesNotSupportOption { + field: "aspect_ratio", + value: format!("{:?}", unsupported), + })) + } + _ => Ok(Some(nearest_aspect_ratio(unsupported))), + }, + } +} + +fn nearest_aspect_ratio(ar: CommonAspectRatio) -> GenerateKling16ProAspectRatio { + use GenerateKling16ProAspectRatio as Ar; + match ar { + CommonAspectRatio::WideFourByThree + | CommonAspectRatio::WideFiveByFour + | CommonAspectRatio::WideThreeByTwo + | CommonAspectRatio::WideTwentyOneByNine => Ar::WideSixteenNine, + CommonAspectRatio::TallThreeByFour + | CommonAspectRatio::TallFourByFive + | CommonAspectRatio::TallTwoByThree + | CommonAspectRatio::TallNineByTwentyOne => Ar::TallNineSixteen, + _ => Ar::Square, + } +} + +fn plan_duration( + duration_seconds: Option, + strategy: RequestMismatchMitigationStrategy, +) -> Result, ArtcraftRouterError> { + use GenerateKling16ProDuration as D; + match duration_seconds { + None => Ok(None), + Some(5) => Ok(Some(D::FiveSeconds)), + Some(10) => Ok(Some(D::TenSeconds)), + Some(other) => match strategy { + RequestMismatchMitigationStrategy::ErrorOut => { + Err(ArtcraftRouterError::Client(ClientError::ModelDoesNotSupportOption { + field: "duration_seconds", + value: format!("{}", other), + })) + } + RequestMismatchMitigationStrategy::PayMoreUpgrade => Ok(Some(D::TenSeconds)), + RequestMismatchMitigationStrategy::PayLessDowngrade => Ok(Some(D::FiveSeconds)), + }, + } +} + +impl PlanArtcraftKling16Pro<'_> { + pub fn is_ten_seconds(&self) -> bool { + matches!(self.duration, Some(GenerateKling16ProDuration::TenSeconds)) + } +} diff --git a/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/plan/artcraft/plan_generate_video_artcraft_kling_2_1_master.rs b/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/plan/artcraft/plan_generate_video_artcraft_kling_2_1_master.rs new file mode 100644 index 0000000000..5cffc505de --- /dev/null +++ b/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/plan/artcraft/plan_generate_video_artcraft_kling_2_1_master.rs @@ -0,0 +1,128 @@ +use crate::api::common_aspect_ratio::CommonAspectRatio; +use crate::api::image_ref::ImageRef; +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_video::generate_video_request::GenerateVideoRequest; +use crate::generate::generate_video::video_generation_plan::VideoGenerationPlan; +use artcraft_api_defs::generate::video::generate_kling_2_1_master_image_to_video::{ + GenerateKling21MasterAspectRatio, GenerateKling21MasterDuration, +}; +use tokens::tokens::media_files::MediaFileToken; + +#[derive(Debug, Clone)] +pub struct PlanArtcraftKling21Master<'a> { + pub prompt: Option<&'a str>, + pub start_frame: &'a MediaFileToken, + pub aspect_ratio: Option, + pub duration: Option, + pub idempotency_token: String, +} + +pub fn plan_generate_video_artcraft_kling_2_1_master<'a>( + request: &'a GenerateVideoRequest<'a>, +) -> Result, ArtcraftRouterError> { + let strategy = request.request_mismatch_mitigation_strategy; + + let start_frame = match request.start_frame { + Some(ImageRef::MediaFileToken(t)) => t, + Some(ImageRef::Url(_)) => { + return Err(ArtcraftRouterError::Client(ClientError::ArtcraftOnlySupportsMediaTokens)) + } + None => { + return Err(ArtcraftRouterError::Client(ClientError::ModelDoesNotSupportOption { + field: "start_frame", + value: "Kling 2.1 Master requires a starting frame".to_string(), + })) + } + }; + + if request.end_frame.is_some() { + return Err(ArtcraftRouterError::Client(ClientError::ModelDoesNotSupportOption { + field: "end_frame", + value: "Kling 2.1 Master does not support an ending frame".to_string(), + })); + } + + let aspect_ratio = plan_aspect_ratio(request.aspect_ratio, strategy)?; + let duration = plan_duration(request.duration_seconds, strategy)?; + + Ok(VideoGenerationPlan::ArtcraftKling21Master(PlanArtcraftKling21Master { + prompt: request.prompt, + start_frame, + aspect_ratio, + duration, + idempotency_token: request.get_or_generate_idempotency_token(), + })) +} + +fn plan_aspect_ratio( + aspect_ratio: Option, + strategy: RequestMismatchMitigationStrategy, +) -> Result, ArtcraftRouterError> { + use GenerateKling21MasterAspectRatio as Ar; + match aspect_ratio { + None => Ok(None), + + Some(CommonAspectRatio::Square) | Some(CommonAspectRatio::SquareHd) => Ok(Some(Ar::Square)), + Some(CommonAspectRatio::WideSixteenByNine) | Some(CommonAspectRatio::Wide) => Ok(Some(Ar::WideSixteenNine)), + Some(CommonAspectRatio::TallNineBySixteen) | Some(CommonAspectRatio::Tall) => Ok(Some(Ar::TallNineSixteen)), + + Some(CommonAspectRatio::Auto) + | Some(CommonAspectRatio::Auto2k) + | Some(CommonAspectRatio::Auto4k) => Ok(Some(Ar::WideSixteenNine)), + + Some(unsupported) => match strategy { + RequestMismatchMitigationStrategy::ErrorOut => { + Err(ArtcraftRouterError::Client(ClientError::ModelDoesNotSupportOption { + field: "aspect_ratio", + value: format!("{:?}", unsupported), + })) + } + _ => Ok(Some(nearest_aspect_ratio(unsupported))), + }, + } +} + +fn nearest_aspect_ratio(ar: CommonAspectRatio) -> GenerateKling21MasterAspectRatio { + use GenerateKling21MasterAspectRatio as Ar; + match ar { + CommonAspectRatio::WideFourByThree + | CommonAspectRatio::WideFiveByFour + | CommonAspectRatio::WideThreeByTwo + | CommonAspectRatio::WideTwentyOneByNine => Ar::WideSixteenNine, + CommonAspectRatio::TallThreeByFour + | CommonAspectRatio::TallFourByFive + | CommonAspectRatio::TallTwoByThree + | CommonAspectRatio::TallNineByTwentyOne => Ar::TallNineSixteen, + _ => Ar::Square, + } +} + +fn plan_duration( + duration_seconds: Option, + strategy: RequestMismatchMitigationStrategy, +) -> Result, ArtcraftRouterError> { + use GenerateKling21MasterDuration as D; + match duration_seconds { + None => Ok(None), + Some(5) => Ok(Some(D::FiveSeconds)), + Some(10) => Ok(Some(D::TenSeconds)), + Some(other) => match strategy { + RequestMismatchMitigationStrategy::ErrorOut => { + Err(ArtcraftRouterError::Client(ClientError::ModelDoesNotSupportOption { + field: "duration_seconds", + value: format!("{}", other), + })) + } + RequestMismatchMitigationStrategy::PayMoreUpgrade => Ok(Some(D::TenSeconds)), + RequestMismatchMitigationStrategy::PayLessDowngrade => Ok(Some(D::FiveSeconds)), + }, + } +} + +impl PlanArtcraftKling21Master<'_> { + pub fn is_ten_seconds(&self) -> bool { + matches!(self.duration, Some(GenerateKling21MasterDuration::TenSeconds)) + } +} diff --git a/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/plan/artcraft/plan_generate_video_artcraft_kling_2_1_pro.rs b/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/plan/artcraft/plan_generate_video_artcraft_kling_2_1_pro.rs new file mode 100644 index 0000000000..e3173cb0a1 --- /dev/null +++ b/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/plan/artcraft/plan_generate_video_artcraft_kling_2_1_pro.rs @@ -0,0 +1,136 @@ +use crate::api::common_aspect_ratio::CommonAspectRatio; +use crate::api::image_ref::ImageRef; +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_video::generate_video_request::GenerateVideoRequest; +use crate::generate::generate_video::video_generation_plan::VideoGenerationPlan; +use artcraft_api_defs::generate::video::generate_kling_2_1_pro_image_to_video::{ + GenerateKling21ProAspectRatio, GenerateKling21ProDuration, +}; +use tokens::tokens::media_files::MediaFileToken; + +#[derive(Debug, Clone)] +pub struct PlanArtcraftKling21Pro<'a> { + pub prompt: Option<&'a str>, + pub start_frame: &'a MediaFileToken, + pub end_frame: Option<&'a MediaFileToken>, + pub aspect_ratio: Option, + pub duration: Option, + pub idempotency_token: String, +} + +pub fn plan_generate_video_artcraft_kling_2_1_pro<'a>( + request: &'a GenerateVideoRequest<'a>, +) -> Result, ArtcraftRouterError> { + let strategy = request.request_mismatch_mitigation_strategy; + + let start_frame = match request.start_frame { + Some(ImageRef::MediaFileToken(t)) => t, + Some(ImageRef::Url(_)) => { + return Err(ArtcraftRouterError::Client(ClientError::ArtcraftOnlySupportsMediaTokens)) + } + None => { + return Err(ArtcraftRouterError::Client(ClientError::ModelDoesNotSupportOption { + field: "start_frame", + value: "Kling 2.1 Pro requires a starting frame".to_string(), + })) + } + }; + let end_frame = resolve_end_frame(request.end_frame)?; + + let aspect_ratio = plan_aspect_ratio(request.aspect_ratio, strategy)?; + let duration = plan_duration(request.duration_seconds, strategy)?; + + Ok(VideoGenerationPlan::ArtcraftKling21Pro(PlanArtcraftKling21Pro { + prompt: request.prompt, + start_frame, + end_frame, + aspect_ratio, + duration, + idempotency_token: request.get_or_generate_idempotency_token(), + })) +} + +fn resolve_end_frame<'a>( + image_ref: Option>, +) -> Result, ArtcraftRouterError> { + match image_ref { + None => Ok(None), + Some(ImageRef::MediaFileToken(t)) => Ok(Some(t)), + Some(ImageRef::Url(_)) => { + Err(ArtcraftRouterError::Client(ClientError::ArtcraftOnlySupportsMediaTokens)) + } + } +} + +fn plan_aspect_ratio( + aspect_ratio: Option, + strategy: RequestMismatchMitigationStrategy, +) -> Result, ArtcraftRouterError> { + use GenerateKling21ProAspectRatio as Ar; + match aspect_ratio { + None => Ok(None), + + Some(CommonAspectRatio::Square) | Some(CommonAspectRatio::SquareHd) => Ok(Some(Ar::Square)), + Some(CommonAspectRatio::WideSixteenByNine) | Some(CommonAspectRatio::Wide) => Ok(Some(Ar::WideSixteenNine)), + Some(CommonAspectRatio::TallNineBySixteen) | Some(CommonAspectRatio::Tall) => Ok(Some(Ar::TallNineSixteen)), + + Some(CommonAspectRatio::Auto) + | Some(CommonAspectRatio::Auto2k) + | Some(CommonAspectRatio::Auto4k) => Ok(Some(Ar::WideSixteenNine)), + + Some(unsupported) => match strategy { + RequestMismatchMitigationStrategy::ErrorOut => { + Err(ArtcraftRouterError::Client(ClientError::ModelDoesNotSupportOption { + field: "aspect_ratio", + value: format!("{:?}", unsupported), + })) + } + _ => Ok(Some(nearest_aspect_ratio(unsupported))), + }, + } +} + +fn nearest_aspect_ratio(ar: CommonAspectRatio) -> GenerateKling21ProAspectRatio { + use GenerateKling21ProAspectRatio as Ar; + match ar { + CommonAspectRatio::WideFourByThree + | CommonAspectRatio::WideFiveByFour + | CommonAspectRatio::WideThreeByTwo + | CommonAspectRatio::WideTwentyOneByNine => Ar::WideSixteenNine, + CommonAspectRatio::TallThreeByFour + | CommonAspectRatio::TallFourByFive + | CommonAspectRatio::TallTwoByThree + | CommonAspectRatio::TallNineByTwentyOne => Ar::TallNineSixteen, + _ => Ar::Square, + } +} + +fn plan_duration( + duration_seconds: Option, + strategy: RequestMismatchMitigationStrategy, +) -> Result, ArtcraftRouterError> { + use GenerateKling21ProDuration as D; + match duration_seconds { + None => Ok(None), + Some(5) => Ok(Some(D::FiveSeconds)), + Some(10) => Ok(Some(D::TenSeconds)), + Some(other) => match strategy { + RequestMismatchMitigationStrategy::ErrorOut => { + Err(ArtcraftRouterError::Client(ClientError::ModelDoesNotSupportOption { + field: "duration_seconds", + value: format!("{}", other), + })) + } + RequestMismatchMitigationStrategy::PayMoreUpgrade => Ok(Some(D::TenSeconds)), + RequestMismatchMitigationStrategy::PayLessDowngrade => Ok(Some(D::FiveSeconds)), + }, + } +} + +impl PlanArtcraftKling21Pro<'_> { + pub fn is_ten_seconds(&self) -> bool { + matches!(self.duration, Some(GenerateKling21ProDuration::TenSeconds)) + } +} diff --git a/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/plan/artcraft/plan_generate_video_artcraft_kling_2_5_turbo_pro.rs b/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/plan/artcraft/plan_generate_video_artcraft_kling_2_5_turbo_pro.rs new file mode 100644 index 0000000000..a065740940 --- /dev/null +++ b/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/plan/artcraft/plan_generate_video_artcraft_kling_2_5_turbo_pro.rs @@ -0,0 +1,127 @@ +use crate::api::common_aspect_ratio::CommonAspectRatio; +use crate::api::image_ref::ImageRef; +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_video::generate_video_request::GenerateVideoRequest; +use crate::generate::generate_video::video_generation_plan::VideoGenerationPlan; +use artcraft_api_defs::generate::video::multi_function::kling_2_5_turbo_multi_function_video_gen::{ + Kling2p5TurboProMultiFunctionVideoGenAspectRatio, Kling2p5TurboProMultiFunctionVideoGenDuration, +}; +use tokens::tokens::media_files::MediaFileToken; + +#[derive(Debug, Clone)] +pub struct PlanArtcraftKling2p5TurboPro<'a> { + pub prompt: Option<&'a str>, + pub negative_prompt: Option<&'a str>, + pub start_frame: Option<&'a MediaFileToken>, + pub end_frame: Option<&'a MediaFileToken>, + pub aspect_ratio: Option, + pub duration: Option, + pub idempotency_token: String, +} + +pub fn plan_generate_video_artcraft_kling_2_5_turbo_pro<'a>( + request: &'a GenerateVideoRequest<'a>, +) -> Result, ArtcraftRouterError> { + let strategy = request.request_mismatch_mitigation_strategy; + + let start_frame = resolve_image_ref(request.start_frame)?; + let end_frame = resolve_image_ref(request.end_frame)?; + + let aspect_ratio = plan_aspect_ratio(request.aspect_ratio, strategy)?; + let duration = plan_duration(request.duration_seconds, strategy)?; + + Ok(VideoGenerationPlan::ArtcraftKling2p5TurboPro(PlanArtcraftKling2p5TurboPro { + prompt: request.prompt, + negative_prompt: request.negative_prompt, + start_frame, + end_frame, + aspect_ratio, + duration, + idempotency_token: request.get_or_generate_idempotency_token(), + })) +} + +fn resolve_image_ref<'a>( + image_ref: Option>, +) -> Result, ArtcraftRouterError> { + match image_ref { + None => Ok(None), + Some(ImageRef::MediaFileToken(t)) => Ok(Some(t)), + Some(ImageRef::Url(_)) => { + Err(ArtcraftRouterError::Client(ClientError::ArtcraftOnlySupportsMediaTokens)) + } + } +} + +fn plan_aspect_ratio( + aspect_ratio: Option, + strategy: RequestMismatchMitigationStrategy, +) -> Result, ArtcraftRouterError> { + use Kling2p5TurboProMultiFunctionVideoGenAspectRatio as Ar; + match aspect_ratio { + None => Ok(None), + + Some(CommonAspectRatio::Square) | Some(CommonAspectRatio::SquareHd) => Ok(Some(Ar::Square)), + Some(CommonAspectRatio::WideSixteenByNine) | Some(CommonAspectRatio::Wide) => Ok(Some(Ar::SixteenByNine)), + Some(CommonAspectRatio::TallNineBySixteen) | Some(CommonAspectRatio::Tall) => Ok(Some(Ar::NineBySixteen)), + + Some(CommonAspectRatio::Auto) + | Some(CommonAspectRatio::Auto2k) + | Some(CommonAspectRatio::Auto4k) => Ok(Some(Ar::SixteenByNine)), + + Some(unsupported) => match strategy { + RequestMismatchMitigationStrategy::ErrorOut => { + Err(ArtcraftRouterError::Client(ClientError::ModelDoesNotSupportOption { + field: "aspect_ratio", + value: format!("{:?}", unsupported), + })) + } + _ => Ok(Some(nearest_aspect_ratio(unsupported))), + }, + } +} + +fn nearest_aspect_ratio(ar: CommonAspectRatio) -> Kling2p5TurboProMultiFunctionVideoGenAspectRatio { + use Kling2p5TurboProMultiFunctionVideoGenAspectRatio as Ar; + match ar { + CommonAspectRatio::WideFourByThree + | CommonAspectRatio::WideFiveByFour + | CommonAspectRatio::WideThreeByTwo + | CommonAspectRatio::WideTwentyOneByNine => Ar::SixteenByNine, + CommonAspectRatio::TallThreeByFour + | CommonAspectRatio::TallFourByFive + | CommonAspectRatio::TallTwoByThree + | CommonAspectRatio::TallNineByTwentyOne => Ar::NineBySixteen, + _ => Ar::Square, + } +} + +fn plan_duration( + duration_seconds: Option, + strategy: RequestMismatchMitigationStrategy, +) -> Result, ArtcraftRouterError> { + use Kling2p5TurboProMultiFunctionVideoGenDuration as D; + match duration_seconds { + None => Ok(None), + Some(5) => Ok(Some(D::FiveSeconds)), + Some(10) => Ok(Some(D::TenSeconds)), + Some(other) => match strategy { + RequestMismatchMitigationStrategy::ErrorOut => { + Err(ArtcraftRouterError::Client(ClientError::ModelDoesNotSupportOption { + field: "duration_seconds", + value: format!("{}", other), + })) + } + RequestMismatchMitigationStrategy::PayMoreUpgrade => Ok(Some(D::TenSeconds)), + RequestMismatchMitigationStrategy::PayLessDowngrade => Ok(Some(D::FiveSeconds)), + }, + } +} + +impl PlanArtcraftKling2p5TurboPro<'_> { + pub fn is_ten_seconds(&self) -> bool { + matches!(self.duration, Some(Kling2p5TurboProMultiFunctionVideoGenDuration::TenSeconds)) + } +} diff --git a/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/plan/artcraft/plan_generate_video_artcraft_kling_2_6_pro.rs b/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/plan/artcraft/plan_generate_video_artcraft_kling_2_6_pro.rs new file mode 100644 index 0000000000..9df6422743 --- /dev/null +++ b/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/plan/artcraft/plan_generate_video_artcraft_kling_2_6_pro.rs @@ -0,0 +1,133 @@ +use crate::api::common_aspect_ratio::CommonAspectRatio; +use crate::api::image_ref::ImageRef; +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_video::generate_video_request::GenerateVideoRequest; +use crate::generate::generate_video::video_generation_plan::VideoGenerationPlan; +use artcraft_api_defs::generate::video::multi_function::kling_2_6_multi_function_video_gen::{ + Kling2p6ProMultiFunctionVideoGenAspectRatio, Kling2p6ProMultiFunctionVideoGenDuration, +}; +use tokens::tokens::media_files::MediaFileToken; + +#[derive(Debug, Clone)] +pub struct PlanArtcraftKling2p6Pro<'a> { + pub prompt: Option<&'a str>, + pub negative_prompt: Option<&'a str>, + pub start_frame: Option<&'a MediaFileToken>, + pub aspect_ratio: Option, + pub duration: Option, + pub generate_audio: Option, + pub idempotency_token: String, +} + +pub fn plan_generate_video_artcraft_kling_2_6_pro<'a>( + request: &'a GenerateVideoRequest<'a>, +) -> Result, ArtcraftRouterError> { + let strategy = request.request_mismatch_mitigation_strategy; + + let start_frame = match request.start_frame { + None => None, + Some(ImageRef::MediaFileToken(t)) => Some(t), + Some(ImageRef::Url(_)) => { + return Err(ArtcraftRouterError::Client(ClientError::ArtcraftOnlySupportsMediaTokens)) + } + }; + + if request.end_frame.is_some() { + return Err(ArtcraftRouterError::Client(ClientError::ModelDoesNotSupportOption { + field: "end_frame", + value: "Kling 2.6 Pro does not support an ending frame".to_string(), + })); + } + + let aspect_ratio = plan_aspect_ratio(request.aspect_ratio, strategy)?; + let duration = plan_duration(request.duration_seconds, strategy)?; + + Ok(VideoGenerationPlan::ArtcraftKling2p6Pro(PlanArtcraftKling2p6Pro { + prompt: request.prompt, + negative_prompt: request.negative_prompt, + start_frame, + aspect_ratio, + duration, + generate_audio: request.generate_audio, + idempotency_token: request.get_or_generate_idempotency_token(), + })) +} + +fn plan_aspect_ratio( + aspect_ratio: Option, + strategy: RequestMismatchMitigationStrategy, +) -> Result, ArtcraftRouterError> { + use Kling2p6ProMultiFunctionVideoGenAspectRatio as Ar; + match aspect_ratio { + None => Ok(None), + + Some(CommonAspectRatio::Square) | Some(CommonAspectRatio::SquareHd) => Ok(Some(Ar::Square)), + Some(CommonAspectRatio::WideSixteenByNine) | Some(CommonAspectRatio::Wide) => Ok(Some(Ar::SixteenByNine)), + Some(CommonAspectRatio::TallNineBySixteen) | Some(CommonAspectRatio::Tall) => Ok(Some(Ar::NineBySixteen)), + + Some(CommonAspectRatio::Auto) + | Some(CommonAspectRatio::Auto2k) + | Some(CommonAspectRatio::Auto4k) => Ok(Some(Ar::SixteenByNine)), + + Some(unsupported) => match strategy { + RequestMismatchMitigationStrategy::ErrorOut => { + Err(ArtcraftRouterError::Client(ClientError::ModelDoesNotSupportOption { + field: "aspect_ratio", + value: format!("{:?}", unsupported), + })) + } + _ => Ok(Some(nearest_aspect_ratio(unsupported))), + }, + } +} + +fn nearest_aspect_ratio(ar: CommonAspectRatio) -> Kling2p6ProMultiFunctionVideoGenAspectRatio { + use Kling2p6ProMultiFunctionVideoGenAspectRatio as Ar; + match ar { + CommonAspectRatio::WideFourByThree + | CommonAspectRatio::WideFiveByFour + | CommonAspectRatio::WideThreeByTwo + | CommonAspectRatio::WideTwentyOneByNine => Ar::SixteenByNine, + CommonAspectRatio::TallThreeByFour + | CommonAspectRatio::TallFourByFive + | CommonAspectRatio::TallTwoByThree + | CommonAspectRatio::TallNineByTwentyOne => Ar::NineBySixteen, + _ => Ar::Square, + } +} + +fn plan_duration( + duration_seconds: Option, + strategy: RequestMismatchMitigationStrategy, +) -> Result, ArtcraftRouterError> { + use Kling2p6ProMultiFunctionVideoGenDuration as D; + match duration_seconds { + None => Ok(None), + Some(5) => Ok(Some(D::FiveSeconds)), + Some(10) => Ok(Some(D::TenSeconds)), + Some(other) => match strategy { + RequestMismatchMitigationStrategy::ErrorOut => { + Err(ArtcraftRouterError::Client(ClientError::ModelDoesNotSupportOption { + field: "duration_seconds", + value: format!("{}", other), + })) + } + RequestMismatchMitigationStrategy::PayMoreUpgrade => Ok(Some(D::TenSeconds)), + RequestMismatchMitigationStrategy::PayLessDowngrade => Ok(Some(D::FiveSeconds)), + }, + } +} + +impl PlanArtcraftKling2p6Pro<'_> { + pub fn is_ten_seconds(&self) -> bool { + matches!(self.duration, Some(Kling2p6ProMultiFunctionVideoGenDuration::TenSeconds)) + } + + /// Legacy Kling 2.6 Pro generate handler forwards `request.generate_audio` as-is + /// to the Fal client, which defaults to `true`. + pub fn generate_audio_for_cost(&self) -> bool { + self.generate_audio.unwrap_or(true) + } +} 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 a83864edd3..e981d8b41a 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 @@ -1,5 +1,10 @@ use crate::client::router_client::RouterClient; use crate::errors::artcraft_router_error::ArtcraftRouterError; +use crate::generate::generate_video::cost::artcraft::estimate_video_cost_artcraft_kling_1_6_pro::estimate_video_cost_artcraft_kling_1_6_pro; +use crate::generate::generate_video::cost::artcraft::estimate_video_cost_artcraft_kling_2_1_master::estimate_video_cost_artcraft_kling_2_1_master; +use crate::generate::generate_video::cost::artcraft::estimate_video_cost_artcraft_kling_2_1_pro::estimate_video_cost_artcraft_kling_2_1_pro; +use crate::generate::generate_video::cost::artcraft::estimate_video_cost_artcraft_kling_2_5_turbo_pro::estimate_video_cost_artcraft_kling_2_5_turbo_pro; +use crate::generate::generate_video::cost::artcraft::estimate_video_cost_artcraft_kling_2_6_pro::estimate_video_cost_artcraft_kling_2_6_pro; use crate::generate::generate_video::cost::artcraft::estimate_video_cost_artcraft_kling3p0_pro::estimate_video_cost_artcraft_kling3p0_pro; use crate::generate::generate_video::cost::artcraft::estimate_video_cost_artcraft_kling3p0_standard::estimate_video_cost_artcraft_kling3p0_standard; use crate::generate::generate_video::cost::artcraft::estimate_video_cost_artcraft_seedance1p5_pro::estimate_video_cost_artcraft_seedance1p5_pro; @@ -16,6 +21,11 @@ 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::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; +use crate::generate::generate_video::execute::artcraft::generate_video_artcraft_kling_2_5_turbo_pro::execute_artcraft_kling_2_5_turbo_pro; +use crate::generate::generate_video::execute::artcraft::generate_video_artcraft_kling_2_6_pro::execute_artcraft_kling_2_6_pro; use crate::generate::generate_video::execute::artcraft::generate_video_artcraft_kling3p0_pro::execute_artcraft_kling3p0_pro; use crate::generate::generate_video::execute::artcraft::generate_video_artcraft_kling3p0_standard::execute_artcraft_kling3p0_standard; use crate::generate::generate_video::execute::artcraft::generate_video_artcraft_seedance1p5_pro::execute_artcraft_seedance1p5_pro; @@ -33,6 +43,11 @@ use crate::generate::generate_video::execute::fal::generate_video_fal_veo_3p1_fa 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::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; +use crate::generate::generate_video::plan::artcraft::plan_generate_video_artcraft_kling_2_1_pro::PlanArtcraftKling21Pro; +use crate::generate::generate_video::plan::artcraft::plan_generate_video_artcraft_kling_2_5_turbo_pro::PlanArtcraftKling2p5TurboPro; +use crate::generate::generate_video::plan::artcraft::plan_generate_video_artcraft_kling_2_6_pro::PlanArtcraftKling2p6Pro; use crate::generate::generate_video::plan::artcraft::plan_generate_video_artcraft_kling3p0_pro::PlanArtcraftKling3p0Pro; use crate::generate::generate_video::plan::artcraft::plan_generate_video_artcraft_kling3p0_standard::PlanArtcraftKling3p0Standard; use crate::generate::generate_video::plan::artcraft::plan_generate_video_artcraft_seedance1p5_pro::PlanArtcraftSeedance1p5Pro; @@ -53,6 +68,11 @@ use crate::generate::generate_video::video_generation_cost_estimate::VideoGenera #[derive(Debug)] pub enum VideoGenerationPlan<'a> { + ArtcraftKling16Pro(PlanArtcraftKling16Pro<'a>), + ArtcraftKling21Master(PlanArtcraftKling21Master<'a>), + ArtcraftKling21Pro(PlanArtcraftKling21Pro<'a>), + ArtcraftKling2p5TurboPro(PlanArtcraftKling2p5TurboPro<'a>), + ArtcraftKling2p6Pro(PlanArtcraftKling2p6Pro<'a>), ArtcraftKling3p0Pro(PlanArtcraftKling3p0Pro<'a>), ArtcraftKling3p0Standard(PlanArtcraftKling3p0Standard<'a>), ArtcraftSeedance1p5Pro(PlanArtcraftSeedance1p5Pro<'a>), @@ -77,6 +97,26 @@ impl<'a> VideoGenerationPlan<'a> { client: &RouterClient, ) -> Result { match self { + VideoGenerationPlan::ArtcraftKling16Pro(plan) => { + let artcraft_client = client.get_artcraft_client_ref()?; + execute_artcraft_kling_1_6_pro(plan, artcraft_client).await + } + VideoGenerationPlan::ArtcraftKling21Master(plan) => { + let artcraft_client = client.get_artcraft_client_ref()?; + execute_artcraft_kling_2_1_master(plan, artcraft_client).await + } + VideoGenerationPlan::ArtcraftKling21Pro(plan) => { + let artcraft_client = client.get_artcraft_client_ref()?; + execute_artcraft_kling_2_1_pro(plan, artcraft_client).await + } + VideoGenerationPlan::ArtcraftKling2p5TurboPro(plan) => { + let artcraft_client = client.get_artcraft_client_ref()?; + execute_artcraft_kling_2_5_turbo_pro(plan, artcraft_client).await + } + VideoGenerationPlan::ArtcraftKling2p6Pro(plan) => { + let artcraft_client = client.get_artcraft_client_ref()?; + execute_artcraft_kling_2_6_pro(plan, artcraft_client).await + } VideoGenerationPlan::ArtcraftKling3p0Pro(plan) => { let artcraft_client = client.get_artcraft_client_ref()?; execute_artcraft_kling3p0_pro(plan, artcraft_client).await @@ -146,6 +186,11 @@ impl<'a> VideoGenerationPlan<'a> { pub fn estimate_costs(&self) -> VideoGenerationCostEstimate { match self { + VideoGenerationPlan::ArtcraftKling16Pro(plan) => estimate_video_cost_artcraft_kling_1_6_pro(plan), + VideoGenerationPlan::ArtcraftKling21Master(plan) => estimate_video_cost_artcraft_kling_2_1_master(plan), + VideoGenerationPlan::ArtcraftKling21Pro(plan) => estimate_video_cost_artcraft_kling_2_1_pro(plan), + VideoGenerationPlan::ArtcraftKling2p5TurboPro(plan) => estimate_video_cost_artcraft_kling_2_5_turbo_pro(plan), + VideoGenerationPlan::ArtcraftKling2p6Pro(plan) => estimate_video_cost_artcraft_kling_2_6_pro(plan), VideoGenerationPlan::ArtcraftKling3p0Pro(plan) => { estimate_video_cost_artcraft_kling3p0_pro(plan) }