mirror of
https://github.com/storytold/artcraft.git
synced 2026-10-25 03:09:30 -04:00
Compare commits
3
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
41ab2035da | ||
|
|
78dbc29b79 | ||
|
|
021a6077c8 |
@@ -41,6 +41,12 @@ pub enum CommonVideoModel {
|
||||
#[serde(rename = "seedance_2p0_fast")]
|
||||
Seedance2p0Fast,
|
||||
|
||||
#[serde(rename = "seedance_2p0_p")]
|
||||
Seedance2p0Plus,
|
||||
|
||||
#[serde(rename = "seedance_2p0_p_fast")]
|
||||
Seedance2p0PlusFast,
|
||||
|
||||
#[serde(rename = "seedance_2p0_g")]
|
||||
Seedance2p0Global,
|
||||
|
||||
|
||||
+12
-1
@@ -54,6 +54,8 @@ use crate::generate::generate_video_v2::providers::artcraft::seedance_2p0::build
|
||||
use crate::generate::generate_video_v2::providers::artcraft::seedance_2p0_fast::build::build_artcraft_seedance_2p0_fast;
|
||||
use crate::generate::generate_video_v2::providers::artcraft::seedance_2p0_g::build::build_artcraft_seedance_2p0_g;
|
||||
use crate::generate::generate_video_v2::providers::artcraft::seedance_2p0_fast_g::build::build_artcraft_seedance_2p0_fast_g;
|
||||
use crate::generate::generate_video_v2::providers::artcraft::seedance_2p0_p::build::build_artcraft_seedance_2p0_p;
|
||||
use crate::generate::generate_video_v2::providers::artcraft::seedance_2p0_p_fast::build::build_artcraft_seedance_2p0_p_fast;
|
||||
use crate::generate::generate_video_v2::providers::kinovi::happy_horse_1p0::build::build_kinovi_happy_horse_1p0;
|
||||
use crate::generate::generate_video_v2::providers::kinovi::seedance_2p0::build::build_kinovi_seedance_2p0;
|
||||
use crate::generate::generate_video_v2::providers::gmicloud::seedance_2p0_g::build::build_gmicloud_seedance_2p0_g;
|
||||
@@ -150,16 +152,20 @@ impl GenerateVideoRequestBuilder {
|
||||
|
||||
pub fn use_new_builder(&self) -> bool {
|
||||
match (self.provider, self.model) {
|
||||
// Artcraft
|
||||
(Provider::Artcraft, CommonVideoModel::HappyHorse1p0) => true,
|
||||
(Provider::Artcraft, CommonVideoModel::Seedance2p0) => true,
|
||||
(Provider::Artcraft, CommonVideoModel::Seedance2p0Fast) => true,
|
||||
(Provider::Artcraft, CommonVideoModel::Seedance2p0Global) => true,
|
||||
(Provider::Artcraft, CommonVideoModel::Seedance2p0FastGlobal) => true,
|
||||
(Provider::Artcraft, CommonVideoModel::Seedance2p0Plus) => true,
|
||||
(Provider::Artcraft, CommonVideoModel::Seedance2p0PlusFast) => true,
|
||||
(Provider::Artcraft, CommonVideoModel::PreviewModel) => true,
|
||||
(Provider::Artcraft, CommonVideoModel::PreviewModelFast) => true,
|
||||
// GmiCloud
|
||||
(Provider::GmiCloud, CommonVideoModel::Seedance2p0Global) => true,
|
||||
|
||||
(Provider::GmiCloud, CommonVideoModel::Seedance2p0FastGlobal) => true,
|
||||
// Kinovi
|
||||
(Provider::Seedance2Pro, CommonVideoModel::HappyHorse1p0) => true,
|
||||
(Provider::Seedance2Pro, CommonVideoModel::Seedance2p0) => true,
|
||||
(Provider::Seedance2Pro, CommonVideoModel::Seedance2p0Fast) => true,
|
||||
@@ -170,15 +176,20 @@ impl GenerateVideoRequestBuilder {
|
||||
// New builder (transitional state)
|
||||
pub fn build2(self) -> Result<VideoGenerationDraftOrRequest, ArtcraftRouterError> {
|
||||
match (self.provider, self.model) {
|
||||
// Artcraft
|
||||
(Provider::Artcraft, CommonVideoModel::HappyHorse1p0) => build_artcraft_happy_horse_1p0(self),
|
||||
(Provider::Artcraft, CommonVideoModel::Seedance2p0) => build_artcraft_seedance_2p0(self),
|
||||
(Provider::Artcraft, CommonVideoModel::Seedance2p0Fast) => build_artcraft_seedance_2p0_fast(self),
|
||||
(Provider::Artcraft, CommonVideoModel::Seedance2p0Global) => build_artcraft_seedance_2p0_g(self),
|
||||
(Provider::Artcraft, CommonVideoModel::Seedance2p0FastGlobal) => build_artcraft_seedance_2p0_fast_g(self),
|
||||
(Provider::Artcraft, CommonVideoModel::Seedance2p0Plus) => build_artcraft_seedance_2p0_p(self),
|
||||
(Provider::Artcraft, CommonVideoModel::Seedance2p0PlusFast) => build_artcraft_seedance_2p0_p_fast(self),
|
||||
(Provider::Artcraft, CommonVideoModel::PreviewModel) => build_artcraft_preview_model(self),
|
||||
(Provider::Artcraft, CommonVideoModel::PreviewModelFast) => build_artcraft_preview_model_fast(self),
|
||||
// GmiCloud
|
||||
(Provider::GmiCloud, CommonVideoModel::Seedance2p0Global) => build_gmicloud_seedance_2p0_g(self),
|
||||
(Provider::GmiCloud, CommonVideoModel::Seedance2p0FastGlobal) => build_gmicloud_seedance_2p0_fast_g(self),
|
||||
// Kinovi
|
||||
(Provider::Seedance2Pro, CommonVideoModel::HappyHorse1p0) => build_kinovi_happy_horse_1p0(self),
|
||||
(Provider::Seedance2Pro, CommonVideoModel::Seedance2p0) => build_kinovi_seedance_2p0(self),
|
||||
(Provider::Seedance2Pro, CommonVideoModel::Seedance2p0Fast) => build_kinovi_seedance_2p0_fast(self),
|
||||
|
||||
+2
@@ -6,3 +6,5 @@ pub mod seedance_2p0;
|
||||
pub mod seedance_2p0_fast;
|
||||
pub mod seedance_2p0_fast_g;
|
||||
pub mod seedance_2p0_g;
|
||||
pub mod seedance_2p0_p;
|
||||
pub mod seedance_2p0_p_fast;
|
||||
|
||||
+327
@@ -0,0 +1,327 @@
|
||||
use artcraft_api_defs::omni_gen::cost_and_generate_requests::omni_gen_video_cost_and_generate_request::OmniGenVideoCostAndGenerateRequest;
|
||||
use enums::common::generation::common_aspect_ratio::CommonAspectRatio as CommonAspectRatioEnum;
|
||||
use enums::common::generation::common_resolution::CommonResolution as CommonResolutionEnum;
|
||||
use enums::common::generation::common_video_model::CommonVideoModel as CommonVideoModelEnum;
|
||||
|
||||
use crate::api::common_aspect_ratio::CommonAspectRatio;
|
||||
use crate::api::common_resolution::CommonResolution;
|
||||
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_builder::GenerateVideoRequestBuilder;
|
||||
use crate::generate::generate_video_v2::providers::artcraft::resolve::{
|
||||
resolve_audio_list_ref, resolve_character_list_ref, resolve_image_list_ref,
|
||||
resolve_image_ref, resolve_video_list_ref,
|
||||
};
|
||||
use crate::generate::generate_video_v2::providers::artcraft::seedance_2p0_p::request::ArtcraftSeedance2p0PlusRequestState;
|
||||
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_p(mut builder: GenerateVideoRequestBuilder) -> Result<VideoGenerationDraftOrRequest, ArtcraftRouterError> {
|
||||
let strategy = builder.request_mismatch_mitigation_strategy;
|
||||
|
||||
let aspect_ratio = plan_aspect_ratio(builder.aspect_ratio.take(), strategy)?;
|
||||
let resolution = plan_output_resolution(builder.resolution.take(), strategy)?;
|
||||
let batch_count = plan_batch_count(builder.video_batch_count.take(), strategy)?;
|
||||
let duration_seconds = plan_duration(builder.duration_seconds.take(), strategy)?;
|
||||
let prompt = builder.prompt.take();
|
||||
|
||||
let start_frame = resolve_image_ref(builder.start_frame.take())?;
|
||||
let end_frame = resolve_image_ref(builder.end_frame.take())?;
|
||||
let reference_images = resolve_image_list_ref(builder.reference_images.take())?;
|
||||
let reference_videos = resolve_video_list_ref(builder.reference_videos.take())?;
|
||||
let reference_audio = resolve_audio_list_ref(builder.reference_audio.take())?;
|
||||
let reference_characters = resolve_character_list_ref(builder.reference_character_tokens.take());
|
||||
let idempotency_token = builder.get_or_generate_idempotency_token();
|
||||
|
||||
let request = OmniGenVideoCostAndGenerateRequest {
|
||||
model: Some(CommonVideoModelEnum::Seedance2p0Plus),
|
||||
idempotency_token: Some(idempotency_token),
|
||||
prompt,
|
||||
start_frame_image_media_token: start_frame,
|
||||
end_frame_image_media_token: end_frame,
|
||||
reference_image_media_tokens: reference_images,
|
||||
reference_video_media_tokens: reference_videos,
|
||||
reference_audio_media_tokens: reference_audio,
|
||||
reference_character_tokens: reference_characters,
|
||||
resolution,
|
||||
aspect_ratio,
|
||||
duration_seconds: duration_seconds.map(|d| d as u16),
|
||||
video_batch_count: Some(batch_count),
|
||||
negative_prompt: None,
|
||||
generate_audio: None,
|
||||
quality: None,
|
||||
};
|
||||
|
||||
let state = ArtcraftSeedance2p0PlusRequestState { request };
|
||||
Ok(VideoGenerationDraftOrRequest::Request(VideoGenerationRequest::ArtcraftSeedance2p0Plus(state)))
|
||||
}
|
||||
|
||||
// -- Plan helpers --
|
||||
|
||||
fn plan_aspect_ratio(
|
||||
aspect_ratio: Option<CommonAspectRatio>,
|
||||
strategy: RequestMismatchMitigationStrategy,
|
||||
) -> Result<Option<CommonAspectRatioEnum>, ArtcraftRouterError> {
|
||||
match aspect_ratio {
|
||||
None
|
||||
| Some(CommonAspectRatio::Auto)
|
||||
| Some(CommonAspectRatio::Auto2k)
|
||||
| Some(CommonAspectRatio::Auto4k) => Ok(None),
|
||||
|
||||
Some(CommonAspectRatio::WideSixteenByNine) | Some(CommonAspectRatio::Wide) => {
|
||||
Ok(Some(CommonAspectRatioEnum::WideSixteenByNine))
|
||||
}
|
||||
Some(CommonAspectRatio::TallNineBySixteen) | Some(CommonAspectRatio::Tall) => {
|
||||
Ok(Some(CommonAspectRatioEnum::TallNineBySixteen))
|
||||
}
|
||||
Some(CommonAspectRatio::Square) | Some(CommonAspectRatio::SquareHd) => {
|
||||
Ok(Some(CommonAspectRatioEnum::Square))
|
||||
}
|
||||
Some(CommonAspectRatio::WideFourByThree) => Ok(Some(CommonAspectRatioEnum::WideFourByThree)),
|
||||
Some(CommonAspectRatio::TallThreeByFour) => Ok(Some(CommonAspectRatioEnum::TallThreeByFour)),
|
||||
Some(CommonAspectRatio::WideTwentyOneByNine) => Ok(Some(CommonAspectRatioEnum::WideTwentyOneByNine)),
|
||||
|
||||
Some(unsupported) => match strategy {
|
||||
RequestMismatchMitigationStrategy::ErrorOut => {
|
||||
Err(ArtcraftRouterError::Client(ClientError::ModelDoesNotSupportOption {
|
||||
field: "aspect_ratio",
|
||||
value: format!("{:?}", unsupported),
|
||||
}))
|
||||
}
|
||||
RequestMismatchMitigationStrategy::PayMoreUpgrade
|
||||
| RequestMismatchMitigationStrategy::PayLessDowngrade => {
|
||||
Ok(Some(nearest_aspect_ratio(unsupported)))
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn nearest_aspect_ratio(aspect_ratio: CommonAspectRatio) -> CommonAspectRatioEnum {
|
||||
match aspect_ratio {
|
||||
CommonAspectRatio::WideFiveByFour => CommonAspectRatioEnum::WideFourByThree,
|
||||
CommonAspectRatio::WideThreeByTwo => CommonAspectRatioEnum::WideFourByThree,
|
||||
CommonAspectRatio::TallFourByFive => CommonAspectRatioEnum::TallThreeByFour,
|
||||
CommonAspectRatio::TallTwoByThree => CommonAspectRatioEnum::TallThreeByFour,
|
||||
CommonAspectRatio::TallNineByTwentyOne => CommonAspectRatioEnum::TallNineBySixteen,
|
||||
_ => CommonAspectRatioEnum::Square,
|
||||
}
|
||||
}
|
||||
|
||||
// Supports output resolutions: 480p, 720p, 1080p.
|
||||
fn plan_output_resolution(
|
||||
resolution: Option<CommonResolution>,
|
||||
strategy: RequestMismatchMitigationStrategy,
|
||||
) -> Result<Option<CommonResolutionEnum>, ArtcraftRouterError> {
|
||||
match resolution {
|
||||
None => Ok(None),
|
||||
Some(CommonResolution::FourEightyP) => Ok(Some(CommonResolutionEnum::FourEightyP)),
|
||||
Some(CommonResolution::SevenTwentyP) => Ok(Some(CommonResolutionEnum::SevenTwentyP)),
|
||||
Some(CommonResolution::TenEightyP) => Ok(Some(CommonResolutionEnum::TenEightyP)),
|
||||
Some(unsupported) => match strategy {
|
||||
RequestMismatchMitigationStrategy::ErrorOut => {
|
||||
Err(ArtcraftRouterError::Client(ClientError::ModelDoesNotSupportOption {
|
||||
field: "resolution",
|
||||
value: format!("{:?}", unsupported),
|
||||
}))
|
||||
}
|
||||
RequestMismatchMitigationStrategy::PayMoreUpgrade
|
||||
| RequestMismatchMitigationStrategy::PayLessDowngrade => {
|
||||
Ok(Some(match unsupported {
|
||||
CommonResolution::HalfK => CommonResolutionEnum::FourEightyP,
|
||||
_ => CommonResolutionEnum::TenEightyP,
|
||||
}))
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn plan_batch_count(
|
||||
video_batch_count: Option<u16>,
|
||||
strategy: RequestMismatchMitigationStrategy,
|
||||
) -> Result<u16, ArtcraftRouterError> {
|
||||
let count = video_batch_count.unwrap_or(1);
|
||||
match count {
|
||||
0 => Err(ArtcraftRouterError::Client(ClientError::UserRequestedZeroGenerations)),
|
||||
1 | 2 | 4 => Ok(count),
|
||||
_ => match strategy {
|
||||
RequestMismatchMitigationStrategy::ErrorOut => {
|
||||
Err(ArtcraftRouterError::Client(ClientError::ModelDoesNotSupportOption {
|
||||
field: "video_batch_count",
|
||||
value: format!("{}", count),
|
||||
}))
|
||||
}
|
||||
RequestMismatchMitigationStrategy::PayMoreUpgrade => Ok(4),
|
||||
RequestMismatchMitigationStrategy::PayLessDowngrade => {
|
||||
Ok(if count < 4 { 2 } else { 4 })
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn plan_duration(
|
||||
duration_seconds: Option<u16>,
|
||||
strategy: RequestMismatchMitigationStrategy,
|
||||
) -> Result<Option<u8>, ArtcraftRouterError> {
|
||||
const MIN: u16 = 4;
|
||||
const MAX: u16 = 15;
|
||||
match duration_seconds {
|
||||
None => Ok(None),
|
||||
Some(d) if d >= MIN && d <= MAX => Ok(Some(d as u8)),
|
||||
Some(d) => match strategy {
|
||||
RequestMismatchMitigationStrategy::ErrorOut => {
|
||||
Err(ArtcraftRouterError::Client(ClientError::ModelDoesNotSupportOption {
|
||||
field: "duration_seconds",
|
||||
value: format!("{}", d),
|
||||
}))
|
||||
}
|
||||
_ => Ok(Some(d.clamp(MIN, MAX) as u8)),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
#[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::common_resolution::CommonResolution;
|
||||
use crate::api::image_list_ref::ImageListRef;
|
||||
use crate::api::image_ref::ImageRef;
|
||||
use crate::api::provider::Provider;
|
||||
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_plus() {
|
||||
let req = unwrap_request(make_builder(|_| {}));
|
||||
assert!(matches!(req.request.model, Some(CommonVideoModelEnum::Seedance2p0Plus)));
|
||||
}
|
||||
|
||||
#[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(CommonResolution::FourEightyP); }));
|
||||
assert_eq!(req.request.resolution, Some(CommonResolutionEnum::FourEightyP));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn res_720p() {
|
||||
let req = unwrap_request(make_builder(|b| { b.resolution = Some(CommonResolution::SevenTwentyP); }));
|
||||
assert_eq!(req.request.resolution, Some(CommonResolutionEnum::SevenTwentyP));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn res_1080p() {
|
||||
let req = unwrap_request(make_builder(|b| { b.resolution = Some(CommonResolution::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_p(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: Provider::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) -> ArtcraftSeedance2p0PlusRequestState {
|
||||
let result = build_artcraft_seedance_2p0_p(builder).expect("build should succeed");
|
||||
match result {
|
||||
VideoGenerationDraftOrRequest::Request(
|
||||
VideoGenerationRequest::ArtcraftSeedance2p0Plus(state)
|
||||
) => state,
|
||||
_ => panic!("expected ArtcraftSeedance2p0Plus request"),
|
||||
}
|
||||
}
|
||||
}
|
||||
+175
@@ -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_p::request::ArtcraftSeedance2p0PlusRequestState;
|
||||
|
||||
/// 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 ArtcraftSeedance2p0PlusCostState {
|
||||
pub resolution: CommonResolution,
|
||||
pub duration_seconds: u16,
|
||||
pub batch_count: u16,
|
||||
}
|
||||
|
||||
impl ArtcraftSeedance2p0PlusCostState {
|
||||
pub fn from_request(request: &ArtcraftSeedance2p0PlusRequestState) -> 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::common_resolution::CommonResolution;
|
||||
use crate::api::common_video_model::CommonVideoModel;
|
||||
use crate::api::provider::Provider;
|
||||
use crate::generate::generate_video::generate_video_request_builder::GenerateVideoRequestBuilder;
|
||||
|
||||
mod pricing_720p {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn batch_1() {
|
||||
assert_eq!(cost_cents(Some(CommonResolution::SevenTwentyP), 4, 1), 100);
|
||||
assert_eq!(cost_cents(Some(CommonResolution::SevenTwentyP), 5, 1), 125);
|
||||
assert_eq!(cost_cents(Some(CommonResolution::SevenTwentyP), 10, 1), 250);
|
||||
assert_eq!(cost_cents(Some(CommonResolution::SevenTwentyP), 15, 1), 375);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn batch_2() {
|
||||
assert_eq!(cost_cents(Some(CommonResolution::SevenTwentyP), 5, 2), 250);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn batch_4() {
|
||||
assert_eq!(cost_cents(Some(CommonResolution::SevenTwentyP), 5, 4), 500);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn none_defaults_to_720p() {
|
||||
assert_eq!(cost_cents(None, 5, 1), cost_cents(Some(CommonResolution::SevenTwentyP), 5, 1));
|
||||
}
|
||||
}
|
||||
|
||||
mod pricing_480p {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn batch_1() {
|
||||
assert_eq!(cost_cents(Some(CommonResolution::FourEightyP), 4, 1), 40);
|
||||
assert_eq!(cost_cents(Some(CommonResolution::FourEightyP), 5, 1), 50);
|
||||
assert_eq!(cost_cents(Some(CommonResolution::FourEightyP), 10, 1), 100);
|
||||
assert_eq!(cost_cents(Some(CommonResolution::FourEightyP), 15, 1), 150);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn batch_2() {
|
||||
assert_eq!(cost_cents(Some(CommonResolution::FourEightyP), 5, 2), 100);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn batch_4() {
|
||||
assert_eq!(cost_cents(Some(CommonResolution::FourEightyP), 5, 4), 200);
|
||||
}
|
||||
}
|
||||
|
||||
mod pricing_1080p {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn batch_1() {
|
||||
assert_eq!(cost_cents(Some(CommonResolution::TenEightyP), 4, 1), 200);
|
||||
assert_eq!(cost_cents(Some(CommonResolution::TenEightyP), 5, 1), 250);
|
||||
assert_eq!(cost_cents(Some(CommonResolution::TenEightyP), 10, 1), 500);
|
||||
assert_eq!(cost_cents(Some(CommonResolution::TenEightyP), 15, 1), 750);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn batch_2() {
|
||||
assert_eq!(cost_cents(Some(CommonResolution::TenEightyP), 5, 2), 500);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn batch_4() {
|
||||
assert_eq!(cost_cents(Some(CommonResolution::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(CommonResolution::FourEightyP), 5, 1);
|
||||
let c720 = cost_cents(Some(CommonResolution::SevenTwentyP), 5, 1);
|
||||
let c1080 = cost_cents(Some(CommonResolution::TenEightyP), 5, 1);
|
||||
assert!(c480 < c720);
|
||||
assert!(c720 < c1080);
|
||||
}
|
||||
}
|
||||
|
||||
mod credits_tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn credits_equal_usd_cents() {
|
||||
for res in [Some(CommonResolution::FourEightyP), Some(CommonResolution::SevenTwentyP), Some(CommonResolution::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<CommonResolution>,
|
||||
duration_seconds: u16,
|
||||
video_batch_count: u16,
|
||||
) -> crate::generate::generate_video::video_generation_cost_estimate::VideoGenerationCostEstimate {
|
||||
GenerateVideoRequestBuilder {
|
||||
model: CommonVideoModel::Seedance2p0Plus,
|
||||
provider: Provider::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<CommonResolution>, duration_seconds: u16, video_batch_count: u16) -> u64 {
|
||||
build_cost(resolution, duration_seconds, video_batch_count).cost_in_usd_cents.unwrap()
|
||||
}
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
pub mod build;
|
||||
pub mod cost;
|
||||
pub mod request;
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
use artcraft_api_defs::omni_gen::cost_and_generate_requests::omni_gen_video_cost_and_generate_request::OmniGenVideoCostAndGenerateRequest;
|
||||
use artcraft_client::endpoints::omni_gen::generate::video::omni_gen_video::omni_gen_video_generate;
|
||||
|
||||
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,
|
||||
};
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct ArtcraftSeedance2p0PlusRequestState {
|
||||
pub request: OmniGenVideoCostAndGenerateRequest,
|
||||
}
|
||||
|
||||
impl ArtcraftSeedance2p0PlusRequestState {
|
||||
pub async fn send(&self, client: &RouterArtcraftClient) -> Result<GenerateVideoResponse, ArtcraftRouterError> {
|
||||
let response = omni_gen_video_generate(
|
||||
&client.api_host,
|
||||
Some(&client.credentials),
|
||||
self.request.clone(),
|
||||
)
|
||||
.await
|
||||
.map_err(|err| ArtcraftRouterError::Provider(ProviderError::Storyteller(err)))?;
|
||||
|
||||
Ok(GenerateVideoResponse::Artcraft(ArtcraftVideoResponsePayload {
|
||||
inference_job_token: response.inference_job_token.clone(),
|
||||
all_inference_job_tokens: vec![response.inference_job_token],
|
||||
}))
|
||||
}
|
||||
}
|
||||
+348
@@ -0,0 +1,348 @@
|
||||
use artcraft_api_defs::omni_gen::cost_and_generate_requests::omni_gen_video_cost_and_generate_request::OmniGenVideoCostAndGenerateRequest;
|
||||
use enums::common::generation::common_aspect_ratio::CommonAspectRatio as CommonAspectRatioEnum;
|
||||
use enums::common::generation::common_resolution::CommonResolution as CommonResolutionEnum;
|
||||
use enums::common::generation::common_video_model::CommonVideoModel as CommonVideoModelEnum;
|
||||
|
||||
use crate::api::common_aspect_ratio::CommonAspectRatio;
|
||||
use crate::api::common_resolution::CommonResolution;
|
||||
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_builder::GenerateVideoRequestBuilder;
|
||||
use crate::generate::generate_video_v2::providers::artcraft::resolve::{
|
||||
resolve_audio_list_ref, resolve_character_list_ref, resolve_image_list_ref,
|
||||
resolve_image_ref, resolve_video_list_ref,
|
||||
};
|
||||
use crate::generate::generate_video_v2::providers::artcraft::seedance_2p0_p_fast::request::ArtcraftSeedance2p0PlusFastRequestState;
|
||||
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_p_fast(mut builder: GenerateVideoRequestBuilder) -> Result<VideoGenerationDraftOrRequest, ArtcraftRouterError> {
|
||||
let strategy = builder.request_mismatch_mitigation_strategy;
|
||||
|
||||
let aspect_ratio = plan_aspect_ratio(builder.aspect_ratio.take(), strategy)?;
|
||||
let resolution = plan_output_resolution(builder.resolution.take(), strategy)?;
|
||||
let batch_count = plan_batch_count(builder.video_batch_count.take(), strategy)?;
|
||||
let duration_seconds = plan_duration(builder.duration_seconds.take(), strategy)?;
|
||||
let prompt = builder.prompt.take();
|
||||
|
||||
let start_frame = resolve_image_ref(builder.start_frame.take())?;
|
||||
let end_frame = resolve_image_ref(builder.end_frame.take())?;
|
||||
let reference_images = resolve_image_list_ref(builder.reference_images.take())?;
|
||||
let reference_videos = resolve_video_list_ref(builder.reference_videos.take())?;
|
||||
let reference_audio = resolve_audio_list_ref(builder.reference_audio.take())?;
|
||||
let reference_characters = resolve_character_list_ref(builder.reference_character_tokens.take());
|
||||
let idempotency_token = builder.get_or_generate_idempotency_token();
|
||||
|
||||
let request = OmniGenVideoCostAndGenerateRequest {
|
||||
model: Some(CommonVideoModelEnum::Seedance2p0PlusFast),
|
||||
idempotency_token: Some(idempotency_token),
|
||||
prompt,
|
||||
start_frame_image_media_token: start_frame,
|
||||
end_frame_image_media_token: end_frame,
|
||||
reference_image_media_tokens: reference_images,
|
||||
reference_video_media_tokens: reference_videos,
|
||||
reference_audio_media_tokens: reference_audio,
|
||||
reference_character_tokens: reference_characters,
|
||||
resolution,
|
||||
aspect_ratio,
|
||||
duration_seconds: duration_seconds.map(|d| d as u16),
|
||||
video_batch_count: Some(batch_count),
|
||||
negative_prompt: None,
|
||||
generate_audio: None,
|
||||
quality: None,
|
||||
};
|
||||
|
||||
let state = ArtcraftSeedance2p0PlusFastRequestState { request };
|
||||
Ok(VideoGenerationDraftOrRequest::Request(VideoGenerationRequest::ArtcraftSeedance2p0PlusFast(state)))
|
||||
}
|
||||
|
||||
// -- Plan helpers --
|
||||
|
||||
fn plan_aspect_ratio(
|
||||
aspect_ratio: Option<CommonAspectRatio>,
|
||||
strategy: RequestMismatchMitigationStrategy,
|
||||
) -> Result<Option<CommonAspectRatioEnum>, ArtcraftRouterError> {
|
||||
match aspect_ratio {
|
||||
None
|
||||
| Some(CommonAspectRatio::Auto)
|
||||
| Some(CommonAspectRatio::Auto2k)
|
||||
| Some(CommonAspectRatio::Auto4k) => Ok(None),
|
||||
|
||||
Some(CommonAspectRatio::WideSixteenByNine) | Some(CommonAspectRatio::Wide) => {
|
||||
Ok(Some(CommonAspectRatioEnum::WideSixteenByNine))
|
||||
}
|
||||
Some(CommonAspectRatio::TallNineBySixteen) | Some(CommonAspectRatio::Tall) => {
|
||||
Ok(Some(CommonAspectRatioEnum::TallNineBySixteen))
|
||||
}
|
||||
Some(CommonAspectRatio::Square) | Some(CommonAspectRatio::SquareHd) => {
|
||||
Ok(Some(CommonAspectRatioEnum::Square))
|
||||
}
|
||||
Some(CommonAspectRatio::WideFourByThree) => Ok(Some(CommonAspectRatioEnum::WideFourByThree)),
|
||||
Some(CommonAspectRatio::TallThreeByFour) => Ok(Some(CommonAspectRatioEnum::TallThreeByFour)),
|
||||
Some(CommonAspectRatio::WideTwentyOneByNine) => Ok(Some(CommonAspectRatioEnum::WideTwentyOneByNine)),
|
||||
|
||||
Some(unsupported) => match strategy {
|
||||
RequestMismatchMitigationStrategy::ErrorOut => {
|
||||
Err(ArtcraftRouterError::Client(ClientError::ModelDoesNotSupportOption {
|
||||
field: "aspect_ratio",
|
||||
value: format!("{:?}", unsupported),
|
||||
}))
|
||||
}
|
||||
RequestMismatchMitigationStrategy::PayMoreUpgrade
|
||||
| RequestMismatchMitigationStrategy::PayLessDowngrade => {
|
||||
Ok(Some(nearest_aspect_ratio(unsupported)))
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn nearest_aspect_ratio(aspect_ratio: CommonAspectRatio) -> CommonAspectRatioEnum {
|
||||
match aspect_ratio {
|
||||
CommonAspectRatio::WideFiveByFour => CommonAspectRatioEnum::WideFourByThree,
|
||||
CommonAspectRatio::WideThreeByTwo => CommonAspectRatioEnum::WideFourByThree,
|
||||
CommonAspectRatio::TallFourByFive => CommonAspectRatioEnum::TallThreeByFour,
|
||||
CommonAspectRatio::TallTwoByThree => CommonAspectRatioEnum::TallThreeByFour,
|
||||
CommonAspectRatio::TallNineByTwentyOne => CommonAspectRatioEnum::TallNineBySixteen,
|
||||
_ => CommonAspectRatioEnum::Square,
|
||||
}
|
||||
}
|
||||
|
||||
// Supports output resolutions: 480p and 720p only.
|
||||
fn plan_output_resolution(
|
||||
resolution: Option<CommonResolution>,
|
||||
strategy: RequestMismatchMitigationStrategy,
|
||||
) -> Result<Option<CommonResolutionEnum>, ArtcraftRouterError> {
|
||||
match resolution {
|
||||
None => Ok(None),
|
||||
Some(CommonResolution::FourEightyP) => Ok(Some(CommonResolutionEnum::FourEightyP)),
|
||||
Some(CommonResolution::SevenTwentyP) => Ok(Some(CommonResolutionEnum::SevenTwentyP)),
|
||||
Some(CommonResolution::TenEightyP) => match strategy {
|
||||
RequestMismatchMitigationStrategy::ErrorOut => {
|
||||
Err(ArtcraftRouterError::Client(ClientError::ModelDoesNotSupportOption {
|
||||
field: "resolution",
|
||||
value: format!("{:?}", CommonResolution::TenEightyP),
|
||||
}))
|
||||
}
|
||||
_ => Ok(Some(CommonResolutionEnum::SevenTwentyP)),
|
||||
},
|
||||
Some(unsupported) => match strategy {
|
||||
RequestMismatchMitigationStrategy::ErrorOut => {
|
||||
Err(ArtcraftRouterError::Client(ClientError::ModelDoesNotSupportOption {
|
||||
field: "resolution",
|
||||
value: format!("{:?}", unsupported),
|
||||
}))
|
||||
}
|
||||
RequestMismatchMitigationStrategy::PayMoreUpgrade
|
||||
| RequestMismatchMitigationStrategy::PayLessDowngrade => {
|
||||
Ok(Some(match unsupported {
|
||||
CommonResolution::HalfK => CommonResolutionEnum::FourEightyP,
|
||||
_ => CommonResolutionEnum::SevenTwentyP,
|
||||
}))
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn plan_batch_count(
|
||||
video_batch_count: Option<u16>,
|
||||
strategy: RequestMismatchMitigationStrategy,
|
||||
) -> Result<u16, ArtcraftRouterError> {
|
||||
let count = video_batch_count.unwrap_or(1);
|
||||
match count {
|
||||
0 => Err(ArtcraftRouterError::Client(ClientError::UserRequestedZeroGenerations)),
|
||||
1 | 2 | 4 => Ok(count),
|
||||
_ => match strategy {
|
||||
RequestMismatchMitigationStrategy::ErrorOut => {
|
||||
Err(ArtcraftRouterError::Client(ClientError::ModelDoesNotSupportOption {
|
||||
field: "video_batch_count",
|
||||
value: format!("{}", count),
|
||||
}))
|
||||
}
|
||||
RequestMismatchMitigationStrategy::PayMoreUpgrade => Ok(4),
|
||||
RequestMismatchMitigationStrategy::PayLessDowngrade => {
|
||||
Ok(if count < 4 { 2 } else { 4 })
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn plan_duration(
|
||||
duration_seconds: Option<u16>,
|
||||
strategy: RequestMismatchMitigationStrategy,
|
||||
) -> Result<Option<u8>, ArtcraftRouterError> {
|
||||
const MIN: u16 = 4;
|
||||
const MAX: u16 = 15;
|
||||
match duration_seconds {
|
||||
None => Ok(None),
|
||||
Some(d) if d >= MIN && d <= MAX => Ok(Some(d as u8)),
|
||||
Some(d) => match strategy {
|
||||
RequestMismatchMitigationStrategy::ErrorOut => {
|
||||
Err(ArtcraftRouterError::Client(ClientError::ModelDoesNotSupportOption {
|
||||
field: "duration_seconds",
|
||||
value: format!("{}", d),
|
||||
}))
|
||||
}
|
||||
_ => Ok(Some(d.clamp(MIN, MAX) as u8)),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
#[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::common_resolution::CommonResolution;
|
||||
use crate::api::common_video_model::CommonVideoModel;
|
||||
use crate::api::image_list_ref::ImageListRef;
|
||||
use crate::api::image_ref::ImageRef;
|
||||
use crate::api::provider::Provider;
|
||||
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_plus_fast() {
|
||||
let req = unwrap_request(make_builder(|_| {}));
|
||||
assert!(matches!(req.request.model, Some(CommonVideoModelEnum::Seedance2p0PlusFast)));
|
||||
}
|
||||
|
||||
#[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(CommonResolution::FourEightyP); }));
|
||||
assert_eq!(req.request.resolution, Some(CommonResolutionEnum::FourEightyP));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn res_720p() {
|
||||
let req = unwrap_request(make_builder(|b| { b.resolution = Some(CommonResolution::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(CommonResolution::TenEightyP); }));
|
||||
assert_eq!(req.request.resolution, Some(CommonResolutionEnum::SevenTwentyP));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn res_1080p_error_out() {
|
||||
let result = build_artcraft_seedance_2p0_p_fast(GenerateVideoRequestBuilder {
|
||||
model: CommonVideoModel::Seedance2p0PlusFast,
|
||||
provider: Provider::Artcraft,
|
||||
resolution: Some(CommonResolution::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_p_fast(GenerateVideoRequestBuilder {
|
||||
model: CommonVideoModel::Seedance2p0PlusFast,
|
||||
provider: Provider::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: CommonVideoModel::Seedance2p0PlusFast,
|
||||
provider: Provider::Artcraft,
|
||||
duration_seconds: Some(5),
|
||||
video_batch_count: Some(1),
|
||||
..Default::default()
|
||||
};
|
||||
f(&mut builder);
|
||||
builder
|
||||
}
|
||||
|
||||
fn unwrap_request(builder: GenerateVideoRequestBuilder) -> ArtcraftSeedance2p0PlusFastRequestState {
|
||||
let result = build_artcraft_seedance_2p0_p_fast(builder).expect("build should succeed");
|
||||
match result {
|
||||
VideoGenerationDraftOrRequest::Request(
|
||||
VideoGenerationRequest::ArtcraftSeedance2p0PlusFast(state)
|
||||
) => state,
|
||||
_ => panic!("expected ArtcraftSeedance2p0PlusFast request"),
|
||||
}
|
||||
}
|
||||
}
|
||||
+146
@@ -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_p_fast::request::ArtcraftSeedance2p0PlusFastRequestState;
|
||||
|
||||
/// 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 ArtcraftSeedance2p0PlusFastCostState {
|
||||
pub resolution: CommonResolution,
|
||||
pub duration_seconds: u16,
|
||||
pub batch_count: u16,
|
||||
}
|
||||
|
||||
impl ArtcraftSeedance2p0PlusFastCostState {
|
||||
pub fn from_request(request: &ArtcraftSeedance2p0PlusFastRequestState) -> 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::common_resolution::CommonResolution;
|
||||
use crate::api::common_video_model::CommonVideoModel;
|
||||
use crate::api::provider::Provider;
|
||||
use crate::generate::generate_video::generate_video_request_builder::GenerateVideoRequestBuilder;
|
||||
|
||||
mod pricing_720p {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn batch_1() {
|
||||
assert_eq!(cost_cents(Some(CommonResolution::SevenTwentyP), 4, 1), 80);
|
||||
assert_eq!(cost_cents(Some(CommonResolution::SevenTwentyP), 5, 1), 100);
|
||||
assert_eq!(cost_cents(Some(CommonResolution::SevenTwentyP), 10, 1), 200);
|
||||
assert_eq!(cost_cents(Some(CommonResolution::SevenTwentyP), 15, 1), 300);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn batch_2() {
|
||||
assert_eq!(cost_cents(Some(CommonResolution::SevenTwentyP), 5, 2), 200);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn batch_4() {
|
||||
assert_eq!(cost_cents(Some(CommonResolution::SevenTwentyP), 5, 4), 400);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn none_defaults_to_720p() {
|
||||
assert_eq!(cost_cents(None, 5, 1), cost_cents(Some(CommonResolution::SevenTwentyP), 5, 1));
|
||||
}
|
||||
}
|
||||
|
||||
mod pricing_480p {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn batch_1() {
|
||||
assert_eq!(cost_cents(Some(CommonResolution::FourEightyP), 4, 1), 36);
|
||||
assert_eq!(cost_cents(Some(CommonResolution::FourEightyP), 5, 1), 45);
|
||||
assert_eq!(cost_cents(Some(CommonResolution::FourEightyP), 10, 1), 90);
|
||||
assert_eq!(cost_cents(Some(CommonResolution::FourEightyP), 15, 1), 135);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn batch_2() {
|
||||
assert_eq!(cost_cents(Some(CommonResolution::FourEightyP), 5, 2), 90);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn batch_4() {
|
||||
assert_eq!(cost_cents(Some(CommonResolution::FourEightyP), 5, 4), 180);
|
||||
}
|
||||
}
|
||||
|
||||
mod relative_pricing_tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn cost_480p_cheaper_than_720p() {
|
||||
assert!(cost_cents(Some(CommonResolution::FourEightyP), 5, 1) < cost_cents(Some(CommonResolution::SevenTwentyP), 5, 1));
|
||||
}
|
||||
}
|
||||
|
||||
mod credits_tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn credits_equal_usd_cents() {
|
||||
for res in [Some(CommonResolution::FourEightyP), Some(CommonResolution::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<CommonResolution>,
|
||||
duration_seconds: u16,
|
||||
video_batch_count: u16,
|
||||
) -> crate::generate::generate_video::video_generation_cost_estimate::VideoGenerationCostEstimate {
|
||||
GenerateVideoRequestBuilder {
|
||||
model: CommonVideoModel::Seedance2p0PlusFast,
|
||||
provider: Provider::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<CommonResolution>, duration_seconds: u16, video_batch_count: u16) -> u64 {
|
||||
build_cost(resolution, duration_seconds, video_batch_count).cost_in_usd_cents.unwrap()
|
||||
}
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
pub mod build;
|
||||
pub mod cost;
|
||||
pub mod request;
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
use artcraft_api_defs::omni_gen::cost_and_generate_requests::omni_gen_video_cost_and_generate_request::OmniGenVideoCostAndGenerateRequest;
|
||||
use artcraft_client::endpoints::omni_gen::generate::video::omni_gen_video::omni_gen_video_generate;
|
||||
|
||||
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,
|
||||
};
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct ArtcraftSeedance2p0PlusFastRequestState {
|
||||
pub request: OmniGenVideoCostAndGenerateRequest,
|
||||
}
|
||||
|
||||
impl ArtcraftSeedance2p0PlusFastRequestState {
|
||||
pub async fn send(&self, client: &RouterArtcraftClient) -> Result<GenerateVideoResponse, ArtcraftRouterError> {
|
||||
let response = omni_gen_video_generate(
|
||||
&client.api_host,
|
||||
Some(&client.credentials),
|
||||
self.request.clone(),
|
||||
)
|
||||
.await
|
||||
.map_err(|err| ArtcraftRouterError::Provider(ProviderError::Storyteller(err)))?;
|
||||
|
||||
Ok(GenerateVideoResponse::Artcraft(ArtcraftVideoResponsePayload {
|
||||
inference_job_token: response.inference_job_token.clone(),
|
||||
all_inference_job_tokens: vec![response.inference_job_token],
|
||||
}))
|
||||
}
|
||||
}
|
||||
+18
@@ -17,6 +17,10 @@ use crate::generate::generate_video_v2::providers::artcraft::seedance_2p0_g::cos
|
||||
use crate::generate::generate_video_v2::providers::artcraft::seedance_2p0_g::request::ArtcraftSeedance2p0GRequestState;
|
||||
use crate::generate::generate_video_v2::providers::artcraft::seedance_2p0_fast_g::cost::ArtcraftSeedance2p0FastGCostState;
|
||||
use crate::generate::generate_video_v2::providers::artcraft::seedance_2p0_fast_g::request::ArtcraftSeedance2p0FastGRequestState;
|
||||
use crate::generate::generate_video_v2::providers::artcraft::seedance_2p0_p::cost::ArtcraftSeedance2p0PlusCostState;
|
||||
use crate::generate::generate_video_v2::providers::artcraft::seedance_2p0_p::request::ArtcraftSeedance2p0PlusRequestState;
|
||||
use crate::generate::generate_video_v2::providers::artcraft::seedance_2p0_p_fast::cost::ArtcraftSeedance2p0PlusFastCostState;
|
||||
use crate::generate::generate_video_v2::providers::artcraft::seedance_2p0_p_fast::request::ArtcraftSeedance2p0PlusFastRequestState;
|
||||
use crate::generate::generate_video_v2::providers::gmicloud::seedance_2p0_g::cost::GmiCloudSeedance2p0GCostState;
|
||||
use crate::generate::generate_video_v2::providers::gmicloud::seedance_2p0_g::request::GmiCloudSeedance2p0GRequestState;
|
||||
use crate::generate::generate_video_v2::providers::gmicloud::seedance_2p0_fast_g::cost::GmiCloudSeedance2p0FastGCostState;
|
||||
@@ -37,6 +41,8 @@ pub enum VideoGenerationRequest {
|
||||
ArtcraftSeedance2p0Fast(ArtcraftSeedance2p0FastRequestState),
|
||||
ArtcraftSeedance2p0G(ArtcraftSeedance2p0GRequestState),
|
||||
ArtcraftSeedance2p0FastG(ArtcraftSeedance2p0FastGRequestState),
|
||||
ArtcraftSeedance2p0Plus(ArtcraftSeedance2p0PlusRequestState),
|
||||
ArtcraftSeedance2p0PlusFast(ArtcraftSeedance2p0PlusFastRequestState),
|
||||
GmiCloudSeedance2p0G(GmiCloudSeedance2p0GRequestState),
|
||||
GmiCloudSeedance2p0FastG(GmiCloudSeedance2p0FastGRequestState),
|
||||
KinoviHappyHorse1p0(KinoviHappyHorse1p0RequestState),
|
||||
@@ -55,6 +61,8 @@ impl VideoGenerationRequest {
|
||||
Self::ArtcraftSeedance2p0Fast(_) => Provider::Artcraft,
|
||||
Self::ArtcraftSeedance2p0G(_) => Provider::Artcraft,
|
||||
Self::ArtcraftSeedance2p0FastG(_) => Provider::Artcraft,
|
||||
Self::ArtcraftSeedance2p0Plus(_) => Provider::Artcraft,
|
||||
Self::ArtcraftSeedance2p0PlusFast(_) => Provider::Artcraft,
|
||||
Self::GmiCloudSeedance2p0G(_) => Provider::GmiCloud,
|
||||
Self::GmiCloudSeedance2p0FastG(_) => Provider::GmiCloud,
|
||||
Self::KinoviHappyHorse1p0(_) => Provider::Seedance2Pro,
|
||||
@@ -73,6 +81,8 @@ impl VideoGenerationRequest {
|
||||
VideoGenerationRequest::ArtcraftSeedance2p0Fast(request) => Ok(ArtcraftSeedance2p0FastCostState::from_request(request).estimate_cost()),
|
||||
VideoGenerationRequest::ArtcraftSeedance2p0G(request) => Ok(ArtcraftSeedance2p0GCostState::from_request(request).estimate_cost()),
|
||||
VideoGenerationRequest::ArtcraftSeedance2p0FastG(request) => Ok(ArtcraftSeedance2p0FastGCostState::from_request(request).estimate_cost()),
|
||||
VideoGenerationRequest::ArtcraftSeedance2p0Plus(request) => Ok(ArtcraftSeedance2p0PlusCostState::from_request(request).estimate_cost()),
|
||||
VideoGenerationRequest::ArtcraftSeedance2p0PlusFast(request) => Ok(ArtcraftSeedance2p0PlusFastCostState::from_request(request).estimate_cost()),
|
||||
VideoGenerationRequest::GmiCloudSeedance2p0G(request) => Ok(GmiCloudSeedance2p0GCostState::from_request(request).estimate_cost()),
|
||||
VideoGenerationRequest::GmiCloudSeedance2p0FastG(request) => Ok(GmiCloudSeedance2p0FastGCostState::from_request(request).estimate_cost()),
|
||||
VideoGenerationRequest::KinoviHappyHorse1p0(request) => Ok(KinoviHappyHorse1p0CostState::from_request(request).estimate_cost()),
|
||||
@@ -113,6 +123,14 @@ impl VideoGenerationRequest {
|
||||
let client_ref = client.get_artcraft_client_ref()?;
|
||||
request.send(client_ref).await
|
||||
},
|
||||
VideoGenerationRequest::ArtcraftSeedance2p0Plus(request) => {
|
||||
let client_ref = client.get_artcraft_client_ref()?;
|
||||
request.send(client_ref).await
|
||||
},
|
||||
VideoGenerationRequest::ArtcraftSeedance2p0PlusFast(request) => {
|
||||
let client_ref = client.get_artcraft_client_ref()?;
|
||||
request.send(client_ref).await
|
||||
},
|
||||
VideoGenerationRequest::GmiCloudSeedance2p0G(request) => {
|
||||
let client_ref = client.get_gmicloud_client_ref()?;
|
||||
request.send(client_ref).await
|
||||
|
||||
@@ -130,6 +130,10 @@ pub enum CommonModelType {
|
||||
Seedance2p0,
|
||||
#[serde(rename = "seedance_2p0_fast")]
|
||||
Seedance2p0Fast,
|
||||
#[serde(rename = "seedance_2p0_p")]
|
||||
Seedance2p0Plus,
|
||||
#[serde(rename = "seedance_2p0_p_fast")]
|
||||
Seedance2p0PlusFast,
|
||||
#[serde(rename = "seedance_2p0_g")]
|
||||
Seedance2p0Global,
|
||||
#[serde(rename = "seedance_2p0_fast_g")]
|
||||
@@ -227,6 +231,8 @@ impl CommonModelType {
|
||||
Self::Seedance1p5Pro => "seedance_1p5_pro",
|
||||
Self::Seedance2p0 => "seedance_2p0",
|
||||
Self::Seedance2p0Fast => "seedance_2p0_fast",
|
||||
Self::Seedance2p0Plus => "seedance_2p0_p",
|
||||
Self::Seedance2p0PlusFast => "seedance_2p0_p_fast",
|
||||
Self::Seedance2p0Global => "seedance_2p0_g",
|
||||
Self::Seedance2p0FastGlobal => "seedance_2p0_fast_g",
|
||||
Self::Sora2 => "sora_2",
|
||||
@@ -301,6 +307,8 @@ impl CommonModelType {
|
||||
"seedance_1p5_pro" => Ok(Self::Seedance1p5Pro),
|
||||
"seedance_2p0" => Ok(Self::Seedance2p0),
|
||||
"seedance_2p0_fast" => Ok(Self::Seedance2p0Fast),
|
||||
"seedance_2p0_p" => Ok(Self::Seedance2p0Plus),
|
||||
"seedance_2p0_p_fast" => Ok(Self::Seedance2p0PlusFast),
|
||||
"seedance_2p0_g" => Ok(Self::Seedance2p0Global),
|
||||
"seedance_2p0_fast_g" => Ok(Self::Seedance2p0FastGlobal),
|
||||
"sora_2" => Ok(Self::Sora2),
|
||||
@@ -379,6 +387,8 @@ impl CommonModelType {
|
||||
Self::Seedance1p5Pro,
|
||||
Self::Seedance2p0,
|
||||
Self::Seedance2p0Fast,
|
||||
Self::Seedance2p0Plus,
|
||||
Self::Seedance2p0PlusFast,
|
||||
Self::Seedance2p0Global,
|
||||
Self::Seedance2p0FastGlobal,
|
||||
Self::Sora2,
|
||||
@@ -454,6 +464,8 @@ impl CommonModelType {
|
||||
Self::Seedance1p5Pro => CommonModelClass::Video,
|
||||
Self::Seedance2p0 => CommonModelClass::Video,
|
||||
Self::Seedance2p0Fast => CommonModelClass::Video,
|
||||
Self::Seedance2p0Plus => CommonModelClass::Video,
|
||||
Self::Seedance2p0PlusFast => CommonModelClass::Video,
|
||||
Self::Seedance2p0Global => CommonModelClass::Video,
|
||||
Self::Seedance2p0FastGlobal => CommonModelClass::Video,
|
||||
Self::Sora2 => CommonModelClass::Video,
|
||||
@@ -538,6 +550,8 @@ mod tests {
|
||||
assert_serialization(CommonModelType::Seedance1p5Pro, "seedance_1p5_pro");
|
||||
assert_serialization(CommonModelType::Seedance2p0, "seedance_2p0");
|
||||
assert_serialization(CommonModelType::Seedance2p0Fast, "seedance_2p0_fast");
|
||||
assert_serialization(CommonModelType::Seedance2p0Plus, "seedance_2p0_p");
|
||||
assert_serialization(CommonModelType::Seedance2p0PlusFast, "seedance_2p0_p_fast");
|
||||
assert_serialization(CommonModelType::Seedance2p0Global, "seedance_2p0_g");
|
||||
assert_serialization(CommonModelType::Seedance2p0FastGlobal, "seedance_2p0_fast_g");
|
||||
assert_serialization(CommonModelType::Sora2, "sora_2");
|
||||
@@ -609,6 +623,8 @@ mod tests {
|
||||
assert_eq!(CommonModelType::Seedance1p5Pro.to_str(), "seedance_1p5_pro");
|
||||
assert_eq!(CommonModelType::Seedance2p0.to_str(), "seedance_2p0");
|
||||
assert_eq!(CommonModelType::Seedance2p0Fast.to_str(), "seedance_2p0_fast");
|
||||
assert_eq!(CommonModelType::Seedance2p0Plus.to_str(), "seedance_2p0_p");
|
||||
assert_eq!(CommonModelType::Seedance2p0PlusFast.to_str(), "seedance_2p0_p_fast");
|
||||
assert_eq!(CommonModelType::Seedance2p0Global.to_str(), "seedance_2p0_g");
|
||||
assert_eq!(CommonModelType::Seedance2p0FastGlobal.to_str(), "seedance_2p0_fast_g");
|
||||
assert_eq!(CommonModelType::Sora2.to_str(), "sora_2");
|
||||
@@ -680,6 +696,8 @@ mod tests {
|
||||
assert_eq!(CommonModelType::from_str("seedance_1p5_pro").unwrap(), CommonModelType::Seedance1p5Pro);
|
||||
assert_eq!(CommonModelType::from_str("seedance_2p0").unwrap(), CommonModelType::Seedance2p0);
|
||||
assert_eq!(CommonModelType::from_str("seedance_2p0_fast").unwrap(), CommonModelType::Seedance2p0Fast);
|
||||
assert_eq!(CommonModelType::from_str("seedance_2p0_p").unwrap(), CommonModelType::Seedance2p0Plus);
|
||||
assert_eq!(CommonModelType::from_str("seedance_2p0_p_fast").unwrap(), CommonModelType::Seedance2p0PlusFast);
|
||||
assert_eq!(CommonModelType::from_str("seedance_2p0_g").unwrap(), CommonModelType::Seedance2p0Global);
|
||||
assert_eq!(CommonModelType::from_str("seedance_2p0_fast_g").unwrap(), CommonModelType::Seedance2p0FastGlobal);
|
||||
assert_eq!(CommonModelType::from_str("sora_2").unwrap(), CommonModelType::Sora2);
|
||||
@@ -705,7 +723,7 @@ mod tests {
|
||||
#[test]
|
||||
fn all_variants() {
|
||||
let mut variants = CommonModelType::all_variants();
|
||||
assert_eq!(variants.len(), 62);
|
||||
assert_eq!(variants.len(), 64);
|
||||
// Image models
|
||||
assert_eq!(variants.pop_first(), Some(CommonModelType::Flux1Dev));
|
||||
assert_eq!(variants.pop_first(), Some(CommonModelType::Flux1Schnell));
|
||||
@@ -753,6 +771,8 @@ mod tests {
|
||||
assert_eq!(variants.pop_first(), Some(CommonModelType::Seedance1p5Pro));
|
||||
assert_eq!(variants.pop_first(), Some(CommonModelType::Seedance2p0));
|
||||
assert_eq!(variants.pop_first(), Some(CommonModelType::Seedance2p0Fast));
|
||||
assert_eq!(variants.pop_first(), Some(CommonModelType::Seedance2p0Plus));
|
||||
assert_eq!(variants.pop_first(), Some(CommonModelType::Seedance2p0PlusFast));
|
||||
assert_eq!(variants.pop_first(), Some(CommonModelType::Seedance2p0Global));
|
||||
assert_eq!(variants.pop_first(), Some(CommonModelType::Seedance2p0FastGlobal));
|
||||
assert_eq!(variants.pop_first(), Some(CommonModelType::Sora2));
|
||||
|
||||
@@ -44,9 +44,17 @@ pub enum CommonVideoModel {
|
||||
#[serde(rename = "seedance_2p0_fast")]
|
||||
Seedance2p0Fast,
|
||||
|
||||
#[serde(rename = "seedance_2p0_p")]
|
||||
Seedance2p0Plus,
|
||||
|
||||
#[serde(rename = "seedance_2p0_p_fast")]
|
||||
Seedance2p0PlusFast,
|
||||
|
||||
// NB: Not in use
|
||||
#[serde(rename = "seedance_2p0_g")]
|
||||
Seedance2p0Global,
|
||||
|
||||
// NB: Not in use
|
||||
#[serde(rename = "seedance_2p0_fast_g")]
|
||||
Seedance2p0FastGlobal,
|
||||
|
||||
@@ -71,9 +79,13 @@ pub enum CommonVideoModel {
|
||||
#[serde(rename = "veo_3p1_fast")]
|
||||
Veo3p1Fast,
|
||||
|
||||
// NB: Temporary model rollout. Previously used for "Seedance 2.0 BytePlus".
|
||||
// This can be reused in the future.
|
||||
#[serde(rename = "preview_model")]
|
||||
PreviewModel,
|
||||
|
||||
// NB: Temporary model rollout. Previously used for "Seedance 2.0 BytePlus Fast".
|
||||
// This can be reused in the future.
|
||||
#[serde(rename = "preview_model_fast")]
|
||||
PreviewModelFast,
|
||||
}
|
||||
@@ -95,6 +107,8 @@ impl CommonVideoModel {
|
||||
Self::Seedance1p5Pro => CommonModelType::Seedance1p5Pro,
|
||||
Self::Seedance2p0 => CommonModelType::Seedance2p0,
|
||||
Self::Seedance2p0Fast => CommonModelType::Seedance2p0Fast,
|
||||
Self::Seedance2p0Plus => CommonModelType::Seedance2p0Plus,
|
||||
Self::Seedance2p0PlusFast => CommonModelType::Seedance2p0PlusFast,
|
||||
Self::Seedance2p0Global => CommonModelType::Seedance2p0Global,
|
||||
Self::Seedance2p0FastGlobal => CommonModelType::Seedance2p0FastGlobal,
|
||||
Self::Sora2 => CommonModelType::Sora2,
|
||||
@@ -131,6 +145,8 @@ mod tests {
|
||||
assert_serialization(CommonVideoModel::Seedance1p5Pro, "seedance_1p5_pro");
|
||||
assert_serialization(CommonVideoModel::Seedance2p0, "seedance_2p0");
|
||||
assert_serialization(CommonVideoModel::Seedance2p0Fast, "seedance_2p0_fast");
|
||||
assert_serialization(CommonVideoModel::Seedance2p0Plus, "seedance_2p0_p");
|
||||
assert_serialization(CommonVideoModel::Seedance2p0PlusFast, "seedance_2p0_p_fast");
|
||||
assert_serialization(CommonVideoModel::Seedance2p0Global, "seedance_2p0_g");
|
||||
assert_serialization(CommonVideoModel::Seedance2p0FastGlobal, "seedance_2p0_fast_g");
|
||||
assert_serialization(CommonVideoModel::Sora2, "sora_2");
|
||||
@@ -160,6 +176,8 @@ mod tests {
|
||||
("seedance_1p5_pro", CommonVideoModel::Seedance1p5Pro),
|
||||
("seedance_2p0", CommonVideoModel::Seedance2p0),
|
||||
("seedance_2p0_fast", CommonVideoModel::Seedance2p0Fast),
|
||||
("seedance_2p0_p", CommonVideoModel::Seedance2p0Plus),
|
||||
("seedance_2p0_p_fast", CommonVideoModel::Seedance2p0PlusFast),
|
||||
("seedance_2p0_g", CommonVideoModel::Seedance2p0Global),
|
||||
("seedance_2p0_fast_g", CommonVideoModel::Seedance2p0FastGlobal),
|
||||
("sora_2", CommonVideoModel::Sora2),
|
||||
@@ -196,6 +214,8 @@ mod tests {
|
||||
CommonVideoModel::Seedance1p5Pro,
|
||||
CommonVideoModel::Seedance2p0,
|
||||
CommonVideoModel::Seedance2p0Fast,
|
||||
CommonVideoModel::Seedance2p0Plus,
|
||||
CommonVideoModel::Seedance2p0PlusFast,
|
||||
CommonVideoModel::Seedance2p0Global,
|
||||
CommonVideoModel::Seedance2p0FastGlobal,
|
||||
CommonVideoModel::Sora2,
|
||||
@@ -231,6 +251,8 @@ mod tests {
|
||||
(CommonVideoModel::Seedance1p5Pro, CommonModelType::Seedance1p5Pro),
|
||||
(CommonVideoModel::Seedance2p0, CommonModelType::Seedance2p0),
|
||||
(CommonVideoModel::Seedance2p0Fast, CommonModelType::Seedance2p0Fast),
|
||||
(CommonVideoModel::Seedance2p0Plus, CommonModelType::Seedance2p0Plus),
|
||||
(CommonVideoModel::Seedance2p0PlusFast, CommonModelType::Seedance2p0PlusFast),
|
||||
(CommonVideoModel::Seedance2p0Global, CommonModelType::Seedance2p0Global),
|
||||
(CommonVideoModel::Seedance2p0FastGlobal, CommonModelType::Seedance2p0FastGlobal),
|
||||
(CommonVideoModel::Sora2, CommonModelType::Sora2),
|
||||
|
||||
@@ -235,8 +235,8 @@ fn build_omni_gen_video_models() -> Vec<OmniGenVideoModelDetails> {
|
||||
// });
|
||||
|
||||
models.push(OmniGenVideoModelDetails {
|
||||
model: CommonVideoModel::PreviewModel,
|
||||
model_creator: None,
|
||||
model: CommonVideoModel::Seedance2p0Plus,
|
||||
model_creator: Some(ModelCreator::Bytedance),
|
||||
full_name: Some("Seedance 2.0 Plus".to_string()),
|
||||
text_prompt_supported: Some(true),
|
||||
starting_keyframe_supported: Some(true),
|
||||
@@ -275,8 +275,8 @@ fn build_omni_gen_video_models() -> Vec<OmniGenVideoModelDetails> {
|
||||
});
|
||||
|
||||
models.push(OmniGenVideoModelDetails {
|
||||
model: CommonVideoModel::PreviewModelFast,
|
||||
model_creator: None,
|
||||
model: CommonVideoModel::Seedance2p0PlusFast,
|
||||
model_creator: Some(ModelCreator::Bytedance),
|
||||
full_name: Some("Seedance 2.0 Plus Fast".to_string()),
|
||||
text_prompt_supported: Some(true),
|
||||
starting_keyframe_supported: Some(true),
|
||||
|
||||
+2
@@ -143,6 +143,8 @@ fn map_video_model(model: CommonVideoModel) -> Result<RouterVideoModel, HandlerE
|
||||
CommonVideoModel::Seedance2p0Fast => RouterVideoModel::Seedance2p0Fast,
|
||||
CommonVideoModel::Seedance2p0Global => RouterVideoModel::Seedance2p0Global,
|
||||
CommonVideoModel::Seedance2p0FastGlobal => RouterVideoModel::Seedance2p0FastGlobal,
|
||||
CommonVideoModel::Seedance2p0Plus=> RouterVideoModel::Seedance2p0Plus,
|
||||
CommonVideoModel::Seedance2p0PlusFast=> RouterVideoModel::Seedance2p0PlusFast,
|
||||
CommonVideoModel::HappyHorse1p0 => RouterVideoModel::HappyHorse1p0,
|
||||
CommonVideoModel::Sora2 => RouterVideoModel::Sora2,
|
||||
CommonVideoModel::Sora2Pro => RouterVideoModel::Sora2Pro,
|
||||
|
||||
+4
@@ -189,12 +189,16 @@ pub async fn omni_gen_video_generate_handler(
|
||||
Some(CommonVideoModel::Seedance2p0Fast) => true,
|
||||
Some(CommonVideoModel::Seedance2p0Global) => true,
|
||||
Some(CommonVideoModel::Seedance2p0FastGlobal) => true,
|
||||
Some(CommonVideoModel::Seedance2p0Plus) => true,
|
||||
Some(CommonVideoModel::Seedance2p0PlusFast) => true,
|
||||
Some(CommonVideoModel::PreviewModel) => true,
|
||||
Some(CommonVideoModel::PreviewModelFast) => true,
|
||||
_ => false,
|
||||
};
|
||||
|
||||
let use_alternate_kinovi = match request.model {
|
||||
Some(CommonVideoModel::Seedance2p0Plus) => true,
|
||||
Some(CommonVideoModel::Seedance2p0PlusFast) => true,
|
||||
Some(CommonVideoModel::PreviewModel) => true,
|
||||
Some(CommonVideoModel::PreviewModelFast) => true,
|
||||
_ => false,
|
||||
|
||||
+4
-2
@@ -44,10 +44,12 @@ pub async fn run_pipeline_v2(args: RunPipelineV2Args<'_>) -> Result<PipelineResu
|
||||
let mut router_builder = router_builder.clone();
|
||||
|
||||
match router_builder.model {
|
||||
CommonVideoModel::PreviewModel => {
|
||||
CommonVideoModel::PreviewModel |
|
||||
CommonVideoModel::Seedance2p0Plus => {
|
||||
router_builder.model = CommonVideoModel::Seedance2p0;
|
||||
},
|
||||
CommonVideoModel::PreviewModelFast => {
|
||||
CommonVideoModel::PreviewModelFast |
|
||||
CommonVideoModel::Seedance2p0PlusFast => {
|
||||
router_builder.model = CommonVideoModel::Seedance2p0Fast;
|
||||
},
|
||||
_ => {}, // Fall-through
|
||||
|
||||
Reference in New Issue
Block a user