mirror of
https://github.com/storytold/artcraft.git
synced 2026-11-04 01:18:34 +00:00
artcraft_router for seedance2fast + kinovi
This commit is contained in:
1 parent
5c339e9971
commit
ced17552c1
12 files changed
+232
-3
No files matched your search
+129
@@ -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);
|
||||
}
|
||||
}
|
||||
+1
@@ -1 +1,2 @@
|
||||
pub mod estimate_video_cost_seedance2pro_seedance2p0;
|
||||
pub mod estimate_video_cost_seedance2pro_seedance2p0_fast;
|
||||
+3
-3
@@ -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<String, ArtcraftRouterError> {
|
||||
@@ -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<Option<String>, 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<Option<Vec<String>>, ArtcraftRouterError> {
|
||||
|
||||
+57
@@ -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<GenerateVideoResponse, ArtcraftRouterError> {
|
||||
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,
|
||||
}))
|
||||
}
|
||||
+1
@@ -1 +1,2 @@
|
||||
pub mod execute_seedance2pro_seedance2p0;
|
||||
pub mod execute_seedance2pro_seedance2p0_fast;
|
||||
+2
@@ -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<VideoGenerationPlan<'_>, 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))),
|
||||
}
|
||||
}
|
||||
|
||||
+1
@@ -1 +1,2 @@
|
||||
pub mod plan_generate_video_seedance2pro_seedance2p0;
|
||||
pub mod plan_generate_video_seedance2pro_seedance2p0_fast;
|
||||
+27
@@ -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<VideoGenerationPlan<'a>, 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"),
|
||||
}
|
||||
}
|
||||
+10
@@ -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),
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 179 B |
Binary file not shown.
|
After Width: | Height: | Size: 92 KiB |
+1
@@ -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,
|
||||
};
|
||||
|
||||
|
||||
Reference in new issue
Block a user