Compare commits
6
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6a35fccba3 | ||
|
|
db925450c5 | ||
|
|
90f1fcb25f | ||
|
|
0245058e05 | ||
|
|
5d311b7a20 | ||
|
|
4836854263 |
No files matched your search
-24
@@ -264,30 +264,6 @@ pub fn plan_batch_count(
|
||||
}
|
||||
}
|
||||
|
||||
/// Batch counts for the Seedance 2.0 Mini family: 1-8 are all valid
|
||||
/// (wider than the shared 1/2/4 planning); higher counts clamp to 8 per the
|
||||
/// mitigation strategy.
|
||||
pub fn plan_mini_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..=8 => Ok(count),
|
||||
_ => match strategy {
|
||||
RequestMismatchMitigationStrategy::ErrorOut => {
|
||||
Err(ArtcraftRouterError::Client(ClientError::ModelDoesNotSupportOption {
|
||||
field: "video_batch_count",
|
||||
value: format!("{}", count),
|
||||
}))
|
||||
}
|
||||
RequestMismatchMitigationStrategy::PayMoreUpgrade
|
||||
| RequestMismatchMitigationStrategy::PayLessDowngrade => Ok(8),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Translate the router-facing bitrate into the API `CommonBitrate`.
|
||||
///
|
||||
/// Bitrate does not affect cost. An unset value leaves the field `None` (the
|
||||
|
||||
+259
@@ -0,0 +1,259 @@
|
||||
//! Public ArtCraft batch planning and retail price snapshots for Kinovi models.
|
||||
|
||||
use artcraft_api_defs::omni_gen::cost_and_generate_requests::omni_gen_video_cost_and_generate_request::OmniGenVideoCostAndGenerateRequest;
|
||||
use tokens::tokens::media_files::MediaFileToken;
|
||||
|
||||
use crate::api::router_provider::RouterProvider;
|
||||
use crate::api::router_resolution::RouterResolution;
|
||||
use crate::api::router_video_model::RouterVideoModel;
|
||||
use crate::api::video_list_ref::VideoListRef;
|
||||
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::video_generation_draft_or_request::VideoGenerationDraftOrRequest;
|
||||
use crate::generate::generate_video::video_generation_request::VideoGenerationRequest;
|
||||
|
||||
const MODELS: [RouterVideoModel; 12] = [
|
||||
RouterVideoModel::Seedance2p5,
|
||||
RouterVideoModel::Seedance2p0,
|
||||
RouterVideoModel::Seedance2p0Fast,
|
||||
RouterVideoModel::Seedance2p0Mini,
|
||||
RouterVideoModel::Seedance2p5Ultra,
|
||||
RouterVideoModel::Seedance2p0BytePlus,
|
||||
RouterVideoModel::Seedance2p0BytePlusFast,
|
||||
RouterVideoModel::Seedance2p0BytePlusMini,
|
||||
RouterVideoModel::Seedance2p0BytePlusUltra,
|
||||
RouterVideoModel::Seedance2p0BytePlusUltraFast,
|
||||
RouterVideoModel::Seedance2p0BytePlusUltraMini,
|
||||
RouterVideoModel::HappyHorse1p0,
|
||||
];
|
||||
|
||||
mod batch_planning {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn batches_one_through_four_survive_http_serialization() {
|
||||
for model in MODELS {
|
||||
for strategy in [
|
||||
RequestMismatchMitigationStrategy::ErrorOut,
|
||||
RequestMismatchMitigationStrategy::PayMoreUpgrade,
|
||||
RequestMismatchMitigationStrategy::PayLessDowngrade,
|
||||
] {
|
||||
for batch in [None, Some(1), Some(2), Some(3), Some(4)] {
|
||||
let mut builder = builder(model, RouterResolution::SevenTwentyP, batch);
|
||||
builder.request_mismatch_mitigation_strategy = strategy;
|
||||
let request = http_request(builder.build2().unwrap());
|
||||
let json = serde_json::to_value(&request).unwrap();
|
||||
assert_eq!(json["video_batch_count"], batch.unwrap_or(1), "{model:?}");
|
||||
let decoded: OmniGenVideoCostAndGenerateRequest = serde_json::from_value(json).unwrap();
|
||||
assert_eq!(decoded.video_batch_count, Some(batch.unwrap_or(1)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn zero_is_rejected_under_every_strategy() {
|
||||
for model in MODELS {
|
||||
for strategy in [
|
||||
RequestMismatchMitigationStrategy::ErrorOut,
|
||||
RequestMismatchMitigationStrategy::PayMoreUpgrade,
|
||||
RequestMismatchMitigationStrategy::PayLessDowngrade,
|
||||
] {
|
||||
let mut builder = builder(model, RouterResolution::SevenTwentyP, Some(0));
|
||||
builder.request_mismatch_mitigation_strategy = strategy;
|
||||
assert!(matches!(builder.build2(), Err(ArtcraftRouterError::Client(
|
||||
ClientError::UserRequestedZeroGenerations
|
||||
))), "{model:?}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn batches_above_four_follow_the_mitigation_strategy() {
|
||||
for model in MODELS {
|
||||
for batch in [5, 6, 7, 8, u16::MAX] {
|
||||
let builder = builder(model, RouterResolution::SevenTwentyP, Some(batch));
|
||||
assert!(matches!(builder.clone().build2(), Err(ArtcraftRouterError::Client(
|
||||
ClientError::ModelDoesNotSupportOption { field: "video_batch_count", .. }
|
||||
))), "{model:?}, batch {batch}");
|
||||
for strategy in [
|
||||
RequestMismatchMitigationStrategy::PayMoreUpgrade,
|
||||
RequestMismatchMitigationStrategy::PayLessDowngrade,
|
||||
] {
|
||||
let mut builder = builder.clone();
|
||||
builder.request_mismatch_mitigation_strategy = strategy;
|
||||
let request = http_request(builder.build2().unwrap());
|
||||
assert_eq!(request.video_batch_count, Some(4), "{model:?}, batch {batch}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mod retail_prices {
|
||||
use super::*;
|
||||
use RouterResolution::{FourEightyP, FourK, SevenTwentyP, TenEightyP};
|
||||
use RouterVideoModel::{HappyHorse1p0, Seedance2p0, Seedance2p0Fast, Seedance2p0Mini, Seedance2p5,
|
||||
Seedance2p5Ultra, Seedance2p0BytePlus, Seedance2p0BytePlusFast, Seedance2p0BytePlusMini, Seedance2p0BytePlusUltra, Seedance2p0BytePlusUltraFast, Seedance2p0BytePlusUltraMini,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn five_second_prices_for_every_resolution_and_batch() {
|
||||
// Literal USD cents / ArtCraft credits for batches [1, 2, 3, 4].
|
||||
// Pin the final rounded totals, not a multiple of an already-rounded price.
|
||||
for (model, resolution, expected) in [
|
||||
(Seedance2p5, FourEightyP, [59, 118, 177, 236]),
|
||||
(Seedance2p5, SevenTwentyP, [134, 268, 401, 535]),
|
||||
(Seedance2p5, TenEightyP, [309, 617, 925, 1233]),
|
||||
(Seedance2p5Ultra, FourEightyP, [70, 140, 209, 279]),
|
||||
(Seedance2p5Ultra, SevenTwentyP, [158, 316, 474, 632]),
|
||||
(Seedance2p5Ultra, TenEightyP, [337, 673, 1009, 1345]),
|
||||
(Seedance2p0, FourEightyP, [39, 78, 117, 155]),
|
||||
(Seedance2p0, SevenTwentyP, [93, 185, 278, 370]),
|
||||
(Seedance2p0, TenEightyP, [233, 466, 699, 933]),
|
||||
(Seedance2p0, FourK, [463, 925, 1388, 1850]),
|
||||
(Seedance2p0Fast, FourEightyP, [36, 71, 107, 143]),
|
||||
(Seedance2p0Fast, SevenTwentyP, [64, 127, 191, 255]),
|
||||
(Seedance2p0Mini, FourEightyP, [18, 35, 52, 69]),
|
||||
(Seedance2p0Mini, SevenTwentyP, [45, 89, 134, 178]),
|
||||
(Seedance2p0BytePlus, FourEightyP, [50, 100, 150, 200]),
|
||||
(Seedance2p0BytePlus, SevenTwentyP, [125, 250, 375, 500]),
|
||||
(Seedance2p0BytePlus, TenEightyP, [250, 500, 750, 1000]),
|
||||
(Seedance2p0BytePlus, FourK, [475, 950, 1425, 1900]),
|
||||
(Seedance2p0BytePlusUltra, FourEightyP, [50, 100, 150, 200]),
|
||||
(Seedance2p0BytePlusUltra, SevenTwentyP, [125, 250, 375, 500]),
|
||||
(Seedance2p0BytePlusUltra, TenEightyP, [250, 500, 750, 1000]),
|
||||
(Seedance2p0BytePlusUltra, FourK, [475, 950, 1425, 1900]),
|
||||
(Seedance2p0BytePlusFast, FourEightyP, [45, 90, 135, 180]),
|
||||
(Seedance2p0BytePlusFast, SevenTwentyP, [100, 200, 300, 400]),
|
||||
(Seedance2p0BytePlusUltraFast, FourEightyP, [45, 90, 135, 180]),
|
||||
(Seedance2p0BytePlusUltraFast, SevenTwentyP, [100, 200, 300, 400]),
|
||||
(Seedance2p0BytePlusMini, FourEightyP, [18, 36, 54, 71]),
|
||||
(Seedance2p0BytePlusMini, SevenTwentyP, [46, 91, 137, 182]),
|
||||
(Seedance2p0BytePlusUltraMini, FourEightyP, [18, 36, 54, 71]),
|
||||
(Seedance2p0BytePlusUltraMini, SevenTwentyP, [46, 91, 137, 182]),
|
||||
(HappyHorse1p0, SevenTwentyP, [85, 171, 256, 342]),
|
||||
(HappyHorse1p0, TenEightyP, [171, 342, 513, 684]),
|
||||
] {
|
||||
assert_batch_prices(builder(model, resolution, None), expected);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn video_reference_prices_for_every_resolution_and_batch() {
|
||||
// Five output seconds, plus ten measured input seconds for Seedance 2.5.
|
||||
for (model, resolution, expected) in [
|
||||
(Seedance2p5, FourEightyP, [109, 218, 326, 435]),
|
||||
(Seedance2p5, SevenTwentyP, [238, 476, 713, 951]),
|
||||
(Seedance2p5, TenEightyP, [578, 1155, 1733, 2310]),
|
||||
(Seedance2p5Ultra, FourEightyP, [129, 257, 386, 514]),
|
||||
(Seedance2p5Ultra, SevenTwentyP, [281, 562, 843, 1124]),
|
||||
(Seedance2p5Ultra, TenEightyP, [628, 1256, 1884, 2511]),
|
||||
(Seedance2p0, FourEightyP, [45, 89, 133, 177]),
|
||||
(Seedance2p0, SevenTwentyP, [111, 221, 331, 441]),
|
||||
(Seedance2p0, TenEightyP, [256, 511, 767, 1022]),
|
||||
(Seedance2p0, FourK, [569, 1138, 1707, 2276]),
|
||||
(Seedance2p0Fast, FourEightyP, [43, 85, 127, 169]),
|
||||
(Seedance2p0Fast, SevenTwentyP, [78, 156, 234, 312]),
|
||||
// Preserve Mini's existing floating-point round-up behavior.
|
||||
(Seedance2p0Mini, FourEightyP, [22, 44, 66, 88]),
|
||||
(Seedance2p0Mini, SevenTwentyP, [54, 108, 161, 215]),
|
||||
(Seedance2p0BytePlus, FourEightyP, [51, 102, 153, 204]),
|
||||
(Seedance2p0BytePlus, SevenTwentyP, [129, 257, 386, 514]),
|
||||
(Seedance2p0BytePlus, TenEightyP, [289, 578, 867, 1156]),
|
||||
(Seedance2p0BytePlus, FourK, [595, 1190, 1785, 2380]),
|
||||
(Seedance2p0BytePlusUltra, FourEightyP, [51, 102, 153, 204]),
|
||||
(Seedance2p0BytePlusUltra, SevenTwentyP, [129, 257, 386, 514]),
|
||||
(Seedance2p0BytePlusUltra, TenEightyP, [289, 578, 867, 1156]),
|
||||
(Seedance2p0BytePlusUltra, FourK, [595, 1190, 1785, 2380]),
|
||||
(Seedance2p0BytePlusFast, FourEightyP, [49, 97, 145, 193]),
|
||||
(Seedance2p0BytePlusFast, SevenTwentyP, [103, 205, 308, 410]),
|
||||
(Seedance2p0BytePlusUltraFast, FourEightyP, [49, 97, 145, 193]),
|
||||
(Seedance2p0BytePlusUltraFast, SevenTwentyP, [103, 205, 308, 410]),
|
||||
(Seedance2p0BytePlusMini, FourEightyP, [23, 45, 68, 90]),
|
||||
(Seedance2p0BytePlusMini, SevenTwentyP, [55, 110, 165, 219]),
|
||||
(Seedance2p0BytePlusUltraMini, FourEightyP, [23, 45, 68, 90]),
|
||||
(Seedance2p0BytePlusUltraMini, SevenTwentyP, [55, 110, 165, 219]),
|
||||
] {
|
||||
let mut builder = builder(model, resolution, None);
|
||||
builder.reference_videos = Some(VideoListRef::MediaFileTokens(vec![
|
||||
MediaFileToken::new("mf_reference".to_string()),
|
||||
]));
|
||||
builder.total_reference_video_input_seconds = Some(10);
|
||||
assert_batch_prices(builder, expected);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn seedance_2p5_input_duration_clamping_and_fallback_scale_with_batch() {
|
||||
for (model, resolution, minimum, maximum) in [
|
||||
(Seedance2p5, FourEightyP, [66, 131, 196, 261], [254, 507, 761, 1014]),
|
||||
(Seedance2p5, SevenTwentyP, [143, 286, 428, 571], [555, 1110, 1664, 2219]),
|
||||
(Seedance2p5, TenEightyP, [347, 693, 1040, 1386], [1348, 2695, 4043, 5390]),
|
||||
(Seedance2p5Ultra, FourEightyP, [78, 155, 232, 309], [300, 600, 899, 1199]),
|
||||
(Seedance2p5Ultra, SevenTwentyP, [169, 338, 506, 675], [656, 1311, 1967, 2622]),
|
||||
(Seedance2p5Ultra, TenEightyP, [377, 754, 1130, 1507], [1465, 2930, 4394, 5859]),
|
||||
] {
|
||||
for (input_seconds, expected) in [
|
||||
(Some(1), minimum), (Some(4), minimum),
|
||||
(None, maximum), (Some(0), maximum), (Some(30), maximum), (Some(99), maximum),
|
||||
] {
|
||||
let mut builder = builder(model, resolution, None);
|
||||
builder.reference_videos = Some(VideoListRef::MediaFileTokens(vec![
|
||||
MediaFileToken::new("mf_reference".to_string()),
|
||||
]));
|
||||
builder.total_reference_video_input_seconds = input_seconds;
|
||||
assert_batch_prices(builder, expected);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn builder(model: RouterVideoModel, resolution: RouterResolution, batch: Option<u16>) -> GenerateVideoRequestBuilder {
|
||||
GenerateVideoRequestBuilder {
|
||||
provider: RouterProvider::Artcraft,
|
||||
model,
|
||||
resolution: Some(resolution),
|
||||
duration_seconds: Some(5),
|
||||
video_batch_count: batch,
|
||||
request_mismatch_mitigation_strategy: RequestMismatchMitigationStrategy::ErrorOut,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
fn assert_batch_prices(builder: GenerateVideoRequestBuilder, expected: [u64; 4]) {
|
||||
for (index, expected_price) in expected.into_iter().enumerate() {
|
||||
let batch = index as u16 + 1;
|
||||
let mut builder = builder.clone();
|
||||
builder.video_batch_count = Some(batch);
|
||||
let context = format!("{:?}, {:?}, batch {batch}", builder.model, builder.resolution);
|
||||
let request = builder.build2().unwrap();
|
||||
let cost = request.estimate_cost().unwrap();
|
||||
assert_eq!(cost.cost_in_credits, Some(expected_price), "{context}");
|
||||
assert_eq!(cost.cost_in_usd_cents, Some(expected_price), "{context}");
|
||||
assert_eq!(http_request(request).video_batch_count, Some(batch), "{context}");
|
||||
}
|
||||
}
|
||||
|
||||
fn http_request(request: VideoGenerationDraftOrRequest) -> OmniGenVideoCostAndGenerateRequest {
|
||||
match request {
|
||||
VideoGenerationDraftOrRequest::Request(request) => match request {
|
||||
VideoGenerationRequest::ArtcraftSeedance2p5(state) => state.request,
|
||||
VideoGenerationRequest::ArtcraftSeedance2p0(state) => state.request,
|
||||
VideoGenerationRequest::ArtcraftSeedance2p0Fast(state) => state.request,
|
||||
VideoGenerationRequest::ArtcraftSeedance2p0Mini(state) => state.request,
|
||||
VideoGenerationRequest::ArtcraftSeedance2p5Ultra(state) => state.request,
|
||||
VideoGenerationRequest::ArtcraftSeedance2p0BytePlus(state) => state.request,
|
||||
VideoGenerationRequest::ArtcraftSeedance2p0BytePlusFast(state) => state.request,
|
||||
VideoGenerationRequest::ArtcraftSeedance2p0BytePlusMini(state) => state.request,
|
||||
VideoGenerationRequest::ArtcraftSeedance2p0BytePlusUltra(state) => state.request,
|
||||
VideoGenerationRequest::ArtcraftSeedance2p0BytePlusUltraFast(state) => state.request,
|
||||
VideoGenerationRequest::ArtcraftSeedance2p0BytePlusUltraMini(state) => state.request,
|
||||
VideoGenerationRequest::ArtcraftHappyHorse1p0(state) => state.request,
|
||||
other => panic!("Unexpected request: {other:?}"),
|
||||
},
|
||||
other => panic!("Expected ArtCraft request: {other:?}"),
|
||||
}
|
||||
}
|
||||
+2
-1
@@ -2,6 +2,8 @@ pub(crate) mod build_common;
|
||||
pub(crate) mod seedance_common;
|
||||
#[cfg(test)]
|
||||
mod parity_tests;
|
||||
#[cfg(test)]
|
||||
mod kinovi_batch_tests;
|
||||
pub mod grok_imagine_video;
|
||||
pub mod grok_imagine_video_1p5;
|
||||
pub mod flux_3;
|
||||
@@ -43,4 +45,3 @@ pub mod veo_3p1_fast;
|
||||
pub mod veo_3p1_lite;
|
||||
pub mod vidu_q3;
|
||||
pub mod vidu_q3_turbo;
|
||||
|
||||
+8
-15
@@ -3,27 +3,21 @@ use enums::common::generation::common_video_model::CommonVideoModel as CommonVid
|
||||
use crate::errors::artcraft_router_error::ArtcraftRouterError;
|
||||
use crate::generate::generate_video::generate_video_request_builder::GenerateVideoRequestBuilder;
|
||||
use crate::generate::generate_video::providers::artcraft::build_common::{
|
||||
build_artcraft_omni_video_request, plan_mini_batch_count, SupportedResolutions, UltraWideSupport,
|
||||
build_artcraft_omni_video_request, SupportedResolutions, UltraWideSupport,
|
||||
};
|
||||
use crate::generate::generate_video::providers::artcraft::seedance_2p0_bp_mini::request::ArtcraftSeedance2p0BytePlusMiniRequestState;
|
||||
use crate::generate::generate_video::video_generation_draft_or_request::VideoGenerationDraftOrRequest;
|
||||
use crate::generate::generate_video::video_generation_request::VideoGenerationRequest;
|
||||
|
||||
pub fn build_artcraft_seedance_2p0_bp_mini(mut builder: GenerateVideoRequestBuilder) -> Result<VideoGenerationDraftOrRequest, ArtcraftRouterError> {
|
||||
pub fn build_artcraft_seedance_2p0_bp_mini(builder: GenerateVideoRequestBuilder) -> Result<VideoGenerationDraftOrRequest, ArtcraftRouterError> {
|
||||
// Seedance 2.0 Mini supports 480p and 720p only (higher resolutions downgrade
|
||||
// to 720p), and all six aspect ratios including 21:9.
|
||||
// Mini supports batches of 1-8, wider than the shared 1/2/4 planning.
|
||||
// Take the batch out before the shared build and set it after.
|
||||
let strategy = builder.request_mismatch_mitigation_strategy;
|
||||
let batch_count = plan_mini_batch_count(builder.video_batch_count.take(), strategy)?;
|
||||
|
||||
let mut request = build_artcraft_omni_video_request(
|
||||
let request = build_artcraft_omni_video_request(
|
||||
builder,
|
||||
CommonVideoModelEnum::Seedance2p0BytePlusMini,
|
||||
SupportedResolutions::Fast,
|
||||
UltraWideSupport::Supported,
|
||||
)?;
|
||||
request.video_batch_count = Some(batch_count);
|
||||
|
||||
let state = ArtcraftSeedance2p0BytePlusMiniRequestState { request };
|
||||
Ok(VideoGenerationDraftOrRequest::Request(VideoGenerationRequest::ArtcraftSeedance2p0BytePlusMini(state)))
|
||||
@@ -87,18 +81,17 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn batch_count_up_to_eight_passed_through() {
|
||||
// Mini supports batches of 1-8; execution and billing must agree.
|
||||
for batch in 1..=8u16 {
|
||||
fn batch_count_up_to_four_passed_through() {
|
||||
for batch in 1..=4u16 {
|
||||
let req = unwrap_request(builder_with(move |b| { b.video_batch_count = Some(batch); }));
|
||||
assert_eq!(req.request.video_batch_count, Some(batch));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn batch_count_above_eight_clamps_to_eight() {
|
||||
let req = unwrap_request(builder_with(|b| { b.video_batch_count = Some(9); }));
|
||||
assert_eq!(req.request.video_batch_count, Some(8));
|
||||
fn batch_count_above_four_clamps_to_four() {
|
||||
let req = unwrap_request(builder_with(|b| { b.video_batch_count = Some(8); }));
|
||||
assert_eq!(req.request.video_batch_count, Some(4));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
+6
-18
@@ -108,18 +108,13 @@ mod tests {
|
||||
mod batch_pricing {
|
||||
use super::*;
|
||||
|
||||
/// Mini bills batches of 1-8 in full (the platform's 1/2/4 cap does not
|
||||
/// apply). Once, batch 8 executed in full but billed as batch 4.
|
||||
/// ArtCraft exposes batches of 1-4 and bills the entire planned batch.
|
||||
#[test]
|
||||
fn every_batch_size_720p_5s() {
|
||||
assert_eq!(cents(RouterResolution::SevenTwentyP, 5, 1, false), 46);
|
||||
assert_eq!(cents(RouterResolution::SevenTwentyP, 5, 2, false), 91);
|
||||
assert_eq!(cents(RouterResolution::SevenTwentyP, 5, 3, false), 137);
|
||||
assert_eq!(cents(RouterResolution::SevenTwentyP, 5, 4, false), 182);
|
||||
assert_eq!(cents(RouterResolution::SevenTwentyP, 5, 5, false), 228);
|
||||
assert_eq!(cents(RouterResolution::SevenTwentyP, 5, 6, false), 273);
|
||||
assert_eq!(cents(RouterResolution::SevenTwentyP, 5, 7, false), 319);
|
||||
assert_eq!(cents(RouterResolution::SevenTwentyP, 5, 8, false), 364);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -128,23 +123,16 @@ mod tests {
|
||||
assert_eq!(cents(RouterResolution::FourEightyP, 5, 2, false), 36);
|
||||
assert_eq!(cents(RouterResolution::FourEightyP, 5, 3, false), 54);
|
||||
assert_eq!(cents(RouterResolution::FourEightyP, 5, 4, false), 71);
|
||||
assert_eq!(cents(RouterResolution::FourEightyP, 5, 5, false), 89);
|
||||
assert_eq!(cents(RouterResolution::FourEightyP, 5, 6, false), 107);
|
||||
assert_eq!(cents(RouterResolution::FourEightyP, 5, 7, false), 125);
|
||||
assert_eq!(cents(RouterResolution::FourEightyP, 5, 8, false), 142);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn batch_eight_with_video_reference() {
|
||||
assert_eq!(cents(RouterResolution::SevenTwentyP, 5, 8, true), 438);
|
||||
fn batch_four_with_video_reference() {
|
||||
assert_eq!(cents(RouterResolution::SevenTwentyP, 5, 4, true), 219);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn batch_above_eight_clamps_to_eight() {
|
||||
assert_eq!(
|
||||
cents(RouterResolution::SevenTwentyP, 5, 9, false),
|
||||
cents(RouterResolution::SevenTwentyP, 5, 8, false),
|
||||
);
|
||||
fn batch_above_four_clamps_to_four() {
|
||||
assert_eq!(cents(RouterResolution::SevenTwentyP, 5, 8, false), 182);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -160,7 +148,7 @@ mod tests {
|
||||
];
|
||||
for res in resolutions {
|
||||
for dur in [4u16, 5, 10, 15] {
|
||||
for batch in [1u16, 2, 4] {
|
||||
for batch in 1..=4 {
|
||||
for has_ref in [false, true] {
|
||||
let cost = build_cost(res, dur, batch, has_ref);
|
||||
assert_eq!(cost.cost_in_credits, cost.cost_in_usd_cents);
|
||||
|
||||
+8
-15
@@ -3,27 +3,21 @@ use enums::common::generation::common_video_model::CommonVideoModel as CommonVid
|
||||
use crate::errors::artcraft_router_error::ArtcraftRouterError;
|
||||
use crate::generate::generate_video::generate_video_request_builder::GenerateVideoRequestBuilder;
|
||||
use crate::generate::generate_video::providers::artcraft::build_common::{
|
||||
build_artcraft_omni_video_request, plan_mini_batch_count, SupportedResolutions, UltraWideSupport,
|
||||
build_artcraft_omni_video_request, SupportedResolutions, UltraWideSupport,
|
||||
};
|
||||
use crate::generate::generate_video::providers::artcraft::seedance_2p0_bpu_mini::request::ArtcraftSeedance2p0BytePlusUltraMiniRequestState;
|
||||
use crate::generate::generate_video::video_generation_draft_or_request::VideoGenerationDraftOrRequest;
|
||||
use crate::generate::generate_video::video_generation_request::VideoGenerationRequest;
|
||||
|
||||
pub fn build_artcraft_seedance_2p0_bpu_mini(mut builder: GenerateVideoRequestBuilder) -> Result<VideoGenerationDraftOrRequest, ArtcraftRouterError> {
|
||||
pub fn build_artcraft_seedance_2p0_bpu_mini(builder: GenerateVideoRequestBuilder) -> Result<VideoGenerationDraftOrRequest, ArtcraftRouterError> {
|
||||
// Seedance 2.0 Mini supports 480p and 720p only (higher resolutions downgrade
|
||||
// to 720p), and all six aspect ratios including 21:9.
|
||||
// Mini supports batches of 1-8, wider than the shared 1/2/4 planning.
|
||||
// Take the batch out before the shared build and set it after.
|
||||
let strategy = builder.request_mismatch_mitigation_strategy;
|
||||
let batch_count = plan_mini_batch_count(builder.video_batch_count.take(), strategy)?;
|
||||
|
||||
let mut request = build_artcraft_omni_video_request(
|
||||
let request = build_artcraft_omni_video_request(
|
||||
builder,
|
||||
CommonVideoModelEnum::Seedance2p0BytePlusUltraMini,
|
||||
SupportedResolutions::Fast,
|
||||
UltraWideSupport::Supported,
|
||||
)?;
|
||||
request.video_batch_count = Some(batch_count);
|
||||
|
||||
let state = ArtcraftSeedance2p0BytePlusUltraMiniRequestState { request };
|
||||
Ok(VideoGenerationDraftOrRequest::Request(VideoGenerationRequest::ArtcraftSeedance2p0BytePlusUltraMini(state)))
|
||||
@@ -87,18 +81,17 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn batch_count_up_to_eight_passed_through() {
|
||||
// Mini supports batches of 1-8; execution and billing must agree.
|
||||
for batch in 1..=8u16 {
|
||||
fn batch_count_up_to_four_passed_through() {
|
||||
for batch in 1..=4u16 {
|
||||
let req = unwrap_request(builder_with(move |b| { b.video_batch_count = Some(batch); }));
|
||||
assert_eq!(req.request.video_batch_count, Some(batch));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn batch_count_above_eight_clamps_to_eight() {
|
||||
let req = unwrap_request(builder_with(|b| { b.video_batch_count = Some(9); }));
|
||||
assert_eq!(req.request.video_batch_count, Some(8));
|
||||
fn batch_count_above_four_clamps_to_four() {
|
||||
let req = unwrap_request(builder_with(|b| { b.video_batch_count = Some(8); }));
|
||||
assert_eq!(req.request.video_batch_count, Some(4));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
+6
-18
@@ -108,18 +108,13 @@ mod tests {
|
||||
mod batch_pricing {
|
||||
use super::*;
|
||||
|
||||
/// Mini bills batches of 1-8 in full (the platform's 1/2/4 cap does not
|
||||
/// apply). Once, batch 8 executed in full but billed as batch 4.
|
||||
/// ArtCraft exposes batches of 1-4 and bills the entire planned batch.
|
||||
#[test]
|
||||
fn every_batch_size_720p_5s() {
|
||||
assert_eq!(cents(RouterResolution::SevenTwentyP, 5, 1, false), 46);
|
||||
assert_eq!(cents(RouterResolution::SevenTwentyP, 5, 2, false), 91);
|
||||
assert_eq!(cents(RouterResolution::SevenTwentyP, 5, 3, false), 137);
|
||||
assert_eq!(cents(RouterResolution::SevenTwentyP, 5, 4, false), 182);
|
||||
assert_eq!(cents(RouterResolution::SevenTwentyP, 5, 5, false), 228);
|
||||
assert_eq!(cents(RouterResolution::SevenTwentyP, 5, 6, false), 273);
|
||||
assert_eq!(cents(RouterResolution::SevenTwentyP, 5, 7, false), 319);
|
||||
assert_eq!(cents(RouterResolution::SevenTwentyP, 5, 8, false), 364);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -128,23 +123,16 @@ mod tests {
|
||||
assert_eq!(cents(RouterResolution::FourEightyP, 5, 2, false), 36);
|
||||
assert_eq!(cents(RouterResolution::FourEightyP, 5, 3, false), 54);
|
||||
assert_eq!(cents(RouterResolution::FourEightyP, 5, 4, false), 71);
|
||||
assert_eq!(cents(RouterResolution::FourEightyP, 5, 5, false), 89);
|
||||
assert_eq!(cents(RouterResolution::FourEightyP, 5, 6, false), 107);
|
||||
assert_eq!(cents(RouterResolution::FourEightyP, 5, 7, false), 125);
|
||||
assert_eq!(cents(RouterResolution::FourEightyP, 5, 8, false), 142);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn batch_eight_with_video_reference() {
|
||||
assert_eq!(cents(RouterResolution::SevenTwentyP, 5, 8, true), 438);
|
||||
fn batch_four_with_video_reference() {
|
||||
assert_eq!(cents(RouterResolution::SevenTwentyP, 5, 4, true), 219);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn batch_above_eight_clamps_to_eight() {
|
||||
assert_eq!(
|
||||
cents(RouterResolution::SevenTwentyP, 5, 9, false),
|
||||
cents(RouterResolution::SevenTwentyP, 5, 8, false),
|
||||
);
|
||||
fn batch_above_four_clamps_to_four() {
|
||||
assert_eq!(cents(RouterResolution::SevenTwentyP, 5, 8, false), 182);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -160,7 +148,7 @@ mod tests {
|
||||
];
|
||||
for res in resolutions {
|
||||
for dur in [4u16, 5, 10, 15] {
|
||||
for batch in [1u16, 2, 4] {
|
||||
for batch in 1..=4 {
|
||||
for has_ref in [false, true] {
|
||||
let cost = build_cost(res, dur, batch, has_ref);
|
||||
assert_eq!(cost.cost_in_credits, cost.cost_in_usd_cents);
|
||||
|
||||
+8
-15
@@ -3,27 +3,21 @@ use enums::common::generation::common_video_model::CommonVideoModel as CommonVid
|
||||
use crate::errors::artcraft_router_error::ArtcraftRouterError;
|
||||
use crate::generate::generate_video::generate_video_request_builder::GenerateVideoRequestBuilder;
|
||||
use crate::generate::generate_video::providers::artcraft::build_common::{
|
||||
build_artcraft_omni_video_request, plan_mini_batch_count, SupportedResolutions, UltraWideSupport,
|
||||
build_artcraft_omni_video_request, SupportedResolutions, UltraWideSupport,
|
||||
};
|
||||
use crate::generate::generate_video::providers::artcraft::seedance_2p0_mini::request::ArtcraftSeedance2p0MiniRequestState;
|
||||
use crate::generate::generate_video::video_generation_draft_or_request::VideoGenerationDraftOrRequest;
|
||||
use crate::generate::generate_video::video_generation_request::VideoGenerationRequest;
|
||||
|
||||
pub fn build_artcraft_seedance_2p0_mini(mut builder: GenerateVideoRequestBuilder) -> Result<VideoGenerationDraftOrRequest, ArtcraftRouterError> {
|
||||
pub fn build_artcraft_seedance_2p0_mini(builder: GenerateVideoRequestBuilder) -> Result<VideoGenerationDraftOrRequest, ArtcraftRouterError> {
|
||||
// Seedance 2.0 Mini supports 480p and 720p only (higher resolutions downgrade
|
||||
// to 720p), and all six aspect ratios including 21:9.
|
||||
// Mini supports batches of 1-8, wider than the shared 1/2/4 planning.
|
||||
// Take the batch out before the shared build and set it after.
|
||||
let strategy = builder.request_mismatch_mitigation_strategy;
|
||||
let batch_count = plan_mini_batch_count(builder.video_batch_count.take(), strategy)?;
|
||||
|
||||
let mut request = build_artcraft_omni_video_request(
|
||||
let request = build_artcraft_omni_video_request(
|
||||
builder,
|
||||
CommonVideoModelEnum::Seedance2p0Mini,
|
||||
SupportedResolutions::Fast,
|
||||
UltraWideSupport::Supported,
|
||||
)?;
|
||||
request.video_batch_count = Some(batch_count);
|
||||
|
||||
let state = ArtcraftSeedance2p0MiniRequestState { request };
|
||||
Ok(VideoGenerationDraftOrRequest::Request(VideoGenerationRequest::ArtcraftSeedance2p0Mini(state)))
|
||||
@@ -87,18 +81,17 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn batch_count_up_to_eight_passed_through() {
|
||||
// Mini supports batches of 1-8; execution and billing must agree.
|
||||
for batch in 1..=8u16 {
|
||||
fn batch_count_up_to_four_passed_through() {
|
||||
for batch in 1..=4u16 {
|
||||
let req = unwrap_request(builder_with(move |b| { b.video_batch_count = Some(batch); }));
|
||||
assert_eq!(req.request.video_batch_count, Some(batch));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn batch_count_above_eight_clamps_to_eight() {
|
||||
let req = unwrap_request(builder_with(|b| { b.video_batch_count = Some(9); }));
|
||||
assert_eq!(req.request.video_batch_count, Some(8));
|
||||
fn batch_count_above_four_clamps_to_four() {
|
||||
let req = unwrap_request(builder_with(|b| { b.video_batch_count = Some(8); }));
|
||||
assert_eq!(req.request.video_batch_count, Some(4));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
+6
-18
@@ -108,18 +108,13 @@ mod tests {
|
||||
mod batch_pricing {
|
||||
use super::*;
|
||||
|
||||
/// Mini bills batches of 1-8 in full (the platform's 1/2/4 cap does not
|
||||
/// apply). Once, batch 8 executed in full but billed as batch 4.
|
||||
/// ArtCraft exposes batches of 1-4 and bills the entire planned batch.
|
||||
#[test]
|
||||
fn every_batch_size_720p_5s() {
|
||||
assert_eq!(cents(RouterResolution::SevenTwentyP, 5, 1, false), 45);
|
||||
assert_eq!(cents(RouterResolution::SevenTwentyP, 5, 2, false), 89);
|
||||
assert_eq!(cents(RouterResolution::SevenTwentyP, 5, 3, false), 134);
|
||||
assert_eq!(cents(RouterResolution::SevenTwentyP, 5, 4, false), 178);
|
||||
assert_eq!(cents(RouterResolution::SevenTwentyP, 5, 5, false), 223);
|
||||
assert_eq!(cents(RouterResolution::SevenTwentyP, 5, 6, false), 267);
|
||||
assert_eq!(cents(RouterResolution::SevenTwentyP, 5, 7, false), 312);
|
||||
assert_eq!(cents(RouterResolution::SevenTwentyP, 5, 8, false), 356);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -128,23 +123,16 @@ mod tests {
|
||||
assert_eq!(cents(RouterResolution::FourEightyP, 5, 2, false), 35);
|
||||
assert_eq!(cents(RouterResolution::FourEightyP, 5, 3, false), 52);
|
||||
assert_eq!(cents(RouterResolution::FourEightyP, 5, 4, false), 69);
|
||||
assert_eq!(cents(RouterResolution::FourEightyP, 5, 5, false), 87);
|
||||
assert_eq!(cents(RouterResolution::FourEightyP, 5, 6, false), 104);
|
||||
assert_eq!(cents(RouterResolution::FourEightyP, 5, 7, false), 121);
|
||||
assert_eq!(cents(RouterResolution::FourEightyP, 5, 8, false), 138);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn batch_eight_with_video_reference() {
|
||||
assert_eq!(cents(RouterResolution::SevenTwentyP, 5, 8, true), 429);
|
||||
fn batch_four_with_video_reference() {
|
||||
assert_eq!(cents(RouterResolution::SevenTwentyP, 5, 4, true), 215);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn batch_above_eight_clamps_to_eight() {
|
||||
assert_eq!(
|
||||
cents(RouterResolution::SevenTwentyP, 5, 9, false),
|
||||
cents(RouterResolution::SevenTwentyP, 5, 8, false),
|
||||
);
|
||||
fn batch_above_four_clamps_to_four() {
|
||||
assert_eq!(cents(RouterResolution::SevenTwentyP, 5, 8, false), 178);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -160,7 +148,7 @@ mod tests {
|
||||
];
|
||||
for res in resolutions {
|
||||
for dur in [4u16, 5, 10, 15] {
|
||||
for batch in [1u16, 2, 4] {
|
||||
for batch in 1..=4 {
|
||||
for has_ref in [false, true] {
|
||||
let cost = build_cost(res, dur, batch, has_ref);
|
||||
assert_eq!(cost.cost_in_credits, cost.cost_in_usd_cents);
|
||||
|
||||
+6
-29
@@ -43,10 +43,8 @@ pub fn build_artcraft_seedance_2p5(mut builder: GenerateVideoRequestBuilder) ->
|
||||
}
|
||||
|
||||
// 2.5 allows 4-30 second durations (longer than the shared 4-15 range in
|
||||
// build_common) and generates a single video per request. Plan both here,
|
||||
// leaving None for the shared builder, then set them after.
|
||||
// build_common). Plan duration here and use the shared 1-4 batch planner.
|
||||
let duration_seconds = plan_duration(builder.duration_seconds.take(), strategy)?;
|
||||
plan_batch_count(builder.video_batch_count.take(), strategy)?;
|
||||
|
||||
let total_input_seconds = builder.total_reference_video_input_seconds.take();
|
||||
let generate_audio = builder.generate_audio.take();
|
||||
@@ -58,7 +56,6 @@ pub fn build_artcraft_seedance_2p5(mut builder: GenerateVideoRequestBuilder) ->
|
||||
UltraWideSupport::Supported,
|
||||
)?;
|
||||
request.duration_seconds = duration_seconds;
|
||||
request.video_batch_count = Some(1);
|
||||
request.generate_audio = generate_audio;
|
||||
|
||||
let state = ArtcraftSeedance2p5RequestState { request, total_input_seconds };
|
||||
@@ -87,28 +84,6 @@ fn plan_duration(
|
||||
}
|
||||
}
|
||||
|
||||
// Seedance 2.5 generates a single video per request (no batching).
|
||||
fn plan_batch_count(
|
||||
video_batch_count: Option<u16>,
|
||||
strategy: RequestMismatchMitigationStrategy,
|
||||
) -> Result<(), ArtcraftRouterError> {
|
||||
let count = video_batch_count.unwrap_or(1);
|
||||
match count {
|
||||
0 => Err(ArtcraftRouterError::Client(ClientError::UserRequestedZeroGenerations)),
|
||||
1 => Ok(()),
|
||||
_ => match strategy {
|
||||
RequestMismatchMitigationStrategy::ErrorOut => {
|
||||
Err(ArtcraftRouterError::Client(ClientError::ModelDoesNotSupportOption {
|
||||
field: "video_batch_count",
|
||||
value: format!("{}", count),
|
||||
}))
|
||||
}
|
||||
RequestMismatchMitigationStrategy::PayMoreUpgrade
|
||||
| RequestMismatchMitigationStrategy::PayLessDowngrade => Ok(()),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use enums::common::generation::common_resolution::CommonResolution as CommonResolutionEnum;
|
||||
@@ -153,9 +128,11 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn batch_count_is_always_one() {
|
||||
let req = unwrap_request(builder_with(|b| { b.video_batch_count = Some(4); }));
|
||||
assert_eq!(req.request.video_batch_count, Some(1));
|
||||
fn batch_counts_one_through_four_are_preserved() {
|
||||
for batch_count in 1..=4 {
|
||||
let req = unwrap_request(builder_with(|b| { b.video_batch_count = Some(batch_count); }));
|
||||
assert_eq!(req.request.video_batch_count, Some(batch_count));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
+4
-1
@@ -8,10 +8,11 @@ use crate::generate::generate_video::providers::artcraft::seedance_2p5::request:
|
||||
/// — when reference videos are attached — the total seconds of reference
|
||||
/// video input, which are billed on top of the output duration (at a lower
|
||||
/// per-second rate). The TOTAL input duration clamps to the 4..=30 second
|
||||
/// billing range. No batching.
|
||||
/// billing range. Batch count multiplies the total before rounding.
|
||||
pub struct ArtcraftSeedance2p5CostState {
|
||||
pub resolution: CommonResolution,
|
||||
pub duration_seconds: u16,
|
||||
pub batch_count: u16,
|
||||
pub has_video_references: bool,
|
||||
pub maybe_total_input_seconds: Option<u16>,
|
||||
}
|
||||
@@ -29,6 +30,7 @@ impl ArtcraftSeedance2p5CostState {
|
||||
Self {
|
||||
resolution,
|
||||
duration_seconds,
|
||||
batch_count: request.request.video_batch_count.unwrap_or(1),
|
||||
has_video_references,
|
||||
maybe_total_input_seconds: request.total_input_seconds,
|
||||
}
|
||||
@@ -43,6 +45,7 @@ impl ArtcraftSeedance2p5CostState {
|
||||
let usd_cents = seedance_2p5_usd_cents(
|
||||
self.resolution,
|
||||
self.duration_seconds,
|
||||
self.batch_count,
|
||||
self.has_video_references,
|
||||
maybe_total_input_seconds,
|
||||
);
|
||||
|
||||
+6
-29
@@ -43,10 +43,8 @@ pub fn build_artcraft_seedance_2p5_u(mut builder: GenerateVideoRequestBuilder) -
|
||||
}
|
||||
|
||||
// 2.5 allows 4-30 second durations (longer than the shared 4-15 range in
|
||||
// build_common) and generates a single video per request. Plan both here,
|
||||
// leaving None for the shared builder, then set them after.
|
||||
// build_common). Plan duration here and use the shared 1-4 batch planner.
|
||||
let duration_seconds = plan_duration(builder.duration_seconds.take(), strategy)?;
|
||||
plan_batch_count(builder.video_batch_count.take(), strategy)?;
|
||||
|
||||
let total_input_seconds = builder.total_reference_video_input_seconds.take();
|
||||
let generate_audio = builder.generate_audio.take();
|
||||
@@ -58,7 +56,6 @@ pub fn build_artcraft_seedance_2p5_u(mut builder: GenerateVideoRequestBuilder) -
|
||||
UltraWideSupport::Supported,
|
||||
)?;
|
||||
request.duration_seconds = duration_seconds;
|
||||
request.video_batch_count = Some(1);
|
||||
request.generate_audio = generate_audio;
|
||||
|
||||
let state = ArtcraftSeedance2p5UltraRequestState { request, total_input_seconds };
|
||||
@@ -87,28 +84,6 @@ fn plan_duration(
|
||||
}
|
||||
}
|
||||
|
||||
// Seedance 2.5 generates a single video per request (no batching).
|
||||
fn plan_batch_count(
|
||||
video_batch_count: Option<u16>,
|
||||
strategy: RequestMismatchMitigationStrategy,
|
||||
) -> Result<(), ArtcraftRouterError> {
|
||||
let count = video_batch_count.unwrap_or(1);
|
||||
match count {
|
||||
0 => Err(ArtcraftRouterError::Client(ClientError::UserRequestedZeroGenerations)),
|
||||
1 => Ok(()),
|
||||
_ => match strategy {
|
||||
RequestMismatchMitigationStrategy::ErrorOut => {
|
||||
Err(ArtcraftRouterError::Client(ClientError::ModelDoesNotSupportOption {
|
||||
field: "video_batch_count",
|
||||
value: format!("{}", count),
|
||||
}))
|
||||
}
|
||||
RequestMismatchMitigationStrategy::PayMoreUpgrade
|
||||
| RequestMismatchMitigationStrategy::PayLessDowngrade => Ok(()),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use enums::common::generation::common_resolution::CommonResolution as CommonResolutionEnum;
|
||||
@@ -153,9 +128,11 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn batch_count_is_always_one() {
|
||||
let req = unwrap_request(builder_with(|b| { b.video_batch_count = Some(4); }));
|
||||
assert_eq!(req.request.video_batch_count, Some(1));
|
||||
fn batch_counts_one_through_four_are_preserved() {
|
||||
for batch_count in 1..=4 {
|
||||
let req = unwrap_request(builder_with(|b| { b.video_batch_count = Some(batch_count); }));
|
||||
assert_eq!(req.request.video_batch_count, Some(batch_count));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
+4
-1
@@ -9,10 +9,11 @@ use crate::generate::generate_video::providers::artcraft::seedance_2p5_u::reques
|
||||
/// reference video input, which are billed on top of the output duration (at
|
||||
/// a lower per-second rate). Input seconds are clamped to
|
||||
/// [`MAX_BILLED_INPUT_SECONDS`] — the model accepts at most 30 seconds of
|
||||
/// video. No batching.
|
||||
/// video. Batch count multiplies the total before rounding.
|
||||
pub struct ArtcraftSeedance2p5UltraCostState {
|
||||
pub resolution: CommonResolution,
|
||||
pub duration_seconds: u16,
|
||||
pub batch_count: u16,
|
||||
pub has_video_references: bool,
|
||||
pub maybe_total_input_seconds: Option<u16>,
|
||||
}
|
||||
@@ -30,6 +31,7 @@ impl ArtcraftSeedance2p5UltraCostState {
|
||||
Self {
|
||||
resolution,
|
||||
duration_seconds,
|
||||
batch_count: request.request.video_batch_count.unwrap_or(1),
|
||||
has_video_references,
|
||||
maybe_total_input_seconds: request.total_input_seconds,
|
||||
}
|
||||
@@ -44,6 +46,7 @@ impl ArtcraftSeedance2p5UltraCostState {
|
||||
let usd_cents = seedance_2p5_ultra_usd_cents(
|
||||
self.resolution,
|
||||
self.duration_seconds,
|
||||
self.batch_count,
|
||||
self.has_video_references,
|
||||
maybe_total_input_seconds,
|
||||
);
|
||||
|
||||
+7
-5
@@ -155,10 +155,11 @@ const SEEDANCE_2P5_VIDEO_REFERENCE_CENTS_PER_SECOND_1080P: f64 = 38.49728574;
|
||||
/// seconds = output duration. With video references, the per-second rate
|
||||
/// drops but the billed seconds are the output duration PLUS the total
|
||||
/// seconds of reference video input (clamped to the 4..=30 second billing
|
||||
/// range). The fractional total is rounded UP to a whole cent. No batching.
|
||||
/// range). Multiply by batch count before rounding UP to a whole cent.
|
||||
pub fn seedance_2p5_usd_cents(
|
||||
resolution: CommonResolution,
|
||||
duration_seconds: u16,
|
||||
batch_count: u16,
|
||||
has_video_references: bool,
|
||||
maybe_total_input_seconds: Option<u16>,
|
||||
) -> u64 {
|
||||
@@ -193,7 +194,7 @@ pub fn seedance_2p5_usd_cents(
|
||||
(rate, u64::from(duration_seconds))
|
||||
};
|
||||
|
||||
(cents_per_second * billed_seconds as f64).ceil() as u64
|
||||
(cents_per_second * billed_seconds as f64 * batch_count as f64).ceil() as u64
|
||||
}
|
||||
|
||||
/// Seedance 2.5 Ultra — 480p price, USD cents per second.
|
||||
@@ -217,11 +218,12 @@ const SEEDANCE_2P5_ULTRA_VIDEO_REFERENCE_CENTS_PER_SECOND_1080P: f64 = 41.844875
|
||||
/// this fallback is only a failsafe). Without video references, billed
|
||||
/// seconds = output duration. With video references, the per-second rate
|
||||
/// drops but the billed seconds are the output duration PLUS the total
|
||||
/// seconds of reference video input. The fractional total is rounded UP to
|
||||
/// a whole cent. No batching.
|
||||
/// seconds of reference video input. Multiply by batch count before rounding
|
||||
/// UP to a whole cent.
|
||||
pub fn seedance_2p5_ultra_usd_cents(
|
||||
resolution: CommonResolution,
|
||||
duration_seconds: u16,
|
||||
batch_count: u16,
|
||||
has_video_references: bool,
|
||||
maybe_total_input_seconds: Option<u16>,
|
||||
) -> u64 {
|
||||
@@ -256,7 +258,7 @@ pub fn seedance_2p5_ultra_usd_cents(
|
||||
(rate, u64::from(duration_seconds))
|
||||
};
|
||||
|
||||
(cents_per_second * billed_seconds as f64).ceil() as u64
|
||||
(cents_per_second * billed_seconds as f64 * batch_count as f64).ceil() as u64
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
|
||||
+21
-2
@@ -134,6 +134,7 @@ fn plan_batch_count(
|
||||
0 => Err(ArtcraftRouterError::Client(ClientError::UserRequestedZeroGenerations)),
|
||||
1 => Ok(None),
|
||||
2 => Ok(Some(KinoviHappyHorse1p0BatchCount::Two)),
|
||||
3 => Ok(Some(KinoviHappyHorse1p0BatchCount::Three)),
|
||||
4 => Ok(Some(KinoviHappyHorse1p0BatchCount::Four)),
|
||||
_ => match strategy {
|
||||
RequestMismatchMitigationStrategy::ErrorOut => {
|
||||
@@ -143,10 +144,10 @@ fn plan_batch_count(
|
||||
}))
|
||||
}
|
||||
RequestMismatchMitigationStrategy::PayMoreUpgrade => {
|
||||
Ok(Some(if count >= 4 { KinoviHappyHorse1p0BatchCount::Four } else { KinoviHappyHorse1p0BatchCount::Two }))
|
||||
Ok(Some(KinoviHappyHorse1p0BatchCount::Four))
|
||||
}
|
||||
RequestMismatchMitigationStrategy::PayLessDowngrade => {
|
||||
Ok(Some(if count <= 2 { KinoviHappyHorse1p0BatchCount::Two } else { KinoviHappyHorse1p0BatchCount::Four }))
|
||||
Ok(Some(KinoviHappyHorse1p0BatchCount::Four))
|
||||
}
|
||||
},
|
||||
}
|
||||
@@ -177,6 +178,24 @@ mod tests {
|
||||
|
||||
// ── Materialized field conversions ──
|
||||
|
||||
#[test]
|
||||
fn supported_batches_reach_cost_estimation() {
|
||||
use crate::generate::generate_video::providers::kinovi::happy_horse_1p0::cost::KinoviHappyHorse1p0CostState;
|
||||
|
||||
for count in 1..=4 {
|
||||
let builder = GenerateVideoRequestBuilder {
|
||||
video_batch_count: Some(count),
|
||||
duration_seconds: Some(4),
|
||||
resolution: Some(RouterResolution::SevenTwentyP),
|
||||
request_mismatch_mitigation_strategy: RequestMismatchMitigationStrategy::ErrorOut,
|
||||
..happy_horse_builder()
|
||||
};
|
||||
let draft = unwrap_draft(build_kinovi_happy_horse_1p0(builder));
|
||||
let cost = KinoviHappyHorse1p0CostState::from_draft(&draft).estimate_cost();
|
||||
assert_eq!(cost.cost_in_credits, Some((132.0 * f64::from(count)).round() as u64), "batch {count}");
|
||||
}
|
||||
}
|
||||
|
||||
mod materialized_field_conversions {
|
||||
use super::*;
|
||||
|
||||
|
||||
+26
-4
@@ -153,7 +153,7 @@ fn plan_output_resolution(
|
||||
}
|
||||
}
|
||||
|
||||
// Seedance2p0 supports batch counts of 1 through 4.
|
||||
// Seedance2p0 supports batch counts of 1 through 8.
|
||||
fn plan_batch_count(
|
||||
video_batch_count: Option<u16>,
|
||||
strategy: RequestMismatchMitigationStrategy,
|
||||
@@ -165,7 +165,11 @@ fn plan_batch_count(
|
||||
2 => Ok(KinoviBatchCount::Two),
|
||||
3 => Ok(KinoviBatchCount::Three),
|
||||
4 => Ok(KinoviBatchCount::Four),
|
||||
// 5 and above:
|
||||
5 => Ok(KinoviBatchCount::Five),
|
||||
6 => Ok(KinoviBatchCount::Six),
|
||||
7 => Ok(KinoviBatchCount::Seven),
|
||||
8 => Ok(KinoviBatchCount::Eight),
|
||||
// 9 and above:
|
||||
_ => match strategy {
|
||||
RequestMismatchMitigationStrategy::ErrorOut => {
|
||||
Err(ArtcraftRouterError::Client(ClientError::ModelDoesNotSupportOption {
|
||||
@@ -173,8 +177,8 @@ fn plan_batch_count(
|
||||
value: format!("{}", count),
|
||||
}))
|
||||
}
|
||||
RequestMismatchMitigationStrategy::PayMoreUpgrade => Ok(KinoviBatchCount::Four),
|
||||
RequestMismatchMitigationStrategy::PayLessDowngrade => Ok(KinoviBatchCount::Four),
|
||||
RequestMismatchMitigationStrategy::PayMoreUpgrade => Ok(KinoviBatchCount::Eight),
|
||||
RequestMismatchMitigationStrategy::PayLessDowngrade => Ok(KinoviBatchCount::Eight),
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -239,6 +243,24 @@ mod tests {
|
||||
|
||||
// ── Materialized field conversions ──
|
||||
|
||||
#[test]
|
||||
fn supported_batches_reach_cost_estimation() {
|
||||
use crate::generate::generate_video::providers::kinovi::seedance_2p0::cost::KinoviSeedance2p0CostState;
|
||||
|
||||
for count in 1..=8 {
|
||||
let builder = GenerateVideoRequestBuilder {
|
||||
video_batch_count: Some(count),
|
||||
duration_seconds: Some(4),
|
||||
resolution: Some(RouterResolution::FourEightyP),
|
||||
request_mismatch_mitigation_strategy: RequestMismatchMitigationStrategy::ErrorOut,
|
||||
..kinovi_web_builder()
|
||||
};
|
||||
let draft = unwrap_draft(build_kinovi_seedance_2p0(builder));
|
||||
let cost = KinoviSeedance2p0CostState::from_draft(&draft).estimate_cost();
|
||||
assert_eq!(cost.cost_in_credits, Some((60.0 * f64::from(count)).round() as u64), "batch {count}");
|
||||
}
|
||||
}
|
||||
|
||||
mod materialized_field_conversions {
|
||||
use super::*;
|
||||
|
||||
|
||||
+26
-4
@@ -167,7 +167,7 @@ fn plan_output_resolution(
|
||||
}
|
||||
}
|
||||
|
||||
// Seedance2p0Fast supports batch counts of 1 through 4.
|
||||
// Seedance2p0Fast supports batch counts of 1 through 8.
|
||||
fn plan_batch_count(
|
||||
video_batch_count: Option<u16>,
|
||||
strategy: RequestMismatchMitigationStrategy,
|
||||
@@ -179,7 +179,11 @@ fn plan_batch_count(
|
||||
2 => Ok(KinoviBatchCount::Two),
|
||||
3 => Ok(KinoviBatchCount::Three),
|
||||
4 => Ok(KinoviBatchCount::Four),
|
||||
// 5 and above:
|
||||
5 => Ok(KinoviBatchCount::Five),
|
||||
6 => Ok(KinoviBatchCount::Six),
|
||||
7 => Ok(KinoviBatchCount::Seven),
|
||||
8 => Ok(KinoviBatchCount::Eight),
|
||||
// 9 and above:
|
||||
_ => match strategy {
|
||||
RequestMismatchMitigationStrategy::ErrorOut => {
|
||||
Err(ArtcraftRouterError::Client(ClientError::ModelDoesNotSupportOption {
|
||||
@@ -187,8 +191,8 @@ fn plan_batch_count(
|
||||
value: format!("{}", count),
|
||||
}))
|
||||
}
|
||||
RequestMismatchMitigationStrategy::PayMoreUpgrade => Ok(KinoviBatchCount::Four),
|
||||
RequestMismatchMitigationStrategy::PayLessDowngrade => Ok(KinoviBatchCount::Four),
|
||||
RequestMismatchMitigationStrategy::PayMoreUpgrade => Ok(KinoviBatchCount::Eight),
|
||||
RequestMismatchMitigationStrategy::PayLessDowngrade => Ok(KinoviBatchCount::Eight),
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -255,6 +259,24 @@ mod tests {
|
||||
|
||||
// ── Materialized field conversions ──
|
||||
|
||||
#[test]
|
||||
fn supported_batches_reach_cost_estimation() {
|
||||
use crate::generate::generate_video::providers::kinovi::seedance_2p0_fast::cost::KinoviSeedance2p0FastCostState;
|
||||
|
||||
for count in 1..=8 {
|
||||
let builder = GenerateVideoRequestBuilder {
|
||||
video_batch_count: Some(count),
|
||||
duration_seconds: Some(4),
|
||||
resolution: Some(RouterResolution::FourEightyP),
|
||||
request_mismatch_mitigation_strategy: RequestMismatchMitigationStrategy::ErrorOut,
|
||||
..kinovi_web_fast_builder()
|
||||
};
|
||||
let draft = unwrap_draft(build_kinovi_seedance_2p0_fast(builder));
|
||||
let cost = KinoviSeedance2p0FastCostState::from_draft(&draft).estimate_cost();
|
||||
assert_eq!(cost.cost_in_credits, Some((42.0 * f64::from(count)).round() as u64), "batch {count}");
|
||||
}
|
||||
}
|
||||
|
||||
mod materialized_field_conversions {
|
||||
use super::*;
|
||||
|
||||
|
||||
+33
-6
@@ -1,5 +1,6 @@
|
||||
use kinovi_web_client::generate::video::generate_seedance_2p5::{
|
||||
KinoviSeedance2p5AspectRatio as KinoviAspectRatio,
|
||||
KinoviSeedance2p5BatchCount as KinoviBatchCount,
|
||||
KinoviSeedance2p5Bitrate,
|
||||
KinoviSeedance2p5OutputFormat,
|
||||
KinoviSeedance2p5OutputResolution as KinoviOutputResolution,
|
||||
@@ -61,7 +62,7 @@ fn do_build_kinovi_seedance_2p5(mut builder: GenerateVideoRequestBuilder) -> Res
|
||||
// The planned value below only applies to reference mode.
|
||||
let aspect_ratio = plan_aspect_ratio(builder.aspect_ratio.take(), strategy)?;
|
||||
let resolution = plan_output_resolution(builder.resolution.take(), strategy)?;
|
||||
plan_batch_count(builder.video_batch_count.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().unwrap_or_default();
|
||||
|
||||
@@ -82,6 +83,7 @@ fn do_build_kinovi_seedance_2p5(mut builder: GenerateVideoRequestBuilder) -> Res
|
||||
};
|
||||
|
||||
Ok(KinoviSeedance2p5DraftState {
|
||||
batch_count,
|
||||
aspect_ratio,
|
||||
resolution,
|
||||
duration_seconds,
|
||||
@@ -210,16 +212,23 @@ fn plan_output_resolution(
|
||||
}
|
||||
}
|
||||
|
||||
// Seedance 2.5 generates a single video per request (no batching).
|
||||
// Seedance 2.5 supports batches of 1–8 videos.
|
||||
fn plan_batch_count(
|
||||
video_batch_count: Option<u16>,
|
||||
strategy: RequestMismatchMitigationStrategy,
|
||||
) -> Result<(), ArtcraftRouterError> {
|
||||
) -> Result<KinoviBatchCount, ArtcraftRouterError> {
|
||||
let count = video_batch_count.unwrap_or(1);
|
||||
match count {
|
||||
0 => Err(ArtcraftRouterError::Client(ClientError::UserRequestedZeroGenerations)),
|
||||
1 => Ok(()),
|
||||
// Over the maximum of 1.
|
||||
1 => Ok(KinoviBatchCount::One),
|
||||
2 => Ok(KinoviBatchCount::Two),
|
||||
3 => Ok(KinoviBatchCount::Three),
|
||||
4 => Ok(KinoviBatchCount::Four),
|
||||
5 => Ok(KinoviBatchCount::Five),
|
||||
6 => Ok(KinoviBatchCount::Six),
|
||||
7 => Ok(KinoviBatchCount::Seven),
|
||||
8 => Ok(KinoviBatchCount::Eight),
|
||||
// Over the maximum of 8.
|
||||
_ => match strategy {
|
||||
RequestMismatchMitigationStrategy::ErrorOut => {
|
||||
Err(ArtcraftRouterError::Client(ClientError::ModelDoesNotSupportOption {
|
||||
@@ -228,7 +237,7 @@ fn plan_batch_count(
|
||||
}))
|
||||
}
|
||||
RequestMismatchMitigationStrategy::PayMoreUpgrade
|
||||
| RequestMismatchMitigationStrategy::PayLessDowngrade => Ok(()),
|
||||
| RequestMismatchMitigationStrategy::PayLessDowngrade => Ok(KinoviBatchCount::Eight),
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -355,6 +364,24 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn supported_batches_reach_cost_estimation() {
|
||||
use crate::generate::generate_video::providers::kinovi::seedance_2p5::cost::KinoviSeedance2p5CostState;
|
||||
|
||||
for count in 1..=8 {
|
||||
let builder = GenerateVideoRequestBuilder {
|
||||
video_batch_count: Some(count),
|
||||
duration_seconds: Some(4),
|
||||
resolution: Some(RouterResolution::FourEightyP),
|
||||
request_mismatch_mitigation_strategy: RequestMismatchMitigationStrategy::ErrorOut,
|
||||
..base_builder()
|
||||
};
|
||||
let draft = unwrap_draft(build_kinovi_seedance_2p5(builder));
|
||||
let cost = KinoviSeedance2p5CostState::from_draft(&draft).estimate_cost();
|
||||
assert_eq!(cost.cost_in_credits, Some((104.0 * f64::from(count)).round() as u64), "batch {count}");
|
||||
}
|
||||
}
|
||||
|
||||
mod materialized_field_conversions {
|
||||
use super::*;
|
||||
|
||||
|
||||
+7
-3
@@ -1,5 +1,5 @@
|
||||
use kinovi_web_client::generate::video::generate_seedance_2p5::{
|
||||
GenerateSeedance2p5Request, KinoviSeedance2p5Modality, KinoviSeedance2p5OutputResolution,
|
||||
GenerateSeedance2p5Request, KinoviSeedance2p5BatchCount, KinoviSeedance2p5Modality, KinoviSeedance2p5OutputResolution,
|
||||
MAX_BILLED_INPUT_SECONDS, MIN_BILLED_INPUT_SECONDS,
|
||||
};
|
||||
use kinovi_web_client::pricing::kinovi_cost_calculator_trait::KinoviCostCalculatorTrait;
|
||||
@@ -18,6 +18,7 @@ use crate::generate::generate_video::providers::kinovi::seedance_2p5::request::K
|
||||
pub struct KinoviSeedance2p5CostState {
|
||||
pub resolution: Option<KinoviSeedance2p5OutputResolution>,
|
||||
pub duration_seconds: u8,
|
||||
pub batch_count: Option<KinoviSeedance2p5BatchCount>,
|
||||
pub has_video_references: bool,
|
||||
pub total_input_seconds: Option<u8>,
|
||||
}
|
||||
@@ -32,6 +33,7 @@ impl KinoviSeedance2p5CostState {
|
||||
Self {
|
||||
resolution: request.request.output_resolution,
|
||||
duration_seconds: request.request.duration_seconds,
|
||||
batch_count: request.request.batch_count,
|
||||
has_video_references,
|
||||
total_input_seconds: request.request.total_input_seconds,
|
||||
}
|
||||
@@ -49,6 +51,7 @@ impl KinoviSeedance2p5CostState {
|
||||
Self {
|
||||
resolution: draft.resolution,
|
||||
duration_seconds: draft.duration_seconds,
|
||||
batch_count: Some(draft.batch_count),
|
||||
has_video_references,
|
||||
total_input_seconds: draft.total_input_seconds,
|
||||
}
|
||||
@@ -57,7 +60,7 @@ impl KinoviSeedance2p5CostState {
|
||||
pub fn estimate_cost(&self) -> VideoGenerationCostEstimate {
|
||||
// Delegate to the kinovi_web_client calculator via a pricing-only
|
||||
// request. Only the modality's video references, the resolution, the
|
||||
// duration, and the input seconds affect the price.
|
||||
// duration, batch count, and the input seconds affect the price.
|
||||
let modality = if self.has_video_references {
|
||||
KinoviSeedance2p5Modality::Reference {
|
||||
aspect_ratio: None,
|
||||
@@ -89,6 +92,7 @@ impl KinoviSeedance2p5CostState {
|
||||
modality,
|
||||
output_resolution: self.resolution,
|
||||
duration_seconds: self.duration_seconds,
|
||||
batch_count: self.batch_count,
|
||||
total_input_seconds,
|
||||
use_face_blur_hack: None,
|
||||
maybe_bitrate: None,
|
||||
@@ -230,7 +234,7 @@ mod tests {
|
||||
has_video_references: bool,
|
||||
total_input_seconds: Option<u8>,
|
||||
) -> KinoviSeedance2p5CostState {
|
||||
KinoviSeedance2p5CostState { resolution, duration_seconds, has_video_references, total_input_seconds }
|
||||
KinoviSeedance2p5CostState { batch_count: None, resolution, duration_seconds, has_video_references, total_input_seconds }
|
||||
}
|
||||
|
||||
fn credits(
|
||||
|
||||
+3
-1
@@ -1,5 +1,5 @@
|
||||
use kinovi_web_client::generate::video::generate_seedance_2p5::{
|
||||
GenerateSeedance2p5Request, KinoviSeedance2p5AspectRatio, KinoviSeedance2p5Bitrate, KinoviSeedance2p5Modality,
|
||||
GenerateSeedance2p5Request, KinoviSeedance2p5BatchCount, KinoviSeedance2p5AspectRatio, KinoviSeedance2p5Bitrate, KinoviSeedance2p5Modality,
|
||||
KinoviSeedance2p5OutputFormat, KinoviSeedance2p5OutputResolution,
|
||||
};
|
||||
|
||||
@@ -27,6 +27,7 @@ pub struct KinoviSeedance2p5DraftState {
|
||||
pub resolution: Option<KinoviSeedance2p5OutputResolution>,
|
||||
|
||||
pub duration_seconds: u8,
|
||||
pub batch_count: KinoviSeedance2p5BatchCount,
|
||||
|
||||
/// Calculation-only: total seconds of reference video input, summed across
|
||||
/// all reference videos (each rounded up to a whole second).
|
||||
@@ -113,6 +114,7 @@ impl KinoviSeedance2p5DraftState {
|
||||
modality,
|
||||
output_resolution: self.resolution,
|
||||
duration_seconds: self.duration_seconds,
|
||||
batch_count: Some(self.batch_count),
|
||||
total_input_seconds: self.total_input_seconds,
|
||||
use_face_blur_hack: None,
|
||||
maybe_bitrate: self.maybe_bitrate,
|
||||
|
||||
+39
-11
@@ -1,5 +1,6 @@
|
||||
use kinovi_web_client::generate::video::generate_seedance_2p5_preview::{
|
||||
KinoviSeedance2p5PreviewAspectRatio as KinoviAspectRatio,
|
||||
KinoviSeedance2p5PreviewBatchCount as KinoviBatchCount,
|
||||
KinoviSeedance2p5PreviewOutputResolution as KinoviOutputResolution,
|
||||
};
|
||||
|
||||
@@ -47,7 +48,7 @@ fn do_build_kinovi_seedance_2p5_preview(mut builder: GenerateVideoRequestBuilder
|
||||
|
||||
let aspect_ratio = plan_aspect_ratio(builder.aspect_ratio.take(), strategy)?;
|
||||
let resolution = plan_output_resolution(builder.resolution.take(), strategy)?;
|
||||
plan_batch_count(builder.video_batch_count.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().unwrap_or_default();
|
||||
|
||||
@@ -61,6 +62,7 @@ fn do_build_kinovi_seedance_2p5_preview(mut builder: GenerateVideoRequestBuilder
|
||||
};
|
||||
|
||||
Ok(KinoviSeedance2p5PreviewDraftState {
|
||||
batch_count,
|
||||
aspect_ratio,
|
||||
resolution,
|
||||
duration_seconds,
|
||||
@@ -177,16 +179,23 @@ fn plan_output_resolution(
|
||||
}
|
||||
}
|
||||
|
||||
// Seedance 2.5 Preview generates a single video per request (no batching).
|
||||
// Seedance 2.5 Preview supports batches of 1–8 videos.
|
||||
fn plan_batch_count(
|
||||
video_batch_count: Option<u16>,
|
||||
strategy: RequestMismatchMitigationStrategy,
|
||||
) -> Result<(), ArtcraftRouterError> {
|
||||
) -> Result<KinoviBatchCount, ArtcraftRouterError> {
|
||||
let count = video_batch_count.unwrap_or(1);
|
||||
match count {
|
||||
0 => Err(ArtcraftRouterError::Client(ClientError::UserRequestedZeroGenerations)),
|
||||
1 => Ok(()),
|
||||
// Over the maximum of 1.
|
||||
1 => Ok(KinoviBatchCount::One),
|
||||
2 => Ok(KinoviBatchCount::Two),
|
||||
3 => Ok(KinoviBatchCount::Three),
|
||||
4 => Ok(KinoviBatchCount::Four),
|
||||
5 => Ok(KinoviBatchCount::Five),
|
||||
6 => Ok(KinoviBatchCount::Six),
|
||||
7 => Ok(KinoviBatchCount::Seven),
|
||||
8 => Ok(KinoviBatchCount::Eight),
|
||||
// Over the maximum of 8.
|
||||
_ => match strategy {
|
||||
RequestMismatchMitigationStrategy::ErrorOut => {
|
||||
Err(ArtcraftRouterError::Client(ClientError::ModelDoesNotSupportOption {
|
||||
@@ -195,7 +204,7 @@ fn plan_batch_count(
|
||||
}))
|
||||
}
|
||||
RequestMismatchMitigationStrategy::PayMoreUpgrade
|
||||
| RequestMismatchMitigationStrategy::PayLessDowngrade => Ok(()),
|
||||
| RequestMismatchMitigationStrategy::PayLessDowngrade => Ok(KinoviBatchCount::Eight),
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -248,6 +257,24 @@ mod tests {
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn supported_batches_reach_cost_estimation() {
|
||||
use crate::generate::generate_video::providers::kinovi::seedance_2p5_preview::cost::KinoviSeedance2p5PreviewCostState;
|
||||
|
||||
for count in 1..=8 {
|
||||
let builder = GenerateVideoRequestBuilder {
|
||||
video_batch_count: Some(count),
|
||||
duration_seconds: Some(4),
|
||||
resolution: Some(RouterResolution::FourEightyP),
|
||||
request_mismatch_mitigation_strategy: RequestMismatchMitigationStrategy::ErrorOut,
|
||||
..preview_builder()
|
||||
};
|
||||
let draft = unwrap_draft(build_kinovi_seedance_2p5_preview(builder));
|
||||
let cost = KinoviSeedance2p5PreviewCostState::from_draft(&draft).estimate_cost();
|
||||
assert_eq!(cost.cost_in_credits, Some((168.52 * f64::from(count)).round() as u64), "batch {count}");
|
||||
}
|
||||
}
|
||||
|
||||
mod materialized_field_conversions {
|
||||
use super::*;
|
||||
|
||||
@@ -346,19 +373,20 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn batch_over_one_downgrades_to_single() {
|
||||
fn batch_over_eight_downgrades_to_eight() {
|
||||
let builder = GenerateVideoRequestBuilder {
|
||||
video_batch_count: Some(4),
|
||||
video_batch_count: Some(9),
|
||||
request_mismatch_mitigation_strategy: RequestMismatchMitigationStrategy::PayLessDowngrade,
|
||||
..preview_builder()
|
||||
};
|
||||
assert!(build_kinovi_seedance_2p5_preview(builder).is_ok());
|
||||
let draft = unwrap_draft(build_kinovi_seedance_2p5_preview(builder));
|
||||
assert!(matches!(draft.batch_count, KinoviBatchCount::Eight));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn batch_over_one_error_out() {
|
||||
fn batch_over_eight_error_out() {
|
||||
let builder = GenerateVideoRequestBuilder {
|
||||
video_batch_count: Some(4),
|
||||
video_batch_count: Some(9),
|
||||
request_mismatch_mitigation_strategy: RequestMismatchMitigationStrategy::ErrorOut,
|
||||
..preview_builder()
|
||||
};
|
||||
|
||||
+8
-5
@@ -1,18 +1,18 @@
|
||||
use kinovi_web_client::pricing::kinovi_cost_calculator_trait::KinoviCostCalculatorTrait;
|
||||
use kinovi_web_client::generate::video::generate_seedance_2p5_preview::{
|
||||
GenerateSeedance2p5PreviewRequest, KinoviSeedance2p5PreviewOutputResolution,
|
||||
GenerateSeedance2p5PreviewRequest, KinoviSeedance2p5PreviewBatchCount, KinoviSeedance2p5PreviewOutputResolution,
|
||||
};
|
||||
|
||||
use crate::generate::generate_video::video_generation_cost_estimate::VideoGenerationCostEstimate;
|
||||
use crate::generate::generate_video::providers::kinovi::seedance_2p5_preview::draft::KinoviSeedance2p5PreviewDraftState;
|
||||
use crate::generate::generate_video::providers::kinovi::seedance_2p5_preview::request::KinoviSeedance2p5PreviewRequestState;
|
||||
|
||||
/// Only the resolution and duration matter — Seedance 2.5 Preview has no
|
||||
/// video-reference surcharge and no batching, so references never change the
|
||||
/// price.
|
||||
/// Resolution, duration, and batch count determine the price.
|
||||
/// Seedance 2.5 Preview has no video-reference surcharge.
|
||||
pub struct KinoviSeedance2p5PreviewCostState {
|
||||
pub resolution: Option<KinoviSeedance2p5PreviewOutputResolution>,
|
||||
pub duration_seconds: u8,
|
||||
pub batch_count: Option<KinoviSeedance2p5PreviewBatchCount>,
|
||||
}
|
||||
|
||||
impl KinoviSeedance2p5PreviewCostState {
|
||||
@@ -20,6 +20,7 @@ impl KinoviSeedance2p5PreviewCostState {
|
||||
Self {
|
||||
resolution: request.request.output_resolution,
|
||||
duration_seconds: request.request.duration_seconds,
|
||||
batch_count: request.request.batch_count,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,6 +28,7 @@ impl KinoviSeedance2p5PreviewCostState {
|
||||
Self {
|
||||
resolution: draft.resolution,
|
||||
duration_seconds: draft.duration_seconds,
|
||||
batch_count: Some(draft.batch_count),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,6 +36,7 @@ impl KinoviSeedance2p5PreviewCostState {
|
||||
let pricing_request = GenerateSeedance2p5PreviewRequest {
|
||||
output_resolution: self.resolution,
|
||||
duration_seconds: self.duration_seconds,
|
||||
batch_count: self.batch_count,
|
||||
|
||||
// No impact on price (references never affect 2.5 Preview pricing)
|
||||
prompt: String::new(),
|
||||
@@ -139,7 +142,7 @@ mod tests {
|
||||
// ── Helpers ──
|
||||
|
||||
fn cost_state(resolution: Option<KinoviOutputResolution>, duration_seconds: u8) -> KinoviSeedance2p5PreviewCostState {
|
||||
KinoviSeedance2p5PreviewCostState { resolution, duration_seconds }
|
||||
KinoviSeedance2p5PreviewCostState { batch_count: None, resolution, duration_seconds }
|
||||
}
|
||||
|
||||
fn credits(resolution: Option<KinoviOutputResolution>, duration_seconds: u8) -> u64 {
|
||||
|
||||
+3
-1
@@ -1,5 +1,5 @@
|
||||
use kinovi_web_client::generate::video::generate_seedance_2p5_preview::{
|
||||
GenerateSeedance2p5PreviewRequest, KinoviSeedance2p5PreviewAspectRatio,
|
||||
GenerateSeedance2p5PreviewRequest, KinoviSeedance2p5PreviewBatchCount, KinoviSeedance2p5PreviewAspectRatio,
|
||||
KinoviSeedance2p5PreviewOutputResolution,
|
||||
};
|
||||
|
||||
@@ -20,6 +20,7 @@ pub struct KinoviSeedance2p5PreviewDraftState {
|
||||
pub aspect_ratio: KinoviSeedance2p5PreviewAspectRatio,
|
||||
pub resolution: Option<KinoviSeedance2p5PreviewOutputResolution>,
|
||||
pub duration_seconds: u8,
|
||||
pub batch_count: KinoviSeedance2p5PreviewBatchCount,
|
||||
|
||||
pub unhandled_request_state: Option<KinoviSeedance2p5PreviewRemainingItems>,
|
||||
}
|
||||
@@ -67,6 +68,7 @@ impl KinoviSeedance2p5PreviewDraftState {
|
||||
aspect_ratio: Some(self.aspect_ratio),
|
||||
output_resolution: self.resolution,
|
||||
duration_seconds: self.duration_seconds,
|
||||
batch_count: Some(self.batch_count),
|
||||
reference_image_urls,
|
||||
reference_video_urls,
|
||||
reference_audio_urls,
|
||||
|
||||
+64
-4
@@ -49,6 +49,7 @@ pub enum KinoviHappyHorse1p0OutputResolution {
|
||||
pub enum KinoviHappyHorse1p0BatchCount {
|
||||
One,
|
||||
Two,
|
||||
Three,
|
||||
Four,
|
||||
}
|
||||
|
||||
@@ -79,6 +80,7 @@ impl GenerateHappyHorse1p0Request {
|
||||
let batch_multiplier: u64 = match self.batch_count {
|
||||
None | Some(KinoviHappyHorse1p0BatchCount::One) => 1,
|
||||
Some(KinoviHappyHorse1p0BatchCount::Two) => 2,
|
||||
Some(KinoviHappyHorse1p0BatchCount::Three) => 3,
|
||||
Some(KinoviHappyHorse1p0BatchCount::Four) => 4,
|
||||
};
|
||||
|
||||
@@ -174,6 +176,7 @@ fn map_batch_count(bc: Option<KinoviHappyHorse1p0BatchCount>) -> KinoviBatchCoun
|
||||
match bc {
|
||||
Some(KinoviHappyHorse1p0BatchCount::One) | None => KinoviBatchCountRaw::One,
|
||||
Some(KinoviHappyHorse1p0BatchCount::Two) => KinoviBatchCountRaw::Two,
|
||||
Some(KinoviHappyHorse1p0BatchCount::Three) => KinoviBatchCountRaw::Three,
|
||||
Some(KinoviHappyHorse1p0BatchCount::Four) => KinoviBatchCountRaw::Four,
|
||||
}
|
||||
}
|
||||
@@ -275,11 +278,68 @@ mod tests {
|
||||
mod batch_tests {
|
||||
use super::*;
|
||||
|
||||
const BATCHES: [KinoviHappyHorse1p0BatchCount; 4] = [
|
||||
KinoviHappyHorse1p0BatchCount::One,
|
||||
KinoviHappyHorse1p0BatchCount::Two,
|
||||
KinoviHappyHorse1p0BatchCount::Three,
|
||||
KinoviHappyHorse1p0BatchCount::Four,
|
||||
];
|
||||
|
||||
#[test]
|
||||
fn batch_1_is_base() {
|
||||
let base = r720(5).calculate_costs().kinovi_credits;
|
||||
let explicit = make_request(5, None, Some(KinoviHappyHorse1p0BatchCount::One)).calculate_costs().kinovi_credits;
|
||||
assert_eq!(base, explicit);
|
||||
fn fixed_five_second_batch_prices() {
|
||||
// Each pair is (credits, USD cents rounded up), for batches 1 through 4.
|
||||
let cases = [
|
||||
(KinoviHappyHorse1p0OutputResolution::SevenTwentyP, [
|
||||
(165, 68), (330, 136), (495, 204), (660, 272),
|
||||
]),
|
||||
(KinoviHappyHorse1p0OutputResolution::TenEightyP, [
|
||||
(330, 136), (660, 272), (990, 408), (1320, 544),
|
||||
]),
|
||||
];
|
||||
for (resolution, prices) in cases {
|
||||
for (batch, (credits, usd_cents)) in BATCHES.into_iter().zip(prices) {
|
||||
let cost = make_request(5, Some(resolution), Some(batch)).calculate_costs();
|
||||
assert_eq!(
|
||||
(cost.kinovi_credits, cost.usd_cents_rounded_up),
|
||||
(credits, usd_cents),
|
||||
"{resolution:?} {batch:?}",
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn all_batches_scale_every_resolution_and_round_the_total() {
|
||||
let batches = [
|
||||
(None, 1),
|
||||
(Some(KinoviHappyHorse1p0BatchCount::One), 1),
|
||||
(Some(KinoviHappyHorse1p0BatchCount::Two), 2),
|
||||
(Some(KinoviHappyHorse1p0BatchCount::Three), 3),
|
||||
(Some(KinoviHappyHorse1p0BatchCount::Four), 4),
|
||||
];
|
||||
for resolution in [
|
||||
None,
|
||||
Some(KinoviHappyHorse1p0OutputResolution::SevenTwentyP),
|
||||
Some(KinoviHappyHorse1p0OutputResolution::TenEightyP),
|
||||
] {
|
||||
for duration in [3, 5, 15] {
|
||||
let single = make_request(duration, resolution, None).calculate_costs();
|
||||
for (batch, count) in batches {
|
||||
let batched = make_request(duration, resolution, batch).calculate_costs();
|
||||
let context = format!("{resolution:?} {duration}s {batch:?}");
|
||||
let expected_credits = single.kinovi_credits * count;
|
||||
assert_eq!(batched.kinovi_credits, expected_credits, "{context}: credits");
|
||||
assert!(
|
||||
(batched.usd_cents_fractional - single.usd_cents_fractional * count as f64).abs() < 1e-8,
|
||||
"{context}: fractional cents",
|
||||
);
|
||||
// Happy Horse currently uses the legacy 243 credits/dollar conversion.
|
||||
let numerator = expected_credits * 100;
|
||||
assert_eq!(batched.usd_cents_rounded_up, numerator.div_ceil(243), "{context}: rounded up");
|
||||
assert_eq!(batched.usd_cents_rounded_down, numerator / 243, "{context}: rounded down");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -68,6 +68,10 @@ pub enum KinoviSeedance2p0BatchCount {
|
||||
Two,
|
||||
Three,
|
||||
Four,
|
||||
Five,
|
||||
Six,
|
||||
Seven,
|
||||
Eight,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
@@ -166,6 +170,10 @@ impl KinoviCostCalculatorTrait for GenerateSeedance2p0Request {
|
||||
Some(KinoviSeedance2p0BatchCount::Two) => 2.0,
|
||||
Some(KinoviSeedance2p0BatchCount::Three) => 3.0,
|
||||
Some(KinoviSeedance2p0BatchCount::Four) => 4.0,
|
||||
Some(KinoviSeedance2p0BatchCount::Five) => 5.0,
|
||||
Some(KinoviSeedance2p0BatchCount::Six) => 6.0,
|
||||
Some(KinoviSeedance2p0BatchCount::Seven) => 7.0,
|
||||
Some(KinoviSeedance2p0BatchCount::Eight) => 8.0,
|
||||
};
|
||||
|
||||
let output_seconds = f64::from(self.duration_seconds) * batch_multiplier;
|
||||
@@ -274,6 +282,10 @@ fn map_batch_count(bc: Option<KinoviSeedance2p0BatchCount>) -> KinoviBatchCountR
|
||||
Some(KinoviSeedance2p0BatchCount::Two) => KinoviBatchCountRaw::Two,
|
||||
Some(KinoviSeedance2p0BatchCount::Three) => KinoviBatchCountRaw::Three,
|
||||
Some(KinoviSeedance2p0BatchCount::Four) => KinoviBatchCountRaw::Four,
|
||||
Some(KinoviSeedance2p0BatchCount::Five) => KinoviBatchCountRaw::Five,
|
||||
Some(KinoviSeedance2p0BatchCount::Six) => KinoviBatchCountRaw::Six,
|
||||
Some(KinoviSeedance2p0BatchCount::Seven) => KinoviBatchCountRaw::Seven,
|
||||
Some(KinoviSeedance2p0BatchCount::Eight) => KinoviBatchCountRaw::Eight,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -612,18 +624,165 @@ mod tests {
|
||||
|
||||
mod batch_tests {
|
||||
use super::*;
|
||||
use crate::test_utils::assert_batch_cost::assert_batch_cost;
|
||||
|
||||
const BATCHES: [KinoviSeedance2p0BatchCount; 8] = [
|
||||
KinoviSeedance2p0BatchCount::One,
|
||||
KinoviSeedance2p0BatchCount::Two,
|
||||
KinoviSeedance2p0BatchCount::Three,
|
||||
KinoviSeedance2p0BatchCount::Four,
|
||||
KinoviSeedance2p0BatchCount::Five,
|
||||
KinoviSeedance2p0BatchCount::Six,
|
||||
KinoviSeedance2p0BatchCount::Seven,
|
||||
KinoviSeedance2p0BatchCount::Eight,
|
||||
];
|
||||
|
||||
// Each pair is (credits, USD cents rounded up), for batches 1 through 8.
|
||||
// Pin literal totals so rate changes fail independently of scaling checks.
|
||||
#[test]
|
||||
fn fixed_five_second_batch_prices_without_video_reference() {
|
||||
let cases = [
|
||||
(KinoviSeedance2p0OutputResolution::FourEightyP, KinoviPricingTier::Consumer, [
|
||||
(75.0, 39), (150.0, 78), (225.0, 117), (300.0, 156),
|
||||
(375.0, 195), (450.0, 234), (525.0, 273), (600.0, 311),
|
||||
]),
|
||||
(KinoviSeedance2p0OutputResolution::SevenTwentyP, KinoviPricingTier::Consumer, [
|
||||
(200.0, 104), (400.0, 208), (600.0, 311), (800.0, 415),
|
||||
(1000.0, 519), (1200.0, 622), (1400.0, 726), (1600.0, 830),
|
||||
]),
|
||||
(KinoviSeedance2p0OutputResolution::TenEightyP, KinoviPricingTier::Consumer, [
|
||||
(450.0, 234), (900.0, 467), (1350.0, 700), (1800.0, 933),
|
||||
(2250.0, 1166), (2700.0, 1400), (3150.0, 1633), (3600.0, 1866),
|
||||
]),
|
||||
(KinoviSeedance2p0OutputResolution::FourK, KinoviPricingTier::Consumer, [
|
||||
(1000.0, 519), (2000.0, 1037), (3000.0, 1555), (4000.0, 2073),
|
||||
(5000.0, 2591), (6000.0, 3110), (7000.0, 3628), (8000.0, 4146),
|
||||
]),
|
||||
(KinoviSeedance2p0OutputResolution::FourEightyP, KinoviPricingTier::Enterprise, [
|
||||
(75.0, 31), (150.0, 62), (225.0, 93), (300.0, 124),
|
||||
(375.0, 155), (450.0, 186), (525.0, 216), (600.0, 247),
|
||||
]),
|
||||
(KinoviSeedance2p0OutputResolution::SevenTwentyP, KinoviPricingTier::Enterprise, [
|
||||
(189.5, 78), (379.0, 156), (568.5, 234), (758.0, 312),
|
||||
(947.5, 390), (1137.0, 468), (1326.5, 546), (1516.0, 624),
|
||||
]),
|
||||
(KinoviSeedance2p0OutputResolution::TenEightyP, KinoviPricingTier::Enterprise, [
|
||||
(450.0, 186), (900.0, 371), (1350.0, 556), (1800.0, 741),
|
||||
(2250.0, 926), (2700.0, 1111), (3150.0, 1296), (3600.0, 1481),
|
||||
]),
|
||||
(KinoviSeedance2p0OutputResolution::FourK, KinoviPricingTier::Enterprise, [
|
||||
(1000.0, 412), (2000.0, 823), (3000.0, 1234), (4000.0, 1646),
|
||||
(5000.0, 2057), (6000.0, 2468), (7000.0, 2879), (8000.0, 3291),
|
||||
]),
|
||||
];
|
||||
for (resolution, tier, prices) in cases {
|
||||
for (batch, (credits, usd_cents)) in BATCHES.into_iter().zip(prices) {
|
||||
let request = build_request(5, Some(resolution), Some(batch));
|
||||
let cost = request.calculate_costs(tier).total_cost;
|
||||
assert_eq!(
|
||||
(cost.kinovi_credits, cost.usd_cents_rounded_up),
|
||||
(credits, usd_cents),
|
||||
"{resolution:?} {tier:?} {batch:?}",
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn batch_multiplies_at_both_tiers() {
|
||||
fn fixed_five_second_batch_prices_with_video_reference() {
|
||||
let cases = [
|
||||
(KinoviSeedance2p0OutputResolution::FourEightyP, KinoviPricingTier::Consumer, [
|
||||
(95.0, 50), (190.0, 99), (285.0, 148), (380.0, 197),
|
||||
(475.0, 247), (570.0, 296), (665.0, 345), (760.0, 394),
|
||||
]),
|
||||
(KinoviSeedance2p0OutputResolution::SevenTwentyP, KinoviPricingTier::Consumer, [
|
||||
(240.0, 125), (480.0, 249), (720.0, 374), (960.0, 498),
|
||||
(1200.0, 622), (1440.0, 747), (1680.0, 871), (1920.0, 995),
|
||||
]),
|
||||
(KinoviSeedance2p0OutputResolution::TenEightyP, KinoviPricingTier::Consumer, [
|
||||
(540.0, 280), (1080.0, 560), (1620.0, 840), (2160.0, 1120),
|
||||
(2700.0, 1400), (3240.0, 1679), (3780.0, 1959), (4320.0, 2239),
|
||||
]),
|
||||
(KinoviSeedance2p0OutputResolution::FourK, KinoviPricingTier::Consumer, [
|
||||
(1200.0, 622), (2400.0, 1244), (3600.0, 1866), (4800.0, 2488),
|
||||
(6000.0, 3110), (7200.0, 3731), (8400.0, 4353), (9600.0, 4975),
|
||||
]),
|
||||
(KinoviSeedance2p0OutputResolution::FourEightyP, KinoviPricingTier::Enterprise, [
|
||||
(95.0, 40), (190.0, 79), (285.0, 118), (380.0, 157),
|
||||
(475.0, 196), (570.0, 235), (665.0, 274), (760.0, 313),
|
||||
]),
|
||||
(KinoviSeedance2p0OutputResolution::SevenTwentyP, KinoviPricingTier::Enterprise, [
|
||||
(227.4, 94), (454.8, 188), (682.2, 281), (909.6, 375),
|
||||
(1137.0, 468), (1364.4, 562), (1591.8, 655), (1819.2, 749),
|
||||
]),
|
||||
(KinoviSeedance2p0OutputResolution::TenEightyP, KinoviPricingTier::Enterprise, [
|
||||
(540.0, 223), (1080.0, 445), (1620.0, 667), (2160.0, 889),
|
||||
(2700.0, 1111), (3240.0, 1333), (3780.0, 1555), (4320.0, 1777),
|
||||
]),
|
||||
(KinoviSeedance2p0OutputResolution::FourK, KinoviPricingTier::Enterprise, [
|
||||
(1200.0, 494), (2400.0, 988), (3600.0, 1481), (4800.0, 1975),
|
||||
(6000.0, 2468), (7200.0, 2962), (8400.0, 3455), (9600.0, 3949),
|
||||
]),
|
||||
];
|
||||
for (resolution, tier, prices) in cases {
|
||||
for (batch, (credits, usd_cents)) in BATCHES.into_iter().zip(prices) {
|
||||
let request = build_request(5, Some(resolution), Some(batch));
|
||||
let request = with_video_ref(request);
|
||||
let cost = request.calculate_costs(tier).total_cost;
|
||||
assert_eq!(
|
||||
(cost.kinovi_credits, cost.usd_cents_rounded_up),
|
||||
(credits, usd_cents),
|
||||
"{resolution:?} {tier:?} {batch:?}",
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn all_batches_scale_every_resolution_and_surcharge_at_both_tiers() {
|
||||
let batches = [
|
||||
(None, 1),
|
||||
(Some(KinoviSeedance2p0BatchCount::One), 1),
|
||||
(Some(KinoviSeedance2p0BatchCount::Two), 2),
|
||||
(Some(KinoviSeedance2p0BatchCount::Three), 3),
|
||||
(Some(KinoviSeedance2p0BatchCount::Four), 4),
|
||||
(Some(KinoviSeedance2p0BatchCount::Five), 5),
|
||||
(Some(KinoviSeedance2p0BatchCount::Six), 6),
|
||||
(Some(KinoviSeedance2p0BatchCount::Seven), 7),
|
||||
(Some(KinoviSeedance2p0BatchCount::Eight), 8),
|
||||
];
|
||||
for tier in [KinoviPricingTier::Enterprise, KinoviPricingTier::Consumer] {
|
||||
let base = r720(5).calculate_costs(tier).total_cost.kinovi_credits;
|
||||
for (batch, multiplier) in [
|
||||
(KinoviSeedance2p0BatchCount::Two, 2.0),
|
||||
(KinoviSeedance2p0BatchCount::Three, 3.0),
|
||||
(KinoviSeedance2p0BatchCount::Four, 4.0),
|
||||
for resolution in [
|
||||
None,
|
||||
Some(KinoviSeedance2p0OutputResolution::FourEightyP),
|
||||
Some(KinoviSeedance2p0OutputResolution::SevenTwentyP),
|
||||
Some(KinoviSeedance2p0OutputResolution::TenEightyP),
|
||||
Some(KinoviSeedance2p0OutputResolution::FourK),
|
||||
] {
|
||||
let batched = build_request(5, None, Some(batch)).calculate_costs(tier).total_cost.kinovi_credits;
|
||||
assert_eq!(batched, base * multiplier, "{batch:?} at {tier:?}");
|
||||
for duration in [4, 5, 15] {
|
||||
for references in [
|
||||
None,
|
||||
Some(vec![]),
|
||||
Some(vec!["https://example.com/ref.mp4".to_string()]),
|
||||
Some(vec!["https://example.com/ref1.mp4".to_string(), "https://example.com/ref2.mp4".to_string()]),
|
||||
] {
|
||||
let mut request = build_request(duration, resolution, None);
|
||||
request.reference_video_urls = references;
|
||||
let single = request.calculate_costs(tier);
|
||||
for (batch, count) in batches {
|
||||
request.batch_count = batch;
|
||||
let batched = request.calculate_costs(tier);
|
||||
let context = format!("{tier:?} {resolution:?} {duration}s {batch:?} references={:?}", request.reference_video_urls);
|
||||
assert_batch_cost(single.base_cost, batched.base_cost, count, tier, &context);
|
||||
assert_batch_cost(single.total_cost, batched.total_cost, count, tier, &context);
|
||||
match (single.video_reference_surcharge_cost, batched.video_reference_surcharge_cost) {
|
||||
(None, None) => {},
|
||||
(Some(single), Some(batched)) => assert_batch_cost(single, batched, count, tier, &context),
|
||||
_ => panic!("{context}: batch count changed surcharge presence"),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+133
-8
@@ -66,6 +66,10 @@ pub enum KinoviSeedance2p0FastBatchCount {
|
||||
Two,
|
||||
Three,
|
||||
Four,
|
||||
Five,
|
||||
Six,
|
||||
Seven,
|
||||
Eight,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
@@ -140,6 +144,10 @@ impl KinoviCostCalculatorTrait for GenerateSeedance2p0FastRequest {
|
||||
Some(KinoviSeedance2p0FastBatchCount::Two) => 2.0,
|
||||
Some(KinoviSeedance2p0FastBatchCount::Three) => 3.0,
|
||||
Some(KinoviSeedance2p0FastBatchCount::Four) => 4.0,
|
||||
Some(KinoviSeedance2p0FastBatchCount::Five) => 5.0,
|
||||
Some(KinoviSeedance2p0FastBatchCount::Six) => 6.0,
|
||||
Some(KinoviSeedance2p0FastBatchCount::Seven) => 7.0,
|
||||
Some(KinoviSeedance2p0FastBatchCount::Eight) => 8.0,
|
||||
};
|
||||
|
||||
let output_seconds = f64::from(self.duration_seconds) * batch_multiplier;
|
||||
@@ -244,6 +252,10 @@ fn map_batch_count(bc: Option<KinoviSeedance2p0FastBatchCount>) -> KinoviBatchCo
|
||||
Some(KinoviSeedance2p0FastBatchCount::Two) => KinoviBatchCountRaw::Two,
|
||||
Some(KinoviSeedance2p0FastBatchCount::Three) => KinoviBatchCountRaw::Three,
|
||||
Some(KinoviSeedance2p0FastBatchCount::Four) => KinoviBatchCountRaw::Four,
|
||||
Some(KinoviSeedance2p0FastBatchCount::Five) => KinoviBatchCountRaw::Five,
|
||||
Some(KinoviSeedance2p0FastBatchCount::Six) => KinoviBatchCountRaw::Six,
|
||||
Some(KinoviSeedance2p0FastBatchCount::Seven) => KinoviBatchCountRaw::Seven,
|
||||
Some(KinoviSeedance2p0FastBatchCount::Eight) => KinoviBatchCountRaw::Eight,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -528,18 +540,131 @@ mod tests {
|
||||
|
||||
mod batch_tests {
|
||||
use super::*;
|
||||
use crate::test_utils::assert_batch_cost::assert_batch_cost;
|
||||
|
||||
const BATCHES: [KinoviSeedance2p0FastBatchCount; 8] = [
|
||||
KinoviSeedance2p0FastBatchCount::One,
|
||||
KinoviSeedance2p0FastBatchCount::Two,
|
||||
KinoviSeedance2p0FastBatchCount::Three,
|
||||
KinoviSeedance2p0FastBatchCount::Four,
|
||||
KinoviSeedance2p0FastBatchCount::Five,
|
||||
KinoviSeedance2p0FastBatchCount::Six,
|
||||
KinoviSeedance2p0FastBatchCount::Seven,
|
||||
KinoviSeedance2p0FastBatchCount::Eight,
|
||||
];
|
||||
|
||||
// Each pair is (credits, USD cents rounded up), for batches 1 through 8.
|
||||
// Pin literal totals so rate changes fail independently of scaling checks.
|
||||
#[test]
|
||||
fn fixed_five_second_batch_prices_without_video_reference() {
|
||||
let cases = [
|
||||
(KinoviSeedance2p0FastOutputResolution::FourEightyP, KinoviPricingTier::Consumer, [
|
||||
(70.0, 37), (140.0, 73), (210.0, 109), (280.0, 146),
|
||||
(350.0, 182), (420.0, 218), (490.0, 254), (560.0, 291),
|
||||
]),
|
||||
(KinoviSeedance2p0FastOutputResolution::SevenTwentyP, KinoviPricingTier::Consumer, [
|
||||
(140.0, 73), (280.0, 146), (420.0, 218), (560.0, 291),
|
||||
(700.0, 363), (840.0, 436), (980.0, 508), (1120.0, 581),
|
||||
]),
|
||||
(KinoviSeedance2p0FastOutputResolution::FourEightyP, KinoviPricingTier::Enterprise, [
|
||||
(52.5, 22), (105.0, 44), (157.5, 65), (210.0, 87),
|
||||
(262.5, 108), (315.0, 130), (367.5, 152), (420.0, 173),
|
||||
]),
|
||||
(KinoviSeedance2p0FastOutputResolution::SevenTwentyP, KinoviPricingTier::Enterprise, [
|
||||
(105.0, 44), (210.0, 87), (315.0, 130), (420.0, 173),
|
||||
(525.0, 216), (630.0, 260), (735.0, 303), (840.0, 346),
|
||||
]),
|
||||
];
|
||||
for (resolution, tier, prices) in cases {
|
||||
for (batch, (credits, usd_cents)) in BATCHES.into_iter().zip(prices) {
|
||||
let request = build_request(5, Some(resolution), Some(batch));
|
||||
let cost = request.calculate_costs(tier).total_cost;
|
||||
assert_eq!(
|
||||
(cost.kinovi_credits, cost.usd_cents_rounded_up),
|
||||
(credits, usd_cents),
|
||||
"{resolution:?} {tier:?} {batch:?}",
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn batch_multiplies_at_both_tiers() {
|
||||
fn fixed_five_second_batch_prices_with_video_reference() {
|
||||
let cases = [
|
||||
(KinoviSeedance2p0FastOutputResolution::FourEightyP, KinoviPricingTier::Consumer, [
|
||||
(90.0, 47), (180.0, 94), (270.0, 140), (360.0, 187),
|
||||
(450.0, 234), (540.0, 280), (630.0, 327), (720.0, 374),
|
||||
]),
|
||||
(KinoviSeedance2p0FastOutputResolution::SevenTwentyP, KinoviPricingTier::Consumer, [
|
||||
(170.0, 89), (340.0, 177), (510.0, 265), (680.0, 353),
|
||||
(850.0, 441), (1020.0, 529), (1190.0, 617), (1360.0, 705),
|
||||
]),
|
||||
(KinoviSeedance2p0FastOutputResolution::FourEightyP, KinoviPricingTier::Enterprise, [
|
||||
(67.5, 28), (135.0, 56), (202.5, 84), (270.0, 112),
|
||||
(337.5, 139), (405.0, 167), (472.5, 195), (540.0, 223),
|
||||
]),
|
||||
(KinoviSeedance2p0FastOutputResolution::SevenTwentyP, KinoviPricingTier::Enterprise, [
|
||||
(127.5, 53), (255.0, 105), (382.5, 158), (510.0, 210),
|
||||
(637.5, 263), (765.0, 315), (892.5, 368), (1020.0, 420),
|
||||
]),
|
||||
];
|
||||
for (resolution, tier, prices) in cases {
|
||||
for (batch, (credits, usd_cents)) in BATCHES.into_iter().zip(prices) {
|
||||
let request = build_request(5, Some(resolution), Some(batch));
|
||||
let request = with_video_ref(request);
|
||||
let cost = request.calculate_costs(tier).total_cost;
|
||||
assert_eq!(
|
||||
(cost.kinovi_credits, cost.usd_cents_rounded_up),
|
||||
(credits, usd_cents),
|
||||
"{resolution:?} {tier:?} {batch:?}",
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn all_batches_scale_every_resolution_and_surcharge_at_both_tiers() {
|
||||
let batches = [
|
||||
(None, 1),
|
||||
(Some(KinoviSeedance2p0FastBatchCount::One), 1),
|
||||
(Some(KinoviSeedance2p0FastBatchCount::Two), 2),
|
||||
(Some(KinoviSeedance2p0FastBatchCount::Three), 3),
|
||||
(Some(KinoviSeedance2p0FastBatchCount::Four), 4),
|
||||
(Some(KinoviSeedance2p0FastBatchCount::Five), 5),
|
||||
(Some(KinoviSeedance2p0FastBatchCount::Six), 6),
|
||||
(Some(KinoviSeedance2p0FastBatchCount::Seven), 7),
|
||||
(Some(KinoviSeedance2p0FastBatchCount::Eight), 8),
|
||||
];
|
||||
for tier in [KinoviPricingTier::Enterprise, KinoviPricingTier::Consumer] {
|
||||
let base = r720(5).calculate_costs(tier).total_cost.kinovi_credits;
|
||||
for (batch, multiplier) in [
|
||||
(KinoviSeedance2p0FastBatchCount::Two, 2.0),
|
||||
(KinoviSeedance2p0FastBatchCount::Three, 3.0),
|
||||
(KinoviSeedance2p0FastBatchCount::Four, 4.0),
|
||||
for resolution in [
|
||||
None,
|
||||
Some(KinoviSeedance2p0FastOutputResolution::FourEightyP),
|
||||
Some(KinoviSeedance2p0FastOutputResolution::SevenTwentyP),
|
||||
] {
|
||||
let batched = build_request(5, None, Some(batch)).calculate_costs(tier).total_cost.kinovi_credits;
|
||||
assert_eq!(batched, base * multiplier, "{batch:?} at {tier:?}");
|
||||
for duration in [4, 5, 15] {
|
||||
for references in [
|
||||
None,
|
||||
Some(vec![]),
|
||||
Some(vec!["https://example.com/ref.mp4".to_string()]),
|
||||
Some(vec!["https://example.com/ref1.mp4".to_string(), "https://example.com/ref2.mp4".to_string()]),
|
||||
] {
|
||||
let mut request = build_request(duration, resolution, None);
|
||||
request.reference_video_urls = references;
|
||||
let single = request.calculate_costs(tier);
|
||||
for (batch, count) in batches {
|
||||
request.batch_count = batch;
|
||||
let batched = request.calculate_costs(tier);
|
||||
let context = format!("{tier:?} {resolution:?} {duration}s {batch:?} references={:?}", request.reference_video_urls);
|
||||
assert_batch_cost(single.base_cost, batched.base_cost, count, tier, &context);
|
||||
assert_batch_cost(single.total_cost, batched.total_cost, count, tier, &context);
|
||||
match (single.video_reference_surcharge_cost, batched.video_reference_surcharge_cost) {
|
||||
(None, None) => {},
|
||||
(Some(single), Some(batched)) => assert_batch_cost(single, batched, count, tier, &context),
|
||||
_ => panic!("{context}: batch count changed surcharge presence"),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+121
-14
@@ -514,24 +514,131 @@ mod tests {
|
||||
|
||||
mod batch_tests {
|
||||
use super::*;
|
||||
use crate::test_utils::assert_batch_cost::assert_batch_cost;
|
||||
|
||||
const BATCHES: [KinoviSeedance2p0MiniBatchCount; 8] = [
|
||||
KinoviSeedance2p0MiniBatchCount::One,
|
||||
KinoviSeedance2p0MiniBatchCount::Two,
|
||||
KinoviSeedance2p0MiniBatchCount::Three,
|
||||
KinoviSeedance2p0MiniBatchCount::Four,
|
||||
KinoviSeedance2p0MiniBatchCount::Five,
|
||||
KinoviSeedance2p0MiniBatchCount::Six,
|
||||
KinoviSeedance2p0MiniBatchCount::Seven,
|
||||
KinoviSeedance2p0MiniBatchCount::Eight,
|
||||
];
|
||||
|
||||
// Each pair is (credits, USD cents rounded up), for batches 1 through 8.
|
||||
// Pin literal totals so rate changes fail independently of scaling checks.
|
||||
#[test]
|
||||
fn fixed_five_second_batch_prices_without_video_reference() {
|
||||
let cases = [
|
||||
(KinoviSeedance2p0MiniOutputResolution::FourEightyP, KinoviPricingTier::Consumer, [
|
||||
(37.5, 20), (75.0, 39), (112.5, 59), (150.0, 78),
|
||||
(187.5, 98), (225.0, 117), (262.5, 137), (300.0, 156),
|
||||
]),
|
||||
(KinoviSeedance2p0MiniOutputResolution::SevenTwentyP, KinoviPricingTier::Consumer, [
|
||||
(100.0, 52), (200.0, 104), (300.0, 156), (400.0, 208),
|
||||
(500.0, 260), (600.0, 311), (700.0, 363), (800.0, 415),
|
||||
]),
|
||||
(KinoviSeedance2p0MiniOutputResolution::FourEightyP, KinoviPricingTier::Enterprise, [
|
||||
(15.0, 7), (30.0, 13), (45.0, 19), (60.0, 25),
|
||||
(75.0, 31), (90.0, 38), (105.0, 44), (120.0, 50),
|
||||
]),
|
||||
(KinoviSeedance2p0MiniOutputResolution::SevenTwentyP, KinoviPricingTier::Enterprise, [
|
||||
(40.0, 17), (80.0, 33), (120.0, 50), (160.0, 66),
|
||||
(200.0, 83), (240.0, 99), (280.0, 116), (320.0, 132),
|
||||
]),
|
||||
];
|
||||
for (resolution, tier, prices) in cases {
|
||||
for (batch, (credits, usd_cents)) in BATCHES.into_iter().zip(prices) {
|
||||
let request = build_request(5, Some(resolution), Some(batch));
|
||||
let cost = request.calculate_costs(tier).total_cost;
|
||||
assert_eq!(
|
||||
(cost.kinovi_credits, cost.usd_cents_rounded_up),
|
||||
(credits, usd_cents),
|
||||
"{resolution:?} {tier:?} {batch:?}",
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn batch_scales_total_linearly_at_both_tiers() {
|
||||
let by_count = [
|
||||
(KinoviSeedance2p0MiniBatchCount::One, 1.0),
|
||||
(KinoviSeedance2p0MiniBatchCount::Two, 2.0),
|
||||
(KinoviSeedance2p0MiniBatchCount::Three, 3.0),
|
||||
(KinoviSeedance2p0MiniBatchCount::Four, 4.0),
|
||||
(KinoviSeedance2p0MiniBatchCount::Five, 5.0),
|
||||
(KinoviSeedance2p0MiniBatchCount::Six, 6.0),
|
||||
(KinoviSeedance2p0MiniBatchCount::Seven, 7.0),
|
||||
(KinoviSeedance2p0MiniBatchCount::Eight, 8.0),
|
||||
fn fixed_five_second_batch_prices_with_video_reference() {
|
||||
let cases = [
|
||||
(KinoviSeedance2p0MiniOutputResolution::FourEightyP, KinoviPricingTier::Consumer, [
|
||||
(47.5, 25), (95.0, 50), (142.5, 74), (190.0, 99),
|
||||
(237.5, 124), (285.0, 148), (332.5, 173), (380.0, 197),
|
||||
]),
|
||||
(KinoviSeedance2p0MiniOutputResolution::SevenTwentyP, KinoviPricingTier::Consumer, [
|
||||
(120.0, 63), (240.0, 125), (360.0, 187), (480.0, 249),
|
||||
(600.0, 311), (720.0, 374), (840.0, 436), (960.0, 498),
|
||||
]),
|
||||
(KinoviSeedance2p0MiniOutputResolution::FourEightyP, KinoviPricingTier::Enterprise, [
|
||||
(19.0, 8), (38.0, 16), (57.0, 24), (76.0, 32),
|
||||
(95.0, 40), (114.0, 47), (133.0, 55), (152.0, 63),
|
||||
]),
|
||||
(KinoviSeedance2p0MiniOutputResolution::SevenTwentyP, KinoviPricingTier::Enterprise, [
|
||||
(48.0, 20), (96.0, 40), (144.0, 60), (192.0, 79),
|
||||
(240.0, 99), (288.0, 119), (336.0, 139), (384.0, 158),
|
||||
]),
|
||||
];
|
||||
for (resolution, tier, prices) in cases {
|
||||
for (batch, (credits, usd_cents)) in BATCHES.into_iter().zip(prices) {
|
||||
let request = build_request(5, Some(resolution), Some(batch));
|
||||
let request = with_video_ref(request);
|
||||
let cost = request.calculate_costs(tier).total_cost;
|
||||
assert_eq!(
|
||||
(cost.kinovi_credits, cost.usd_cents_rounded_up),
|
||||
(credits, usd_cents),
|
||||
"{resolution:?} {tier:?} {batch:?}",
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn all_batches_scale_every_resolution_and_surcharge_at_both_tiers() {
|
||||
let batches = [
|
||||
(None, 1),
|
||||
(Some(KinoviSeedance2p0MiniBatchCount::One), 1),
|
||||
(Some(KinoviSeedance2p0MiniBatchCount::Two), 2),
|
||||
(Some(KinoviSeedance2p0MiniBatchCount::Three), 3),
|
||||
(Some(KinoviSeedance2p0MiniBatchCount::Four), 4),
|
||||
(Some(KinoviSeedance2p0MiniBatchCount::Five), 5),
|
||||
(Some(KinoviSeedance2p0MiniBatchCount::Six), 6),
|
||||
(Some(KinoviSeedance2p0MiniBatchCount::Seven), 7),
|
||||
(Some(KinoviSeedance2p0MiniBatchCount::Eight), 8),
|
||||
];
|
||||
for tier in [KinoviPricingTier::Enterprise, KinoviPricingTier::Consumer] {
|
||||
let base = r720(5).calculate_costs(tier).total_cost.kinovi_credits;
|
||||
for (count, multiplier) in by_count {
|
||||
let credits = build_request(5, None, Some(count)).calculate_costs(tier).total_cost.kinovi_credits;
|
||||
assert_eq!(credits, base * multiplier, "batch {count:?} at {tier:?}");
|
||||
for resolution in [
|
||||
None,
|
||||
Some(KinoviSeedance2p0MiniOutputResolution::FourEightyP),
|
||||
Some(KinoviSeedance2p0MiniOutputResolution::SevenTwentyP),
|
||||
] {
|
||||
for duration in [4, 5, 15] {
|
||||
for references in [
|
||||
None,
|
||||
Some(vec![]),
|
||||
Some(vec!["https://example.com/ref.mp4".to_string()]),
|
||||
Some(vec!["https://example.com/ref1.mp4".to_string(), "https://example.com/ref2.mp4".to_string()]),
|
||||
] {
|
||||
let mut request = build_request(duration, resolution, None);
|
||||
request.reference_video_urls = references;
|
||||
let single = request.calculate_costs(tier);
|
||||
for (batch, count) in batches {
|
||||
request.batch_count = batch;
|
||||
let batched = request.calculate_costs(tier);
|
||||
let context = format!("{tier:?} {resolution:?} {duration}s {batch:?} references={:?}", request.reference_video_urls);
|
||||
assert_batch_cost(single.base_cost, batched.base_cost, count, tier, &context);
|
||||
assert_batch_cost(single.total_cost, batched.total_cost, count, tier, &context);
|
||||
match (single.video_reference_surcharge_cost, batched.video_reference_surcharge_cost) {
|
||||
(None, None) => {},
|
||||
(Some(single), Some(batched)) => assert_batch_cost(single, batched, count, tier, &context),
|
||||
_ => panic!("{context}: batch count changed surcharge presence"),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,6 +48,7 @@ pub struct GenerateSeedance2p5Request {
|
||||
pub output_resolution: Option<KinoviSeedance2p5OutputResolution>,
|
||||
|
||||
pub duration_seconds: u8,
|
||||
pub batch_count: Option<KinoviSeedance2p5BatchCount>,
|
||||
|
||||
/// CALCULATION-ONLY (never sent on the wire): the total seconds of
|
||||
/// reference video input, summed across all reference videos. When video
|
||||
@@ -137,6 +138,19 @@ pub enum KinoviSeedance2p5OutputFormat {
|
||||
Mov,
|
||||
}
|
||||
|
||||
/// Number of videos to generate in one request (1–8).
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum KinoviSeedance2p5BatchCount {
|
||||
One,
|
||||
Two,
|
||||
Three,
|
||||
Four,
|
||||
Five,
|
||||
Six,
|
||||
Seven,
|
||||
Eight,
|
||||
}
|
||||
|
||||
// ── Pricing ──
|
||||
//
|
||||
// Seedance 2.5 credit pricing (credits per billed second). Without video
|
||||
@@ -233,7 +247,18 @@ impl KinoviCostCalculatorTrait for GenerateSeedance2p5Request {
|
||||
(rate, u64::from(self.duration_seconds))
|
||||
};
|
||||
|
||||
let total_credits = rate.credits(tier) * billed_seconds as f64;
|
||||
let batch_multiplier: f64 = match self.batch_count {
|
||||
None | Some(KinoviSeedance2p5BatchCount::One) => 1.0,
|
||||
Some(KinoviSeedance2p5BatchCount::Two) => 2.0,
|
||||
Some(KinoviSeedance2p5BatchCount::Three) => 3.0,
|
||||
Some(KinoviSeedance2p5BatchCount::Four) => 4.0,
|
||||
Some(KinoviSeedance2p5BatchCount::Five) => 5.0,
|
||||
Some(KinoviSeedance2p5BatchCount::Six) => 6.0,
|
||||
Some(KinoviSeedance2p5BatchCount::Seven) => 7.0,
|
||||
Some(KinoviSeedance2p5BatchCount::Eight) => 8.0,
|
||||
};
|
||||
|
||||
let total_credits = rate.credits(tier) * billed_seconds as f64 * batch_multiplier;
|
||||
tier.cost_from_credits(total_credits)
|
||||
}
|
||||
}
|
||||
@@ -308,7 +333,7 @@ fn to_raw_request(req: GenerateSeedance2p5Request) -> WorkflowRunTaskRequest {
|
||||
aspect_ratio,
|
||||
output_resolution: Some(map_output_resolution(req.output_resolution)),
|
||||
duration_seconds: req.duration_seconds,
|
||||
batch_count: KinoviBatchCountRaw::One,
|
||||
batch_count: map_batch_count(req.batch_count),
|
||||
start_frame_url,
|
||||
end_frame_url,
|
||||
reference_image_urls,
|
||||
@@ -354,6 +379,19 @@ fn map_output_resolution(res: Option<KinoviSeedance2p5OutputResolution>) -> Kino
|
||||
}
|
||||
}
|
||||
|
||||
fn map_batch_count(bc: Option<KinoviSeedance2p5BatchCount>) -> KinoviBatchCountRaw {
|
||||
match bc {
|
||||
Some(KinoviSeedance2p5BatchCount::One) | None => KinoviBatchCountRaw::One,
|
||||
Some(KinoviSeedance2p5BatchCount::Two) => KinoviBatchCountRaw::Two,
|
||||
Some(KinoviSeedance2p5BatchCount::Three) => KinoviBatchCountRaw::Three,
|
||||
Some(KinoviSeedance2p5BatchCount::Four) => KinoviBatchCountRaw::Four,
|
||||
Some(KinoviSeedance2p5BatchCount::Five) => KinoviBatchCountRaw::Five,
|
||||
Some(KinoviSeedance2p5BatchCount::Six) => KinoviBatchCountRaw::Six,
|
||||
Some(KinoviSeedance2p5BatchCount::Seven) => KinoviBatchCountRaw::Seven,
|
||||
Some(KinoviSeedance2p5BatchCount::Eight) => KinoviBatchCountRaw::Eight,
|
||||
}
|
||||
}
|
||||
|
||||
// ── Tests ──
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -691,10 +729,185 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
mod batch_price_tests {
|
||||
use super::*;
|
||||
|
||||
const BATCHES: [KinoviSeedance2p5BatchCount; 8] = [
|
||||
KinoviSeedance2p5BatchCount::One,
|
||||
KinoviSeedance2p5BatchCount::Two,
|
||||
KinoviSeedance2p5BatchCount::Three,
|
||||
KinoviSeedance2p5BatchCount::Four,
|
||||
KinoviSeedance2p5BatchCount::Five,
|
||||
KinoviSeedance2p5BatchCount::Six,
|
||||
KinoviSeedance2p5BatchCount::Seven,
|
||||
KinoviSeedance2p5BatchCount::Eight,
|
||||
];
|
||||
|
||||
// Each pair is (credits, USD cents rounded up), for batches 1 through 8.
|
||||
// Pin literal totals so rate changes fail independently of scaling checks.
|
||||
// Video-reference cases bill 5s output + 10s input per video.
|
||||
#[test]
|
||||
fn fixed_five_second_batch_prices_without_video_reference() {
|
||||
let cases = [
|
||||
(KinoviSeedance2p5OutputResolution::FourEightyP, KinoviPricingTier::Consumer, [
|
||||
(130.0, 68), (260.0, 135), (390.0, 203), (520.0, 270),
|
||||
(650.0, 337), (780.0, 405), (910.0, 472), (1040.0, 539),
|
||||
]),
|
||||
(KinoviSeedance2p5OutputResolution::SevenTwentyP, KinoviPricingTier::Consumer, [
|
||||
(295.0, 153), (590.0, 306), (885.0, 459), (1180.0, 612),
|
||||
(1475.0, 765), (1770.0, 918), (2065.0, 1071), (2360.0, 1223),
|
||||
]),
|
||||
(KinoviSeedance2p5OutputResolution::TenEightyP, KinoviPricingTier::Consumer, [
|
||||
(717.0, 372), (1434.0, 744), (2151.0, 1115), (2868.0, 1487),
|
||||
(3585.0, 1858), (4302.0, 2230), (5019.0, 2601), (5736.0, 2973),
|
||||
]),
|
||||
(KinoviSeedance2p5OutputResolution::FourEightyP, KinoviPricingTier::Enterprise, [
|
||||
(130.0, 54), (260.0, 107), (390.0, 161), (520.0, 214),
|
||||
(650.0, 268), (780.0, 321), (910.0, 375), (1040.0, 428),
|
||||
]),
|
||||
(KinoviSeedance2p5OutputResolution::SevenTwentyP, KinoviPricingTier::Enterprise, [
|
||||
(295.0, 122), (590.0, 243), (885.0, 364), (1180.0, 486),
|
||||
(1475.0, 607), (1770.0, 728), (2065.0, 850), (2360.0, 971),
|
||||
]),
|
||||
(KinoviSeedance2p5OutputResolution::TenEightyP, KinoviPricingTier::Enterprise, [
|
||||
(681.15, 281), (1362.30, 561), (2043.45, 841), (2724.60, 1121),
|
||||
(3405.75, 1401), (4086.90, 1681), (4768.05, 1961), (5449.20, 2241),
|
||||
]),
|
||||
];
|
||||
for (resolution, tier, prices) in cases {
|
||||
for (batch, (credits, usd_cents)) in BATCHES.into_iter().zip(prices) {
|
||||
let mut request = text_to_video_request(5, Some(resolution));
|
||||
request.batch_count = Some(batch);
|
||||
let cost = request.calculate_costs(tier);
|
||||
assert_eq!(
|
||||
(cost.kinovi_credits, cost.usd_cents_rounded_up),
|
||||
(credits, usd_cents),
|
||||
"{resolution:?} {tier:?} {batch:?}",
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fixed_five_second_batch_prices_with_video_reference() {
|
||||
let cases = [
|
||||
(KinoviSeedance2p5OutputResolution::FourEightyP, KinoviPricingTier::Consumer, [
|
||||
(240.0, 125), (480.0, 249), (720.0, 374), (960.0, 498),
|
||||
(1200.0, 622), (1440.0, 747), (1680.0, 871), (1920.0, 995),
|
||||
]),
|
||||
(KinoviSeedance2p5OutputResolution::SevenTwentyP, KinoviPricingTier::Consumer, [
|
||||
(525.0, 273), (1050.0, 545), (1575.0, 817), (2100.0, 1089),
|
||||
(2625.0, 1361), (3150.0, 1633), (3675.0, 1905), (4200.0, 2177),
|
||||
]),
|
||||
(KinoviSeedance2p5OutputResolution::TenEightyP, KinoviPricingTier::Consumer, [
|
||||
(1285.2, 666), (2570.4, 1332), (3855.6, 1998), (5140.8, 2664),
|
||||
(6426.0, 3330), (7711.2, 3996), (8996.4, 4662), (10281.6, 5328),
|
||||
]),
|
||||
(KinoviSeedance2p5OutputResolution::FourEightyP, KinoviPricingTier::Enterprise, [
|
||||
(240.0, 99), (480.0, 198), (720.0, 297), (960.0, 395),
|
||||
(1200.0, 494), (1440.0, 593), (1680.0, 691), (1920.0, 790),
|
||||
]),
|
||||
(KinoviSeedance2p5OutputResolution::SevenTwentyP, KinoviPricingTier::Enterprise, [
|
||||
(525.0, 216), (1050.0, 432), (1575.0, 648), (2100.0, 864),
|
||||
(2625.0, 1080), (3150.0, 1296), (3675.0, 1512), (4200.0, 1728),
|
||||
]),
|
||||
(KinoviSeedance2p5OutputResolution::TenEightyP, KinoviPricingTier::Enterprise, [
|
||||
(1221.0, 503), (2442.0, 1005), (3663.0, 1507), (4884.0, 2009),
|
||||
(6105.0, 2511), (7326.0, 3013), (8547.0, 3515), (9768.0, 4018),
|
||||
]),
|
||||
];
|
||||
for (resolution, tier, prices) in cases {
|
||||
for (batch, (credits, usd_cents)) in BATCHES.into_iter().zip(prices) {
|
||||
let mut request = text_to_video_request(5, Some(resolution));
|
||||
request.batch_count = Some(batch);
|
||||
set_reference_urls(&mut request, None, Some(vec!["https://example.com/ref.mp4".to_string()]), None);
|
||||
request.total_input_seconds = Some(10);
|
||||
let cost = request.calculate_costs(tier);
|
||||
assert_eq!(
|
||||
(cost.kinovi_credits, cost.usd_cents_rounded_up),
|
||||
(credits, usd_cents),
|
||||
"{resolution:?} {tier:?} {batch:?}",
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ── Relative pricing ──
|
||||
|
||||
mod relative_tests {
|
||||
use super::*;
|
||||
use crate::test_utils::assert_batch_cost::assert_batch_cost;
|
||||
|
||||
#[test]
|
||||
fn all_batches_scale_every_resolution_modality_and_input_duration() {
|
||||
let batches = [
|
||||
(None, 1),
|
||||
(Some(KinoviSeedance2p5BatchCount::One), 1),
|
||||
(Some(KinoviSeedance2p5BatchCount::Two), 2),
|
||||
(Some(KinoviSeedance2p5BatchCount::Three), 3),
|
||||
(Some(KinoviSeedance2p5BatchCount::Four), 4),
|
||||
(Some(KinoviSeedance2p5BatchCount::Five), 5),
|
||||
(Some(KinoviSeedance2p5BatchCount::Six), 6),
|
||||
(Some(KinoviSeedance2p5BatchCount::Seven), 7),
|
||||
(Some(KinoviSeedance2p5BatchCount::Eight), 8),
|
||||
];
|
||||
for tier in [KinoviPricingTier::Enterprise, KinoviPricingTier::Consumer] {
|
||||
for resolution in [
|
||||
None,
|
||||
Some(KinoviSeedance2p5OutputResolution::FourEightyP),
|
||||
Some(KinoviSeedance2p5OutputResolution::SevenTwentyP),
|
||||
Some(KinoviSeedance2p5OutputResolution::TenEightyP),
|
||||
] {
|
||||
for duration in [4, 5, 30] {
|
||||
let text_request = text_to_video_request(duration, resolution);
|
||||
let mut keyframe = keyframe_request(duration);
|
||||
keyframe.output_resolution = resolution;
|
||||
let mut image_audio = text_request.clone();
|
||||
set_reference_urls(
|
||||
&mut image_audio,
|
||||
Some(vec!["https://example.com/image.png".to_string()]),
|
||||
None,
|
||||
Some(vec!["https://example.com/audio.mp3".to_string()]),
|
||||
);
|
||||
let mut empty_videos = text_request.clone();
|
||||
set_reference_urls(&mut empty_videos, None, Some(vec![]), None);
|
||||
let mut requests = vec![
|
||||
("text".to_string(), text_request.clone()),
|
||||
("keyframe".to_string(), keyframe),
|
||||
("image/audio".to_string(), image_audio),
|
||||
("empty videos".to_string(), empty_videos),
|
||||
];
|
||||
// Include unknown/zero inputs, the minimum, ordinary input, the
|
||||
// maximum, and values clamped at either end of the billed range.
|
||||
for input_seconds in [None, Some(0), Some(1), Some(4), Some(10), Some(30), Some(31), Some(u8::MAX)] {
|
||||
let mut request = text_request.clone();
|
||||
set_reference_urls(&mut request, None, Some(vec!["https://example.com/ref.mp4".to_string()]), None);
|
||||
request.total_input_seconds = input_seconds;
|
||||
requests.push((format!("video input={input_seconds:?}"), request));
|
||||
}
|
||||
for (modality, mut request) in requests {
|
||||
let single = request.calculate_costs(tier);
|
||||
for (batch, count) in batches {
|
||||
request.batch_count = batch;
|
||||
let batched = request.calculate_costs(tier);
|
||||
let context = format!("{tier:?} {resolution:?} {duration}s {batch:?} {modality}");
|
||||
assert_batch_cost(single, batched, count, tier, &context);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn batch_multiplies_output_and_reference_input_costs() {
|
||||
let mut request = video_ref_480(5, Some(10));
|
||||
request.batch_count = Some(KinoviSeedance2p5BatchCount::Eight);
|
||||
assert_eq!(request.calculate_consumer_costs().kinovi_credits, 1920.0);
|
||||
assert_eq!(request.calculate_enterprise_costs().kinovi_credits, 1920.0);
|
||||
assert!(matches!(to_raw_request(request).batch_count, KinoviBatchCountRaw::Eight));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn video_reference_rate_is_cheaper_per_second() {
|
||||
@@ -769,6 +982,7 @@ mod tests {
|
||||
output_resolution: Option<KinoviSeedance2p5OutputResolution>,
|
||||
) -> GenerateSeedance2p5Request {
|
||||
GenerateSeedance2p5Request {
|
||||
batch_count: None,
|
||||
prompt: String::new(),
|
||||
modality: KinoviSeedance2p5Modality::Reference {
|
||||
aspect_ratio: None,
|
||||
@@ -800,6 +1014,7 @@ mod tests {
|
||||
|
||||
fn keyframe_request(duration: u8) -> GenerateSeedance2p5Request {
|
||||
GenerateSeedance2p5Request {
|
||||
batch_count: None,
|
||||
prompt: String::new(),
|
||||
modality: KinoviSeedance2p5Modality::Keyframe {
|
||||
start_frame_url: "https://example.com/start.png".to_string(),
|
||||
@@ -862,6 +1077,7 @@ mod tests {
|
||||
#[test]
|
||||
fn keyframe_maps_frames_and_no_references() {
|
||||
let raw = to_raw_request(GenerateSeedance2p5Request {
|
||||
batch_count: None,
|
||||
prompt: "Car drives into the sunset".to_string(),
|
||||
modality: KinoviSeedance2p5Modality::Keyframe {
|
||||
start_frame_url: "https://example.com/start.png".to_string(),
|
||||
@@ -888,6 +1104,7 @@ mod tests {
|
||||
#[test]
|
||||
fn reference_maps_references_and_no_frames() {
|
||||
let raw = to_raw_request(GenerateSeedance2p5Request {
|
||||
batch_count: None,
|
||||
prompt: "The t-rex @image1 eats the banana".to_string(),
|
||||
modality: KinoviSeedance2p5Modality::Reference {
|
||||
aspect_ratio: Some(KinoviSeedance2p5AspectRatio::UltraWide21x9),
|
||||
@@ -918,6 +1135,7 @@ mod tests {
|
||||
#[test]
|
||||
fn resolution_1080p_maps_to_raw_1080p() {
|
||||
let raw = to_raw_request(GenerateSeedance2p5Request {
|
||||
batch_count: None,
|
||||
prompt: "A car driving on the beach".to_string(),
|
||||
modality: KinoviSeedance2p5Modality::Reference {
|
||||
aspect_ratio: None,
|
||||
@@ -940,6 +1158,7 @@ mod tests {
|
||||
#[test]
|
||||
fn reference_aspect_ratio_defaults_to_16x9() {
|
||||
let raw = to_raw_request(GenerateSeedance2p5Request {
|
||||
batch_count: None,
|
||||
prompt: "Lightning hits a building".to_string(),
|
||||
modality: KinoviSeedance2p5Modality::Reference {
|
||||
aspect_ratio: None,
|
||||
@@ -972,6 +1191,7 @@ mod tests {
|
||||
session: &session,
|
||||
host_override: None,
|
||||
request: GenerateSeedance2p5Request {
|
||||
batch_count: None,
|
||||
prompt: "Lightning hits a building".to_string(),
|
||||
modality: KinoviSeedance2p5Modality::Reference {
|
||||
aspect_ratio: Some(KinoviSeedance2p5AspectRatio::Landscape16x9),
|
||||
@@ -1006,6 +1226,7 @@ mod tests {
|
||||
session: &session,
|
||||
host_override: None,
|
||||
request: GenerateSeedance2p5Request {
|
||||
batch_count: None,
|
||||
prompt: "A car driving on the beach".to_string(),
|
||||
modality: KinoviSeedance2p5Modality::Reference {
|
||||
aspect_ratio: Some(KinoviSeedance2p5AspectRatio::Landscape16x9),
|
||||
@@ -1038,6 +1259,7 @@ mod tests {
|
||||
session: &session,
|
||||
host_override: None,
|
||||
request: GenerateSeedance2p5Request {
|
||||
batch_count: None,
|
||||
prompt: "Car drives into the sunset".to_string(),
|
||||
modality: KinoviSeedance2p5Modality::Keyframe {
|
||||
start_frame_url: "https://static.seedance2-pro.com/materials/20260807/1786128486168-a2bf6132.png".to_string(),
|
||||
@@ -1068,6 +1290,7 @@ mod tests {
|
||||
session: &session,
|
||||
host_override: None,
|
||||
request: GenerateSeedance2p5Request {
|
||||
batch_count: None,
|
||||
prompt: "Car drives into the sunset".to_string(),
|
||||
modality: KinoviSeedance2p5Modality::Keyframe {
|
||||
start_frame_url: "https://static.seedance2-pro.com/materials/20260807/1786128486168-a2bf6132.png".to_string(),
|
||||
@@ -1098,6 +1321,7 @@ mod tests {
|
||||
session: &session,
|
||||
host_override: None,
|
||||
request: GenerateSeedance2p5Request {
|
||||
batch_count: None,
|
||||
prompt: "The t-rex @image1 eats the banana @image2".to_string(),
|
||||
modality: KinoviSeedance2p5Modality::Reference {
|
||||
aspect_ratio: Some(KinoviSeedance2p5AspectRatio::UltraWide21x9),
|
||||
|
||||
+147
-2
@@ -28,6 +28,7 @@ pub struct GenerateSeedance2p5PreviewRequest {
|
||||
pub aspect_ratio: Option<KinoviSeedance2p5PreviewAspectRatio>,
|
||||
pub output_resolution: Option<KinoviSeedance2p5PreviewOutputResolution>,
|
||||
pub duration_seconds: u8,
|
||||
pub batch_count: Option<KinoviSeedance2p5PreviewBatchCount>,
|
||||
/// Reference images, referenced in prompts as @image1, @image2, etc.
|
||||
pub reference_image_urls: Option<Vec<String>>,
|
||||
/// Reference videos, referenced in prompts as @video1, @video2, etc.
|
||||
@@ -58,6 +59,19 @@ pub enum KinoviSeedance2p5PreviewOutputResolution {
|
||||
SevenTwentyP,
|
||||
}
|
||||
|
||||
/// Number of videos to generate in one request (1–8).
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum KinoviSeedance2p5PreviewBatchCount {
|
||||
One,
|
||||
Two,
|
||||
Three,
|
||||
Four,
|
||||
Five,
|
||||
Six,
|
||||
Seven,
|
||||
Eight,
|
||||
}
|
||||
|
||||
// ── Pricing ──
|
||||
//
|
||||
// Seedance 2.5 Preview credit pricing (per second of output duration):
|
||||
@@ -97,7 +111,18 @@ impl KinoviCostCalculatorTrait for GenerateSeedance2p5PreviewRequest {
|
||||
Some(KinoviSeedance2p5PreviewOutputResolution::FourEightyP) => SEEDANCE_2P5_PREVIEW_480P,
|
||||
Some(KinoviSeedance2p5PreviewOutputResolution::SevenTwentyP) | None => SEEDANCE_2P5_PREVIEW_720P,
|
||||
};
|
||||
let total_credits = rate.credits(tier) * f64::from(self.duration_seconds);
|
||||
let batch_multiplier: f64 = match self.batch_count {
|
||||
None | Some(KinoviSeedance2p5PreviewBatchCount::One) => 1.0,
|
||||
Some(KinoviSeedance2p5PreviewBatchCount::Two) => 2.0,
|
||||
Some(KinoviSeedance2p5PreviewBatchCount::Three) => 3.0,
|
||||
Some(KinoviSeedance2p5PreviewBatchCount::Four) => 4.0,
|
||||
Some(KinoviSeedance2p5PreviewBatchCount::Five) => 5.0,
|
||||
Some(KinoviSeedance2p5PreviewBatchCount::Six) => 6.0,
|
||||
Some(KinoviSeedance2p5PreviewBatchCount::Seven) => 7.0,
|
||||
Some(KinoviSeedance2p5PreviewBatchCount::Eight) => 8.0,
|
||||
};
|
||||
|
||||
let total_credits = rate.credits(tier) * f64::from(self.duration_seconds) * batch_multiplier;
|
||||
tier.cost_from_credits(total_credits)
|
||||
}
|
||||
}
|
||||
@@ -139,7 +164,7 @@ fn to_raw_request(req: GenerateSeedance2p5PreviewRequest) -> WorkflowRunTaskRequ
|
||||
aspect_ratio: map_aspect_ratio(req.aspect_ratio),
|
||||
output_resolution: Some(map_output_resolution(req.output_resolution)),
|
||||
duration_seconds: req.duration_seconds,
|
||||
batch_count: KinoviBatchCountRaw::One,
|
||||
batch_count: map_batch_count(req.batch_count),
|
||||
start_frame_url: None,
|
||||
end_frame_url: None,
|
||||
reference_image_urls: req.reference_image_urls,
|
||||
@@ -174,6 +199,19 @@ fn map_output_resolution(res: Option<KinoviSeedance2p5PreviewOutputResolution>)
|
||||
}
|
||||
}
|
||||
|
||||
fn map_batch_count(bc: Option<KinoviSeedance2p5PreviewBatchCount>) -> KinoviBatchCountRaw {
|
||||
match bc {
|
||||
Some(KinoviSeedance2p5PreviewBatchCount::One) | None => KinoviBatchCountRaw::One,
|
||||
Some(KinoviSeedance2p5PreviewBatchCount::Two) => KinoviBatchCountRaw::Two,
|
||||
Some(KinoviSeedance2p5PreviewBatchCount::Three) => KinoviBatchCountRaw::Three,
|
||||
Some(KinoviSeedance2p5PreviewBatchCount::Four) => KinoviBatchCountRaw::Four,
|
||||
Some(KinoviSeedance2p5PreviewBatchCount::Five) => KinoviBatchCountRaw::Five,
|
||||
Some(KinoviSeedance2p5PreviewBatchCount::Six) => KinoviBatchCountRaw::Six,
|
||||
Some(KinoviSeedance2p5PreviewBatchCount::Seven) => KinoviBatchCountRaw::Seven,
|
||||
Some(KinoviSeedance2p5PreviewBatchCount::Eight) => KinoviBatchCountRaw::Eight,
|
||||
}
|
||||
}
|
||||
|
||||
// ── Tests ──
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -507,6 +545,110 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
mod batch_tests {
|
||||
use super::*;
|
||||
use crate::test_utils::assert_batch_cost::assert_batch_cost;
|
||||
|
||||
const BATCHES: [KinoviSeedance2p5PreviewBatchCount; 8] = [
|
||||
KinoviSeedance2p5PreviewBatchCount::One,
|
||||
KinoviSeedance2p5PreviewBatchCount::Two,
|
||||
KinoviSeedance2p5PreviewBatchCount::Three,
|
||||
KinoviSeedance2p5PreviewBatchCount::Four,
|
||||
KinoviSeedance2p5PreviewBatchCount::Five,
|
||||
KinoviSeedance2p5PreviewBatchCount::Six,
|
||||
KinoviSeedance2p5PreviewBatchCount::Seven,
|
||||
KinoviSeedance2p5PreviewBatchCount::Eight,
|
||||
];
|
||||
|
||||
// Each pair is (credits, USD cents rounded up), for batches 1 through 8.
|
||||
// Pin literal totals so rate changes fail independently of scaling checks.
|
||||
#[test]
|
||||
fn fixed_five_second_batch_prices_with_and_without_references() {
|
||||
let cases = [
|
||||
(KinoviSeedance2p5PreviewOutputResolution::FourEightyP, KinoviPricingTier::Consumer, [
|
||||
(230.75, 120), (461.50, 240), (692.25, 359), (923.00, 479),
|
||||
(1153.75, 598), (1384.50, 718), (1615.25, 838), (1846.00, 957),
|
||||
]),
|
||||
(KinoviSeedance2p5PreviewOutputResolution::SevenTwentyP, KinoviPricingTier::Consumer, [
|
||||
(460.15, 239), (920.30, 477), (1380.45, 716), (1840.60, 954),
|
||||
(2300.75, 1193), (2760.90, 1431), (3221.05, 1670), (3681.20, 1908),
|
||||
]),
|
||||
(KinoviSeedance2p5PreviewOutputResolution::FourEightyP, KinoviPricingTier::Enterprise, [
|
||||
(210.65, 87), (421.30, 174), (631.95, 260), (842.60, 347),
|
||||
(1053.25, 434), (1263.90, 520), (1474.55, 607), (1685.20, 694),
|
||||
]),
|
||||
(KinoviSeedance2p5PreviewOutputResolution::SevenTwentyP, KinoviPricingTier::Enterprise, [
|
||||
(421.3, 174), (842.6, 347), (1263.9, 520), (1685.2, 694),
|
||||
(2106.5, 867), (2527.8, 1040), (2949.1, 1213), (3370.4, 1387),
|
||||
]),
|
||||
];
|
||||
for (resolution, tier, prices) in cases {
|
||||
for has_references in [false, true] {
|
||||
for (batch, (credits, usd_cents)) in BATCHES.into_iter().zip(prices) {
|
||||
let mut request = build_request(5, Some(resolution));
|
||||
request.batch_count = Some(batch);
|
||||
if has_references {
|
||||
request = with_all_reference_types(request);
|
||||
}
|
||||
let cost = request.calculate_costs(tier);
|
||||
assert_eq!(
|
||||
(cost.kinovi_credits, cost.usd_cents_rounded_up),
|
||||
(credits, usd_cents),
|
||||
"{resolution:?} {tier:?} {batch:?} references={has_references}",
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn all_batches_scale_every_resolution_with_and_without_references() {
|
||||
let batches = [
|
||||
(None, 1),
|
||||
(Some(KinoviSeedance2p5PreviewBatchCount::One), 1),
|
||||
(Some(KinoviSeedance2p5PreviewBatchCount::Two), 2),
|
||||
(Some(KinoviSeedance2p5PreviewBatchCount::Three), 3),
|
||||
(Some(KinoviSeedance2p5PreviewBatchCount::Four), 4),
|
||||
(Some(KinoviSeedance2p5PreviewBatchCount::Five), 5),
|
||||
(Some(KinoviSeedance2p5PreviewBatchCount::Six), 6),
|
||||
(Some(KinoviSeedance2p5PreviewBatchCount::Seven), 7),
|
||||
(Some(KinoviSeedance2p5PreviewBatchCount::Eight), 8),
|
||||
];
|
||||
for tier in [KinoviPricingTier::Enterprise, KinoviPricingTier::Consumer] {
|
||||
for resolution in [
|
||||
None,
|
||||
Some(KinoviSeedance2p5PreviewOutputResolution::FourEightyP),
|
||||
Some(KinoviSeedance2p5PreviewOutputResolution::SevenTwentyP),
|
||||
] {
|
||||
for duration in [4, 5, 30] {
|
||||
for has_references in [false, true] {
|
||||
let mut request = build_request(duration, resolution);
|
||||
if has_references {
|
||||
request = with_all_reference_types(request);
|
||||
}
|
||||
let single = request.calculate_costs(tier);
|
||||
for (batch, count) in batches {
|
||||
request.batch_count = batch;
|
||||
let batched = request.calculate_costs(tier);
|
||||
let context = format!("{tier:?} {resolution:?} {duration}s {batch:?} references={has_references}");
|
||||
assert_batch_cost(single, batched, count, tier, &context);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn batch_costs_and_request_count_agree() {
|
||||
let mut request = r480(4);
|
||||
request.batch_count = Some(KinoviSeedance2p5PreviewBatchCount::Eight);
|
||||
assert_eq!(request.calculate_consumer_costs().kinovi_credits, 1476.8);
|
||||
assert_eq!(request.calculate_enterprise_costs().kinovi_credits, 1348.16);
|
||||
assert!(matches!(to_raw_request(request).batch_count, KinoviBatchCountRaw::Eight));
|
||||
}
|
||||
}
|
||||
|
||||
// ── Helpers ──
|
||||
|
||||
fn build_request(
|
||||
@@ -514,6 +656,7 @@ mod tests {
|
||||
output_resolution: Option<KinoviSeedance2p5PreviewOutputResolution>,
|
||||
) -> GenerateSeedance2p5PreviewRequest {
|
||||
GenerateSeedance2p5PreviewRequest {
|
||||
batch_count: None,
|
||||
prompt: String::new(),
|
||||
aspect_ratio: None,
|
||||
output_resolution,
|
||||
@@ -556,6 +699,7 @@ mod tests {
|
||||
session: &session,
|
||||
host_override: None,
|
||||
request: GenerateSeedance2p5PreviewRequest {
|
||||
batch_count: None,
|
||||
prompt: "A man is running from a t-rex".to_string(),
|
||||
aspect_ratio: Some(KinoviSeedance2p5PreviewAspectRatio::Landscape16x9),
|
||||
output_resolution: Some(KinoviSeedance2p5PreviewOutputResolution::FourEightyP),
|
||||
@@ -582,6 +726,7 @@ mod tests {
|
||||
session: &session,
|
||||
host_override: None,
|
||||
request: GenerateSeedance2p5PreviewRequest {
|
||||
batch_count: None,
|
||||
prompt: "A corgi and a shiba are playing chess against one another".to_string(),
|
||||
aspect_ratio: None,
|
||||
output_resolution: None,
|
||||
|
||||
+24
@@ -816,6 +816,30 @@ mod tests {
|
||||
assert!(json.contains(r#""batchCount":8"#), "{json}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn batch_count_serializes_for_all_video_models() {
|
||||
for model in [
|
||||
KinoviModelTypeRaw::Seedance2Pro,
|
||||
KinoviModelTypeRaw::Seedance2Fast,
|
||||
KinoviModelTypeRaw::Seedance2Mini,
|
||||
KinoviModelTypeRaw::Seedance2p5,
|
||||
KinoviModelTypeRaw::Seedance2p5Preview,
|
||||
KinoviModelTypeRaw::HappyHorse1p0,
|
||||
] {
|
||||
let batches = match model {
|
||||
KinoviModelTypeRaw::HappyHorse1p0 => [(KinoviBatchCountRaw::One, None), (KinoviBatchCountRaw::Three, Some(3))],
|
||||
_ => [(KinoviBatchCountRaw::One, None), (KinoviBatchCountRaw::Eight, Some(8))],
|
||||
};
|
||||
for (batch, expected) in batches {
|
||||
let mut request = mini_request(KinoviAspectRatioRaw::Landscape16x9, None, batch);
|
||||
request.model_type = model;
|
||||
let body = serde_json::to_value(build_batch_request(request)).unwrap();
|
||||
let params = &body["0"]["json"]["apiParams"];
|
||||
assert_eq!(params.get("batchCount"), expected.map(serde_json::Value::from).as_ref(), "{model:?}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn non_mini_still_uses_resolution_field() {
|
||||
let mut req = mini_request(KinoviAspectRatioRaw::Landscape16x9, None, KinoviBatchCountRaw::One);
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
use crate::pricing::cost::kinovi_fractional_generation_cost::KinoviFractionalGenerationCost;
|
||||
use crate::pricing::kinovi_pricing_tier::KinoviPricingTier;
|
||||
|
||||
const FLOAT_TOLERANCE: f64 = 1e-8;
|
||||
|
||||
/// Check scaling of credits and fractional cents, then independently compute
|
||||
/// whole cents from the complete batch using integer arithmetic. Multiplying
|
||||
/// a single video's already-rounded cents can overcharge a batch.
|
||||
pub(crate) fn assert_batch_cost(
|
||||
single: KinoviFractionalGenerationCost,
|
||||
batched: KinoviFractionalGenerationCost,
|
||||
count: u64,
|
||||
tier: KinoviPricingTier,
|
||||
context: &str,
|
||||
) {
|
||||
let expected_credit_hundredths = (single.kinovi_credits * 100.0).round() as u64 * count;
|
||||
let expected_credits = expected_credit_hundredths as f64 / 100.0;
|
||||
let expected_fractional_cents = single.usd_cents_fractional * count as f64;
|
||||
assert_eq!(batched.kinovi_credits, expected_credits, "{context}: credits");
|
||||
assert!(
|
||||
(batched.usd_cents_fractional - expected_fractional_cents).abs() < FLOAT_TOLERANCE,
|
||||
"{context}: fractional cents: {} != {expected_fractional_cents}",
|
||||
batched.usd_cents_fractional,
|
||||
);
|
||||
|
||||
// Credit purchase rates expressed in hundredths of a credit per dollar.
|
||||
let credits_per_dollar_hundredths = match tier {
|
||||
KinoviPricingTier::Consumer => 19_298,
|
||||
KinoviPricingTier::Enterprise => 24_316,
|
||||
};
|
||||
let numerator = expected_credit_hundredths * 100;
|
||||
assert_eq!(batched.usd_cents_rounded_up, numerator.div_ceil(credits_per_dollar_hundredths), "{context}: rounded up");
|
||||
assert_eq!(batched.usd_cents_rounded_down, numerator / credits_per_dollar_hundredths, "{context}: rounded down");
|
||||
}
|
||||
@@ -1,3 +1,6 @@
|
||||
#[cfg(test)]
|
||||
pub (crate) mod assert_batch_cost;
|
||||
|
||||
#[cfg(test)]
|
||||
pub (crate) mod get_test_cookies;
|
||||
|
||||
|
||||
+1
-1
@@ -27,7 +27,7 @@ pub fn happy_horse_video_models() -> Vec<OmniGenVideoModelDetails> {
|
||||
CommonResolution::TenEightyP,
|
||||
]),
|
||||
resolution_default: Some(CommonResolution::SevenTwentyP),
|
||||
batch_size_options: Some(vec![1, 2, 4]),
|
||||
batch_size_options: Some(vec![1, 2, 3, 4]),
|
||||
batch_size_default: Some(1),
|
||||
duration_seconds_min: Some(3),
|
||||
duration_seconds_max: Some(15),
|
||||
|
||||
+2
-2
@@ -51,7 +51,7 @@ pub fn seedance_2p5_video_models() -> Vec<OmniGenVideoModelDetails> {
|
||||
bitrate_default: Some(CommonBitrate::Normal),
|
||||
output_format_options: Some(vec![CommonVideoOutputFormat::Mp4, CommonVideoOutputFormat::Mov]),
|
||||
output_format_default: Some(CommonVideoOutputFormat::Mp4),
|
||||
batch_size_options: Some(vec![1]),
|
||||
batch_size_options: Some(vec![1, 2, 3, 4]),
|
||||
batch_size_default: Some(1),
|
||||
duration_seconds_min: Some(4),
|
||||
duration_seconds_max: Some(30),
|
||||
@@ -103,7 +103,7 @@ pub fn seedance_2p5_video_models() -> Vec<OmniGenVideoModelDetails> {
|
||||
CommonVideoOutputFormat::Mov
|
||||
]),
|
||||
output_format_default: Some(CommonVideoOutputFormat::Mp4),
|
||||
batch_size_options: Some(vec![1]),
|
||||
batch_size_options: Some(vec![1, 2, 3, 4]),
|
||||
batch_size_default: Some(1),
|
||||
duration_seconds_min: Some(4),
|
||||
duration_seconds_max: Some(30),
|
||||
|
||||
@@ -156,3 +156,31 @@ fn build_omni_gen_video_model_providers() -> Vec<OmniGenVideoModelProviderDetail
|
||||
|
||||
providers
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn kinovi_batch_options_are_one_through_four() {
|
||||
let models = build_omni_gen_video_models();
|
||||
for model in [
|
||||
CommonVideoModel::Seedance2p5,
|
||||
CommonVideoModel::Seedance2p0,
|
||||
CommonVideoModel::Seedance2p0Fast,
|
||||
CommonVideoModel::Seedance2p0Mini,
|
||||
CommonVideoModel::Seedance2p5Ultra,
|
||||
CommonVideoModel::Seedance2p0BytePlus,
|
||||
CommonVideoModel::Seedance2p0BytePlusFast,
|
||||
CommonVideoModel::Seedance2p0BytePlusMini,
|
||||
CommonVideoModel::Seedance2p0BytePlusUltra,
|
||||
CommonVideoModel::Seedance2p0BytePlusUltraFast,
|
||||
CommonVideoModel::Seedance2p0BytePlusUltraMini,
|
||||
CommonVideoModel::HappyHorse1p0,
|
||||
] {
|
||||
let details = models.iter().find(|details| details.model == model).unwrap();
|
||||
assert_eq!(details.batch_size_options.as_deref(), Some([1, 2, 3, 4].as_slice()), "{model:?}");
|
||||
assert_eq!(details.batch_size_default, Some(1), "{model:?}");
|
||||
}
|
||||
}
|
||||
}
|
||||
+55
@@ -137,3 +137,58 @@ fn to_omni_gen_request(request: OmniApiVideoGenerateRequest) -> OmniGenVideoCost
|
||||
estimate_only: None,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use artcraft_router::api::router_provider::RouterProvider;
|
||||
use enums::common::generation::common_video_model::CommonVideoModel;
|
||||
use serde_json::json;
|
||||
|
||||
use crate::http_server::endpoints::omni_gen::generate::video::helpers::hydrate_router_request::hydrate_to_router_request;
|
||||
use crate::http_server::endpoints::omni_gen::shared_utils::video::validate_video_request::validate_video_request as validate_omni_gen;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn kinovi_batches_reach_shared_generation_with_the_full_retail_price() {
|
||||
// Both handlers use this hydration and ArtCraft pricing path. The API
|
||||
// handler additionally converts its request into the omni_gen shape.
|
||||
for (model, expected_prices) in [
|
||||
(CommonVideoModel::Seedance2p5, [134, 268, 401, 535]),
|
||||
(CommonVideoModel::Seedance2p0, [93, 185, 278, 370]),
|
||||
(CommonVideoModel::Seedance2p0Fast, [64, 127, 191, 255]),
|
||||
(CommonVideoModel::Seedance2p0Mini, [45, 89, 134, 178]),
|
||||
(CommonVideoModel::Seedance2p5Ultra, [158, 316, 474, 632]),
|
||||
(CommonVideoModel::Seedance2p0BytePlus, [125, 250, 375, 500]),
|
||||
(CommonVideoModel::Seedance2p0BytePlusFast, [100, 200, 300, 400]),
|
||||
(CommonVideoModel::Seedance2p0BytePlusMini, [46, 91, 137, 182]),
|
||||
(CommonVideoModel::Seedance2p0BytePlusUltra, [125, 250, 375, 500]),
|
||||
(CommonVideoModel::Seedance2p0BytePlusUltraFast, [100, 200, 300, 400]),
|
||||
(CommonVideoModel::Seedance2p0BytePlusUltraMini, [46, 91, 137, 182]),
|
||||
(CommonVideoModel::HappyHorse1p0, [85, 171, 256, 342]),
|
||||
] {
|
||||
for (index, price) in expected_prices.iter().copied().enumerate() {
|
||||
let batch = index as u16 + 1;
|
||||
let body = json!({
|
||||
"model": model,
|
||||
"duration_seconds": 5,
|
||||
"resolution": "seven_twenty_p",
|
||||
"video_batch_count": batch,
|
||||
});
|
||||
let api_request: OmniApiVideoGenerateRequest = serde_json::from_value(body.clone()).unwrap();
|
||||
validate_video_request(&api_request).unwrap();
|
||||
let converted_request = to_omni_gen_request(api_request);
|
||||
let gen_request: OmniGenVideoCostAndGenerateRequest = serde_json::from_value(body).unwrap();
|
||||
for request in [gen_request, converted_request] {
|
||||
validate_omni_gen(&request).unwrap();
|
||||
let builder = hydrate_to_router_request(&request).unwrap();
|
||||
assert_eq!(builder.video_batch_count, Some(batch), "{model:?}");
|
||||
assert!(matches!(builder.provider, RouterProvider::Artcraft));
|
||||
let cost = builder.build2().unwrap().estimate_cost().unwrap();
|
||||
assert_eq!(cost.cost_in_credits, Some(price), "{model:?}, batch {batch}");
|
||||
assert_eq!(cost.cost_in_usd_cents, Some(price), "{model:?}, batch {batch}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+2
@@ -1,4 +1,5 @@
|
||||
use crate::http_server::common_responses::common_web_error::CommonWebError;
|
||||
use crate::http_server::endpoints::omni_gen::shared_utils::video::validate_kinovi_batch_count::validate_kinovi_batch_count;
|
||||
use crate::http_server::endpoints::omni_api::shared_utils::video::validate_seedance_2p0_limits::validate_seedance_2p0_limits;
|
||||
use crate::http_server::endpoints::omni_api::shared_utils::video::validate_when_image_required::validate_when_image_required;
|
||||
use artcraft_api_defs::omni_api::generate_requests::omni_api_video_generate_request::OmniApiVideoGenerateRequest;
|
||||
@@ -7,6 +8,7 @@ use artcraft_api_defs::omni_api::generate_requests::omni_api_video_generate_requ
|
||||
pub fn validate_video_request(
|
||||
request: &OmniApiVideoGenerateRequest,
|
||||
) -> Result<(), CommonWebError> {
|
||||
validate_kinovi_batch_count(request.model, request.video_batch_count)?;
|
||||
validate_when_image_required(request)?;
|
||||
validate_seedance_2p0_limits(request)?;
|
||||
Ok(())
|
||||
|
||||
+12
-228
@@ -88,8 +88,8 @@ mod seedance_2p0_fast {
|
||||
}
|
||||
}
|
||||
|
||||
/// Every batch size 1-10 at every resolution (5s).
|
||||
/// Batches over the platform max of 4 downgrade to 4 (3 upgrades to 4); execution and billing agree.
|
||||
/// Every supported batch size 1-4 at every resolution (5s).
|
||||
/// Batches 1-4 are preserved; larger batches are rejected before billing.
|
||||
/// 1080p and 4K downgrade to 720p (and price accordingly).
|
||||
#[tokio::test]
|
||||
#[cfg_attr(feature = "skip_database_tests", ignore)]
|
||||
@@ -101,42 +101,18 @@ mod seedance_2p0_fast {
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(2), ExpectedCredits(71)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(3), ExpectedCredits(107)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(4), ExpectedCredits(143)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(5), ExpectedCredits(143)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(6), ExpectedCredits(143)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(7), ExpectedCredits(143)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(8), ExpectedCredits(143)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(9), ExpectedCredits(143)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(10), ExpectedCredits(143)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(1), ExpectedCredits(64)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(2), ExpectedCredits(127)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(3), ExpectedCredits(191)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(4), ExpectedCredits(255)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(5), ExpectedCredits(255)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(6), ExpectedCredits(255)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(7), ExpectedCredits(255)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(8), ExpectedCredits(255)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(9), ExpectedCredits(255)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(10), ExpectedCredits(255)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(1), ExpectedCredits(64)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(2), ExpectedCredits(127)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(3), ExpectedCredits(191)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(4), ExpectedCredits(255)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(5), ExpectedCredits(255)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(6), ExpectedCredits(255)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(7), ExpectedCredits(255)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(8), ExpectedCredits(255)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(9), ExpectedCredits(255)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(10), ExpectedCredits(255)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(1), ExpectedCredits(64)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(2), ExpectedCredits(127)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(3), ExpectedCredits(191)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(4), ExpectedCredits(255)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(5), ExpectedCredits(255)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(6), ExpectedCredits(255)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(7), ExpectedCredits(255)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(8), ExpectedCredits(255)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(9), ExpectedCredits(255)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(10), ExpectedCredits(255)),
|
||||
];
|
||||
|
||||
for (resolution, seconds, batch, expected) in cases {
|
||||
@@ -156,27 +132,15 @@ mod seedance_2p0_fast {
|
||||
(Some(CommonResolution::FourEightyP), Seconds(6), Batch(2), ExpectedCredits(86)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(7), Batch(3), ExpectedCredits(150)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(9), Batch(4), ExpectedCredits(257)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(11), Batch(5), ExpectedCredits(314)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(13), Batch(8), ExpectedCredits(371)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(15), Batch(10), ExpectedCredits(428)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(6), Batch(2), ExpectedCredits(153)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(7), Batch(3), ExpectedCredits(267)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(9), Batch(4), ExpectedCredits(458)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(11), Batch(5), ExpectedCredits(560)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(13), Batch(8), ExpectedCredits(662)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(15), Batch(10), ExpectedCredits(764)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(6), Batch(2), ExpectedCredits(153)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(7), Batch(3), ExpectedCredits(267)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(9), Batch(4), ExpectedCredits(458)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(11), Batch(5), ExpectedCredits(560)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(13), Batch(8), ExpectedCredits(662)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(15), Batch(10), ExpectedCredits(764)),
|
||||
(Some(CommonResolution::FourK), Seconds(6), Batch(2), ExpectedCredits(153)),
|
||||
(Some(CommonResolution::FourK), Seconds(7), Batch(3), ExpectedCredits(267)),
|
||||
(Some(CommonResolution::FourK), Seconds(9), Batch(4), ExpectedCredits(458)),
|
||||
(Some(CommonResolution::FourK), Seconds(11), Batch(5), ExpectedCredits(560)),
|
||||
(Some(CommonResolution::FourK), Seconds(13), Batch(8), ExpectedCredits(662)),
|
||||
(Some(CommonResolution::FourK), Seconds(15), Batch(10), ExpectedCredits(764)),
|
||||
];
|
||||
|
||||
for (resolution, seconds, batch, expected) in cases {
|
||||
@@ -252,8 +216,8 @@ mod seedance_2p0_fast {
|
||||
}
|
||||
|
||||
/// With video references Fast switches to its with-references rate card (480p 8.41, 720p 15.60 cents/s, ceil-rounded). Charges are asserted on the refunded ledger entry (the fixture media is unreachable).
|
||||
/// Every batch size 1-10 at every resolution (5s).
|
||||
/// Batches over the platform max of 4 downgrade to 4 (3 upgrades to 4); execution and billing agree.
|
||||
/// Every supported batch size 1-4 at every resolution (5s).
|
||||
/// Batches 1-4 are preserved; larger batches are rejected before billing.
|
||||
#[tokio::test]
|
||||
#[cfg_attr(feature = "skip_database_tests", ignore)]
|
||||
async fn video_references_charge_every_batch_size_at_every_resolution() {
|
||||
@@ -264,42 +228,18 @@ mod seedance_2p0_fast {
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(2), ExpectedCredits(85)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(3), ExpectedCredits(127)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(4), ExpectedCredits(169)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(5), ExpectedCredits(169)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(6), ExpectedCredits(169)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(7), ExpectedCredits(169)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(8), ExpectedCredits(169)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(9), ExpectedCredits(169)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(10), ExpectedCredits(169)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(1), ExpectedCredits(78)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(2), ExpectedCredits(156)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(3), ExpectedCredits(234)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(4), ExpectedCredits(312)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(5), ExpectedCredits(312)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(6), ExpectedCredits(312)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(7), ExpectedCredits(312)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(8), ExpectedCredits(312)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(9), ExpectedCredits(312)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(10), ExpectedCredits(312)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(1), ExpectedCredits(78)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(2), ExpectedCredits(156)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(3), ExpectedCredits(234)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(4), ExpectedCredits(312)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(5), ExpectedCredits(312)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(6), ExpectedCredits(312)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(7), ExpectedCredits(312)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(8), ExpectedCredits(312)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(9), ExpectedCredits(312)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(10), ExpectedCredits(312)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(1), ExpectedCredits(78)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(2), ExpectedCredits(156)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(3), ExpectedCredits(234)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(4), ExpectedCredits(312)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(5), ExpectedCredits(312)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(6), ExpectedCredits(312)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(7), ExpectedCredits(312)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(8), ExpectedCredits(312)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(9), ExpectedCredits(312)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(10), ExpectedCredits(312)),
|
||||
];
|
||||
|
||||
for (resolution, seconds, batch, expected) in cases {
|
||||
@@ -336,27 +276,15 @@ mod seedance_2p0_fast {
|
||||
(Some(CommonResolution::FourEightyP), Seconds(6), Batch(2), ExpectedCredits(101)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(7), Batch(3), ExpectedCredits(177)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(9), Batch(4), ExpectedCredits(303)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(11), Batch(5), ExpectedCredits(371)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(13), Batch(8), ExpectedCredits(438)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(15), Batch(10), ExpectedCredits(505)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(6), Batch(2), ExpectedCredits(188)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(7), Batch(3), ExpectedCredits(328)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(9), Batch(4), ExpectedCredits(562)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(11), Batch(5), ExpectedCredits(687)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(13), Batch(8), ExpectedCredits(812)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(15), Batch(10), ExpectedCredits(936)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(6), Batch(2), ExpectedCredits(188)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(7), Batch(3), ExpectedCredits(328)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(9), Batch(4), ExpectedCredits(562)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(11), Batch(5), ExpectedCredits(687)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(13), Batch(8), ExpectedCredits(812)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(15), Batch(10), ExpectedCredits(936)),
|
||||
(Some(CommonResolution::FourK), Seconds(6), Batch(2), ExpectedCredits(188)),
|
||||
(Some(CommonResolution::FourK), Seconds(7), Batch(3), ExpectedCredits(328)),
|
||||
(Some(CommonResolution::FourK), Seconds(9), Batch(4), ExpectedCredits(562)),
|
||||
(Some(CommonResolution::FourK), Seconds(11), Batch(5), ExpectedCredits(687)),
|
||||
(Some(CommonResolution::FourK), Seconds(13), Batch(8), ExpectedCredits(812)),
|
||||
(Some(CommonResolution::FourK), Seconds(15), Batch(10), ExpectedCredits(936)),
|
||||
];
|
||||
|
||||
for (resolution, seconds, batch, expected) in cases {
|
||||
@@ -438,8 +366,8 @@ mod seedance_2p0_bp_fast {
|
||||
}
|
||||
}
|
||||
|
||||
/// Every batch size 1-10 at every resolution (5s).
|
||||
/// Batches over the platform max of 4 downgrade to 4 (3 upgrades to 4); execution and billing agree.
|
||||
/// Every supported batch size 1-4 at every resolution (5s).
|
||||
/// Batches 1-4 are preserved; larger batches are rejected before billing.
|
||||
/// 1080p and 4K downgrade to 720p (and price accordingly).
|
||||
#[tokio::test]
|
||||
#[cfg_attr(feature = "skip_database_tests", ignore)]
|
||||
@@ -451,42 +379,18 @@ mod seedance_2p0_bp_fast {
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(2), ExpectedCredits(90)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(3), ExpectedCredits(135)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(4), ExpectedCredits(180)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(5), ExpectedCredits(180)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(6), ExpectedCredits(180)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(7), ExpectedCredits(180)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(8), ExpectedCredits(180)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(9), ExpectedCredits(180)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(10), ExpectedCredits(180)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(1), ExpectedCredits(100)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(2), ExpectedCredits(200)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(3), ExpectedCredits(300)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(4), ExpectedCredits(400)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(5), ExpectedCredits(400)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(6), ExpectedCredits(400)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(7), ExpectedCredits(400)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(8), ExpectedCredits(400)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(9), ExpectedCredits(400)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(10), ExpectedCredits(400)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(1), ExpectedCredits(100)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(2), ExpectedCredits(200)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(3), ExpectedCredits(300)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(4), ExpectedCredits(400)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(5), ExpectedCredits(400)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(6), ExpectedCredits(400)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(7), ExpectedCredits(400)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(8), ExpectedCredits(400)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(9), ExpectedCredits(400)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(10), ExpectedCredits(400)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(1), ExpectedCredits(100)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(2), ExpectedCredits(200)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(3), ExpectedCredits(300)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(4), ExpectedCredits(400)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(5), ExpectedCredits(400)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(6), ExpectedCredits(400)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(7), ExpectedCredits(400)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(8), ExpectedCredits(400)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(9), ExpectedCredits(400)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(10), ExpectedCredits(400)),
|
||||
];
|
||||
|
||||
for (resolution, seconds, batch, expected) in cases {
|
||||
@@ -506,27 +410,15 @@ mod seedance_2p0_bp_fast {
|
||||
(Some(CommonResolution::FourEightyP), Seconds(6), Batch(2), ExpectedCredits(108)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(7), Batch(3), ExpectedCredits(189)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(9), Batch(4), ExpectedCredits(324)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(11), Batch(5), ExpectedCredits(396)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(13), Batch(8), ExpectedCredits(468)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(15), Batch(10), ExpectedCredits(540)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(6), Batch(2), ExpectedCredits(240)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(7), Batch(3), ExpectedCredits(420)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(9), Batch(4), ExpectedCredits(720)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(11), Batch(5), ExpectedCredits(880)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(13), Batch(8), ExpectedCredits(1040)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(15), Batch(10), ExpectedCredits(1200)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(6), Batch(2), ExpectedCredits(240)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(7), Batch(3), ExpectedCredits(420)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(9), Batch(4), ExpectedCredits(720)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(11), Batch(5), ExpectedCredits(880)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(13), Batch(8), ExpectedCredits(1040)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(15), Batch(10), ExpectedCredits(1200)),
|
||||
(Some(CommonResolution::FourK), Seconds(6), Batch(2), ExpectedCredits(240)),
|
||||
(Some(CommonResolution::FourK), Seconds(7), Batch(3), ExpectedCredits(420)),
|
||||
(Some(CommonResolution::FourK), Seconds(9), Batch(4), ExpectedCredits(720)),
|
||||
(Some(CommonResolution::FourK), Seconds(11), Batch(5), ExpectedCredits(880)),
|
||||
(Some(CommonResolution::FourK), Seconds(13), Batch(8), ExpectedCredits(1040)),
|
||||
(Some(CommonResolution::FourK), Seconds(15), Batch(10), ExpectedCredits(1200)),
|
||||
];
|
||||
|
||||
for (resolution, seconds, batch, expected) in cases {
|
||||
@@ -602,8 +494,8 @@ mod seedance_2p0_bp_fast {
|
||||
}
|
||||
|
||||
/// The BytePlus Fast rate card is flat: reference videos never change the price. Charges are asserted on the refunded ledger entry (the fixture media is unreachable).
|
||||
/// Every batch size 1-10 at every resolution (5s).
|
||||
/// Batches over the platform max of 4 downgrade to 4 (3 upgrades to 4); execution and billing agree.
|
||||
/// Every supported batch size 1-4 at every resolution (5s).
|
||||
/// Batches 1-4 are preserved; larger batches are rejected before billing.
|
||||
#[tokio::test]
|
||||
#[cfg_attr(feature = "skip_database_tests", ignore)]
|
||||
async fn video_references_charge_every_batch_size_at_every_resolution() {
|
||||
@@ -614,42 +506,18 @@ mod seedance_2p0_bp_fast {
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(2), ExpectedCredits(97)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(3), ExpectedCredits(145)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(4), ExpectedCredits(193)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(5), ExpectedCredits(193)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(6), ExpectedCredits(193)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(7), ExpectedCredits(193)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(8), ExpectedCredits(193)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(9), ExpectedCredits(193)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(10), ExpectedCredits(193)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(1), ExpectedCredits(103)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(2), ExpectedCredits(205)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(3), ExpectedCredits(308)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(4), ExpectedCredits(410)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(5), ExpectedCredits(410)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(6), ExpectedCredits(410)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(7), ExpectedCredits(410)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(8), ExpectedCredits(410)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(9), ExpectedCredits(410)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(10), ExpectedCredits(410)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(1), ExpectedCredits(103)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(2), ExpectedCredits(205)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(3), ExpectedCredits(308)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(4), ExpectedCredits(410)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(5), ExpectedCredits(410)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(6), ExpectedCredits(410)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(7), ExpectedCredits(410)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(8), ExpectedCredits(410)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(9), ExpectedCredits(410)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(10), ExpectedCredits(410)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(1), ExpectedCredits(103)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(2), ExpectedCredits(205)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(3), ExpectedCredits(308)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(4), ExpectedCredits(410)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(5), ExpectedCredits(410)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(6), ExpectedCredits(410)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(7), ExpectedCredits(410)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(8), ExpectedCredits(410)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(9), ExpectedCredits(410)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(10), ExpectedCredits(410)),
|
||||
];
|
||||
|
||||
for (resolution, seconds, batch, expected) in cases {
|
||||
@@ -686,27 +554,15 @@ mod seedance_2p0_bp_fast {
|
||||
(Some(CommonResolution::FourEightyP), Seconds(6), Batch(2), ExpectedCredits(116)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(7), Batch(3), ExpectedCredits(203)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(9), Batch(4), ExpectedCredits(348)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(11), Batch(5), ExpectedCredits(425)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(13), Batch(8), ExpectedCredits(502)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(15), Batch(10), ExpectedCredits(579)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(6), Batch(2), ExpectedCredits(246)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(7), Batch(3), ExpectedCredits(431)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(9), Batch(4), ExpectedCredits(738)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(11), Batch(5), ExpectedCredits(902)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(13), Batch(8), ExpectedCredits(1066)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(15), Batch(10), ExpectedCredits(1230)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(6), Batch(2), ExpectedCredits(246)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(7), Batch(3), ExpectedCredits(431)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(9), Batch(4), ExpectedCredits(738)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(11), Batch(5), ExpectedCredits(902)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(13), Batch(8), ExpectedCredits(1066)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(15), Batch(10), ExpectedCredits(1230)),
|
||||
(Some(CommonResolution::FourK), Seconds(6), Batch(2), ExpectedCredits(246)),
|
||||
(Some(CommonResolution::FourK), Seconds(7), Batch(3), ExpectedCredits(431)),
|
||||
(Some(CommonResolution::FourK), Seconds(9), Batch(4), ExpectedCredits(738)),
|
||||
(Some(CommonResolution::FourK), Seconds(11), Batch(5), ExpectedCredits(902)),
|
||||
(Some(CommonResolution::FourK), Seconds(13), Batch(8), ExpectedCredits(1066)),
|
||||
(Some(CommonResolution::FourK), Seconds(15), Batch(10), ExpectedCredits(1230)),
|
||||
];
|
||||
|
||||
for (resolution, seconds, batch, expected) in cases {
|
||||
@@ -804,8 +660,8 @@ mod seedance_2p0_bpu_fast {
|
||||
}
|
||||
}
|
||||
|
||||
/// Every batch size 1-10 at every resolution (5s).
|
||||
/// Batches over the platform max of 4 downgrade to 4 (3 upgrades to 4); execution and billing agree.
|
||||
/// Every supported batch size 1-4 at every resolution (5s).
|
||||
/// Batches 1-4 are preserved; larger batches are rejected before billing.
|
||||
/// 1080p and 4K downgrade to 720p (and price accordingly).
|
||||
#[tokio::test]
|
||||
#[cfg_attr(feature = "skip_database_tests", ignore)]
|
||||
@@ -817,42 +673,18 @@ mod seedance_2p0_bpu_fast {
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(2), ExpectedCredits(90)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(3), ExpectedCredits(135)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(4), ExpectedCredits(180)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(5), ExpectedCredits(180)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(6), ExpectedCredits(180)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(7), ExpectedCredits(180)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(8), ExpectedCredits(180)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(9), ExpectedCredits(180)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(10), ExpectedCredits(180)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(1), ExpectedCredits(100)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(2), ExpectedCredits(200)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(3), ExpectedCredits(300)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(4), ExpectedCredits(400)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(5), ExpectedCredits(400)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(6), ExpectedCredits(400)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(7), ExpectedCredits(400)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(8), ExpectedCredits(400)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(9), ExpectedCredits(400)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(10), ExpectedCredits(400)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(1), ExpectedCredits(100)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(2), ExpectedCredits(200)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(3), ExpectedCredits(300)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(4), ExpectedCredits(400)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(5), ExpectedCredits(400)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(6), ExpectedCredits(400)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(7), ExpectedCredits(400)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(8), ExpectedCredits(400)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(9), ExpectedCredits(400)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(10), ExpectedCredits(400)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(1), ExpectedCredits(100)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(2), ExpectedCredits(200)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(3), ExpectedCredits(300)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(4), ExpectedCredits(400)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(5), ExpectedCredits(400)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(6), ExpectedCredits(400)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(7), ExpectedCredits(400)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(8), ExpectedCredits(400)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(9), ExpectedCredits(400)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(10), ExpectedCredits(400)),
|
||||
];
|
||||
|
||||
for (resolution, seconds, batch, expected) in cases {
|
||||
@@ -872,27 +704,15 @@ mod seedance_2p0_bpu_fast {
|
||||
(Some(CommonResolution::FourEightyP), Seconds(6), Batch(2), ExpectedCredits(108)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(7), Batch(3), ExpectedCredits(189)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(9), Batch(4), ExpectedCredits(324)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(11), Batch(5), ExpectedCredits(396)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(13), Batch(8), ExpectedCredits(468)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(15), Batch(10), ExpectedCredits(540)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(6), Batch(2), ExpectedCredits(240)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(7), Batch(3), ExpectedCredits(420)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(9), Batch(4), ExpectedCredits(720)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(11), Batch(5), ExpectedCredits(880)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(13), Batch(8), ExpectedCredits(1040)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(15), Batch(10), ExpectedCredits(1200)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(6), Batch(2), ExpectedCredits(240)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(7), Batch(3), ExpectedCredits(420)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(9), Batch(4), ExpectedCredits(720)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(11), Batch(5), ExpectedCredits(880)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(13), Batch(8), ExpectedCredits(1040)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(15), Batch(10), ExpectedCredits(1200)),
|
||||
(Some(CommonResolution::FourK), Seconds(6), Batch(2), ExpectedCredits(240)),
|
||||
(Some(CommonResolution::FourK), Seconds(7), Batch(3), ExpectedCredits(420)),
|
||||
(Some(CommonResolution::FourK), Seconds(9), Batch(4), ExpectedCredits(720)),
|
||||
(Some(CommonResolution::FourK), Seconds(11), Batch(5), ExpectedCredits(880)),
|
||||
(Some(CommonResolution::FourK), Seconds(13), Batch(8), ExpectedCredits(1040)),
|
||||
(Some(CommonResolution::FourK), Seconds(15), Batch(10), ExpectedCredits(1200)),
|
||||
];
|
||||
|
||||
for (resolution, seconds, batch, expected) in cases {
|
||||
@@ -968,8 +788,8 @@ mod seedance_2p0_bpu_fast {
|
||||
}
|
||||
|
||||
/// The BytePlus Fast rate card is flat: reference videos never change the price. Charges are asserted on the refunded ledger entry (the fixture media is unreachable).
|
||||
/// Every batch size 1-10 at every resolution (5s).
|
||||
/// Batches over the platform max of 4 downgrade to 4 (3 upgrades to 4); execution and billing agree.
|
||||
/// Every supported batch size 1-4 at every resolution (5s).
|
||||
/// Batches 1-4 are preserved; larger batches are rejected before billing.
|
||||
#[tokio::test]
|
||||
#[cfg_attr(feature = "skip_database_tests", ignore)]
|
||||
async fn video_references_charge_every_batch_size_at_every_resolution() {
|
||||
@@ -980,42 +800,18 @@ mod seedance_2p0_bpu_fast {
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(2), ExpectedCredits(97)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(3), ExpectedCredits(145)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(4), ExpectedCredits(193)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(5), ExpectedCredits(193)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(6), ExpectedCredits(193)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(7), ExpectedCredits(193)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(8), ExpectedCredits(193)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(9), ExpectedCredits(193)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(10), ExpectedCredits(193)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(1), ExpectedCredits(103)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(2), ExpectedCredits(205)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(3), ExpectedCredits(308)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(4), ExpectedCredits(410)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(5), ExpectedCredits(410)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(6), ExpectedCredits(410)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(7), ExpectedCredits(410)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(8), ExpectedCredits(410)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(9), ExpectedCredits(410)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(10), ExpectedCredits(410)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(1), ExpectedCredits(103)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(2), ExpectedCredits(205)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(3), ExpectedCredits(308)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(4), ExpectedCredits(410)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(5), ExpectedCredits(410)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(6), ExpectedCredits(410)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(7), ExpectedCredits(410)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(8), ExpectedCredits(410)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(9), ExpectedCredits(410)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(10), ExpectedCredits(410)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(1), ExpectedCredits(103)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(2), ExpectedCredits(205)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(3), ExpectedCredits(308)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(4), ExpectedCredits(410)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(5), ExpectedCredits(410)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(6), ExpectedCredits(410)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(7), ExpectedCredits(410)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(8), ExpectedCredits(410)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(9), ExpectedCredits(410)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(10), ExpectedCredits(410)),
|
||||
];
|
||||
|
||||
for (resolution, seconds, batch, expected) in cases {
|
||||
@@ -1052,27 +848,15 @@ mod seedance_2p0_bpu_fast {
|
||||
(Some(CommonResolution::FourEightyP), Seconds(6), Batch(2), ExpectedCredits(116)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(7), Batch(3), ExpectedCredits(203)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(9), Batch(4), ExpectedCredits(348)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(11), Batch(5), ExpectedCredits(425)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(13), Batch(8), ExpectedCredits(502)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(15), Batch(10), ExpectedCredits(579)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(6), Batch(2), ExpectedCredits(246)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(7), Batch(3), ExpectedCredits(431)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(9), Batch(4), ExpectedCredits(738)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(11), Batch(5), ExpectedCredits(902)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(13), Batch(8), ExpectedCredits(1066)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(15), Batch(10), ExpectedCredits(1230)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(6), Batch(2), ExpectedCredits(246)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(7), Batch(3), ExpectedCredits(431)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(9), Batch(4), ExpectedCredits(738)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(11), Batch(5), ExpectedCredits(902)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(13), Batch(8), ExpectedCredits(1066)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(15), Batch(10), ExpectedCredits(1230)),
|
||||
(Some(CommonResolution::FourK), Seconds(6), Batch(2), ExpectedCredits(246)),
|
||||
(Some(CommonResolution::FourK), Seconds(7), Batch(3), ExpectedCredits(431)),
|
||||
(Some(CommonResolution::FourK), Seconds(9), Batch(4), ExpectedCredits(738)),
|
||||
(Some(CommonResolution::FourK), Seconds(11), Batch(5), ExpectedCredits(902)),
|
||||
(Some(CommonResolution::FourK), Seconds(13), Batch(8), ExpectedCredits(1066)),
|
||||
(Some(CommonResolution::FourK), Seconds(15), Batch(10), ExpectedCredits(1230)),
|
||||
];
|
||||
|
||||
for (resolution, seconds, batch, expected) in cases {
|
||||
|
||||
+13
-231
@@ -12,7 +12,7 @@
|
||||
//!
|
||||
//! The Mini tier offers 480p and 720p; 1080p and 4K requests downgrade to
|
||||
//! 720p on both execution and billing, which the tables below pin. Batches
|
||||
//! of 1-8 bill in full (once, batch 8 executed in full but billed as 4).
|
||||
//! of 1-4 bill in full; larger batches are rejected before billing.
|
||||
|
||||
use enums::common::generation::common_resolution::CommonResolution;
|
||||
use enums::common::generation::common_video_model::CommonVideoModel;
|
||||
@@ -91,8 +91,8 @@ mod seedance_2p0_mini {
|
||||
}
|
||||
}
|
||||
|
||||
/// Every batch size 1-10 at every resolution (5s).
|
||||
/// Mini supports batches of 1-8 and bills them in full; 9-10 clamp to 8.
|
||||
/// Every supported batch size 1-4 at every resolution (5s).
|
||||
/// Mini supports batches of 1-4 and bills them in full.
|
||||
/// 1080p and 4K downgrade to 720p (and price accordingly).
|
||||
#[tokio::test]
|
||||
#[cfg_attr(feature = "skip_database_tests", ignore)]
|
||||
@@ -104,42 +104,18 @@ mod seedance_2p0_mini {
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(2), ExpectedCredits(35)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(3), ExpectedCredits(52)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(4), ExpectedCredits(69)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(5), ExpectedCredits(87)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(6), ExpectedCredits(104)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(7), ExpectedCredits(121)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(8), ExpectedCredits(138)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(9), ExpectedCredits(138)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(10), ExpectedCredits(138)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(1), ExpectedCredits(45)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(2), ExpectedCredits(89)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(3), ExpectedCredits(134)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(4), ExpectedCredits(178)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(5), ExpectedCredits(223)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(6), ExpectedCredits(267)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(7), ExpectedCredits(312)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(8), ExpectedCredits(356)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(9), ExpectedCredits(356)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(10), ExpectedCredits(356)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(1), ExpectedCredits(45)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(2), ExpectedCredits(89)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(3), ExpectedCredits(134)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(4), ExpectedCredits(178)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(5), ExpectedCredits(223)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(6), ExpectedCredits(267)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(7), ExpectedCredits(312)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(8), ExpectedCredits(356)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(9), ExpectedCredits(356)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(10), ExpectedCredits(356)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(1), ExpectedCredits(45)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(2), ExpectedCredits(89)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(3), ExpectedCredits(134)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(4), ExpectedCredits(178)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(5), ExpectedCredits(223)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(6), ExpectedCredits(267)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(7), ExpectedCredits(312)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(8), ExpectedCredits(356)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(9), ExpectedCredits(356)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(10), ExpectedCredits(356)),
|
||||
];
|
||||
|
||||
for (resolution, seconds, batch, expected) in cases {
|
||||
@@ -159,27 +135,15 @@ mod seedance_2p0_mini {
|
||||
(Some(CommonResolution::FourEightyP), Seconds(6), Batch(2), ExpectedCredits(42)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(7), Batch(3), ExpectedCredits(73)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(9), Batch(4), ExpectedCredits(125)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(11), Batch(5), ExpectedCredits(190)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(13), Batch(8), ExpectedCredits(359)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(15), Batch(10), ExpectedCredits(414)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(6), Batch(2), ExpectedCredits(107)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(7), Batch(3), ExpectedCredits(187)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(9), Batch(4), ExpectedCredits(321)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(11), Batch(5), ExpectedCredits(490)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(13), Batch(8), ExpectedCredits(926)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(15), Batch(10), ExpectedCredits(1068)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(6), Batch(2), ExpectedCredits(107)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(7), Batch(3), ExpectedCredits(187)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(9), Batch(4), ExpectedCredits(321)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(11), Batch(5), ExpectedCredits(490)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(13), Batch(8), ExpectedCredits(926)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(15), Batch(10), ExpectedCredits(1068)),
|
||||
(Some(CommonResolution::FourK), Seconds(6), Batch(2), ExpectedCredits(107)),
|
||||
(Some(CommonResolution::FourK), Seconds(7), Batch(3), ExpectedCredits(187)),
|
||||
(Some(CommonResolution::FourK), Seconds(9), Batch(4), ExpectedCredits(321)),
|
||||
(Some(CommonResolution::FourK), Seconds(11), Batch(5), ExpectedCredits(490)),
|
||||
(Some(CommonResolution::FourK), Seconds(13), Batch(8), ExpectedCredits(926)),
|
||||
(Some(CommonResolution::FourK), Seconds(15), Batch(10), ExpectedCredits(1068)),
|
||||
];
|
||||
|
||||
for (resolution, seconds, batch, expected) in cases {
|
||||
@@ -255,8 +219,8 @@ mod seedance_2p0_mini {
|
||||
}
|
||||
|
||||
/// With video references Mini adds its per-second surcharge before the single ceil-rounding. Charges are asserted on the refunded ledger entry (the fixture media is unreachable).
|
||||
/// Every batch size 1-10 at every resolution (5s).
|
||||
/// Mini supports batches of 1-8 and bills them in full; 9-10 clamp to 8.
|
||||
/// Every supported batch size 1-4 at every resolution (5s).
|
||||
/// Mini supports batches of 1-4 and bills them in full.
|
||||
#[tokio::test]
|
||||
#[cfg_attr(feature = "skip_database_tests", ignore)]
|
||||
async fn video_references_charge_every_batch_size_at_every_resolution() {
|
||||
@@ -267,42 +231,18 @@ mod seedance_2p0_mini {
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(2), ExpectedCredits(44)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(3), ExpectedCredits(66)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(4), ExpectedCredits(88)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(5), ExpectedCredits(109)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(6), ExpectedCredits(131)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(7), ExpectedCredits(153)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(8), ExpectedCredits(175)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(9), ExpectedCredits(175)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(10), ExpectedCredits(175)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(1), ExpectedCredits(54)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(2), ExpectedCredits(108)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(3), ExpectedCredits(161)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(4), ExpectedCredits(215)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(5), ExpectedCredits(268)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(6), ExpectedCredits(322)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(7), ExpectedCredits(375)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(8), ExpectedCredits(429)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(9), ExpectedCredits(429)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(10), ExpectedCredits(429)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(1), ExpectedCredits(54)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(2), ExpectedCredits(108)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(3), ExpectedCredits(161)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(4), ExpectedCredits(215)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(5), ExpectedCredits(268)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(6), ExpectedCredits(322)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(7), ExpectedCredits(375)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(8), ExpectedCredits(429)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(9), ExpectedCredits(429)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(10), ExpectedCredits(429)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(1), ExpectedCredits(54)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(2), ExpectedCredits(108)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(3), ExpectedCredits(161)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(4), ExpectedCredits(215)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(5), ExpectedCredits(268)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(6), ExpectedCredits(322)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(7), ExpectedCredits(375)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(8), ExpectedCredits(429)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(9), ExpectedCredits(429)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(10), ExpectedCredits(429)),
|
||||
];
|
||||
|
||||
for (resolution, seconds, batch, expected) in cases {
|
||||
@@ -339,27 +279,15 @@ mod seedance_2p0_mini {
|
||||
(Some(CommonResolution::FourEightyP), Seconds(6), Batch(2), ExpectedCredits(53)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(7), Batch(3), ExpectedCredits(92)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(9), Batch(4), ExpectedCredits(157)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(11), Batch(5), ExpectedCredits(240)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(13), Batch(8), ExpectedCredits(453)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(15), Batch(10), ExpectedCredits(523)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(6), Batch(2), ExpectedCredits(129)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(7), Batch(3), ExpectedCredits(225)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(9), Batch(4), ExpectedCredits(386)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(11), Batch(5), ExpectedCredits(589)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(13), Batch(8), ExpectedCredits(1113)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(15), Batch(10), ExpectedCredits(1285)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(6), Batch(2), ExpectedCredits(129)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(7), Batch(3), ExpectedCredits(225)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(9), Batch(4), ExpectedCredits(386)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(11), Batch(5), ExpectedCredits(589)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(13), Batch(8), ExpectedCredits(1113)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(15), Batch(10), ExpectedCredits(1285)),
|
||||
(Some(CommonResolution::FourK), Seconds(6), Batch(2), ExpectedCredits(129)),
|
||||
(Some(CommonResolution::FourK), Seconds(7), Batch(3), ExpectedCredits(225)),
|
||||
(Some(CommonResolution::FourK), Seconds(9), Batch(4), ExpectedCredits(386)),
|
||||
(Some(CommonResolution::FourK), Seconds(11), Batch(5), ExpectedCredits(589)),
|
||||
(Some(CommonResolution::FourK), Seconds(13), Batch(8), ExpectedCredits(1113)),
|
||||
(Some(CommonResolution::FourK), Seconds(15), Batch(10), ExpectedCredits(1285)),
|
||||
];
|
||||
|
||||
for (resolution, seconds, batch, expected) in cases {
|
||||
@@ -442,8 +370,8 @@ mod seedance_2p0_bp_mini {
|
||||
}
|
||||
}
|
||||
|
||||
/// Every batch size 1-10 at every resolution (5s).
|
||||
/// Mini supports batches of 1-8 and bills them in full; 9-10 clamp to 8.
|
||||
/// Every supported batch size 1-4 at every resolution (5s).
|
||||
/// Mini supports batches of 1-4 and bills them in full.
|
||||
/// 1080p and 4K downgrade to 720p (and price accordingly).
|
||||
#[tokio::test]
|
||||
#[cfg_attr(feature = "skip_database_tests", ignore)]
|
||||
@@ -455,42 +383,18 @@ mod seedance_2p0_bp_mini {
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(2), ExpectedCredits(36)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(3), ExpectedCredits(54)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(4), ExpectedCredits(71)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(5), ExpectedCredits(89)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(6), ExpectedCredits(107)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(7), ExpectedCredits(125)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(8), ExpectedCredits(142)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(9), ExpectedCredits(142)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(10), ExpectedCredits(142)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(1), ExpectedCredits(46)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(2), ExpectedCredits(91)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(3), ExpectedCredits(137)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(4), ExpectedCredits(182)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(5), ExpectedCredits(228)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(6), ExpectedCredits(273)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(7), ExpectedCredits(319)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(8), ExpectedCredits(364)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(9), ExpectedCredits(364)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(10), ExpectedCredits(364)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(1), ExpectedCredits(46)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(2), ExpectedCredits(91)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(3), ExpectedCredits(137)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(4), ExpectedCredits(182)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(5), ExpectedCredits(228)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(6), ExpectedCredits(273)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(7), ExpectedCredits(319)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(8), ExpectedCredits(364)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(9), ExpectedCredits(364)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(10), ExpectedCredits(364)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(1), ExpectedCredits(46)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(2), ExpectedCredits(91)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(3), ExpectedCredits(137)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(4), ExpectedCredits(182)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(5), ExpectedCredits(228)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(6), ExpectedCredits(273)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(7), ExpectedCredits(319)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(8), ExpectedCredits(364)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(9), ExpectedCredits(364)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(10), ExpectedCredits(364)),
|
||||
];
|
||||
|
||||
for (resolution, seconds, batch, expected) in cases {
|
||||
@@ -510,27 +414,15 @@ mod seedance_2p0_bp_mini {
|
||||
(Some(CommonResolution::FourEightyP), Seconds(6), Batch(2), ExpectedCredits(43)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(7), Batch(3), ExpectedCredits(75)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(9), Batch(4), ExpectedCredits(128)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(11), Batch(5), ExpectedCredits(196)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(13), Batch(8), ExpectedCredits(370)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(15), Batch(10), ExpectedCredits(426)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(6), Batch(2), ExpectedCredits(110)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(7), Batch(3), ExpectedCredits(192)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(9), Batch(4), ExpectedCredits(328)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(11), Batch(5), ExpectedCredits(501)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(13), Batch(8), ExpectedCredits(947)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(15), Batch(10), ExpectedCredits(1092)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(6), Batch(2), ExpectedCredits(110)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(7), Batch(3), ExpectedCredits(192)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(9), Batch(4), ExpectedCredits(328)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(11), Batch(5), ExpectedCredits(501)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(13), Batch(8), ExpectedCredits(947)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(15), Batch(10), ExpectedCredits(1092)),
|
||||
(Some(CommonResolution::FourK), Seconds(6), Batch(2), ExpectedCredits(110)),
|
||||
(Some(CommonResolution::FourK), Seconds(7), Batch(3), ExpectedCredits(192)),
|
||||
(Some(CommonResolution::FourK), Seconds(9), Batch(4), ExpectedCredits(328)),
|
||||
(Some(CommonResolution::FourK), Seconds(11), Batch(5), ExpectedCredits(501)),
|
||||
(Some(CommonResolution::FourK), Seconds(13), Batch(8), ExpectedCredits(947)),
|
||||
(Some(CommonResolution::FourK), Seconds(15), Batch(10), ExpectedCredits(1092)),
|
||||
];
|
||||
|
||||
for (resolution, seconds, batch, expected) in cases {
|
||||
@@ -606,8 +498,8 @@ mod seedance_2p0_bp_mini {
|
||||
}
|
||||
|
||||
/// With video references Mini adds its per-second surcharge before the single ceil-rounding. Charges are asserted on the refunded ledger entry (the fixture media is unreachable).
|
||||
/// Every batch size 1-10 at every resolution (5s).
|
||||
/// Mini supports batches of 1-8 and bills them in full; 9-10 clamp to 8.
|
||||
/// Every supported batch size 1-4 at every resolution (5s).
|
||||
/// Mini supports batches of 1-4 and bills them in full.
|
||||
#[tokio::test]
|
||||
#[cfg_attr(feature = "skip_database_tests", ignore)]
|
||||
async fn video_references_charge_every_batch_size_at_every_resolution() {
|
||||
@@ -618,42 +510,18 @@ mod seedance_2p0_bp_mini {
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(2), ExpectedCredits(45)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(3), ExpectedCredits(68)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(4), ExpectedCredits(90)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(5), ExpectedCredits(113)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(6), ExpectedCredits(135)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(7), ExpectedCredits(158)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(8), ExpectedCredits(180)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(9), ExpectedCredits(180)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(10), ExpectedCredits(180)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(1), ExpectedCredits(55)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(2), ExpectedCredits(110)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(3), ExpectedCredits(165)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(4), ExpectedCredits(219)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(5), ExpectedCredits(274)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(6), ExpectedCredits(329)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(7), ExpectedCredits(384)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(8), ExpectedCredits(438)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(9), ExpectedCredits(438)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(10), ExpectedCredits(438)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(1), ExpectedCredits(55)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(2), ExpectedCredits(110)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(3), ExpectedCredits(165)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(4), ExpectedCredits(219)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(5), ExpectedCredits(274)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(6), ExpectedCredits(329)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(7), ExpectedCredits(384)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(8), ExpectedCredits(438)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(9), ExpectedCredits(438)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(10), ExpectedCredits(438)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(1), ExpectedCredits(55)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(2), ExpectedCredits(110)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(3), ExpectedCredits(165)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(4), ExpectedCredits(219)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(5), ExpectedCredits(274)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(6), ExpectedCredits(329)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(7), ExpectedCredits(384)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(8), ExpectedCredits(438)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(9), ExpectedCredits(438)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(10), ExpectedCredits(438)),
|
||||
];
|
||||
|
||||
for (resolution, seconds, batch, expected) in cases {
|
||||
@@ -690,27 +558,15 @@ mod seedance_2p0_bp_mini {
|
||||
(Some(CommonResolution::FourEightyP), Seconds(6), Batch(2), ExpectedCredits(54)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(7), Batch(3), ExpectedCredits(95)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(9), Batch(4), ExpectedCredits(162)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(11), Batch(5), ExpectedCredits(248)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(13), Batch(8), ExpectedCredits(468)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(15), Batch(10), ExpectedCredits(540)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(6), Batch(2), ExpectedCredits(132)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(7), Batch(3), ExpectedCredits(230)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(9), Batch(4), ExpectedCredits(395)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(11), Batch(5), ExpectedCredits(603)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(13), Batch(8), ExpectedCredits(1139)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(15), Batch(10), ExpectedCredits(1314)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(6), Batch(2), ExpectedCredits(132)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(7), Batch(3), ExpectedCredits(230)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(9), Batch(4), ExpectedCredits(395)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(11), Batch(5), ExpectedCredits(603)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(13), Batch(8), ExpectedCredits(1139)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(15), Batch(10), ExpectedCredits(1314)),
|
||||
(Some(CommonResolution::FourK), Seconds(6), Batch(2), ExpectedCredits(132)),
|
||||
(Some(CommonResolution::FourK), Seconds(7), Batch(3), ExpectedCredits(230)),
|
||||
(Some(CommonResolution::FourK), Seconds(9), Batch(4), ExpectedCredits(395)),
|
||||
(Some(CommonResolution::FourK), Seconds(11), Batch(5), ExpectedCredits(603)),
|
||||
(Some(CommonResolution::FourK), Seconds(13), Batch(8), ExpectedCredits(1139)),
|
||||
(Some(CommonResolution::FourK), Seconds(15), Batch(10), ExpectedCredits(1314)),
|
||||
];
|
||||
|
||||
for (resolution, seconds, batch, expected) in cases {
|
||||
@@ -791,8 +647,8 @@ mod seedance_2p0_bpu_mini {
|
||||
}
|
||||
}
|
||||
|
||||
/// Every batch size 1-10 at every resolution (5s).
|
||||
/// Mini supports batches of 1-8 and bills them in full; 9-10 clamp to 8.
|
||||
/// Every supported batch size 1-4 at every resolution (5s).
|
||||
/// Mini supports batches of 1-4 and bills them in full.
|
||||
/// 1080p and 4K downgrade to 720p (and price accordingly).
|
||||
#[tokio::test]
|
||||
#[cfg_attr(feature = "skip_database_tests", ignore)]
|
||||
@@ -804,42 +660,18 @@ mod seedance_2p0_bpu_mini {
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(2), ExpectedCredits(36)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(3), ExpectedCredits(54)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(4), ExpectedCredits(71)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(5), ExpectedCredits(89)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(6), ExpectedCredits(107)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(7), ExpectedCredits(125)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(8), ExpectedCredits(142)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(9), ExpectedCredits(142)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(10), ExpectedCredits(142)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(1), ExpectedCredits(46)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(2), ExpectedCredits(91)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(3), ExpectedCredits(137)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(4), ExpectedCredits(182)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(5), ExpectedCredits(228)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(6), ExpectedCredits(273)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(7), ExpectedCredits(319)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(8), ExpectedCredits(364)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(9), ExpectedCredits(364)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(10), ExpectedCredits(364)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(1), ExpectedCredits(46)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(2), ExpectedCredits(91)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(3), ExpectedCredits(137)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(4), ExpectedCredits(182)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(5), ExpectedCredits(228)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(6), ExpectedCredits(273)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(7), ExpectedCredits(319)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(8), ExpectedCredits(364)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(9), ExpectedCredits(364)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(10), ExpectedCredits(364)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(1), ExpectedCredits(46)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(2), ExpectedCredits(91)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(3), ExpectedCredits(137)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(4), ExpectedCredits(182)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(5), ExpectedCredits(228)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(6), ExpectedCredits(273)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(7), ExpectedCredits(319)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(8), ExpectedCredits(364)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(9), ExpectedCredits(364)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(10), ExpectedCredits(364)),
|
||||
];
|
||||
|
||||
for (resolution, seconds, batch, expected) in cases {
|
||||
@@ -859,27 +691,15 @@ mod seedance_2p0_bpu_mini {
|
||||
(Some(CommonResolution::FourEightyP), Seconds(6), Batch(2), ExpectedCredits(43)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(7), Batch(3), ExpectedCredits(75)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(9), Batch(4), ExpectedCredits(128)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(11), Batch(5), ExpectedCredits(196)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(13), Batch(8), ExpectedCredits(370)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(15), Batch(10), ExpectedCredits(426)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(6), Batch(2), ExpectedCredits(110)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(7), Batch(3), ExpectedCredits(192)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(9), Batch(4), ExpectedCredits(328)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(11), Batch(5), ExpectedCredits(501)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(13), Batch(8), ExpectedCredits(947)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(15), Batch(10), ExpectedCredits(1092)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(6), Batch(2), ExpectedCredits(110)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(7), Batch(3), ExpectedCredits(192)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(9), Batch(4), ExpectedCredits(328)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(11), Batch(5), ExpectedCredits(501)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(13), Batch(8), ExpectedCredits(947)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(15), Batch(10), ExpectedCredits(1092)),
|
||||
(Some(CommonResolution::FourK), Seconds(6), Batch(2), ExpectedCredits(110)),
|
||||
(Some(CommonResolution::FourK), Seconds(7), Batch(3), ExpectedCredits(192)),
|
||||
(Some(CommonResolution::FourK), Seconds(9), Batch(4), ExpectedCredits(328)),
|
||||
(Some(CommonResolution::FourK), Seconds(11), Batch(5), ExpectedCredits(501)),
|
||||
(Some(CommonResolution::FourK), Seconds(13), Batch(8), ExpectedCredits(947)),
|
||||
(Some(CommonResolution::FourK), Seconds(15), Batch(10), ExpectedCredits(1092)),
|
||||
];
|
||||
|
||||
for (resolution, seconds, batch, expected) in cases {
|
||||
@@ -955,8 +775,8 @@ mod seedance_2p0_bpu_mini {
|
||||
}
|
||||
|
||||
/// With video references Mini adds its per-second surcharge before the single ceil-rounding. Charges are asserted on the refunded ledger entry (the fixture media is unreachable).
|
||||
/// Every batch size 1-10 at every resolution (5s).
|
||||
/// Mini supports batches of 1-8 and bills them in full; 9-10 clamp to 8.
|
||||
/// Every supported batch size 1-4 at every resolution (5s).
|
||||
/// Mini supports batches of 1-4 and bills them in full.
|
||||
#[tokio::test]
|
||||
#[cfg_attr(feature = "skip_database_tests", ignore)]
|
||||
async fn video_references_charge_every_batch_size_at_every_resolution() {
|
||||
@@ -967,42 +787,18 @@ mod seedance_2p0_bpu_mini {
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(2), ExpectedCredits(45)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(3), ExpectedCredits(68)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(4), ExpectedCredits(90)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(5), ExpectedCredits(113)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(6), ExpectedCredits(135)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(7), ExpectedCredits(158)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(8), ExpectedCredits(180)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(9), ExpectedCredits(180)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(10), ExpectedCredits(180)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(1), ExpectedCredits(55)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(2), ExpectedCredits(110)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(3), ExpectedCredits(165)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(4), ExpectedCredits(219)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(5), ExpectedCredits(274)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(6), ExpectedCredits(329)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(7), ExpectedCredits(384)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(8), ExpectedCredits(438)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(9), ExpectedCredits(438)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(10), ExpectedCredits(438)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(1), ExpectedCredits(55)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(2), ExpectedCredits(110)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(3), ExpectedCredits(165)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(4), ExpectedCredits(219)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(5), ExpectedCredits(274)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(6), ExpectedCredits(329)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(7), ExpectedCredits(384)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(8), ExpectedCredits(438)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(9), ExpectedCredits(438)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(10), ExpectedCredits(438)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(1), ExpectedCredits(55)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(2), ExpectedCredits(110)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(3), ExpectedCredits(165)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(4), ExpectedCredits(219)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(5), ExpectedCredits(274)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(6), ExpectedCredits(329)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(7), ExpectedCredits(384)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(8), ExpectedCredits(438)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(9), ExpectedCredits(438)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(10), ExpectedCredits(438)),
|
||||
];
|
||||
|
||||
for (resolution, seconds, batch, expected) in cases {
|
||||
@@ -1039,27 +835,15 @@ mod seedance_2p0_bpu_mini {
|
||||
(Some(CommonResolution::FourEightyP), Seconds(6), Batch(2), ExpectedCredits(54)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(7), Batch(3), ExpectedCredits(95)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(9), Batch(4), ExpectedCredits(162)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(11), Batch(5), ExpectedCredits(248)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(13), Batch(8), ExpectedCredits(468)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(15), Batch(10), ExpectedCredits(540)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(6), Batch(2), ExpectedCredits(132)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(7), Batch(3), ExpectedCredits(230)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(9), Batch(4), ExpectedCredits(395)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(11), Batch(5), ExpectedCredits(603)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(13), Batch(8), ExpectedCredits(1139)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(15), Batch(10), ExpectedCredits(1314)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(6), Batch(2), ExpectedCredits(132)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(7), Batch(3), ExpectedCredits(230)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(9), Batch(4), ExpectedCredits(395)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(11), Batch(5), ExpectedCredits(603)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(13), Batch(8), ExpectedCredits(1139)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(15), Batch(10), ExpectedCredits(1314)),
|
||||
(Some(CommonResolution::FourK), Seconds(6), Batch(2), ExpectedCredits(132)),
|
||||
(Some(CommonResolution::FourK), Seconds(7), Batch(3), ExpectedCredits(230)),
|
||||
(Some(CommonResolution::FourK), Seconds(9), Batch(4), ExpectedCredits(395)),
|
||||
(Some(CommonResolution::FourK), Seconds(11), Batch(5), ExpectedCredits(603)),
|
||||
(Some(CommonResolution::FourK), Seconds(13), Batch(8), ExpectedCredits(1139)),
|
||||
(Some(CommonResolution::FourK), Seconds(15), Batch(10), ExpectedCredits(1314)),
|
||||
];
|
||||
|
||||
for (resolution, seconds, batch, expected) in cases {
|
||||
@@ -1089,7 +873,6 @@ mod premium {
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(10), Batch(1), ExpectedCredits(89), ExpectedCredits(91), CreditsDelta(2)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(15), Batch(1), ExpectedCredits(134), ExpectedCredits(137), CreditsDelta(3)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(15), Batch(1), ExpectedCredits(52), ExpectedCredits(54), CreditsDelta(2)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(8), ExpectedCredits(356), ExpectedCredits(364), CreditsDelta(8)),
|
||||
];
|
||||
|
||||
for (resolution, seconds, batch, base, variant, delta) in cases {
|
||||
@@ -1113,7 +896,6 @@ mod premium {
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(10), Batch(1), ExpectedCredits(89), ExpectedCredits(91), CreditsDelta(2)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(15), Batch(1), ExpectedCredits(134), ExpectedCredits(137), CreditsDelta(3)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(15), Batch(1), ExpectedCredits(52), ExpectedCredits(54), CreditsDelta(2)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(8), ExpectedCredits(356), ExpectedCredits(364), CreditsDelta(8)),
|
||||
];
|
||||
|
||||
for (resolution, seconds, batch, base, variant, delta) in cases {
|
||||
|
||||
+12
-230
@@ -91,8 +91,8 @@ mod seedance_2p0 {
|
||||
}
|
||||
}
|
||||
|
||||
/// Every batch size 1-10 at every resolution (5s).
|
||||
/// Batches over the platform max of 4 downgrade to 4 (3 upgrades to 4); execution and billing agree.
|
||||
/// Every supported batch size 1-4 at every resolution (5s).
|
||||
/// Batches 1-4 are preserved; larger batches are rejected before billing.
|
||||
#[tokio::test]
|
||||
#[cfg_attr(feature = "skip_database_tests", ignore)]
|
||||
async fn charges_every_batch_size_at_every_resolution() {
|
||||
@@ -103,42 +103,18 @@ mod seedance_2p0 {
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(2), ExpectedCredits(78)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(3), ExpectedCredits(117)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(4), ExpectedCredits(155)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(5), ExpectedCredits(155)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(6), ExpectedCredits(155)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(7), ExpectedCredits(155)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(8), ExpectedCredits(155)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(9), ExpectedCredits(155)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(10), ExpectedCredits(155)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(1), ExpectedCredits(93)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(2), ExpectedCredits(185)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(3), ExpectedCredits(278)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(4), ExpectedCredits(370)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(5), ExpectedCredits(370)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(6), ExpectedCredits(370)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(7), ExpectedCredits(370)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(8), ExpectedCredits(370)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(9), ExpectedCredits(370)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(10), ExpectedCredits(370)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(1), ExpectedCredits(233)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(2), ExpectedCredits(466)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(3), ExpectedCredits(699)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(4), ExpectedCredits(933)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(5), ExpectedCredits(933)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(6), ExpectedCredits(933)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(7), ExpectedCredits(933)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(8), ExpectedCredits(933)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(9), ExpectedCredits(933)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(10), ExpectedCredits(933)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(1), ExpectedCredits(463)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(2), ExpectedCredits(925)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(3), ExpectedCredits(1388)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(4), ExpectedCredits(1850)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(5), ExpectedCredits(1850)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(6), ExpectedCredits(1850)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(7), ExpectedCredits(1850)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(8), ExpectedCredits(1850)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(9), ExpectedCredits(1850)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(10), ExpectedCredits(1850)),
|
||||
];
|
||||
|
||||
for (resolution, seconds, batch, expected) in cases {
|
||||
@@ -158,27 +134,15 @@ mod seedance_2p0 {
|
||||
(Some(CommonResolution::FourEightyP), Seconds(6), Batch(2), ExpectedCredits(93)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(7), Batch(3), ExpectedCredits(163)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(9), Batch(4), ExpectedCredits(280)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(11), Batch(5), ExpectedCredits(342)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(13), Batch(8), ExpectedCredits(404)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(15), Batch(10), ExpectedCredits(466)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(6), Batch(2), ExpectedCredits(222)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(7), Batch(3), ExpectedCredits(389)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(9), Batch(4), ExpectedCredits(666)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(11), Batch(5), ExpectedCredits(814)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(13), Batch(8), ExpectedCredits(962)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(15), Batch(10), ExpectedCredits(1110)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(6), Batch(2), ExpectedCredits(560)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(7), Batch(3), ExpectedCredits(979)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(9), Batch(4), ExpectedCredits(1679)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(11), Batch(5), ExpectedCredits(2052)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(13), Batch(8), ExpectedCredits(2425)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(15), Batch(10), ExpectedCredits(2798)),
|
||||
(Some(CommonResolution::FourK), Seconds(6), Batch(2), ExpectedCredits(1110)),
|
||||
(Some(CommonResolution::FourK), Seconds(7), Batch(3), ExpectedCredits(1943)),
|
||||
(Some(CommonResolution::FourK), Seconds(9), Batch(4), ExpectedCredits(3330)),
|
||||
(Some(CommonResolution::FourK), Seconds(11), Batch(5), ExpectedCredits(4070)),
|
||||
(Some(CommonResolution::FourK), Seconds(13), Batch(8), ExpectedCredits(4810)),
|
||||
(Some(CommonResolution::FourK), Seconds(15), Batch(10), ExpectedCredits(5550)),
|
||||
];
|
||||
|
||||
for (resolution, seconds, batch, expected) in cases {
|
||||
@@ -254,8 +218,8 @@ mod seedance_2p0 {
|
||||
}
|
||||
|
||||
/// With video references Seedance 2.0 switches to its with-references rate card (480p 8.81, 720p 22.05, 1080p 51.10, 4K 113.80 cents/s, ceil-rounded). The unreachable fixture media fails the upload after billing, so the exact charge is asserted on the refunded ledger entry.
|
||||
/// Every batch size 1-10 at every resolution (5s).
|
||||
/// Batches over the platform max of 4 downgrade to 4 (3 upgrades to 4); execution and billing agree.
|
||||
/// Every supported batch size 1-4 at every resolution (5s).
|
||||
/// Batches 1-4 are preserved; larger batches are rejected before billing.
|
||||
#[tokio::test]
|
||||
#[cfg_attr(feature = "skip_database_tests", ignore)]
|
||||
async fn video_references_charge_every_batch_size_at_every_resolution() {
|
||||
@@ -266,42 +230,18 @@ mod seedance_2p0 {
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(2), ExpectedCredits(89)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(3), ExpectedCredits(133)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(4), ExpectedCredits(177)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(5), ExpectedCredits(177)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(6), ExpectedCredits(177)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(7), ExpectedCredits(177)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(8), ExpectedCredits(177)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(9), ExpectedCredits(177)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(10), ExpectedCredits(177)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(1), ExpectedCredits(111)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(2), ExpectedCredits(221)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(3), ExpectedCredits(331)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(4), ExpectedCredits(441)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(5), ExpectedCredits(441)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(6), ExpectedCredits(441)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(7), ExpectedCredits(441)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(8), ExpectedCredits(441)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(9), ExpectedCredits(441)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(10), ExpectedCredits(441)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(1), ExpectedCredits(256)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(2), ExpectedCredits(511)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(3), ExpectedCredits(767)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(4), ExpectedCredits(1022)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(5), ExpectedCredits(1022)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(6), ExpectedCredits(1022)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(7), ExpectedCredits(1022)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(8), ExpectedCredits(1022)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(9), ExpectedCredits(1022)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(10), ExpectedCredits(1022)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(1), ExpectedCredits(569)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(2), ExpectedCredits(1138)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(3), ExpectedCredits(1707)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(4), ExpectedCredits(2276)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(5), ExpectedCredits(2276)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(6), ExpectedCredits(2276)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(7), ExpectedCredits(2276)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(8), ExpectedCredits(2276)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(9), ExpectedCredits(2276)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(10), ExpectedCredits(2276)),
|
||||
];
|
||||
|
||||
for (resolution, seconds, batch, expected) in cases {
|
||||
@@ -338,27 +278,15 @@ mod seedance_2p0 {
|
||||
(Some(CommonResolution::FourEightyP), Seconds(6), Batch(2), ExpectedCredits(106)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(7), Batch(3), ExpectedCredits(186)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(9), Batch(4), ExpectedCredits(318)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(11), Batch(5), ExpectedCredits(388)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(13), Batch(8), ExpectedCredits(459)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(15), Batch(10), ExpectedCredits(529)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(6), Batch(2), ExpectedCredits(265)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(7), Batch(3), ExpectedCredits(464)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(9), Batch(4), ExpectedCredits(794)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(11), Batch(5), ExpectedCredits(971)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(13), Batch(8), ExpectedCredits(1147)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(15), Batch(10), ExpectedCredits(1323)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(6), Batch(2), ExpectedCredits(614)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(7), Batch(3), ExpectedCredits(1074)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(9), Batch(4), ExpectedCredits(1840)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(11), Batch(5), ExpectedCredits(2249)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(13), Batch(8), ExpectedCredits(2658)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(15), Batch(10), ExpectedCredits(3066)),
|
||||
(Some(CommonResolution::FourK), Seconds(6), Batch(2), ExpectedCredits(1366)),
|
||||
(Some(CommonResolution::FourK), Seconds(7), Batch(3), ExpectedCredits(2390)),
|
||||
(Some(CommonResolution::FourK), Seconds(9), Batch(4), ExpectedCredits(4097)),
|
||||
(Some(CommonResolution::FourK), Seconds(11), Batch(5), ExpectedCredits(5008)),
|
||||
(Some(CommonResolution::FourK), Seconds(13), Batch(8), ExpectedCredits(5918)),
|
||||
(Some(CommonResolution::FourK), Seconds(15), Batch(10), ExpectedCredits(6828)),
|
||||
];
|
||||
|
||||
for (resolution, seconds, batch, expected) in cases {
|
||||
@@ -463,8 +391,8 @@ mod seedance_2p0_bp {
|
||||
}
|
||||
}
|
||||
|
||||
/// Every batch size 1-10 at every resolution (5s).
|
||||
/// Batches over the platform max of 4 downgrade to 4 (3 upgrades to 4); execution and billing agree.
|
||||
/// Every supported batch size 1-4 at every resolution (5s).
|
||||
/// Batches 1-4 are preserved; larger batches are rejected before billing.
|
||||
#[tokio::test]
|
||||
#[cfg_attr(feature = "skip_database_tests", ignore)]
|
||||
async fn charges_every_batch_size_at_every_resolution() {
|
||||
@@ -475,42 +403,18 @@ mod seedance_2p0_bp {
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(2), ExpectedCredits(100)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(3), ExpectedCredits(150)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(4), ExpectedCredits(200)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(5), ExpectedCredits(200)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(6), ExpectedCredits(200)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(7), ExpectedCredits(200)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(8), ExpectedCredits(200)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(9), ExpectedCredits(200)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(10), ExpectedCredits(200)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(1), ExpectedCredits(125)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(2), ExpectedCredits(250)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(3), ExpectedCredits(375)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(4), ExpectedCredits(500)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(5), ExpectedCredits(500)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(6), ExpectedCredits(500)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(7), ExpectedCredits(500)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(8), ExpectedCredits(500)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(9), ExpectedCredits(500)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(10), ExpectedCredits(500)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(1), ExpectedCredits(250)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(2), ExpectedCredits(500)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(3), ExpectedCredits(750)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(4), ExpectedCredits(1000)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(5), ExpectedCredits(1000)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(6), ExpectedCredits(1000)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(7), ExpectedCredits(1000)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(8), ExpectedCredits(1000)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(9), ExpectedCredits(1000)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(10), ExpectedCredits(1000)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(1), ExpectedCredits(475)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(2), ExpectedCredits(950)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(3), ExpectedCredits(1425)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(4), ExpectedCredits(1900)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(5), ExpectedCredits(1900)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(6), ExpectedCredits(1900)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(7), ExpectedCredits(1900)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(8), ExpectedCredits(1900)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(9), ExpectedCredits(1900)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(10), ExpectedCredits(1900)),
|
||||
];
|
||||
|
||||
for (resolution, seconds, batch, expected) in cases {
|
||||
@@ -530,27 +434,15 @@ mod seedance_2p0_bp {
|
||||
(Some(CommonResolution::FourEightyP), Seconds(6), Batch(2), ExpectedCredits(120)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(7), Batch(3), ExpectedCredits(210)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(9), Batch(4), ExpectedCredits(360)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(11), Batch(5), ExpectedCredits(440)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(13), Batch(8), ExpectedCredits(520)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(15), Batch(10), ExpectedCredits(600)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(6), Batch(2), ExpectedCredits(300)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(7), Batch(3), ExpectedCredits(525)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(9), Batch(4), ExpectedCredits(900)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(11), Batch(5), ExpectedCredits(1100)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(13), Batch(8), ExpectedCredits(1300)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(15), Batch(10), ExpectedCredits(1500)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(6), Batch(2), ExpectedCredits(600)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(7), Batch(3), ExpectedCredits(1050)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(9), Batch(4), ExpectedCredits(1800)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(11), Batch(5), ExpectedCredits(2200)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(13), Batch(8), ExpectedCredits(2600)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(15), Batch(10), ExpectedCredits(3000)),
|
||||
(Some(CommonResolution::FourK), Seconds(6), Batch(2), ExpectedCredits(1140)),
|
||||
(Some(CommonResolution::FourK), Seconds(7), Batch(3), ExpectedCredits(1995)),
|
||||
(Some(CommonResolution::FourK), Seconds(9), Batch(4), ExpectedCredits(3420)),
|
||||
(Some(CommonResolution::FourK), Seconds(11), Batch(5), ExpectedCredits(4180)),
|
||||
(Some(CommonResolution::FourK), Seconds(13), Batch(8), ExpectedCredits(4940)),
|
||||
(Some(CommonResolution::FourK), Seconds(15), Batch(10), ExpectedCredits(5700)),
|
||||
];
|
||||
|
||||
for (resolution, seconds, batch, expected) in cases {
|
||||
@@ -626,8 +518,8 @@ mod seedance_2p0_bp {
|
||||
}
|
||||
|
||||
/// The BytePlus rate cards are flat below 4K: reference videos only change the 4K price (a 23.00 cents/s surcharge). Charges are asserted on the refunded ledger entry (the fixture media is unreachable).
|
||||
/// Every batch size 1-10 at every resolution (5s).
|
||||
/// Batches over the platform max of 4 downgrade to 4 (3 upgrades to 4); execution and billing agree.
|
||||
/// Every supported batch size 1-4 at every resolution (5s).
|
||||
/// Batches 1-4 are preserved; larger batches are rejected before billing.
|
||||
#[tokio::test]
|
||||
#[cfg_attr(feature = "skip_database_tests", ignore)]
|
||||
async fn video_references_charge_every_batch_size_at_every_resolution() {
|
||||
@@ -638,42 +530,18 @@ mod seedance_2p0_bp {
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(2), ExpectedCredits(102)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(3), ExpectedCredits(153)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(4), ExpectedCredits(204)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(5), ExpectedCredits(204)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(6), ExpectedCredits(204)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(7), ExpectedCredits(204)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(8), ExpectedCredits(204)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(9), ExpectedCredits(204)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(10), ExpectedCredits(204)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(1), ExpectedCredits(129)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(2), ExpectedCredits(257)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(3), ExpectedCredits(386)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(4), ExpectedCredits(514)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(5), ExpectedCredits(514)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(6), ExpectedCredits(514)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(7), ExpectedCredits(514)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(8), ExpectedCredits(514)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(9), ExpectedCredits(514)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(10), ExpectedCredits(514)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(1), ExpectedCredits(289)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(2), ExpectedCredits(578)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(3), ExpectedCredits(867)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(4), ExpectedCredits(1156)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(5), ExpectedCredits(1156)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(6), ExpectedCredits(1156)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(7), ExpectedCredits(1156)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(8), ExpectedCredits(1156)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(9), ExpectedCredits(1156)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(10), ExpectedCredits(1156)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(1), ExpectedCredits(595)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(2), ExpectedCredits(1190)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(3), ExpectedCredits(1785)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(4), ExpectedCredits(2380)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(5), ExpectedCredits(2380)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(6), ExpectedCredits(2380)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(7), ExpectedCredits(2380)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(8), ExpectedCredits(2380)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(9), ExpectedCredits(2380)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(10), ExpectedCredits(2380)),
|
||||
];
|
||||
|
||||
for (resolution, seconds, batch, expected) in cases {
|
||||
@@ -710,27 +578,15 @@ mod seedance_2p0_bp {
|
||||
(Some(CommonResolution::FourEightyP), Seconds(6), Batch(2), ExpectedCredits(123)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(7), Batch(3), ExpectedCredits(215)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(9), Batch(4), ExpectedCredits(368)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(11), Batch(5), ExpectedCredits(449)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(13), Batch(8), ExpectedCredits(531)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(15), Batch(10), ExpectedCredits(612)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(6), Batch(2), ExpectedCredits(309)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(7), Batch(3), ExpectedCredits(540)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(9), Batch(4), ExpectedCredits(926)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(11), Batch(5), ExpectedCredits(1131)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(13), Batch(8), ExpectedCredits(1337)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(15), Batch(10), ExpectedCredits(1542)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(6), Batch(2), ExpectedCredits(694)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(7), Batch(3), ExpectedCredits(1214)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(9), Batch(4), ExpectedCredits(2081)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(11), Batch(5), ExpectedCredits(2544)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(13), Batch(8), ExpectedCredits(3006)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(15), Batch(10), ExpectedCredits(3468)),
|
||||
(Some(CommonResolution::FourK), Seconds(6), Batch(2), ExpectedCredits(1428)),
|
||||
(Some(CommonResolution::FourK), Seconds(7), Batch(3), ExpectedCredits(2499)),
|
||||
(Some(CommonResolution::FourK), Seconds(9), Batch(4), ExpectedCredits(4284)),
|
||||
(Some(CommonResolution::FourK), Seconds(11), Batch(5), ExpectedCredits(5236)),
|
||||
(Some(CommonResolution::FourK), Seconds(13), Batch(8), ExpectedCredits(6188)),
|
||||
(Some(CommonResolution::FourK), Seconds(15), Batch(10), ExpectedCredits(7140)),
|
||||
];
|
||||
|
||||
for (resolution, seconds, batch, expected) in cases {
|
||||
@@ -828,8 +684,8 @@ mod seedance_2p0_bpu {
|
||||
}
|
||||
}
|
||||
|
||||
/// Every batch size 1-10 at every resolution (5s).
|
||||
/// Batches over the platform max of 4 downgrade to 4 (3 upgrades to 4); execution and billing agree.
|
||||
/// Every supported batch size 1-4 at every resolution (5s).
|
||||
/// Batches 1-4 are preserved; larger batches are rejected before billing.
|
||||
#[tokio::test]
|
||||
#[cfg_attr(feature = "skip_database_tests", ignore)]
|
||||
async fn charges_every_batch_size_at_every_resolution() {
|
||||
@@ -840,42 +696,18 @@ mod seedance_2p0_bpu {
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(2), ExpectedCredits(100)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(3), ExpectedCredits(150)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(4), ExpectedCredits(200)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(5), ExpectedCredits(200)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(6), ExpectedCredits(200)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(7), ExpectedCredits(200)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(8), ExpectedCredits(200)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(9), ExpectedCredits(200)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(10), ExpectedCredits(200)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(1), ExpectedCredits(125)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(2), ExpectedCredits(250)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(3), ExpectedCredits(375)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(4), ExpectedCredits(500)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(5), ExpectedCredits(500)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(6), ExpectedCredits(500)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(7), ExpectedCredits(500)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(8), ExpectedCredits(500)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(9), ExpectedCredits(500)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(10), ExpectedCredits(500)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(1), ExpectedCredits(250)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(2), ExpectedCredits(500)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(3), ExpectedCredits(750)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(4), ExpectedCredits(1000)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(5), ExpectedCredits(1000)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(6), ExpectedCredits(1000)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(7), ExpectedCredits(1000)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(8), ExpectedCredits(1000)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(9), ExpectedCredits(1000)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(10), ExpectedCredits(1000)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(1), ExpectedCredits(475)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(2), ExpectedCredits(950)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(3), ExpectedCredits(1425)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(4), ExpectedCredits(1900)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(5), ExpectedCredits(1900)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(6), ExpectedCredits(1900)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(7), ExpectedCredits(1900)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(8), ExpectedCredits(1900)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(9), ExpectedCredits(1900)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(10), ExpectedCredits(1900)),
|
||||
];
|
||||
|
||||
for (resolution, seconds, batch, expected) in cases {
|
||||
@@ -895,27 +727,15 @@ mod seedance_2p0_bpu {
|
||||
(Some(CommonResolution::FourEightyP), Seconds(6), Batch(2), ExpectedCredits(120)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(7), Batch(3), ExpectedCredits(210)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(9), Batch(4), ExpectedCredits(360)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(11), Batch(5), ExpectedCredits(440)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(13), Batch(8), ExpectedCredits(520)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(15), Batch(10), ExpectedCredits(600)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(6), Batch(2), ExpectedCredits(300)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(7), Batch(3), ExpectedCredits(525)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(9), Batch(4), ExpectedCredits(900)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(11), Batch(5), ExpectedCredits(1100)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(13), Batch(8), ExpectedCredits(1300)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(15), Batch(10), ExpectedCredits(1500)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(6), Batch(2), ExpectedCredits(600)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(7), Batch(3), ExpectedCredits(1050)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(9), Batch(4), ExpectedCredits(1800)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(11), Batch(5), ExpectedCredits(2200)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(13), Batch(8), ExpectedCredits(2600)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(15), Batch(10), ExpectedCredits(3000)),
|
||||
(Some(CommonResolution::FourK), Seconds(6), Batch(2), ExpectedCredits(1140)),
|
||||
(Some(CommonResolution::FourK), Seconds(7), Batch(3), ExpectedCredits(1995)),
|
||||
(Some(CommonResolution::FourK), Seconds(9), Batch(4), ExpectedCredits(3420)),
|
||||
(Some(CommonResolution::FourK), Seconds(11), Batch(5), ExpectedCredits(4180)),
|
||||
(Some(CommonResolution::FourK), Seconds(13), Batch(8), ExpectedCredits(4940)),
|
||||
(Some(CommonResolution::FourK), Seconds(15), Batch(10), ExpectedCredits(5700)),
|
||||
];
|
||||
|
||||
for (resolution, seconds, batch, expected) in cases {
|
||||
@@ -991,8 +811,8 @@ mod seedance_2p0_bpu {
|
||||
}
|
||||
|
||||
/// The BytePlus rate cards are flat below 4K: reference videos only change the 4K price (a 23.00 cents/s surcharge). Charges are asserted on the refunded ledger entry (the fixture media is unreachable).
|
||||
/// Every batch size 1-10 at every resolution (5s).
|
||||
/// Batches over the platform max of 4 downgrade to 4 (3 upgrades to 4); execution and billing agree.
|
||||
/// Every supported batch size 1-4 at every resolution (5s).
|
||||
/// Batches 1-4 are preserved; larger batches are rejected before billing.
|
||||
#[tokio::test]
|
||||
#[cfg_attr(feature = "skip_database_tests", ignore)]
|
||||
async fn video_references_charge_every_batch_size_at_every_resolution() {
|
||||
@@ -1003,42 +823,18 @@ mod seedance_2p0_bpu {
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(2), ExpectedCredits(102)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(3), ExpectedCredits(153)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(4), ExpectedCredits(204)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(5), ExpectedCredits(204)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(6), ExpectedCredits(204)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(7), ExpectedCredits(204)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(8), ExpectedCredits(204)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(9), ExpectedCredits(204)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(10), ExpectedCredits(204)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(1), ExpectedCredits(129)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(2), ExpectedCredits(257)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(3), ExpectedCredits(386)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(4), ExpectedCredits(514)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(5), ExpectedCredits(514)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(6), ExpectedCredits(514)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(7), ExpectedCredits(514)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(8), ExpectedCredits(514)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(9), ExpectedCredits(514)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(10), ExpectedCredits(514)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(1), ExpectedCredits(289)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(2), ExpectedCredits(578)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(3), ExpectedCredits(867)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(4), ExpectedCredits(1156)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(5), ExpectedCredits(1156)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(6), ExpectedCredits(1156)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(7), ExpectedCredits(1156)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(8), ExpectedCredits(1156)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(9), ExpectedCredits(1156)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(10), ExpectedCredits(1156)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(1), ExpectedCredits(595)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(2), ExpectedCredits(1190)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(3), ExpectedCredits(1785)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(4), ExpectedCredits(2380)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(5), ExpectedCredits(2380)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(6), ExpectedCredits(2380)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(7), ExpectedCredits(2380)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(8), ExpectedCredits(2380)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(9), ExpectedCredits(2380)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(10), ExpectedCredits(2380)),
|
||||
];
|
||||
|
||||
for (resolution, seconds, batch, expected) in cases {
|
||||
@@ -1075,27 +871,15 @@ mod seedance_2p0_bpu {
|
||||
(Some(CommonResolution::FourEightyP), Seconds(6), Batch(2), ExpectedCredits(123)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(7), Batch(3), ExpectedCredits(215)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(9), Batch(4), ExpectedCredits(368)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(11), Batch(5), ExpectedCredits(449)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(13), Batch(8), ExpectedCredits(531)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(15), Batch(10), ExpectedCredits(612)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(6), Batch(2), ExpectedCredits(309)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(7), Batch(3), ExpectedCredits(540)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(9), Batch(4), ExpectedCredits(926)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(11), Batch(5), ExpectedCredits(1131)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(13), Batch(8), ExpectedCredits(1337)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(15), Batch(10), ExpectedCredits(1542)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(6), Batch(2), ExpectedCredits(694)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(7), Batch(3), ExpectedCredits(1214)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(9), Batch(4), ExpectedCredits(2081)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(11), Batch(5), ExpectedCredits(2544)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(13), Batch(8), ExpectedCredits(3006)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(15), Batch(10), ExpectedCredits(3468)),
|
||||
(Some(CommonResolution::FourK), Seconds(6), Batch(2), ExpectedCredits(1428)),
|
||||
(Some(CommonResolution::FourK), Seconds(7), Batch(3), ExpectedCredits(2499)),
|
||||
(Some(CommonResolution::FourK), Seconds(9), Batch(4), ExpectedCredits(4284)),
|
||||
(Some(CommonResolution::FourK), Seconds(11), Batch(5), ExpectedCredits(5236)),
|
||||
(Some(CommonResolution::FourK), Seconds(13), Batch(8), ExpectedCredits(6188)),
|
||||
(Some(CommonResolution::FourK), Seconds(15), Batch(10), ExpectedCredits(7140)),
|
||||
];
|
||||
|
||||
for (resolution, seconds, batch, expected) in cases {
|
||||
@@ -1146,7 +930,6 @@ mod premium {
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(4), ExpectedCredits(370), ExpectedCredits(500), CreditsDelta(130)),
|
||||
// Batch 8 is accepted but downgrades to the platform max of 4 on both
|
||||
// execution and billing, so it prices identically to batch 4.
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(8), ExpectedCredits(370), ExpectedCredits(500), CreditsDelta(130)),
|
||||
// 4K premium: the base tier prices at 92.50 cents/s while the BytePlus
|
||||
// tiers price at 95.0 cents/s.
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(1), ExpectedCredits(463), ExpectedCredits(475), CreditsDelta(12)),
|
||||
@@ -1177,7 +960,6 @@ mod premium {
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(4), ExpectedCredits(370), ExpectedCredits(500), CreditsDelta(130)),
|
||||
// Batch 8 is accepted but downgrades to the platform max of 4 on both
|
||||
// execution and billing, so it prices identically to batch 4.
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(8), ExpectedCredits(370), ExpectedCredits(500), CreditsDelta(130)),
|
||||
// 4K premium: the base tier prices at 92.50 cents/s while the BytePlus
|
||||
// tiers price at 95.0 cents/s.
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(1), ExpectedCredits(463), ExpectedCredits(475), CreditsDelta(12)),
|
||||
|
||||
+107
-252
@@ -12,8 +12,7 @@
|
||||
//! 720p 18.72427984 ¢/s, 1080p 41.84487580 ¢/s
|
||||
//!
|
||||
//! Both models offer 480p/720p/1080p (4K downgrades to 1080p), durations
|
||||
//! run 4-30s, and every request generates a single video regardless of the
|
||||
//! requested batch size.
|
||||
//! run 4-30s, and batches of 1-4 bill the full batch.
|
||||
|
||||
use enums::common::generation::common_resolution::CommonResolution;
|
||||
use enums::common::generation::common_video_model::CommonVideoModel;
|
||||
@@ -153,8 +152,8 @@ mod seedance_2p5 {
|
||||
}
|
||||
}
|
||||
|
||||
/// Every batch size 1-10 at every resolution (5s).
|
||||
/// Seedance 2.5 generates a single video per request: every requested batch size 1-10 bills exactly one video.
|
||||
/// Every supported batch size 1-4 at every resolution (5s).
|
||||
/// Seedance 2.5 bills every video in the batch, rounding once after multiplying.
|
||||
/// Seedance 2.5 offers 480p, 720p, and 1080p; 4K downgrades to 1080p (and prices accordingly).
|
||||
#[tokio::test]
|
||||
#[cfg_attr(feature = "skip_database_tests", ignore)]
|
||||
@@ -163,45 +162,21 @@ mod seedance_2p5 {
|
||||
|
||||
let cases: &[(Option<CommonResolution>, Seconds, Batch, ExpectedCredits)] = &[
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(1), ExpectedCredits(59)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(2), ExpectedCredits(59)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(3), ExpectedCredits(59)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(4), ExpectedCredits(59)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(5), ExpectedCredits(59)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(6), ExpectedCredits(59)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(7), ExpectedCredits(59)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(8), ExpectedCredits(59)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(9), ExpectedCredits(59)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(10), ExpectedCredits(59)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(2), ExpectedCredits(118)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(3), ExpectedCredits(177)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(4), ExpectedCredits(236)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(1), ExpectedCredits(134)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(2), ExpectedCredits(134)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(3), ExpectedCredits(134)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(4), ExpectedCredits(134)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(5), ExpectedCredits(134)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(6), ExpectedCredits(134)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(7), ExpectedCredits(134)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(8), ExpectedCredits(134)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(9), ExpectedCredits(134)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(10), ExpectedCredits(134)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(2), ExpectedCredits(268)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(3), ExpectedCredits(401)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(4), ExpectedCredits(535)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(1), ExpectedCredits(309)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(2), ExpectedCredits(309)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(3), ExpectedCredits(309)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(4), ExpectedCredits(309)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(5), ExpectedCredits(309)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(6), ExpectedCredits(309)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(7), ExpectedCredits(309)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(8), ExpectedCredits(309)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(9), ExpectedCredits(309)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(10), ExpectedCredits(309)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(2), ExpectedCredits(617)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(3), ExpectedCredits(925)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(4), ExpectedCredits(1233)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(1), ExpectedCredits(309)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(2), ExpectedCredits(309)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(3), ExpectedCredits(309)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(4), ExpectedCredits(309)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(5), ExpectedCredits(309)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(6), ExpectedCredits(309)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(7), ExpectedCredits(309)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(8), ExpectedCredits(309)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(9), ExpectedCredits(309)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(10), ExpectedCredits(309)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(2), ExpectedCredits(617)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(3), ExpectedCredits(925)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(4), ExpectedCredits(1233)),
|
||||
];
|
||||
|
||||
for (resolution, seconds, batch, expected) in cases {
|
||||
@@ -211,37 +186,25 @@ mod seedance_2p5 {
|
||||
}
|
||||
}
|
||||
|
||||
/// Spot checks across duration x batch x resolution (batch always bills as one video).
|
||||
/// Spot checks across duration x batch x resolution.
|
||||
#[tokio::test]
|
||||
#[cfg_attr(feature = "skip_database_tests", ignore)]
|
||||
async fn charges_spot_checked_combinations() {
|
||||
let harness = TestHarness::create().await;
|
||||
|
||||
let cases: &[(Option<CommonResolution>, Seconds, Batch, ExpectedCredits)] = &[
|
||||
(Some(CommonResolution::FourEightyP), Seconds(6), Batch(2), ExpectedCredits(71)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(7), Batch(3), ExpectedCredits(83)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(9), Batch(4), ExpectedCredits(106)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(11), Batch(5), ExpectedCredits(130)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(13), Batch(8), ExpectedCredits(154)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(15), Batch(10), ExpectedCredits(177)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(6), Batch(2), ExpectedCredits(161)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(7), Batch(3), ExpectedCredits(187)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(9), Batch(4), ExpectedCredits(241)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(11), Batch(5), ExpectedCredits(294)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(13), Batch(8), ExpectedCredits(348)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(15), Batch(10), ExpectedCredits(401)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(6), Batch(2), ExpectedCredits(370)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(7), Batch(3), ExpectedCredits(432)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(9), Batch(4), ExpectedCredits(555)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(11), Batch(5), ExpectedCredits(678)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(13), Batch(8), ExpectedCredits(802)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(15), Batch(10), ExpectedCredits(925)),
|
||||
(Some(CommonResolution::FourK), Seconds(6), Batch(2), ExpectedCredits(370)),
|
||||
(Some(CommonResolution::FourK), Seconds(7), Batch(3), ExpectedCredits(432)),
|
||||
(Some(CommonResolution::FourK), Seconds(9), Batch(4), ExpectedCredits(555)),
|
||||
(Some(CommonResolution::FourK), Seconds(11), Batch(5), ExpectedCredits(678)),
|
||||
(Some(CommonResolution::FourK), Seconds(13), Batch(8), ExpectedCredits(802)),
|
||||
(Some(CommonResolution::FourK), Seconds(15), Batch(10), ExpectedCredits(925)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(6), Batch(2), ExpectedCredits(142)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(7), Batch(3), ExpectedCredits(248)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(9), Batch(4), ExpectedCredits(424)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(6), Batch(2), ExpectedCredits(321)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(7), Batch(3), ExpectedCredits(561)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(9), Batch(4), ExpectedCredits(962)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(6), Batch(2), ExpectedCredits(740)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(7), Batch(3), ExpectedCredits(1295)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(9), Batch(4), ExpectedCredits(2219)),
|
||||
(Some(CommonResolution::FourK), Seconds(6), Batch(2), ExpectedCredits(740)),
|
||||
(Some(CommonResolution::FourK), Seconds(7), Batch(3), ExpectedCredits(1295)),
|
||||
(Some(CommonResolution::FourK), Seconds(9), Batch(4), ExpectedCredits(2219)),
|
||||
];
|
||||
|
||||
for (resolution, seconds, batch, expected) in cases {
|
||||
@@ -377,8 +340,8 @@ mod seedance_2p5 {
|
||||
}
|
||||
|
||||
/// With video references the per-second rate drops but billed seconds = output duration + probed input seconds. The fixture media is unreachable, so the probe fails open to the worst-case 30 input seconds, the upload then fails, and the exact charge is asserted on the refunded ledger entry.
|
||||
/// Every batch size 1-10 at every resolution (5s).
|
||||
/// Seedance 2.5 generates a single video per request: every requested batch size 1-10 bills exactly one video.
|
||||
/// Every supported batch size 1-4 at every resolution (5s).
|
||||
/// Seedance 2.5 bills every video in the batch, rounding once after multiplying.
|
||||
#[tokio::test]
|
||||
#[cfg_attr(feature = "skip_database_tests", ignore)]
|
||||
async fn video_references_charge_every_batch_size_at_every_resolution() {
|
||||
@@ -386,45 +349,21 @@ mod seedance_2p5 {
|
||||
|
||||
let cases: &[(Option<CommonResolution>, Seconds, Batch, ExpectedCredits)] = &[
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(1), ExpectedCredits(254)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(2), ExpectedCredits(254)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(3), ExpectedCredits(254)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(4), ExpectedCredits(254)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(5), ExpectedCredits(254)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(6), ExpectedCredits(254)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(7), ExpectedCredits(254)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(8), ExpectedCredits(254)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(9), ExpectedCredits(254)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(10), ExpectedCredits(254)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(2), ExpectedCredits(507)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(3), ExpectedCredits(761)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(4), ExpectedCredits(1014)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(1), ExpectedCredits(555)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(2), ExpectedCredits(555)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(3), ExpectedCredits(555)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(4), ExpectedCredits(555)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(5), ExpectedCredits(555)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(6), ExpectedCredits(555)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(7), ExpectedCredits(555)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(8), ExpectedCredits(555)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(9), ExpectedCredits(555)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(10), ExpectedCredits(555)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(2), ExpectedCredits(1110)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(3), ExpectedCredits(1664)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(4), ExpectedCredits(2219)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(1), ExpectedCredits(1348)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(2), ExpectedCredits(1348)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(3), ExpectedCredits(1348)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(4), ExpectedCredits(1348)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(5), ExpectedCredits(1348)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(6), ExpectedCredits(1348)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(7), ExpectedCredits(1348)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(8), ExpectedCredits(1348)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(9), ExpectedCredits(1348)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(10), ExpectedCredits(1348)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(2), ExpectedCredits(2695)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(3), ExpectedCredits(4043)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(4), ExpectedCredits(5390)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(1), ExpectedCredits(1348)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(2), ExpectedCredits(1348)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(3), ExpectedCredits(1348)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(4), ExpectedCredits(1348)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(5), ExpectedCredits(1348)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(6), ExpectedCredits(1348)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(7), ExpectedCredits(1348)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(8), ExpectedCredits(1348)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(9), ExpectedCredits(1348)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(10), ExpectedCredits(1348)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(2), ExpectedCredits(2695)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(3), ExpectedCredits(4043)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(4), ExpectedCredits(5390)),
|
||||
];
|
||||
|
||||
for (resolution, seconds, batch, expected) in cases {
|
||||
@@ -442,30 +381,18 @@ mod seedance_2p5 {
|
||||
let harness = TestHarness::create().await;
|
||||
|
||||
let cases: &[(Option<CommonResolution>, Seconds, Batch, ExpectedCredits)] = &[
|
||||
(Some(CommonResolution::FourEightyP), Seconds(6), Batch(2), ExpectedCredits(261)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(7), Batch(3), ExpectedCredits(268)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(9), Batch(4), ExpectedCredits(283)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(11), Batch(5), ExpectedCredits(297)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(13), Batch(8), ExpectedCredits(312)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(15), Batch(10), ExpectedCredits(326)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(6), Batch(2), ExpectedCredits(571)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(7), Batch(3), ExpectedCredits(587)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(9), Batch(4), ExpectedCredits(618)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(11), Batch(5), ExpectedCredits(650)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(13), Batch(8), ExpectedCredits(682)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(15), Batch(10), ExpectedCredits(713)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(6), Batch(2), ExpectedCredits(1386)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(7), Batch(3), ExpectedCredits(1425)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(9), Batch(4), ExpectedCredits(1502)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(11), Batch(5), ExpectedCredits(1579)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(13), Batch(8), ExpectedCredits(1656)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(15), Batch(10), ExpectedCredits(1733)),
|
||||
(Some(CommonResolution::FourK), Seconds(6), Batch(2), ExpectedCredits(1386)),
|
||||
(Some(CommonResolution::FourK), Seconds(7), Batch(3), ExpectedCredits(1425)),
|
||||
(Some(CommonResolution::FourK), Seconds(9), Batch(4), ExpectedCredits(1502)),
|
||||
(Some(CommonResolution::FourK), Seconds(11), Batch(5), ExpectedCredits(1579)),
|
||||
(Some(CommonResolution::FourK), Seconds(13), Batch(8), ExpectedCredits(1656)),
|
||||
(Some(CommonResolution::FourK), Seconds(15), Batch(10), ExpectedCredits(1733)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(6), Batch(2), ExpectedCredits(522)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(7), Batch(3), ExpectedCredits(804)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(9), Batch(4), ExpectedCredits(1130)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(6), Batch(2), ExpectedCredits(1141)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(7), Batch(3), ExpectedCredits(1759)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(9), Batch(4), ExpectedCredits(2472)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(6), Batch(2), ExpectedCredits(2772)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(7), Batch(3), ExpectedCredits(4274)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(9), Batch(4), ExpectedCredits(6006)),
|
||||
(Some(CommonResolution::FourK), Seconds(6), Batch(2), ExpectedCredits(2772)),
|
||||
(Some(CommonResolution::FourK), Seconds(7), Batch(3), ExpectedCredits(4274)),
|
||||
(Some(CommonResolution::FourK), Seconds(9), Batch(4), ExpectedCredits(6006)),
|
||||
];
|
||||
|
||||
for (resolution, seconds, batch, expected) in cases {
|
||||
@@ -609,8 +536,8 @@ mod seedance_2p5_u {
|
||||
}
|
||||
}
|
||||
|
||||
/// Every batch size 1-10 at every resolution (5s).
|
||||
/// Seedance 2.5 generates a single video per request: every requested batch size 1-10 bills exactly one video.
|
||||
/// Every supported batch size 1-4 at every resolution (5s).
|
||||
/// Seedance 2.5 bills every video in the batch, rounding once after multiplying.
|
||||
/// Seedance 2.5 Ultra offers 480p, 720p, and 1080p; 4K downgrades to 1080p (and prices accordingly).
|
||||
#[tokio::test]
|
||||
#[cfg_attr(feature = "skip_database_tests", ignore)]
|
||||
@@ -619,45 +546,21 @@ mod seedance_2p5_u {
|
||||
|
||||
let cases: &[(Option<CommonResolution>, Seconds, Batch, ExpectedCredits)] = &[
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(1), ExpectedCredits(70)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(2), ExpectedCredits(70)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(3), ExpectedCredits(70)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(4), ExpectedCredits(70)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(5), ExpectedCredits(70)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(6), ExpectedCredits(70)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(7), ExpectedCredits(70)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(8), ExpectedCredits(70)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(9), ExpectedCredits(70)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(10), ExpectedCredits(70)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(2), ExpectedCredits(140)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(3), ExpectedCredits(209)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(4), ExpectedCredits(279)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(1), ExpectedCredits(158)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(2), ExpectedCredits(158)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(3), ExpectedCredits(158)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(4), ExpectedCredits(158)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(5), ExpectedCredits(158)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(6), ExpectedCredits(158)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(7), ExpectedCredits(158)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(8), ExpectedCredits(158)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(9), ExpectedCredits(158)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(10), ExpectedCredits(158)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(2), ExpectedCredits(316)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(3), ExpectedCredits(474)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(4), ExpectedCredits(632)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(1), ExpectedCredits(337)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(2), ExpectedCredits(337)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(3), ExpectedCredits(337)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(4), ExpectedCredits(337)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(5), ExpectedCredits(337)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(6), ExpectedCredits(337)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(7), ExpectedCredits(337)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(8), ExpectedCredits(337)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(9), ExpectedCredits(337)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(10), ExpectedCredits(337)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(2), ExpectedCredits(673)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(3), ExpectedCredits(1009)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(4), ExpectedCredits(1345)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(1), ExpectedCredits(337)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(2), ExpectedCredits(337)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(3), ExpectedCredits(337)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(4), ExpectedCredits(337)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(5), ExpectedCredits(337)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(6), ExpectedCredits(337)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(7), ExpectedCredits(337)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(8), ExpectedCredits(337)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(9), ExpectedCredits(337)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(10), ExpectedCredits(337)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(2), ExpectedCredits(673)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(3), ExpectedCredits(1009)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(4), ExpectedCredits(1345)),
|
||||
];
|
||||
|
||||
for (resolution, seconds, batch, expected) in cases {
|
||||
@@ -667,37 +570,25 @@ mod seedance_2p5_u {
|
||||
}
|
||||
}
|
||||
|
||||
/// Spot checks across duration x batch x resolution (batch always bills as one video).
|
||||
/// Spot checks across duration x batch x resolution.
|
||||
#[tokio::test]
|
||||
#[cfg_attr(feature = "skip_database_tests", ignore)]
|
||||
async fn charges_spot_checked_combinations() {
|
||||
let harness = TestHarness::create().await;
|
||||
|
||||
let cases: &[(Option<CommonResolution>, Seconds, Batch, ExpectedCredits)] = &[
|
||||
(Some(CommonResolution::FourEightyP), Seconds(6), Batch(2), ExpectedCredits(84)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(7), Batch(3), ExpectedCredits(98)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(9), Batch(4), ExpectedCredits(126)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(11), Batch(5), ExpectedCredits(154)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(13), Batch(8), ExpectedCredits(181)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(15), Batch(10), ExpectedCredits(209)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(6), Batch(2), ExpectedCredits(190)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(7), Batch(3), ExpectedCredits(221)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(9), Batch(4), ExpectedCredits(285)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(11), Batch(5), ExpectedCredits(348)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(13), Batch(8), ExpectedCredits(411)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(15), Batch(10), ExpectedCredits(474)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(6), Batch(2), ExpectedCredits(404)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(7), Batch(3), ExpectedCredits(471)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(9), Batch(4), ExpectedCredits(606)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(11), Batch(5), ExpectedCredits(740)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(13), Batch(8), ExpectedCredits(874)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(15), Batch(10), ExpectedCredits(1009)),
|
||||
(Some(CommonResolution::FourK), Seconds(6), Batch(2), ExpectedCredits(404)),
|
||||
(Some(CommonResolution::FourK), Seconds(7), Batch(3), ExpectedCredits(471)),
|
||||
(Some(CommonResolution::FourK), Seconds(9), Batch(4), ExpectedCredits(606)),
|
||||
(Some(CommonResolution::FourK), Seconds(11), Batch(5), ExpectedCredits(740)),
|
||||
(Some(CommonResolution::FourK), Seconds(13), Batch(8), ExpectedCredits(874)),
|
||||
(Some(CommonResolution::FourK), Seconds(15), Batch(10), ExpectedCredits(1009)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(6), Batch(2), ExpectedCredits(167)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(7), Batch(3), ExpectedCredits(293)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(9), Batch(4), ExpectedCredits(501)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(6), Batch(2), ExpectedCredits(379)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(7), Batch(3), ExpectedCredits(663)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(9), Batch(4), ExpectedCredits(1137)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(6), Batch(2), ExpectedCredits(807)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(7), Batch(3), ExpectedCredits(1412)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(9), Batch(4), ExpectedCredits(2421)),
|
||||
(Some(CommonResolution::FourK), Seconds(6), Batch(2), ExpectedCredits(807)),
|
||||
(Some(CommonResolution::FourK), Seconds(7), Batch(3), ExpectedCredits(1412)),
|
||||
(Some(CommonResolution::FourK), Seconds(9), Batch(4), ExpectedCredits(2421)),
|
||||
];
|
||||
|
||||
for (resolution, seconds, batch, expected) in cases {
|
||||
@@ -833,8 +724,8 @@ mod seedance_2p5_u {
|
||||
}
|
||||
|
||||
/// With video references the per-second rate drops but billed seconds = output duration + probed input seconds. The fixture media is unreachable, so the probe fails open to the worst-case 30 input seconds, the upload then fails, and the exact charge is asserted on the refunded ledger entry.
|
||||
/// Every batch size 1-10 at every resolution (5s).
|
||||
/// Seedance 2.5 generates a single video per request: every requested batch size 1-10 bills exactly one video.
|
||||
/// Every supported batch size 1-4 at every resolution (5s).
|
||||
/// Seedance 2.5 bills every video in the batch, rounding once after multiplying.
|
||||
#[tokio::test]
|
||||
#[cfg_attr(feature = "skip_database_tests", ignore)]
|
||||
async fn video_references_charge_every_batch_size_at_every_resolution() {
|
||||
@@ -842,45 +733,21 @@ mod seedance_2p5_u {
|
||||
|
||||
let cases: &[(Option<CommonResolution>, Seconds, Batch, ExpectedCredits)] = &[
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(1), ExpectedCredits(300)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(2), ExpectedCredits(300)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(3), ExpectedCredits(300)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(4), ExpectedCredits(300)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(5), ExpectedCredits(300)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(6), ExpectedCredits(300)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(7), ExpectedCredits(300)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(8), ExpectedCredits(300)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(9), ExpectedCredits(300)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(10), ExpectedCredits(300)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(2), ExpectedCredits(600)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(3), ExpectedCredits(899)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(4), ExpectedCredits(1199)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(1), ExpectedCredits(656)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(2), ExpectedCredits(656)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(3), ExpectedCredits(656)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(4), ExpectedCredits(656)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(5), ExpectedCredits(656)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(6), ExpectedCredits(656)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(7), ExpectedCredits(656)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(8), ExpectedCredits(656)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(9), ExpectedCredits(656)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(10), ExpectedCredits(656)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(2), ExpectedCredits(1311)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(3), ExpectedCredits(1967)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(4), ExpectedCredits(2622)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(1), ExpectedCredits(1465)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(2), ExpectedCredits(1465)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(3), ExpectedCredits(1465)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(4), ExpectedCredits(1465)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(5), ExpectedCredits(1465)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(6), ExpectedCredits(1465)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(7), ExpectedCredits(1465)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(8), ExpectedCredits(1465)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(9), ExpectedCredits(1465)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(10), ExpectedCredits(1465)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(2), ExpectedCredits(2930)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(3), ExpectedCredits(4394)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(4), ExpectedCredits(5859)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(1), ExpectedCredits(1465)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(2), ExpectedCredits(1465)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(3), ExpectedCredits(1465)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(4), ExpectedCredits(1465)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(5), ExpectedCredits(1465)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(6), ExpectedCredits(1465)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(7), ExpectedCredits(1465)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(8), ExpectedCredits(1465)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(9), ExpectedCredits(1465)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(10), ExpectedCredits(1465)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(2), ExpectedCredits(2930)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(3), ExpectedCredits(4394)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(4), ExpectedCredits(5859)),
|
||||
];
|
||||
|
||||
for (resolution, seconds, batch, expected) in cases {
|
||||
@@ -898,30 +765,18 @@ mod seedance_2p5_u {
|
||||
let harness = TestHarness::create().await;
|
||||
|
||||
let cases: &[(Option<CommonResolution>, Seconds, Batch, ExpectedCredits)] = &[
|
||||
(Some(CommonResolution::FourEightyP), Seconds(6), Batch(2), ExpectedCredits(309)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(7), Batch(3), ExpectedCredits(317)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(9), Batch(4), ExpectedCredits(334)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(11), Batch(5), ExpectedCredits(351)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(13), Batch(8), ExpectedCredits(369)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(15), Batch(10), ExpectedCredits(386)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(6), Batch(2), ExpectedCredits(675)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(7), Batch(3), ExpectedCredits(693)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(9), Batch(4), ExpectedCredits(731)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(11), Batch(5), ExpectedCredits(768)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(13), Batch(8), ExpectedCredits(806)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(15), Batch(10), ExpectedCredits(843)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(6), Batch(2), ExpectedCredits(1507)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(7), Batch(3), ExpectedCredits(1549)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(9), Batch(4), ExpectedCredits(1632)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(11), Batch(5), ExpectedCredits(1716)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(13), Batch(8), ExpectedCredits(1800)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(15), Batch(10), ExpectedCredits(1884)),
|
||||
(Some(CommonResolution::FourK), Seconds(6), Batch(2), ExpectedCredits(1507)),
|
||||
(Some(CommonResolution::FourK), Seconds(7), Batch(3), ExpectedCredits(1549)),
|
||||
(Some(CommonResolution::FourK), Seconds(9), Batch(4), ExpectedCredits(1632)),
|
||||
(Some(CommonResolution::FourK), Seconds(11), Batch(5), ExpectedCredits(1716)),
|
||||
(Some(CommonResolution::FourK), Seconds(13), Batch(8), ExpectedCredits(1800)),
|
||||
(Some(CommonResolution::FourK), Seconds(15), Batch(10), ExpectedCredits(1884)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(6), Batch(2), ExpectedCredits(617)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(7), Batch(3), ExpectedCredits(951)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(9), Batch(4), ExpectedCredits(1336)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(6), Batch(2), ExpectedCredits(1349)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(7), Batch(3), ExpectedCredits(2079)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(9), Batch(4), ExpectedCredits(2921)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(6), Batch(2), ExpectedCredits(3013)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(7), Batch(3), ExpectedCredits(4645)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(9), Batch(4), ExpectedCredits(6528)),
|
||||
(Some(CommonResolution::FourK), Seconds(6), Batch(2), ExpectedCredits(3013)),
|
||||
(Some(CommonResolution::FourK), Seconds(7), Batch(3), ExpectedCredits(4645)),
|
||||
(Some(CommonResolution::FourK), Seconds(9), Batch(4), ExpectedCredits(6528)),
|
||||
];
|
||||
|
||||
for (resolution, seconds, batch, expected) in cases {
|
||||
|
||||
+2
-1
@@ -1,3 +1,4 @@
|
||||
pub(crate) mod validate_kinovi_batch_count;
|
||||
mod validate_seedance_2p0_limits;
|
||||
pub mod validate_video_request;
|
||||
mod validate_when_image_required;
|
||||
mod validate_when_image_required;
|
||||
+99
@@ -0,0 +1,99 @@
|
||||
use enums::common::generation::common_video_model::CommonVideoModel;
|
||||
|
||||
use crate::http_server::common_responses::common_web_error::CommonWebError;
|
||||
|
||||
/// Keep the public batch limit aligned with ArtCraft billing before the
|
||||
/// execution builder reaches Kinovi, whose Seedance API accepts larger batches.
|
||||
pub(crate) fn validate_kinovi_batch_count(
|
||||
model: Option<CommonVideoModel>,
|
||||
video_batch_count: Option<u16>,
|
||||
) -> Result<(), CommonWebError> {
|
||||
if matches!(model, Some(
|
||||
CommonVideoModel::Seedance2p5
|
||||
| CommonVideoModel::Seedance2p0
|
||||
| CommonVideoModel::Seedance2p0Fast
|
||||
| CommonVideoModel::Seedance2p0Mini
|
||||
| CommonVideoModel::Seedance2p5Ultra
|
||||
| CommonVideoModel::Seedance2p0BytePlus
|
||||
| CommonVideoModel::Seedance2p0BytePlusFast
|
||||
| CommonVideoModel::Seedance2p0BytePlusMini
|
||||
| CommonVideoModel::Seedance2p0BytePlusUltra
|
||||
| CommonVideoModel::Seedance2p0BytePlusUltraFast
|
||||
| CommonVideoModel::Seedance2p0BytePlusUltraMini
|
||||
| CommonVideoModel::HappyHorse1p0
|
||||
)) && !(1..=4).contains(&video_batch_count.unwrap_or(1)) {
|
||||
return Err(CommonWebError::BadInputWithSimpleMessage(
|
||||
"video_batch_count must be between 1 and 4 for this model".to_string(),
|
||||
));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use artcraft_api_defs::omni_api::generate_requests::omni_api_video_generate_request::OmniApiVideoGenerateRequest;
|
||||
use artcraft_api_defs::omni_gen::cost_and_generate_requests::omni_gen_video_cost_and_generate_request::OmniGenVideoCostAndGenerateRequest;
|
||||
use serde_json::json;
|
||||
|
||||
use crate::http_server::endpoints::omni_api::shared_utils::video::validate_video_request::validate_video_request as validate_omni_api;
|
||||
use crate::http_server::endpoints::omni_gen::shared_utils::video::validate_video_request::validate_video_request as validate_omni_gen;
|
||||
|
||||
use super::*;
|
||||
|
||||
const MODELS: [CommonVideoModel; 12] = [
|
||||
CommonVideoModel::Seedance2p5,
|
||||
CommonVideoModel::Seedance2p0,
|
||||
CommonVideoModel::Seedance2p0Fast,
|
||||
CommonVideoModel::Seedance2p0Mini,
|
||||
CommonVideoModel::Seedance2p5Ultra,
|
||||
CommonVideoModel::Seedance2p0BytePlus,
|
||||
CommonVideoModel::Seedance2p0BytePlusFast,
|
||||
CommonVideoModel::Seedance2p0BytePlusMini,
|
||||
CommonVideoModel::Seedance2p0BytePlusUltra,
|
||||
CommonVideoModel::Seedance2p0BytePlusUltraFast,
|
||||
CommonVideoModel::Seedance2p0BytePlusUltraMini,
|
||||
CommonVideoModel::HappyHorse1p0,
|
||||
];
|
||||
|
||||
#[test]
|
||||
fn both_endpoints_accept_batches_one_through_four_and_default() {
|
||||
for model in MODELS {
|
||||
for batch in [None, Some(1), Some(2), Some(3), Some(4)] {
|
||||
for result in validate_both(model, batch) {
|
||||
assert!(result.is_ok(), "{:?}, batch {:?}: {:?}", model, batch, result);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn both_endpoints_reject_zero_and_batches_above_four() {
|
||||
for model in MODELS {
|
||||
for batch in [0, 5, 6, 7, 8, u16::MAX] {
|
||||
for result in validate_both(model, Some(batch)) {
|
||||
assert!(matches!(result, Err(CommonWebError::BadInputWithSimpleMessage(_))),
|
||||
"{:?}, batch {}", model, batch);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn other_models_keep_their_existing_validation() {
|
||||
for model in [
|
||||
CommonVideoModel::Seedance2p5Preview,
|
||||
CommonVideoModel::Veo3p1,
|
||||
] {
|
||||
for result in validate_both(model, Some(8)) {
|
||||
assert!(result.is_ok(), "{:?}: {:?}", model, result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn validate_both(model: CommonVideoModel, batch: Option<u16>) -> [Result<(), CommonWebError>; 2] {
|
||||
let json = json!({"model": model, "video_batch_count": batch});
|
||||
let omni_gen: OmniGenVideoCostAndGenerateRequest = serde_json::from_value(json.clone()).unwrap();
|
||||
let omni_api: OmniApiVideoGenerateRequest = serde_json::from_value(json).unwrap();
|
||||
[validate_omni_gen(&omni_gen), validate_omni_api(&omni_api)]
|
||||
}
|
||||
}
|
||||
+2
@@ -1,4 +1,5 @@
|
||||
use crate::http_server::common_responses::common_web_error::CommonWebError;
|
||||
use crate::http_server::endpoints::omni_gen::shared_utils::video::validate_kinovi_batch_count::validate_kinovi_batch_count;
|
||||
use crate::http_server::endpoints::omni_gen::shared_utils::video::validate_seedance_2p0_limits::validate_seedance_2p0_limits;
|
||||
use crate::http_server::endpoints::omni_gen::shared_utils::video::validate_when_image_required::validate_when_image_required;
|
||||
use artcraft_api_defs::omni_gen::cost_and_generate_requests::omni_gen_video_cost_and_generate_request::OmniGenVideoCostAndGenerateRequest;
|
||||
@@ -7,6 +8,7 @@ use artcraft_api_defs::omni_gen::cost_and_generate_requests::omni_gen_video_cost
|
||||
pub fn validate_video_request(
|
||||
request: &OmniGenVideoCostAndGenerateRequest,
|
||||
) -> Result<(), CommonWebError> {
|
||||
validate_kinovi_batch_count(request.model, request.video_batch_count)?;
|
||||
validate_when_image_required(request)?;
|
||||
validate_seedance_2p0_limits(request)?;
|
||||
Ok(())
|
||||
|
||||
Reference in new issue
Block a user