Compare commits

...
Author SHA1 Message Date
Brandon Thomas f47f425e76 router BytePlus ultra 2026-05-28 16:20:01 -04:00
Brandon Thomas 636c7f19e6 plus ultra variant 2026-05-28 16:04:44 -04:00
18 changed files with 930 additions and 1 deletions
@@ -56,6 +56,12 @@ pub enum RouterVideoModel {
#[serde(rename = "seedance_2p0_u_fast")]
Seedance2p0UltraFast,
#[serde(rename = "seedance_2p0_bpu")]
Seedance2p0BytePlusUltra,
#[serde(rename = "seedance_2p0_bpu_fast")]
Seedance2p0BytePlusUltraFast,
#[serde(rename = "happy_horse_1p0")]
HappyHorse1p0,
@@ -28,6 +28,8 @@ use crate::generate::generate_video_v2::providers::artcraft::seedance_2p0_u::bui
use crate::generate::generate_video_v2::providers::artcraft::seedance_2p0_u_fast::build::build_artcraft_seedance_2p0_u_fast;
use crate::generate::generate_video_v2::providers::artcraft::seedance_2p0_bp::build::build_artcraft_seedance_2p0_bp;
use crate::generate::generate_video_v2::providers::artcraft::seedance_2p0_bp_fast::build::build_artcraft_seedance_2p0_bp_fast;
use crate::generate::generate_video_v2::providers::artcraft::seedance_2p0_bpu::build::build_artcraft_seedance_2p0_bpu;
use crate::generate::generate_video_v2::providers::artcraft::seedance_2p0_bpu_fast::build::build_artcraft_seedance_2p0_bpu_fast;
use crate::generate::generate_video_v2::providers::artcraft::sora_2::build::build_artcraft_sora_2;
use crate::generate::generate_video_v2::providers::artcraft::sora_2_pro::build::build_artcraft_sora_2_pro;
use crate::generate::generate_video_v2::providers::artcraft::veo_2::build::build_artcraft_veo_2;
@@ -163,6 +165,8 @@ impl GenerateVideoRequestBuilder {
(RouterProvider::Artcraft, RouterVideoModel::Seedance2p0UltraFast) => build_artcraft_seedance_2p0_u_fast(self),
(RouterProvider::Artcraft, RouterVideoModel::Seedance2p0BytePlus) => build_artcraft_seedance_2p0_bp(self),
(RouterProvider::Artcraft, RouterVideoModel::Seedance2p0BytePlusFast) => build_artcraft_seedance_2p0_bp_fast(self),
(RouterProvider::Artcraft, RouterVideoModel::Seedance2p0BytePlusUltra) => build_artcraft_seedance_2p0_bpu(self),
(RouterProvider::Artcraft, RouterVideoModel::Seedance2p0BytePlusUltraFast) => build_artcraft_seedance_2p0_bpu_fast(self),
(RouterProvider::Artcraft, RouterVideoModel::PreviewModel) => build_artcraft_preview_model(self),
(RouterProvider::Artcraft, RouterVideoModel::PreviewModelFast) => build_artcraft_preview_model_fast(self),
(RouterProvider::Artcraft, RouterVideoModel::Seedance10Lite) => build_artcraft_seedance_1p0_lite(self),
@@ -1,4 +1,6 @@
pub(crate) mod build_common;
#[cfg(test)]
mod parity_tests;
pub mod grok_imagine_video;
pub mod happy_horse_1p0;
pub mod kling_1_6_pro;
@@ -20,6 +22,8 @@ pub mod seedance_2p0_u_fast;
pub mod seedance_2p0_u;
pub mod seedance_2p0_bp;
pub mod seedance_2p0_bp_fast;
pub mod seedance_2p0_bpu;
pub mod seedance_2p0_bpu_fast;
pub mod sora_2;
pub mod sora_2_pro;
pub mod veo_2;
@@ -0,0 +1,131 @@
//! Cost-parity tests: the BPU (BytePlus Ultra) variants must price identically to
//! the BP (BytePlus) variants across every (resolution × duration × batch) combo.
//!
//! When BPU and BP pricing must diverge in the future, these tests should be
//! intentionally adjusted and split rather than relaxed.
use crate::api::router_provider::RouterProvider;
use crate::api::router_resolution::RouterResolution;
use crate::api::router_video_model::RouterVideoModel;
use crate::generate::generate_video::generate_video_request_builder::GenerateVideoRequestBuilder;
use crate::generate::generate_video::video_generation_cost_estimate::VideoGenerationCostEstimate;
const RESOLUTIONS: [Option<RouterResolution>; 4] = [
None,
Some(RouterResolution::FourEightyP),
Some(RouterResolution::SevenTwentyP),
Some(RouterResolution::TenEightyP),
];
/// Durations spanning the supported window (1..=15 seconds) plus over-the-cap
/// inputs that exercise duration clamping in the builder.
const DURATIONS: [u16; 17] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 30, 99];
const BATCHES: [u16; 4] = [1, 2, 3, 4];
mod seedance_2p0_bpu_matches_seedance_2p0_bp {
use super::*;
#[test]
fn cost_parity_across_all_combos() {
for &res in &RESOLUTIONS {
for &dur in &DURATIONS {
for &batch in &BATCHES {
let bp = cost_cents(RouterVideoModel::Seedance2p0BytePlus, res, dur, batch);
let bpu = cost_cents(RouterVideoModel::Seedance2p0BytePlusUltra, res, dur, batch);
assert_eq!(
bpu, bp,
"cost mismatch at res={:?} dur={} batch={}: bp={} bpu={}",
res, dur, batch, bp, bpu,
);
}
}
}
}
#[test]
fn credits_parity_across_all_combos() {
for &res in &RESOLUTIONS {
for &dur in &DURATIONS {
for &batch in &BATCHES {
let bp = build_cost(RouterVideoModel::Seedance2p0BytePlus, res, dur, batch);
let bpu = build_cost(RouterVideoModel::Seedance2p0BytePlusUltra, res, dur, batch);
assert_eq!(
bpu.cost_in_credits, bp.cost_in_credits,
"credit mismatch at res={:?} dur={} batch={}",
res, dur, batch,
);
}
}
}
}
}
mod seedance_2p0_bpu_fast_matches_seedance_2p0_bp_fast {
use super::*;
#[test]
fn cost_parity_across_all_combos() {
for &res in &RESOLUTIONS {
for &dur in &DURATIONS {
for &batch in &BATCHES {
let bp = cost_cents(RouterVideoModel::Seedance2p0BytePlusFast, res, dur, batch);
let bpu = cost_cents(RouterVideoModel::Seedance2p0BytePlusUltraFast, res, dur, batch);
assert_eq!(
bpu, bp,
"cost mismatch at res={:?} dur={} batch={}: bp_fast={} bpu_fast={}",
res, dur, batch, bp, bpu,
);
}
}
}
}
#[test]
fn credits_parity_across_all_combos() {
for &res in &RESOLUTIONS {
for &dur in &DURATIONS {
for &batch in &BATCHES {
let bp = build_cost(RouterVideoModel::Seedance2p0BytePlusFast, res, dur, batch);
let bpu = build_cost(RouterVideoModel::Seedance2p0BytePlusUltraFast, res, dur, batch);
assert_eq!(
bpu.cost_in_credits, bp.cost_in_credits,
"credit mismatch at res={:?} dur={} batch={}",
res, dur, batch,
);
}
}
}
}
}
fn build_cost(
model: RouterVideoModel,
resolution: Option<RouterResolution>,
duration_seconds: u16,
video_batch_count: u16,
) -> VideoGenerationCostEstimate {
GenerateVideoRequestBuilder {
model,
provider: RouterProvider::Artcraft,
resolution,
duration_seconds: Some(duration_seconds),
video_batch_count: Some(video_batch_count),
..Default::default()
}
.build2()
.expect("build2 should succeed")
.estimate_cost()
.expect("estimate_cost should succeed")
}
fn cost_cents(
model: RouterVideoModel,
resolution: Option<RouterResolution>,
duration_seconds: u16,
video_batch_count: u16,
) -> u64 {
build_cost(model, resolution, duration_seconds, video_batch_count)
.cost_in_usd_cents
.expect("cost_in_usd_cents should be Some")
}
@@ -0,0 +1,167 @@
use enums::common::generation::common_video_model::CommonVideoModel as CommonVideoModelEnum;
use crate::errors::artcraft_router_error::ArtcraftRouterError;
use crate::generate::generate_video::generate_video_request_builder::GenerateVideoRequestBuilder;
use crate::generate::generate_video_v2::providers::artcraft::build_common::{
build_artcraft_omni_video_request, SupportedResolutions, UltraWideSupport,
};
use crate::generate::generate_video_v2::providers::artcraft::seedance_2p0_bpu::request::ArtcraftSeedance2p0BytePlusUltraRequestState;
use crate::generate::generate_video_v2::video_generation_draft_or_request::VideoGenerationDraftOrRequest;
use crate::generate::generate_video_v2::video_generation_request::VideoGenerationRequest;
pub fn build_artcraft_seedance_2p0_bpu(builder: GenerateVideoRequestBuilder) -> Result<VideoGenerationDraftOrRequest, ArtcraftRouterError> {
let request = build_artcraft_omni_video_request(
builder,
CommonVideoModelEnum::Seedance2p0BytePlusUltra,
SupportedResolutions::Full,
UltraWideSupport::Supported,
)?;
let state = ArtcraftSeedance2p0BytePlusUltraRequestState { request };
Ok(VideoGenerationDraftOrRequest::Request(VideoGenerationRequest::ArtcraftSeedance2p0BytePlusUltra(state)))
}
#[cfg(test)]
mod tests {
use enums::common::generation::common_resolution::CommonResolution as CommonResolutionEnum;
use enums::common::generation::common_video_model::CommonVideoModel as CommonVideoModelEnum;
use tokens::tokens::characters::CharacterToken;
use tokens::tokens::media_files::MediaFileToken;
use crate::api::character_list_ref::CharacterListRef;
use crate::api::router_resolution::RouterResolution;
use crate::api::image_list_ref::ImageListRef;
use crate::api::image_ref::ImageRef;
use crate::api::router_provider::RouterProvider;
use crate::generate::generate_video::generate_video_request_builder::GenerateVideoRequestBuilder;
use crate::generate::generate_video_v2::video_generation_draft_or_request::VideoGenerationDraftOrRequest;
use crate::generate::generate_video_v2::video_generation_request::VideoGenerationRequest;
use super::*;
mod field_conversions {
use super::*;
#[test]
fn model_is_seedance_2p0_bpu() {
let req = unwrap_request(make_builder(|_| {}));
assert!(matches!(req.request.model, Some(CommonVideoModelEnum::Seedance2p0BytePlusUltra)));
}
#[test]
fn prompt_passed_through() {
let req = unwrap_request(make_builder(|b| { b.prompt = Some("test".to_string()); }));
assert_eq!(req.request.prompt, Some("test".to_string()));
}
#[test]
fn duration_passed_through() {
let req = unwrap_request(make_builder(|b| { b.duration_seconds = Some(10); }));
assert_eq!(req.request.duration_seconds, Some(10));
}
#[test]
fn duration_clamped_to_max() {
let req = unwrap_request(make_builder(|b| { b.duration_seconds = Some(99); }));
assert_eq!(req.request.duration_seconds, Some(15));
}
#[test]
fn batch_count_passed_through() {
let req = unwrap_request(make_builder(|b| { b.video_batch_count = Some(4); }));
assert_eq!(req.request.video_batch_count, Some(4));
}
}
mod resolution_tests {
use super::*;
#[test]
fn res_480p() {
let req = unwrap_request(make_builder(|b| { b.resolution = Some(RouterResolution::FourEightyP); }));
assert_eq!(req.request.resolution, Some(CommonResolutionEnum::FourEightyP));
}
#[test]
fn res_720p() {
let req = unwrap_request(make_builder(|b| { b.resolution = Some(RouterResolution::SevenTwentyP); }));
assert_eq!(req.request.resolution, Some(CommonResolutionEnum::SevenTwentyP));
}
#[test]
fn res_1080p() {
let req = unwrap_request(make_builder(|b| { b.resolution = Some(RouterResolution::TenEightyP); }));
assert_eq!(req.request.resolution, Some(CommonResolutionEnum::TenEightyP));
}
#[test]
fn none_stays_none() {
let req = unwrap_request(make_builder(|_| {}));
assert!(req.request.resolution.is_none());
}
}
mod media_token_tests {
use super::*;
#[test]
fn start_frame_token_passed_through() {
let token = MediaFileToken::new("mf_start".to_string());
let req = unwrap_request(make_builder(|b| {
b.start_frame = Some(ImageRef::MediaFileToken(token.clone()));
}));
assert_eq!(req.request.start_frame_image_media_token, Some(token));
}
#[test]
fn url_start_frame_rejected() {
let result = build_artcraft_seedance_2p0_bpu(GenerateVideoRequestBuilder {
start_frame: Some(ImageRef::Url("https://example.com".to_string())),
..base_builder()
});
assert!(result.is_err());
}
#[test]
fn reference_image_tokens_passed_through() {
let tokens = vec![MediaFileToken::new("mf_a".to_string()), MediaFileToken::new("mf_b".to_string())];
let req = unwrap_request(make_builder(|b| {
b.reference_images = Some(ImageListRef::MediaFileTokens(tokens.clone()));
}));
assert_eq!(req.request.reference_image_media_tokens, Some(tokens));
}
#[test]
fn character_tokens_passed_through() {
let tokens = vec![CharacterToken::new("char_a".to_string()), CharacterToken::new("char_b".to_string())];
let req = unwrap_request(make_builder(|b| {
b.reference_character_tokens = Some(CharacterListRef::CharacterTokens(tokens.clone()));
}));
assert_eq!(req.request.reference_character_tokens, Some(tokens));
}
}
fn base_builder() -> GenerateVideoRequestBuilder {
GenerateVideoRequestBuilder {
provider: RouterProvider::Artcraft,
duration_seconds: Some(5),
video_batch_count: Some(1),
..Default::default()
}
}
fn make_builder(f: impl FnOnce(&mut GenerateVideoRequestBuilder)) -> GenerateVideoRequestBuilder {
let mut builder = base_builder();
f(&mut builder);
builder
}
fn unwrap_request(builder: GenerateVideoRequestBuilder) -> ArtcraftSeedance2p0BytePlusUltraRequestState {
let result = build_artcraft_seedance_2p0_bpu(builder).expect("build should succeed");
match result {
VideoGenerationDraftOrRequest::Request(
VideoGenerationRequest::ArtcraftSeedance2p0BytePlusUltra(state)
) => state,
_ => panic!("expected ArtcraftSeedance2p0BytePlusUltra request"),
}
}
}
@@ -0,0 +1,175 @@
use enums::common::generation::common_resolution::CommonResolution;
use crate::generate::generate_video::video_generation_cost_estimate::VideoGenerationCostEstimate;
use crate::generate::generate_video_v2::providers::artcraft::seedance_2p0_bpu::request::ArtcraftSeedance2p0BytePlusUltraRequestState;
/// USD cents per second by resolution:
/// 480p: $0.10/s = 10.0 ¢/s
/// 720p: $0.25/s = 25.0 ¢/s
/// 1080p: $0.50/s = 50.0 ¢/s
const CENTS_PER_SECOND_480P: f64 = 10.0;
const CENTS_PER_SECOND_720P: f64 = 25.0;
const CENTS_PER_SECOND_1080P: f64 = 50.0;
pub struct ArtcraftSeedance2p0BytePlusUltraCostState {
pub resolution: CommonResolution,
pub duration_seconds: u16,
pub batch_count: u16,
}
impl ArtcraftSeedance2p0BytePlusUltraCostState {
pub fn from_request(request: &ArtcraftSeedance2p0BytePlusUltraRequestState) -> Self {
let resolution = request.request.resolution
.unwrap_or(CommonResolution::SevenTwentyP);
let duration_seconds = request.request.duration_seconds.unwrap_or(5);
let batch_count = request.request.video_batch_count.unwrap_or(1);
Self { resolution, duration_seconds, batch_count }
}
pub fn estimate_cost(&self) -> VideoGenerationCostEstimate {
let cents_per_second = match self.resolution {
CommonResolution::FourEightyP => CENTS_PER_SECOND_480P,
CommonResolution::TenEightyP => CENTS_PER_SECOND_1080P,
_ => CENTS_PER_SECOND_720P,
};
let usd_cents = (self.duration_seconds as f64 * cents_per_second * self.batch_count as f64).round() as u64;
VideoGenerationCostEstimate {
cost_in_credits: Some(usd_cents),
cost_in_usd_cents: Some(usd_cents),
is_free: false,
is_unlimited: false,
is_rate_limited: false,
has_watermark: false,
failures_are_refunded: None,
}
}
}
#[cfg(test)]
mod tests {
use crate::api::router_resolution::RouterResolution;
use crate::api::router_video_model::RouterVideoModel;
use crate::api::router_provider::RouterProvider;
use crate::generate::generate_video::generate_video_request_builder::GenerateVideoRequestBuilder;
mod pricing_720p {
use super::*;
#[test]
fn batch_1() {
assert_eq!(cost_cents(Some(RouterResolution::SevenTwentyP), 4, 1), 100);
assert_eq!(cost_cents(Some(RouterResolution::SevenTwentyP), 5, 1), 125);
assert_eq!(cost_cents(Some(RouterResolution::SevenTwentyP), 10, 1), 250);
assert_eq!(cost_cents(Some(RouterResolution::SevenTwentyP), 15, 1), 375);
}
#[test]
fn batch_2() {
assert_eq!(cost_cents(Some(RouterResolution::SevenTwentyP), 5, 2), 250);
}
#[test]
fn batch_4() {
assert_eq!(cost_cents(Some(RouterResolution::SevenTwentyP), 5, 4), 500);
}
#[test]
fn none_defaults_to_720p() {
assert_eq!(cost_cents(None, 5, 1), cost_cents(Some(RouterResolution::SevenTwentyP), 5, 1));
}
}
mod pricing_480p {
use super::*;
#[test]
fn batch_1() {
assert_eq!(cost_cents(Some(RouterResolution::FourEightyP), 4, 1), 40);
assert_eq!(cost_cents(Some(RouterResolution::FourEightyP), 5, 1), 50);
assert_eq!(cost_cents(Some(RouterResolution::FourEightyP), 10, 1), 100);
assert_eq!(cost_cents(Some(RouterResolution::FourEightyP), 15, 1), 150);
}
#[test]
fn batch_2() {
assert_eq!(cost_cents(Some(RouterResolution::FourEightyP), 5, 2), 100);
}
#[test]
fn batch_4() {
assert_eq!(cost_cents(Some(RouterResolution::FourEightyP), 5, 4), 200);
}
}
mod pricing_1080p {
use super::*;
#[test]
fn batch_1() {
assert_eq!(cost_cents(Some(RouterResolution::TenEightyP), 4, 1), 200);
assert_eq!(cost_cents(Some(RouterResolution::TenEightyP), 5, 1), 250);
assert_eq!(cost_cents(Some(RouterResolution::TenEightyP), 10, 1), 500);
assert_eq!(cost_cents(Some(RouterResolution::TenEightyP), 15, 1), 750);
}
#[test]
fn batch_2() {
assert_eq!(cost_cents(Some(RouterResolution::TenEightyP), 5, 2), 500);
}
#[test]
fn batch_4() {
assert_eq!(cost_cents(Some(RouterResolution::TenEightyP), 5, 4), 1000);
}
}
mod relative_pricing_tests {
use super::*;
#[test]
fn cost_480p_cheaper_than_720p_cheaper_than_1080p() {
let c480 = cost_cents(Some(RouterResolution::FourEightyP), 5, 1);
let c720 = cost_cents(Some(RouterResolution::SevenTwentyP), 5, 1);
let c1080 = cost_cents(Some(RouterResolution::TenEightyP), 5, 1);
assert!(c480 < c720);
assert!(c720 < c1080);
}
}
mod credits_tests {
use super::*;
#[test]
fn credits_equal_usd_cents() {
for res in [Some(RouterResolution::FourEightyP), Some(RouterResolution::SevenTwentyP), Some(RouterResolution::TenEightyP), None] {
for dur in [4, 5, 10, 15] {
for batch in [1, 2, 4] {
let cost = build_cost(res, dur, batch);
assert_eq!(cost.cost_in_credits, cost.cost_in_usd_cents);
}
}
}
}
}
fn build_cost(
resolution: Option<RouterResolution>,
duration_seconds: u16,
video_batch_count: u16,
) -> crate::generate::generate_video::video_generation_cost_estimate::VideoGenerationCostEstimate {
GenerateVideoRequestBuilder {
model: RouterVideoModel::Seedance2p0BytePlusUltra,
provider: RouterProvider::Artcraft,
resolution,
duration_seconds: Some(duration_seconds),
video_batch_count: Some(video_batch_count),
..Default::default()
}.build2().expect("build2").estimate_cost().expect("estimate_cost")
}
fn cost_cents(resolution: Option<RouterResolution>, duration_seconds: u16, video_batch_count: u16) -> u64 {
build_cost(resolution, duration_seconds, video_batch_count).cost_in_usd_cents.unwrap()
}
}
@@ -0,0 +1,3 @@
pub mod build;
pub mod cost;
pub mod request;
@@ -0,0 +1,17 @@
use artcraft_api_defs::omni_gen::cost_and_generate_requests::omni_gen_video_cost_and_generate_request::OmniGenVideoCostAndGenerateRequest;
use crate::client::router_artcraft_client::RouterArtcraftClient;
use crate::errors::artcraft_router_error::ArtcraftRouterError;
use crate::generate::generate_video::generate_video_response::GenerateVideoResponse;
use crate::generate::generate_video_v2::providers::artcraft::request_common::send_artcraft_omni_video_request;
#[derive(Clone, Debug)]
pub struct ArtcraftSeedance2p0BytePlusUltraRequestState {
pub request: OmniGenVideoCostAndGenerateRequest,
}
impl ArtcraftSeedance2p0BytePlusUltraRequestState {
pub async fn send(&self, client: &RouterArtcraftClient) -> Result<GenerateVideoResponse, ArtcraftRouterError> {
send_artcraft_omni_video_request(&self.request, client).await
}
}
@@ -0,0 +1,180 @@
use enums::common::generation::common_video_model::CommonVideoModel as CommonVideoModelEnum;
use crate::errors::artcraft_router_error::ArtcraftRouterError;
use crate::generate::generate_video::generate_video_request_builder::GenerateVideoRequestBuilder;
use crate::generate::generate_video_v2::providers::artcraft::build_common::{
build_artcraft_omni_video_request, SupportedResolutions, UltraWideSupport,
};
use crate::generate::generate_video_v2::providers::artcraft::seedance_2p0_bpu_fast::request::ArtcraftSeedance2p0BytePlusUltraFastRequestState;
use crate::generate::generate_video_v2::video_generation_draft_or_request::VideoGenerationDraftOrRequest;
use crate::generate::generate_video_v2::video_generation_request::VideoGenerationRequest;
pub fn build_artcraft_seedance_2p0_bpu_fast(builder: GenerateVideoRequestBuilder) -> Result<VideoGenerationDraftOrRequest, ArtcraftRouterError> {
let request = build_artcraft_omni_video_request(
builder,
CommonVideoModelEnum::Seedance2p0BytePlusUltraFast,
SupportedResolutions::Fast,
UltraWideSupport::Supported,
)?;
let state = ArtcraftSeedance2p0BytePlusUltraFastRequestState { request };
Ok(VideoGenerationDraftOrRequest::Request(VideoGenerationRequest::ArtcraftSeedance2p0BytePlusUltraFast(state)))
}
#[cfg(test)]
mod tests {
use enums::common::generation::common_resolution::CommonResolution as CommonResolutionEnum;
use enums::common::generation::common_video_model::CommonVideoModel as CommonVideoModelEnum;
use tokens::tokens::characters::CharacterToken;
use tokens::tokens::media_files::MediaFileToken;
use crate::api::character_list_ref::CharacterListRef;
use crate::api::router_resolution::RouterResolution;
use crate::api::router_video_model::RouterVideoModel;
use crate::api::image_list_ref::ImageListRef;
use crate::api::image_ref::ImageRef;
use crate::api::router_provider::RouterProvider;
use crate::client::request_mismatch_mitigation_strategy::RequestMismatchMitigationStrategy;
use crate::generate::generate_video::generate_video_request_builder::GenerateVideoRequestBuilder;
use crate::generate::generate_video_v2::video_generation_draft_or_request::VideoGenerationDraftOrRequest;
use crate::generate::generate_video_v2::video_generation_request::VideoGenerationRequest;
use super::*;
mod field_conversions {
use super::*;
#[test]
fn model_is_seedance_2p0_bpu_fast() {
let req = unwrap_request(make_builder(|_| {}));
assert!(matches!(req.request.model, Some(CommonVideoModelEnum::Seedance2p0BytePlusUltraFast)));
}
#[test]
fn prompt_passed_through() {
let req = unwrap_request(make_builder(|b| { b.prompt = Some("test".to_string()); }));
assert_eq!(req.request.prompt, Some("test".to_string()));
}
#[test]
fn duration_passed_through() {
let req = unwrap_request(make_builder(|b| { b.duration_seconds = Some(10); }));
assert_eq!(req.request.duration_seconds, Some(10));
}
#[test]
fn duration_clamped_to_max() {
let req = unwrap_request(make_builder(|b| { b.duration_seconds = Some(99); }));
assert_eq!(req.request.duration_seconds, Some(15));
}
#[test]
fn batch_count_passed_through() {
let req = unwrap_request(make_builder(|b| { b.video_batch_count = Some(4); }));
assert_eq!(req.request.video_batch_count, Some(4));
}
}
mod resolution_tests {
use super::*;
#[test]
fn res_480p() {
let req = unwrap_request(make_builder(|b| { b.resolution = Some(RouterResolution::FourEightyP); }));
assert_eq!(req.request.resolution, Some(CommonResolutionEnum::FourEightyP));
}
#[test]
fn res_720p() {
let req = unwrap_request(make_builder(|b| { b.resolution = Some(RouterResolution::SevenTwentyP); }));
assert_eq!(req.request.resolution, Some(CommonResolutionEnum::SevenTwentyP));
}
#[test]
fn res_1080p_downgrades_to_720p() {
let req = unwrap_request(make_builder(|b| { b.resolution = Some(RouterResolution::TenEightyP); }));
assert_eq!(req.request.resolution, Some(CommonResolutionEnum::SevenTwentyP));
}
#[test]
fn res_1080p_error_out() {
let result = build_artcraft_seedance_2p0_bpu_fast(GenerateVideoRequestBuilder {
model: RouterVideoModel::Seedance2p0BytePlusUltraFast,
provider: RouterProvider::Artcraft,
resolution: Some(RouterResolution::TenEightyP),
request_mismatch_mitigation_strategy: RequestMismatchMitigationStrategy::ErrorOut,
..Default::default()
});
assert!(result.is_err());
}
#[test]
fn none_stays_none() {
let req = unwrap_request(make_builder(|_| {}));
assert!(req.request.resolution.is_none());
}
}
mod media_token_tests {
use super::*;
#[test]
fn start_frame_token_passed_through() {
let token = MediaFileToken::new("mf_start".to_string());
let req = unwrap_request(make_builder(|b| {
b.start_frame = Some(ImageRef::MediaFileToken(token.clone()));
}));
assert_eq!(req.request.start_frame_image_media_token, Some(token));
}
#[test]
fn url_start_frame_rejected() {
let result = build_artcraft_seedance_2p0_bpu_fast(GenerateVideoRequestBuilder {
model: RouterVideoModel::Seedance2p0BytePlusUltraFast,
provider: RouterProvider::Artcraft,
start_frame: Some(ImageRef::Url("https://example.com".to_string())),
..Default::default()
});
assert!(result.is_err());
}
#[test]
fn reference_image_tokens_passed_through() {
let tokens = vec![MediaFileToken::new("mf_a".to_string()), MediaFileToken::new("mf_b".to_string())];
let req = unwrap_request(make_builder(|b| {
b.reference_images = Some(ImageListRef::MediaFileTokens(tokens.clone()));
}));
assert_eq!(req.request.reference_image_media_tokens, Some(tokens));
}
#[test]
fn character_tokens_passed_through() {
let tokens = vec![CharacterToken::new("char_a".to_string()), CharacterToken::new("char_b".to_string())];
let req = unwrap_request(make_builder(|b| {
b.reference_character_tokens = Some(CharacterListRef::CharacterTokens(tokens.clone()));
}));
assert_eq!(req.request.reference_character_tokens, Some(tokens));
}
}
fn make_builder(f: impl FnOnce(&mut GenerateVideoRequestBuilder)) -> GenerateVideoRequestBuilder {
let mut builder = GenerateVideoRequestBuilder {
model: RouterVideoModel::Seedance2p0BytePlusUltraFast,
provider: RouterProvider::Artcraft,
duration_seconds: Some(5),
video_batch_count: Some(1),
..Default::default()
};
f(&mut builder);
builder
}
fn unwrap_request(builder: GenerateVideoRequestBuilder) -> ArtcraftSeedance2p0BytePlusUltraFastRequestState {
let result = build_artcraft_seedance_2p0_bpu_fast(builder).expect("build should succeed");
match result {
VideoGenerationDraftOrRequest::Request(
VideoGenerationRequest::ArtcraftSeedance2p0BytePlusUltraFast(state)
) => state,
_ => panic!("expected ArtcraftSeedance2p0BytePlusUltraFast request"),
}
}
}
@@ -0,0 +1,146 @@
use enums::common::generation::common_resolution::CommonResolution;
use crate::generate::generate_video::video_generation_cost_estimate::VideoGenerationCostEstimate;
use crate::generate::generate_video_v2::providers::artcraft::seedance_2p0_bpu_fast::request::ArtcraftSeedance2p0BytePlusUltraFastRequestState;
/// USD cents per second by resolution:
/// 480p: $0.09/s = 9.0 ¢/s
/// 720p: $0.20/s = 20.0 ¢/s
const CENTS_PER_SECOND_480P: f64 = 9.0;
const CENTS_PER_SECOND_720P: f64 = 20.0;
pub struct ArtcraftSeedance2p0BytePlusUltraFastCostState {
pub resolution: CommonResolution,
pub duration_seconds: u16,
pub batch_count: u16,
}
impl ArtcraftSeedance2p0BytePlusUltraFastCostState {
pub fn from_request(request: &ArtcraftSeedance2p0BytePlusUltraFastRequestState) -> Self {
let resolution = request.request.resolution
.unwrap_or(CommonResolution::SevenTwentyP);
let duration_seconds = request.request.duration_seconds.unwrap_or(5);
let batch_count = request.request.video_batch_count.unwrap_or(1);
Self { resolution, duration_seconds, batch_count }
}
pub fn estimate_cost(&self) -> VideoGenerationCostEstimate {
let cents_per_second = match self.resolution {
CommonResolution::FourEightyP => CENTS_PER_SECOND_480P,
_ => CENTS_PER_SECOND_720P,
};
let usd_cents = (self.duration_seconds as f64 * cents_per_second * self.batch_count as f64).round() as u64;
VideoGenerationCostEstimate {
cost_in_credits: Some(usd_cents),
cost_in_usd_cents: Some(usd_cents),
is_free: false,
is_unlimited: false,
is_rate_limited: false,
has_watermark: false,
failures_are_refunded: None,
}
}
}
#[cfg(test)]
mod tests {
use crate::api::router_resolution::RouterResolution;
use crate::api::router_video_model::RouterVideoModel;
use crate::api::router_provider::RouterProvider;
use crate::generate::generate_video::generate_video_request_builder::GenerateVideoRequestBuilder;
mod pricing_720p {
use super::*;
#[test]
fn batch_1() {
assert_eq!(cost_cents(Some(RouterResolution::SevenTwentyP), 4, 1), 80);
assert_eq!(cost_cents(Some(RouterResolution::SevenTwentyP), 5, 1), 100);
assert_eq!(cost_cents(Some(RouterResolution::SevenTwentyP), 10, 1), 200);
assert_eq!(cost_cents(Some(RouterResolution::SevenTwentyP), 15, 1), 300);
}
#[test]
fn batch_2() {
assert_eq!(cost_cents(Some(RouterResolution::SevenTwentyP), 5, 2), 200);
}
#[test]
fn batch_4() {
assert_eq!(cost_cents(Some(RouterResolution::SevenTwentyP), 5, 4), 400);
}
#[test]
fn none_defaults_to_720p() {
assert_eq!(cost_cents(None, 5, 1), cost_cents(Some(RouterResolution::SevenTwentyP), 5, 1));
}
}
mod pricing_480p {
use super::*;
#[test]
fn batch_1() {
assert_eq!(cost_cents(Some(RouterResolution::FourEightyP), 4, 1), 36);
assert_eq!(cost_cents(Some(RouterResolution::FourEightyP), 5, 1), 45);
assert_eq!(cost_cents(Some(RouterResolution::FourEightyP), 10, 1), 90);
assert_eq!(cost_cents(Some(RouterResolution::FourEightyP), 15, 1), 135);
}
#[test]
fn batch_2() {
assert_eq!(cost_cents(Some(RouterResolution::FourEightyP), 5, 2), 90);
}
#[test]
fn batch_4() {
assert_eq!(cost_cents(Some(RouterResolution::FourEightyP), 5, 4), 180);
}
}
mod relative_pricing_tests {
use super::*;
#[test]
fn cost_480p_cheaper_than_720p() {
assert!(cost_cents(Some(RouterResolution::FourEightyP), 5, 1) < cost_cents(Some(RouterResolution::SevenTwentyP), 5, 1));
}
}
mod credits_tests {
use super::*;
#[test]
fn credits_equal_usd_cents() {
for res in [Some(RouterResolution::FourEightyP), Some(RouterResolution::SevenTwentyP), None] {
for dur in [4, 5, 10, 15] {
for batch in [1, 2, 4] {
let cost = build_cost(res, dur, batch);
assert_eq!(cost.cost_in_credits, cost.cost_in_usd_cents);
}
}
}
}
}
fn build_cost(
resolution: Option<RouterResolution>,
duration_seconds: u16,
video_batch_count: u16,
) -> crate::generate::generate_video::video_generation_cost_estimate::VideoGenerationCostEstimate {
GenerateVideoRequestBuilder {
model: RouterVideoModel::Seedance2p0BytePlusUltraFast,
provider: RouterProvider::Artcraft,
resolution,
duration_seconds: Some(duration_seconds),
video_batch_count: Some(video_batch_count),
..Default::default()
}.build2().expect("build2").estimate_cost().expect("estimate_cost")
}
fn cost_cents(resolution: Option<RouterResolution>, duration_seconds: u16, video_batch_count: u16) -> u64 {
build_cost(resolution, duration_seconds, video_batch_count).cost_in_usd_cents.unwrap()
}
}
@@ -0,0 +1,3 @@
pub mod build;
pub mod cost;
pub mod request;
@@ -0,0 +1,17 @@
use artcraft_api_defs::omni_gen::cost_and_generate_requests::omni_gen_video_cost_and_generate_request::OmniGenVideoCostAndGenerateRequest;
use crate::client::router_artcraft_client::RouterArtcraftClient;
use crate::errors::artcraft_router_error::ArtcraftRouterError;
use crate::generate::generate_video::generate_video_response::GenerateVideoResponse;
use crate::generate::generate_video_v2::providers::artcraft::request_common::send_artcraft_omni_video_request;
#[derive(Clone, Debug)]
pub struct ArtcraftSeedance2p0BytePlusUltraFastRequestState {
pub request: OmniGenVideoCostAndGenerateRequest,
}
impl ArtcraftSeedance2p0BytePlusUltraFastRequestState {
pub async fn send(&self, client: &RouterArtcraftClient) -> Result<GenerateVideoResponse, ArtcraftRouterError> {
send_artcraft_omni_video_request(&self.request, client).await
}
}
@@ -41,6 +41,10 @@ use crate::generate::generate_video_v2::providers::artcraft::seedance_2p0_bp::co
use crate::generate::generate_video_v2::providers::artcraft::seedance_2p0_bp::request::ArtcraftSeedance2p0BytePlusRequestState;
use crate::generate::generate_video_v2::providers::artcraft::seedance_2p0_bp_fast::cost::ArtcraftSeedance2p0BytePlusFastCostState;
use crate::generate::generate_video_v2::providers::artcraft::seedance_2p0_bp_fast::request::ArtcraftSeedance2p0BytePlusFastRequestState;
use crate::generate::generate_video_v2::providers::artcraft::seedance_2p0_bpu::cost::ArtcraftSeedance2p0BytePlusUltraCostState;
use crate::generate::generate_video_v2::providers::artcraft::seedance_2p0_bpu::request::ArtcraftSeedance2p0BytePlusUltraRequestState;
use crate::generate::generate_video_v2::providers::artcraft::seedance_2p0_bpu_fast::cost::ArtcraftSeedance2p0BytePlusUltraFastCostState;
use crate::generate::generate_video_v2::providers::artcraft::seedance_2p0_bpu_fast::request::ArtcraftSeedance2p0BytePlusUltraFastRequestState;
use crate::generate::generate_video_v2::providers::artcraft::sora_2::cost::ArtcraftSora2CostState;
use crate::generate::generate_video_v2::providers::artcraft::sora_2::request::ArtcraftSora2RequestState;
use crate::generate::generate_video_v2::providers::artcraft::sora_2_pro::cost::ArtcraftSora2ProCostState;
@@ -121,6 +125,8 @@ pub enum VideoGenerationRequest {
ArtcraftSeedance2p0UltraFast(ArtcraftSeedance2p0UltraFastRequestState),
ArtcraftSeedance2p0BytePlus(ArtcraftSeedance2p0BytePlusRequestState),
ArtcraftSeedance2p0BytePlusFast(ArtcraftSeedance2p0BytePlusFastRequestState),
ArtcraftSeedance2p0BytePlusUltra(ArtcraftSeedance2p0BytePlusUltraRequestState),
ArtcraftSeedance2p0BytePlusUltraFast(ArtcraftSeedance2p0BytePlusUltraFastRequestState),
ArtcraftSora2(ArtcraftSora2RequestState),
ArtcraftSora2Pro(ArtcraftSora2ProRequestState),
ArtcraftVeo2(ArtcraftVeo2RequestState),
@@ -175,6 +181,8 @@ impl VideoGenerationRequest {
Self::ArtcraftSeedance2p0UltraFast(_) => RouterProvider::Artcraft,
Self::ArtcraftSeedance2p0BytePlus(_) => RouterProvider::Artcraft,
Self::ArtcraftSeedance2p0BytePlusFast(_) => RouterProvider::Artcraft,
Self::ArtcraftSeedance2p0BytePlusUltra(_) => RouterProvider::Artcraft,
Self::ArtcraftSeedance2p0BytePlusUltraFast(_) => RouterProvider::Artcraft,
Self::ArtcraftSora2(_) => RouterProvider::Artcraft,
Self::ArtcraftSora2Pro(_) => RouterProvider::Artcraft,
Self::ArtcraftVeo2(_) => RouterProvider::Artcraft,
@@ -229,6 +237,8 @@ impl VideoGenerationRequest {
VideoGenerationRequest::ArtcraftSeedance2p0UltraFast(request) => Ok(ArtcraftSeedance2p0UltraFastCostState::from_request(request).estimate_cost()),
VideoGenerationRequest::ArtcraftSeedance2p0BytePlus(request) => Ok(ArtcraftSeedance2p0BytePlusCostState::from_request(request).estimate_cost()),
VideoGenerationRequest::ArtcraftSeedance2p0BytePlusFast(request) => Ok(ArtcraftSeedance2p0BytePlusFastCostState::from_request(request).estimate_cost()),
VideoGenerationRequest::ArtcraftSeedance2p0BytePlusUltra(request) => Ok(ArtcraftSeedance2p0BytePlusUltraCostState::from_request(request).estimate_cost()),
VideoGenerationRequest::ArtcraftSeedance2p0BytePlusUltraFast(request) => Ok(ArtcraftSeedance2p0BytePlusUltraFastCostState::from_request(request).estimate_cost()),
VideoGenerationRequest::ArtcraftSora2(request) => Ok(ArtcraftSora2CostState::from_request(request).estimate_cost()),
VideoGenerationRequest::ArtcraftSora2Pro(request) => Ok(ArtcraftSora2ProCostState::from_request(request).estimate_cost()),
VideoGenerationRequest::ArtcraftVeo2(request) => Ok(ArtcraftVeo2CostState::from_request(request).estimate_cost()),
@@ -341,6 +351,14 @@ impl VideoGenerationRequest {
let client_ref = client.get_artcraft_client_ref()?;
request.send(client_ref).await
},
VideoGenerationRequest::ArtcraftSeedance2p0BytePlusUltra(request) => {
let client_ref = client.get_artcraft_client_ref()?;
request.send(client_ref).await
},
VideoGenerationRequest::ArtcraftSeedance2p0BytePlusUltraFast(request) => {
let client_ref = client.get_artcraft_client_ref()?;
request.send(client_ref).await
},
VideoGenerationRequest::ArtcraftSora2(request) => {
let client_ref = client.get_artcraft_client_ref()?;
request.send(client_ref).await
@@ -72,6 +72,8 @@ fn video_model_to_common_model_type(model: RouterVideoModel) -> Option<CommonMod
RouterVideoModel::Seedance2p0BytePlusFast => Some(CommonModelType::Seedance2p0BytePlusFast),
RouterVideoModel::Seedance2p0Ultra => Some(CommonModelType::Seedance2p0Ultra),
RouterVideoModel::Seedance2p0UltraFast => Some(CommonModelType::Seedance2p0UltraFast),
RouterVideoModel::Seedance2p0BytePlusUltra => Some(CommonModelType::Seedance2p0BytePlusUltra),
RouterVideoModel::Seedance2p0BytePlusUltraFast => Some(CommonModelType::Seedance2p0BytePlusUltraFast),
RouterVideoModel::PreviewModel => Some(CommonModelType::PreviewModel),
RouterVideoModel::PreviewModelFast => Some(CommonModelType::PreviewModelFast),
RouterVideoModel::GrokImagineVideo => Some(CommonModelType::GrokImagineVideo),
@@ -183,6 +183,10 @@ pub enum InferenceModelType {
Seedance2p0Ultra,
#[serde(rename = "seedance_2p0_u_fast")]
Seedance2p0UltraFast,
#[serde(rename = "seedance_2p0_bpu")]
Seedance2p0BytePlusUltra,
#[serde(rename = "seedance_2p0_bpu_fast")]
Seedance2p0BytePlusUltraFast,
#[serde(rename = "sora_2")]
Sora2,
#[serde(rename = "sora_2_pro")]
@@ -299,6 +303,8 @@ impl InferenceModelType {
Self::Seedance2p0BytePlusFast => "seedance_2p0_bp_fast",
Self::Seedance2p0Ultra => "seedance_2p0_u",
Self::Seedance2p0UltraFast => "seedance_2p0_u_fast",
Self::Seedance2p0BytePlusUltra => "seedance_2p0_bpu",
Self::Seedance2p0BytePlusUltraFast => "seedance_2p0_bpu_fast",
Self::Sora2 => "sora_2",
Self::Sora2Pro => "sora_2_pro",
Self::Veo2 => "veo_2",
@@ -395,6 +401,8 @@ impl InferenceModelType {
"seedance_2p0_bp_fast" => Ok(Self::Seedance2p0BytePlusFast),
"seedance_2p0_u" => Ok(Self::Seedance2p0Ultra),
"seedance_2p0_u_fast" => Ok(Self::Seedance2p0UltraFast),
"seedance_2p0_bpu" => Ok(Self::Seedance2p0BytePlusUltra),
"seedance_2p0_bpu_fast" => Ok(Self::Seedance2p0BytePlusUltraFast),
"sora_2" => Ok(Self::Sora2),
"sora_2_pro" => Ok(Self::Sora2Pro),
"veo_2" => Ok(Self::Veo2),
@@ -495,6 +503,8 @@ impl InferenceModelType {
Self::Seedance2p0BytePlusFast,
Self::Seedance2p0Ultra,
Self::Seedance2p0UltraFast,
Self::Seedance2p0BytePlusUltra,
Self::Seedance2p0BytePlusUltraFast,
Self::Sora2,
Self::Sora2Pro,
Self::Veo2,
@@ -578,6 +588,8 @@ impl InferenceModelType {
CommonModelType::Seedance2p0BytePlusFast => Self::Seedance2p0BytePlusFast,
CommonModelType::Seedance2p0Ultra => Self::Seedance2p0Ultra,
CommonModelType::Seedance2p0UltraFast => Self::Seedance2p0UltraFast,
CommonModelType::Seedance2p0BytePlusUltra => Self::Seedance2p0BytePlusUltra,
CommonModelType::Seedance2p0BytePlusUltraFast => Self::Seedance2p0BytePlusUltraFast,
CommonModelType::Sora2 => Self::Sora2,
CommonModelType::Sora2Pro => Self::Sora2Pro,
CommonModelType::Veo2 => Self::Veo2,
@@ -684,6 +696,8 @@ mod tests {
assert_serialization(InferenceModelType::Seedance2p0BytePlusFast, "seedance_2p0_bp_fast");
assert_serialization(InferenceModelType::Seedance2p0Ultra, "seedance_2p0_u");
assert_serialization(InferenceModelType::Seedance2p0UltraFast, "seedance_2p0_u_fast");
assert_serialization(InferenceModelType::Seedance2p0BytePlusUltra, "seedance_2p0_bpu");
assert_serialization(InferenceModelType::Seedance2p0BytePlusUltraFast, "seedance_2p0_bpu_fast");
assert_serialization(InferenceModelType::Sora2, "sora_2");
assert_serialization(InferenceModelType::Sora2Pro, "sora_2_pro");
assert_serialization(InferenceModelType::Veo2, "veo_2");
@@ -779,6 +793,8 @@ mod tests {
assert_eq!(InferenceModelType::Seedance2p0BytePlusFast.to_str(), "seedance_2p0_bp_fast");
assert_eq!(InferenceModelType::Seedance2p0Ultra.to_str(), "seedance_2p0_u");
assert_eq!(InferenceModelType::Seedance2p0UltraFast.to_str(), "seedance_2p0_u_fast");
assert_eq!(InferenceModelType::Seedance2p0BytePlusUltra.to_str(), "seedance_2p0_bpu");
assert_eq!(InferenceModelType::Seedance2p0BytePlusUltraFast.to_str(), "seedance_2p0_bpu_fast");
assert_eq!(InferenceModelType::Sora2.to_str(), "sora_2");
assert_eq!(InferenceModelType::Sora2Pro.to_str(), "sora_2_pro");
assert_eq!(InferenceModelType::Veo2.to_str(), "veo_2");
@@ -874,6 +890,8 @@ mod tests {
assert_eq!(InferenceModelType::from_str("seedance_2p0_bp_fast").unwrap(), InferenceModelType::Seedance2p0BytePlusFast);
assert_eq!(InferenceModelType::from_str("seedance_2p0_u").unwrap(), InferenceModelType::Seedance2p0Ultra);
assert_eq!(InferenceModelType::from_str("seedance_2p0_u_fast").unwrap(), InferenceModelType::Seedance2p0UltraFast);
assert_eq!(InferenceModelType::from_str("seedance_2p0_bpu").unwrap(), InferenceModelType::Seedance2p0BytePlusUltra);
assert_eq!(InferenceModelType::from_str("seedance_2p0_bpu_fast").unwrap(), InferenceModelType::Seedance2p0BytePlusUltraFast);
assert_eq!(InferenceModelType::from_str("sora_2").unwrap(), InferenceModelType::Sora2);
assert_eq!(InferenceModelType::from_str("sora_2_pro").unwrap(), InferenceModelType::Sora2Pro);
assert_eq!(InferenceModelType::from_str("veo_2").unwrap(), InferenceModelType::Veo2);
@@ -151,6 +151,10 @@ pub enum CommonModelType {
Seedance2p0Ultra,
#[serde(rename = "seedance_2p0_u_fast")]
Seedance2p0UltraFast,
#[serde(rename = "seedance_2p0_bpu")]
Seedance2p0BytePlusUltra,
#[serde(rename = "seedance_2p0_bpu_fast")]
Seedance2p0BytePlusUltraFast,
#[serde(rename = "sora_2")]
Sora2,
#[serde(rename = "sora_2_pro")]
@@ -251,6 +255,8 @@ impl CommonModelType {
Self::Seedance2p0BytePlusFast => "seedance_2p0_bp_fast",
Self::Seedance2p0Ultra => "seedance_2p0_u",
Self::Seedance2p0UltraFast => "seedance_2p0_u_fast",
Self::Seedance2p0BytePlusUltra => "seedance_2p0_bpu",
Self::Seedance2p0BytePlusUltraFast => "seedance_2p0_bpu_fast",
Self::Sora2 => "sora_2",
Self::Sora2Pro => "sora_2_pro",
Self::Veo2 => "veo_2",
@@ -330,6 +336,8 @@ impl CommonModelType {
"seedance_2p0_bp_fast" => Ok(Self::Seedance2p0BytePlusFast),
"seedance_2p0_u" => Ok(Self::Seedance2p0Ultra),
"seedance_2p0_u_fast" => Ok(Self::Seedance2p0UltraFast),
"seedance_2p0_bpu" => Ok(Self::Seedance2p0BytePlusUltra),
"seedance_2p0_bpu_fast" => Ok(Self::Seedance2p0BytePlusUltraFast),
"sora_2" => Ok(Self::Sora2),
"sora_2_pro" => Ok(Self::Sora2Pro),
"veo_2" => Ok(Self::Veo2),
@@ -413,6 +421,8 @@ impl CommonModelType {
Self::Seedance2p0BytePlusFast,
Self::Seedance2p0Ultra,
Self::Seedance2p0UltraFast,
Self::Seedance2p0BytePlusUltra,
Self::Seedance2p0BytePlusUltraFast,
Self::Sora2,
Self::Sora2Pro,
Self::Veo2,
@@ -493,6 +503,8 @@ impl CommonModelType {
Self::Seedance2p0BytePlusFast => CommonModelClass::Video,
Self::Seedance2p0Ultra => CommonModelClass::Video,
Self::Seedance2p0UltraFast => CommonModelClass::Video,
Self::Seedance2p0BytePlusUltra => CommonModelClass::Video,
Self::Seedance2p0BytePlusUltraFast => CommonModelClass::Video,
Self::Sora2 => CommonModelClass::Video,
Self::Sora2Pro => CommonModelClass::Video,
Self::Veo2 => CommonModelClass::Video,
@@ -582,6 +594,8 @@ mod tests {
assert_serialization(CommonModelType::Seedance2p0BytePlusFast, "seedance_2p0_bp_fast");
assert_serialization(CommonModelType::Seedance2p0Ultra, "seedance_2p0_u");
assert_serialization(CommonModelType::Seedance2p0UltraFast, "seedance_2p0_u_fast");
assert_serialization(CommonModelType::Seedance2p0BytePlusUltra, "seedance_2p0_bpu");
assert_serialization(CommonModelType::Seedance2p0BytePlusUltraFast, "seedance_2p0_bpu_fast");
assert_serialization(CommonModelType::Sora2, "sora_2");
assert_serialization(CommonModelType::Sora2Pro, "sora_2_pro");
assert_serialization(CommonModelType::Veo2, "veo_2");
@@ -658,6 +672,8 @@ mod tests {
assert_eq!(CommonModelType::Seedance2p0BytePlusFast.to_str(), "seedance_2p0_bp_fast");
assert_eq!(CommonModelType::Seedance2p0Ultra.to_str(), "seedance_2p0_u");
assert_eq!(CommonModelType::Seedance2p0UltraFast.to_str(), "seedance_2p0_u_fast");
assert_eq!(CommonModelType::Seedance2p0BytePlusUltra.to_str(), "seedance_2p0_bpu");
assert_eq!(CommonModelType::Seedance2p0BytePlusUltraFast.to_str(), "seedance_2p0_bpu_fast");
assert_eq!(CommonModelType::Sora2.to_str(), "sora_2");
assert_eq!(CommonModelType::Sora2Pro.to_str(), "sora_2_pro");
assert_eq!(CommonModelType::Veo2.to_str(), "veo_2");
@@ -734,6 +750,8 @@ mod tests {
assert_eq!(CommonModelType::from_str("seedance_2p0_bp_fast").unwrap(), CommonModelType::Seedance2p0BytePlusFast);
assert_eq!(CommonModelType::from_str("seedance_2p0_u").unwrap(), CommonModelType::Seedance2p0Ultra);
assert_eq!(CommonModelType::from_str("seedance_2p0_u_fast").unwrap(), CommonModelType::Seedance2p0UltraFast);
assert_eq!(CommonModelType::from_str("seedance_2p0_bpu").unwrap(), CommonModelType::Seedance2p0BytePlusUltra);
assert_eq!(CommonModelType::from_str("seedance_2p0_bpu_fast").unwrap(), CommonModelType::Seedance2p0BytePlusUltraFast);
assert_eq!(CommonModelType::from_str("sora_2").unwrap(), CommonModelType::Sora2);
assert_eq!(CommonModelType::from_str("sora_2_pro").unwrap(), CommonModelType::Sora2Pro);
assert_eq!(CommonModelType::from_str("veo_2").unwrap(), CommonModelType::Veo2);
@@ -757,7 +775,7 @@ mod tests {
#[test]
fn all_variants() {
let mut variants = CommonModelType::all_variants();
assert_eq!(variants.len(), 67);
assert_eq!(variants.len(), 69);
// Image models
assert_eq!(variants.pop_first(), Some(CommonModelType::Flux1Dev));
assert_eq!(variants.pop_first(), Some(CommonModelType::Flux1Schnell));
@@ -812,6 +830,8 @@ mod tests {
assert_eq!(variants.pop_first(), Some(CommonModelType::Seedance2p0BytePlusFast));
assert_eq!(variants.pop_first(), Some(CommonModelType::Seedance2p0Ultra));
assert_eq!(variants.pop_first(), Some(CommonModelType::Seedance2p0UltraFast));
assert_eq!(variants.pop_first(), Some(CommonModelType::Seedance2p0BytePlusUltra));
assert_eq!(variants.pop_first(), Some(CommonModelType::Seedance2p0BytePlusUltraFast));
assert_eq!(variants.pop_first(), Some(CommonModelType::Sora2));
assert_eq!(variants.pop_first(), Some(CommonModelType::Sora2Pro));
assert_eq!(variants.pop_first(), Some(CommonModelType::Veo2));
@@ -62,6 +62,12 @@ pub enum CommonVideoModel {
#[serde(rename = "seedance_2p0_u_fast")]
Seedance2p0UltraFast,
#[serde(rename = "seedance_2p0_bpu")]
Seedance2p0BytePlusUltra,
#[serde(rename = "seedance_2p0_bpu_fast")]
Seedance2p0BytePlusUltraFast,
#[serde(rename = "sora_2")]
Sora2,
@@ -116,6 +122,8 @@ impl CommonVideoModel {
Self::Seedance2p0BytePlusFast => CommonModelType::Seedance2p0BytePlusFast,
Self::Seedance2p0Ultra => CommonModelType::Seedance2p0Ultra,
Self::Seedance2p0UltraFast => CommonModelType::Seedance2p0UltraFast,
Self::Seedance2p0BytePlusUltra => CommonModelType::Seedance2p0BytePlusUltra,
Self::Seedance2p0BytePlusUltraFast => CommonModelType::Seedance2p0BytePlusUltraFast,
Self::Sora2 => CommonModelType::Sora2,
Self::Sora2Pro => CommonModelType::Sora2Pro,
Self::Veo2 => CommonModelType::Veo2,
@@ -155,6 +163,8 @@ mod tests {
assert_serialization(CommonVideoModel::Seedance2p0BytePlusFast, "seedance_2p0_bp_fast");
assert_serialization(CommonVideoModel::Seedance2p0Ultra, "seedance_2p0_u");
assert_serialization(CommonVideoModel::Seedance2p0UltraFast, "seedance_2p0_u_fast");
assert_serialization(CommonVideoModel::Seedance2p0BytePlusUltra, "seedance_2p0_bpu");
assert_serialization(CommonVideoModel::Seedance2p0BytePlusUltraFast, "seedance_2p0_bpu_fast");
assert_serialization(CommonVideoModel::Sora2, "sora_2");
assert_serialization(CommonVideoModel::Sora2Pro, "sora_2_pro");
assert_serialization(CommonVideoModel::Veo2, "veo_2");
@@ -187,6 +197,8 @@ mod tests {
("seedance_2p0_bp_fast", CommonVideoModel::Seedance2p0BytePlusFast),
("seedance_2p0_u", CommonVideoModel::Seedance2p0Ultra),
("seedance_2p0_u_fast", CommonVideoModel::Seedance2p0UltraFast),
("seedance_2p0_bpu", CommonVideoModel::Seedance2p0BytePlusUltra),
("seedance_2p0_bpu_fast", CommonVideoModel::Seedance2p0BytePlusUltraFast),
("sora_2", CommonVideoModel::Sora2),
("sora_2_pro", CommonVideoModel::Sora2Pro),
("veo_2", CommonVideoModel::Veo2),
@@ -226,6 +238,8 @@ mod tests {
CommonVideoModel::Seedance2p0BytePlusFast,
CommonVideoModel::Seedance2p0Ultra,
CommonVideoModel::Seedance2p0UltraFast,
CommonVideoModel::Seedance2p0BytePlusUltra,
CommonVideoModel::Seedance2p0BytePlusUltraFast,
CommonVideoModel::Sora2,
CommonVideoModel::Sora2Pro,
CommonVideoModel::Veo2,
@@ -264,6 +278,8 @@ mod tests {
(CommonVideoModel::Seedance2p0BytePlusFast, CommonModelType::Seedance2p0BytePlusFast),
(CommonVideoModel::Seedance2p0Ultra, CommonModelType::Seedance2p0Ultra),
(CommonVideoModel::Seedance2p0UltraFast, CommonModelType::Seedance2p0UltraFast),
(CommonVideoModel::Seedance2p0BytePlusUltra, CommonModelType::Seedance2p0BytePlusUltra),
(CommonVideoModel::Seedance2p0BytePlusUltraFast, CommonModelType::Seedance2p0BytePlusUltraFast),
(CommonVideoModel::Sora2, CommonModelType::Sora2),
(CommonVideoModel::Sora2Pro, CommonModelType::Sora2Pro),
(CommonVideoModel::Veo2, CommonModelType::Veo2),
@@ -146,6 +146,8 @@ fn map_video_model(model: CommonVideoModel) -> Result<RouterVideoModel, HandlerE
CommonVideoModel::Seedance2p0UltraFast => RouterVideoModel::Seedance2p0UltraFast,
CommonVideoModel::Seedance2p0BytePlus=> RouterVideoModel::Seedance2p0BytePlus,
CommonVideoModel::Seedance2p0BytePlusFast=> RouterVideoModel::Seedance2p0BytePlusFast,
CommonVideoModel::Seedance2p0BytePlusUltra => RouterVideoModel::Seedance2p0BytePlusUltra,
CommonVideoModel::Seedance2p0BytePlusUltraFast => RouterVideoModel::Seedance2p0BytePlusUltraFast,
CommonVideoModel::HappyHorse1p0 => RouterVideoModel::HappyHorse1p0,
CommonVideoModel::Sora2 => RouterVideoModel::Sora2,
CommonVideoModel::Sora2Pro => RouterVideoModel::Sora2Pro,