mirror of
https://github.com/storytold/artcraft.git
synced 2026-11-04 13:28:06 -05:00
Compare commits
5
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
55ed5d9243 | ||
|
|
3d4eca9ad0 | ||
|
|
7ae6302166 | ||
|
|
ac613b007b | ||
|
|
47aa6a6c06 |
+25
-25
@@ -15,7 +15,7 @@ use crate::generate::generate_video::video_generation_cost_estimate::VideoGenera
|
||||
/// Kept as f64 because per-second rates are fractional; rounding happens
|
||||
/// once at the end after multiplying by duration × batch.
|
||||
const CENTS_PER_SECOND_480P: f64 = 7.772;
|
||||
const CENTS_PER_SECOND_720P: f64 = 16.0;
|
||||
const CENTS_PER_SECOND_720P: f64 = 18.5;
|
||||
const CENTS_PER_SECOND_1080P: f64 = 46.632;
|
||||
|
||||
/// USD cents per second, in hundredths of a cent, when one or more
|
||||
@@ -131,32 +131,32 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn batch_1() {
|
||||
assert_eq!(cost_cents(Some(RouterResolution::SevenTwentyP), 4, 1), 64);
|
||||
assert_eq!(cost_cents(Some(RouterResolution::SevenTwentyP), 5, 1), 80);
|
||||
assert_eq!(cost_cents(Some(RouterResolution::SevenTwentyP), 6, 1), 96);
|
||||
assert_eq!(cost_cents(Some(RouterResolution::SevenTwentyP), 10, 1), 160);
|
||||
assert_eq!(cost_cents(Some(RouterResolution::SevenTwentyP), 15, 1), 240);
|
||||
assert_eq!(cost_cents(Some(RouterResolution::SevenTwentyP), 4, 1), 74);
|
||||
assert_eq!(cost_cents(Some(RouterResolution::SevenTwentyP), 5, 1), 93);
|
||||
assert_eq!(cost_cents(Some(RouterResolution::SevenTwentyP), 6, 1), 111);
|
||||
assert_eq!(cost_cents(Some(RouterResolution::SevenTwentyP), 10, 1), 185);
|
||||
assert_eq!(cost_cents(Some(RouterResolution::SevenTwentyP), 15, 1), 278);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn batch_2() {
|
||||
assert_eq!(cost_cents(Some(RouterResolution::SevenTwentyP), 4, 2), 128);
|
||||
assert_eq!(cost_cents(Some(RouterResolution::SevenTwentyP), 5, 2), 160);
|
||||
assert_eq!(cost_cents(Some(RouterResolution::SevenTwentyP), 15, 2), 480);
|
||||
assert_eq!(cost_cents(Some(RouterResolution::SevenTwentyP), 4, 2), 148);
|
||||
assert_eq!(cost_cents(Some(RouterResolution::SevenTwentyP), 5, 2), 185);
|
||||
assert_eq!(cost_cents(Some(RouterResolution::SevenTwentyP), 15, 2), 555);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn batch_3() {
|
||||
assert_eq!(cost_cents(Some(RouterResolution::SevenTwentyP), 4, 3), 192);
|
||||
assert_eq!(cost_cents(Some(RouterResolution::SevenTwentyP), 5, 3), 240);
|
||||
assert_eq!(cost_cents(Some(RouterResolution::SevenTwentyP), 15, 3), 720);
|
||||
assert_eq!(cost_cents(Some(RouterResolution::SevenTwentyP), 4, 3), 222);
|
||||
assert_eq!(cost_cents(Some(RouterResolution::SevenTwentyP), 5, 3), 278);
|
||||
assert_eq!(cost_cents(Some(RouterResolution::SevenTwentyP), 15, 3), 833);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn batch_4() {
|
||||
assert_eq!(cost_cents(Some(RouterResolution::SevenTwentyP), 4, 4), 256);
|
||||
assert_eq!(cost_cents(Some(RouterResolution::SevenTwentyP), 5, 4), 320);
|
||||
assert_eq!(cost_cents(Some(RouterResolution::SevenTwentyP), 15, 4), 960);
|
||||
assert_eq!(cost_cents(Some(RouterResolution::SevenTwentyP), 4, 4), 296);
|
||||
assert_eq!(cost_cents(Some(RouterResolution::SevenTwentyP), 5, 4), 370);
|
||||
assert_eq!(cost_cents(Some(RouterResolution::SevenTwentyP), 15, 4), 1110);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -242,10 +242,10 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn explicit_4k_without_video_reference() {
|
||||
assert_eq!(artcraft_4k_cents(4, 1, false), 358);
|
||||
assert_eq!(artcraft_4k_cents(5, 1, false), 447);
|
||||
assert_eq!(artcraft_4k_cents(10, 1, false), 893);
|
||||
assert_eq!(artcraft_4k_cents(15, 1, false), 1340);
|
||||
assert_eq!(artcraft_4k_cents(4, 1, false), 370);
|
||||
assert_eq!(artcraft_4k_cents(5, 1, false), 463);
|
||||
assert_eq!(artcraft_4k_cents(10, 1, false), 925);
|
||||
assert_eq!(artcraft_4k_cents(15, 1, false), 1388);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -499,9 +499,9 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn refs_cost_more_at_720p() {
|
||||
assert_ref_delta(CommonResolution::SevenTwentyP, 5, 80, 111, 31);
|
||||
assert_ref_delta(CommonResolution::SevenTwentyP, 10, 160, 221, 61);
|
||||
assert_ref_delta(CommonResolution::SevenTwentyP, 15, 240, 331, 91);
|
||||
assert_ref_delta(CommonResolution::SevenTwentyP, 5, 93, 111, 18);
|
||||
assert_ref_delta(CommonResolution::SevenTwentyP, 10, 185, 221, 36);
|
||||
assert_ref_delta(CommonResolution::SevenTwentyP, 15, 278, 331, 53);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -513,9 +513,9 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn refs_cost_more_at_4k() {
|
||||
assert_ref_delta(CommonResolution::FourK, 5, 447, 569, 122);
|
||||
assert_ref_delta(CommonResolution::FourK, 10, 893, 1138, 245);
|
||||
assert_ref_delta(CommonResolution::FourK, 15, 1340, 1707, 367);
|
||||
assert_ref_delta(CommonResolution::FourK, 5, 463, 569, 106);
|
||||
assert_ref_delta(CommonResolution::FourK, 10, 925, 1138, 213);
|
||||
assert_ref_delta(CommonResolution::FourK, 15, 1388, 1707, 319);
|
||||
}
|
||||
|
||||
/// Price the same generation with and without a reference video; pin
|
||||
|
||||
+106
-8
@@ -13,7 +13,7 @@ use crate::generate::generate_video::providers::artcraft::seedance_2p0_fast::req
|
||||
///
|
||||
/// Kept as f64 because per-second rates are fractional; rounding happens
|
||||
/// once at the end after multiplying by duration * batch.
|
||||
const CENTS_PER_SECOND_480P: f64 = 6.40;
|
||||
const CENTS_PER_SECOND_480P: f64 = 7.13;
|
||||
const CENTS_PER_SECOND_720P: f64 = 12.727;
|
||||
|
||||
/// USD cents per second, in hundredths of a cent, when one or more
|
||||
@@ -149,25 +149,25 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn batch_1() {
|
||||
assert_eq!(cost_cents(Some(RouterResolution::FourEightyP), 4, 1), 26);
|
||||
assert_eq!(cost_cents(Some(RouterResolution::FourEightyP), 5, 1), 32);
|
||||
assert_eq!(cost_cents(Some(RouterResolution::FourEightyP), 10, 1), 64);
|
||||
assert_eq!(cost_cents(Some(RouterResolution::FourEightyP), 15, 1), 96);
|
||||
assert_eq!(cost_cents(Some(RouterResolution::FourEightyP), 4, 1), 29);
|
||||
assert_eq!(cost_cents(Some(RouterResolution::FourEightyP), 5, 1), 36);
|
||||
assert_eq!(cost_cents(Some(RouterResolution::FourEightyP), 10, 1), 71);
|
||||
assert_eq!(cost_cents(Some(RouterResolution::FourEightyP), 15, 1), 107);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn batch_2() {
|
||||
assert_eq!(cost_cents(Some(RouterResolution::FourEightyP), 5, 2), 64);
|
||||
assert_eq!(cost_cents(Some(RouterResolution::FourEightyP), 5, 2), 71);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn batch_3() {
|
||||
assert_eq!(cost_cents(Some(RouterResolution::FourEightyP), 5, 3), 96);
|
||||
assert_eq!(cost_cents(Some(RouterResolution::FourEightyP), 5, 3), 107);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn batch_4() {
|
||||
assert_eq!(cost_cents(Some(RouterResolution::FourEightyP), 5, 4), 128);
|
||||
assert_eq!(cost_cents(Some(RouterResolution::FourEightyP), 5, 4), 143);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -275,6 +275,76 @@ mod tests {
|
||||
assert!(b1 < b2);
|
||||
assert!(b2 < b4);
|
||||
}
|
||||
|
||||
/// A video reference must never make a generation cheaper than the same
|
||||
/// generation without one, at any resolution, duration, or batch size.
|
||||
#[test]
|
||||
fn no_reference_cheaper_than_with_reference() {
|
||||
for resolution in [RouterResolution::FourEightyP, RouterResolution::SevenTwentyP] {
|
||||
for duration in 4..=15u16 {
|
||||
for batch in [1, 2, 4] {
|
||||
let no_ref = cost_cents(Some(resolution), duration, batch);
|
||||
let with_ref = cost_cents_with_video_ref(Some(resolution), duration, batch);
|
||||
assert!(
|
||||
no_ref < with_ref,
|
||||
"{resolution:?} {duration}s batch {batch}: no-ref {no_ref}¢ should be cheaper than with-ref {with_ref}¢",
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 480p must stay cheaper than 720p in the with-reference rate card too
|
||||
/// (the no-reference case is covered by [`cost_480p_cheaper_than_720p`]).
|
||||
#[test]
|
||||
fn cost_480p_cheaper_than_720p_with_reference() {
|
||||
for duration in 4..=15u16 {
|
||||
let c480 = cost_cents_with_video_ref(Some(RouterResolution::FourEightyP), duration, 1);
|
||||
let c720 = cost_cents_with_video_ref(Some(RouterResolution::SevenTwentyP), duration, 1);
|
||||
assert!(c480 < c720, "{duration}s with-ref: 480p ({c480}¢) should be cheaper than 720p ({c720}¢)");
|
||||
}
|
||||
}
|
||||
|
||||
/// The BytePlus variants are premium tiers: plain Fast must stay cheaper
|
||||
/// at every resolution, duration, and reference configuration.
|
||||
#[test]
|
||||
fn fast_cheaper_than_byteplus_variants() {
|
||||
let premium_models = [
|
||||
RouterVideoModel::Seedance2p0BytePlusFast,
|
||||
RouterVideoModel::Seedance2p0BytePlusUltraFast,
|
||||
];
|
||||
for premium_model in premium_models {
|
||||
for resolution in [RouterResolution::FourEightyP, RouterResolution::SevenTwentyP] {
|
||||
for with_video_reference in [false, true] {
|
||||
for duration in 4..=15u16 {
|
||||
let plain = model_cost_cents(RouterVideoModel::Seedance2p0Fast, resolution, duration, 1, with_video_reference);
|
||||
let premium = model_cost_cents(premium_model, resolution, duration, 1, with_video_reference);
|
||||
assert!(
|
||||
plain < premium,
|
||||
"{premium_model:?} {resolution:?} {duration}s ref={with_video_reference}: plain Fast ({plain}¢) should be cheaper than the premium tier ({premium}¢)",
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// BytePlus Fast and BytePlus Ultra Fast share one rate card.
|
||||
#[test]
|
||||
fn byteplus_fast_and_ultra_fast_price_identically() {
|
||||
for resolution in [RouterResolution::FourEightyP, RouterResolution::SevenTwentyP] {
|
||||
for with_video_reference in [false, true] {
|
||||
for duration in 4..=15u16 {
|
||||
let bp = model_cost_cents(RouterVideoModel::Seedance2p0BytePlusFast, resolution, duration, 1, with_video_reference);
|
||||
let bpu = model_cost_cents(RouterVideoModel::Seedance2p0BytePlusUltraFast, resolution, duration, 1, with_video_reference);
|
||||
assert_eq!(
|
||||
bp, bpu,
|
||||
"{resolution:?} {duration}s ref={with_video_reference}: BytePlus Fast ({bp}¢) and Ultra Fast ({bpu}¢) should price identically",
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// -- Credits equal cents --
|
||||
@@ -359,4 +429,32 @@ mod tests {
|
||||
.cost_in_usd_cents
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
/// Like [`cost_cents`], for any Fast-tier model and reference configuration.
|
||||
fn model_cost_cents(
|
||||
model: RouterVideoModel,
|
||||
resolution: RouterResolution,
|
||||
duration_seconds: u16,
|
||||
video_batch_count: u16,
|
||||
with_video_reference: bool,
|
||||
) -> u64 {
|
||||
let reference_videos = with_video_reference.then(|| VideoListRef::MediaFileTokens(vec![
|
||||
MediaFileToken::new("mf_ref".to_string()),
|
||||
]));
|
||||
let builder = GenerateVideoRequestBuilder {
|
||||
model,
|
||||
provider: RouterProvider::Artcraft,
|
||||
resolution: Some(resolution),
|
||||
duration_seconds: Some(duration_seconds),
|
||||
video_batch_count: Some(video_batch_count),
|
||||
reference_videos,
|
||||
..Default::default()
|
||||
};
|
||||
builder.build2()
|
||||
.expect("build2 should succeed")
|
||||
.estimate_cost()
|
||||
.expect("estimate_cost should succeed")
|
||||
.cost_in_usd_cents
|
||||
.unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
+8
-8
@@ -11,8 +11,8 @@ use kinovi_web_client::generate::video::generate_seedance_2p5::{
|
||||
// in hundredths of a USD cent per second so the math is exact integer arithmetic
|
||||
// (no floating point), then rounded up to whole cents.
|
||||
|
||||
/// 4K output price, in hundredths of a USD cent per second (89.30 ¢/s).
|
||||
const FOUR_K_CENTI_CENTS_PER_SECOND: u64 = 8930;
|
||||
/// 4K output price, in hundredths of a USD cent per second (92.50 ¢/s).
|
||||
const FOUR_K_CENTI_CENTS_PER_SECOND: u64 = 9250;
|
||||
|
||||
/// ArtCraft's price (USD cents) for Seedance 2.0 at 4K with no reference
|
||||
/// videos attached. Each model prices reference videos through its own
|
||||
@@ -273,16 +273,16 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn four_k_base_prices() {
|
||||
assert_eq!(seedance_2p0_four_k_usd_cents(4, 1), 358);
|
||||
assert_eq!(seedance_2p0_four_k_usd_cents(5, 1), 447);
|
||||
assert_eq!(seedance_2p0_four_k_usd_cents(10, 1), 893);
|
||||
assert_eq!(seedance_2p0_four_k_usd_cents(15, 1), 1340);
|
||||
assert_eq!(seedance_2p0_four_k_usd_cents(4, 1), 370);
|
||||
assert_eq!(seedance_2p0_four_k_usd_cents(5, 1), 463);
|
||||
assert_eq!(seedance_2p0_four_k_usd_cents(10, 1), 925);
|
||||
assert_eq!(seedance_2p0_four_k_usd_cents(15, 1), 1388);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn batch_count_multiplies() {
|
||||
assert_eq!(seedance_2p0_four_k_usd_cents(5, 2), 893);
|
||||
assert_eq!(seedance_2p0_four_k_usd_cents(5, 4), 1786);
|
||||
assert_eq!(seedance_2p0_four_k_usd_cents(5, 2), 925);
|
||||
assert_eq!(seedance_2p0_four_k_usd_cents(5, 4), 1850);
|
||||
}
|
||||
|
||||
// ── Seedance 2.0 Mini ──
|
||||
|
||||
+112
-19
@@ -25,6 +25,8 @@ use artcraft_api_defs::generate::video::multi_function::seedance_2p0_multi_funct
|
||||
Seedance2p0AspectRatio, Seedance2p0BatchCount, Seedance2p0MultiFunctionVideoGenRequest,
|
||||
Seedance2p0MultiFunctionVideoGenResponse, Seedance2p0OutputResolution,
|
||||
};
|
||||
use artcraft_router::generate::generate_video::providers::artcraft::seedance_2p0::cost::ArtcraftSeedance2p0CostState;
|
||||
use enums::common::generation::common_resolution::CommonResolution;
|
||||
use enums::by_table::prompt_context_items::prompt_context_semantic_type::PromptContextSemanticType;
|
||||
use enums::by_table::prompts::prompt_type::PromptType;
|
||||
use enums::common::generation::common_aspect_ratio::CommonAspectRatio;
|
||||
@@ -214,7 +216,16 @@ pub async fn seedance_2p0_multi_function_video_gen_handler(
|
||||
let batch_count = map_batch_count(request.batch_count);
|
||||
let duration_seconds = request.duration_seconds.unwrap_or(5).clamp(4, 15);
|
||||
|
||||
let cost_in_cents = estimate_cost_upfront(aspect_ratio, output_resolution, batch_count, duration_seconds);
|
||||
let has_video_reference = request.reference_video_media_tokens
|
||||
.as_ref()
|
||||
.is_some_and(|tokens| !tokens.is_empty());
|
||||
|
||||
let cost_in_cents = estimate_cost_upfront(
|
||||
output_resolution,
|
||||
batch_count,
|
||||
duration_seconds,
|
||||
has_video_reference,
|
||||
);
|
||||
|
||||
let apriori_job_token = InferenceJobToken::generate();
|
||||
|
||||
@@ -605,30 +616,39 @@ fn map_batch_count(batch_count: Option<Seedance2p0BatchCount>) -> KinoviBatchCou
|
||||
}
|
||||
}
|
||||
|
||||
/// Estimate the cost without needing uploaded URLs. We construct a temporary
|
||||
/// `GenerateVideoRequest` with dummy values for the URL fields.
|
||||
/// Price through the Artcraft Seedance 2.0 rate card — the same card the
|
||||
/// omni_gen pipeline bills through — so this legacy endpoint stays in
|
||||
/// lockstep with reprices and charges the with-video-reference rates.
|
||||
/// (It previously billed the kinovi client's raw supplier-cost estimate,
|
||||
/// which had no video-reference pricing at all.)
|
||||
fn estimate_cost_upfront(
|
||||
aspect_ratio: KinoviAspectRatio,
|
||||
output_resolution: Option<KinoviOutputResolution>,
|
||||
batch_count: KinoviBatchCount,
|
||||
duration_seconds: u8,
|
||||
has_video_reference: bool,
|
||||
) -> u64 {
|
||||
let request = KinoviGenerateVideoRequest {
|
||||
model_type: KinoviModelType::Seedance2Pro,
|
||||
prompt: String::new(),
|
||||
aspect_ratio,
|
||||
duration_seconds,
|
||||
batch_count,
|
||||
output_resolution,
|
||||
start_frame_url: None,
|
||||
end_frame_url: None,
|
||||
reference_image_urls: None,
|
||||
reference_video_urls: None,
|
||||
reference_audio_urls: None,
|
||||
character_ids: None,
|
||||
use_face_blur_hack: None,
|
||||
let resolution = match output_resolution {
|
||||
Some(KinoviOutputResolution::FourEightyP) => CommonResolution::FourEightyP,
|
||||
// Unset resolves to 720p, matching what the generation request sends.
|
||||
Some(KinoviOutputResolution::SevenTwentyP) | None => CommonResolution::SevenTwentyP,
|
||||
Some(KinoviOutputResolution::TenEightyP) => CommonResolution::TenEightyP,
|
||||
};
|
||||
request.estimate_cost_in_usd_cents()
|
||||
|
||||
let batch_count = match batch_count {
|
||||
KinoviBatchCount::One => 1,
|
||||
KinoviBatchCount::Two => 2,
|
||||
KinoviBatchCount::Four => 4,
|
||||
};
|
||||
|
||||
ArtcraftSeedance2p0CostState {
|
||||
resolution,
|
||||
duration_seconds: u16::from(duration_seconds),
|
||||
batch_count,
|
||||
has_video_reference,
|
||||
}
|
||||
.estimate_cost()
|
||||
.cost_in_usd_cents
|
||||
.expect("the Artcraft Seedance 2.0 rate card always returns a price")
|
||||
}
|
||||
|
||||
/// Uploads all media files and calls generate_video using the given session.
|
||||
@@ -930,6 +950,79 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
mod pricing_tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn prices_720p_and_unset_identically() {
|
||||
assert_eq!(estimate_cost_upfront(Some(KinoviOutputResolution::SevenTwentyP), KinoviBatchCount::One, 5, false), 93);
|
||||
assert_eq!(estimate_cost_upfront(None, KinoviBatchCount::One, 5, false), 93);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn prices_480p_and_1080p() {
|
||||
assert_eq!(estimate_cost_upfront(Some(KinoviOutputResolution::FourEightyP), KinoviBatchCount::One, 5, false), 39);
|
||||
assert_eq!(estimate_cost_upfront(Some(KinoviOutputResolution::TenEightyP), KinoviBatchCount::One, 5, false), 233);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn video_references_price_on_the_with_reference_card() {
|
||||
assert_eq!(estimate_cost_upfront(Some(KinoviOutputResolution::FourEightyP), KinoviBatchCount::One, 5, true), 45);
|
||||
assert_eq!(estimate_cost_upfront(Some(KinoviOutputResolution::SevenTwentyP), KinoviBatchCount::One, 5, true), 111);
|
||||
assert_eq!(estimate_cost_upfront(None, KinoviBatchCount::One, 5, true), 111);
|
||||
assert_eq!(estimate_cost_upfront(Some(KinoviOutputResolution::TenEightyP), KinoviBatchCount::One, 5, true), 256);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn batch_count_multiplies() {
|
||||
assert_eq!(estimate_cost_upfront(Some(KinoviOutputResolution::SevenTwentyP), KinoviBatchCount::Two, 5, false), 185);
|
||||
assert_eq!(estimate_cost_upfront(Some(KinoviOutputResolution::SevenTwentyP), KinoviBatchCount::Four, 5, false), 370);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ten_seconds_720p() {
|
||||
assert_eq!(estimate_cost_upfront(Some(KinoviOutputResolution::SevenTwentyP), KinoviBatchCount::One, 10, false), 185);
|
||||
assert_eq!(estimate_cost_upfront(Some(KinoviOutputResolution::SevenTwentyP), KinoviBatchCount::One, 10, true), 221);
|
||||
}
|
||||
|
||||
/// The legacy endpoint must charge exactly what the Artcraft Seedance 2.0
|
||||
/// rate card (the omni_gen billing path) charges for the same parameters.
|
||||
#[test]
|
||||
fn matches_artcraft_rate_card_for_all_combos() {
|
||||
let resolutions = [
|
||||
(None, CommonResolution::SevenTwentyP),
|
||||
(Some(KinoviOutputResolution::FourEightyP), CommonResolution::FourEightyP),
|
||||
(Some(KinoviOutputResolution::SevenTwentyP), CommonResolution::SevenTwentyP),
|
||||
(Some(KinoviOutputResolution::TenEightyP), CommonResolution::TenEightyP),
|
||||
];
|
||||
let batches = [(KinoviBatchCount::One, 1u16), (KinoviBatchCount::Two, 2), (KinoviBatchCount::Four, 4)];
|
||||
|
||||
for (kinovi_res, common_res) in resolutions {
|
||||
for (kinovi_batch, batch_count) in batches {
|
||||
for duration in [4u8, 5, 10, 15] {
|
||||
for has_ref in [false, true] {
|
||||
let card_cents = ArtcraftSeedance2p0CostState {
|
||||
resolution: common_res,
|
||||
duration_seconds: u16::from(duration),
|
||||
batch_count,
|
||||
has_video_reference: has_ref,
|
||||
}
|
||||
.estimate_cost()
|
||||
.cost_in_usd_cents
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
estimate_cost_upfront(kinovi_res, kinovi_batch, duration, has_ref),
|
||||
card_cents,
|
||||
"mismatch at res={kinovi_res:?} batch={batch_count} duration={duration} has_ref={has_ref}",
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn base_request() -> Seedance2p0MultiFunctionVideoGenRequest {
|
||||
Seedance2p0MultiFunctionVideoGenRequest {
|
||||
uuid_idempotency_token: "a1b2c3d4-e5f6-7890-abcd-ef1234567890".to_string(),
|
||||
|
||||
+1
-1
@@ -27,7 +27,7 @@ mod billing_parity {
|
||||
CommonVideoModel::Seedance2p0,
|
||||
Some(CommonResolution::SevenTwentyP),
|
||||
Seconds(5),
|
||||
ExpectedCredits(80),
|
||||
ExpectedCredits(93),
|
||||
).await;
|
||||
}
|
||||
|
||||
|
||||
+30
-30
@@ -18,7 +18,7 @@ use crate::http_server::endpoints::omni_gen::generate::video::tests::support::{
|
||||
};
|
||||
|
||||
// ── Seedance 2.0 Fast (Volcengine) ──
|
||||
// Rates: 480p 5.181 ¢/s, 720p 12.727 ¢/s, rounded once after
|
||||
// Rates: 480p 7.13 ¢/s, 720p 12.727 ¢/s, rounded once after
|
||||
// duration × batch. Credits = cents.
|
||||
mod seedance_2p0_fast {
|
||||
use super::*;
|
||||
@@ -31,18 +31,18 @@ mod seedance_2p0_fast {
|
||||
let harness = TestHarness::create().await;
|
||||
|
||||
let cases: &[(Option<CommonResolution>, Seconds, Batch, ExpectedCredits)] = &[
|
||||
(Some(CommonResolution::FourEightyP), Seconds(4), Batch(1), ExpectedCredits(26)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(1), ExpectedCredits(32)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(6), Batch(1), ExpectedCredits(38)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(7), Batch(1), ExpectedCredits(45)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(8), Batch(1), ExpectedCredits(51)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(9), Batch(1), ExpectedCredits(58)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(10), Batch(1), ExpectedCredits(64)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(11), Batch(1), ExpectedCredits(70)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(12), Batch(1), ExpectedCredits(77)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(13), Batch(1), ExpectedCredits(83)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(14), Batch(1), ExpectedCredits(90)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(15), Batch(1), ExpectedCredits(96)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(4), Batch(1), ExpectedCredits(29)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(1), ExpectedCredits(36)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(6), Batch(1), ExpectedCredits(43)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(7), Batch(1), ExpectedCredits(50)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(8), Batch(1), ExpectedCredits(57)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(9), Batch(1), ExpectedCredits(64)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(10), Batch(1), ExpectedCredits(71)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(11), Batch(1), ExpectedCredits(78)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(12), Batch(1), ExpectedCredits(86)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(13), Batch(1), ExpectedCredits(93)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(14), Batch(1), ExpectedCredits(100)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(15), Batch(1), ExpectedCredits(107)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(4), Batch(1), ExpectedCredits(51)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(1), ExpectedCredits(64)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(6), Batch(1), ExpectedCredits(76)),
|
||||
@@ -97,16 +97,16 @@ mod seedance_2p0_fast {
|
||||
let harness = TestHarness::create().await;
|
||||
|
||||
let cases: &[(Option<CommonResolution>, Seconds, Batch, ExpectedCredits)] = &[
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(1), ExpectedCredits(32)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(2), ExpectedCredits(64)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(3), ExpectedCredits(96)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(4), ExpectedCredits(128)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(5), ExpectedCredits(128)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(6), ExpectedCredits(128)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(7), ExpectedCredits(128)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(8), ExpectedCredits(128)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(9), ExpectedCredits(128)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(10), ExpectedCredits(128)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(1), ExpectedCredits(36)),
|
||||
(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)),
|
||||
@@ -153,12 +153,12 @@ mod seedance_2p0_fast {
|
||||
let harness = TestHarness::create().await;
|
||||
|
||||
let cases: &[(Option<CommonResolution>, Seconds, Batch, ExpectedCredits)] = &[
|
||||
(Some(CommonResolution::FourEightyP), Seconds(6), Batch(2), ExpectedCredits(77)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(7), Batch(3), ExpectedCredits(134)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(9), Batch(4), ExpectedCredits(230)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(11), Batch(5), ExpectedCredits(282)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(13), Batch(8), ExpectedCredits(333)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(15), Batch(10), ExpectedCredits(384)),
|
||||
(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)),
|
||||
@@ -1115,7 +1115,7 @@ mod premium {
|
||||
let harness = TestHarness::create().await;
|
||||
|
||||
let cases: &[(Option<CommonResolution>, Seconds, Batch, ExpectedCredits, ExpectedCredits, CreditsDelta)] = &[
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(1), ExpectedCredits(32), ExpectedCredits(45), CreditsDelta(13)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(1), ExpectedCredits(36), ExpectedCredits(45), CreditsDelta(9)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(1), ExpectedCredits(64), ExpectedCredits(100), CreditsDelta(36)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(10), Batch(1), ExpectedCredits(127), ExpectedCredits(200), CreditsDelta(73)),
|
||||
];
|
||||
|
||||
+80
-80
@@ -22,7 +22,7 @@ use crate::http_server::endpoints::omni_gen::generate::video::tests::support::{
|
||||
};
|
||||
|
||||
// ── Seedance 2.0 (Volcengine) ──
|
||||
// Rates: 480p 7.772 ¢/s, 720p 16 ¢/s, 1080p 46.632 ¢/s, 4K 86.6 ¢/s
|
||||
// Rates: 480p 7.772 ¢/s, 720p 18.5 ¢/s, 1080p 46.632 ¢/s, 4K 92.50 ¢/s
|
||||
// (4K ceil-rounds; the rest round once after duration × batch).
|
||||
mod seedance_2p0 {
|
||||
use super::*;
|
||||
@@ -46,18 +46,18 @@ mod seedance_2p0 {
|
||||
(Some(CommonResolution::FourEightyP), Seconds(13), Batch(1), ExpectedCredits(101)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(14), Batch(1), ExpectedCredits(109)),
|
||||
(Some(CommonResolution::FourEightyP), Seconds(15), Batch(1), ExpectedCredits(117)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(4), Batch(1), ExpectedCredits(64)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(1), ExpectedCredits(80)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(6), Batch(1), ExpectedCredits(96)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(7), Batch(1), ExpectedCredits(112)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(8), Batch(1), ExpectedCredits(128)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(9), Batch(1), ExpectedCredits(144)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(10), Batch(1), ExpectedCredits(160)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(11), Batch(1), ExpectedCredits(176)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(12), Batch(1), ExpectedCredits(192)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(13), Batch(1), ExpectedCredits(208)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(14), Batch(1), ExpectedCredits(224)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(15), Batch(1), ExpectedCredits(240)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(4), Batch(1), ExpectedCredits(74)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(1), ExpectedCredits(93)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(6), Batch(1), ExpectedCredits(111)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(7), Batch(1), ExpectedCredits(130)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(8), Batch(1), ExpectedCredits(148)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(9), Batch(1), ExpectedCredits(167)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(10), Batch(1), ExpectedCredits(185)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(11), Batch(1), ExpectedCredits(204)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(12), Batch(1), ExpectedCredits(222)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(13), Batch(1), ExpectedCredits(241)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(14), Batch(1), ExpectedCredits(259)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(15), Batch(1), ExpectedCredits(278)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(4), Batch(1), ExpectedCredits(187)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(1), ExpectedCredits(233)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(6), Batch(1), ExpectedCredits(280)),
|
||||
@@ -70,18 +70,18 @@ mod seedance_2p0 {
|
||||
(Some(CommonResolution::TenEightyP), Seconds(13), Batch(1), ExpectedCredits(606)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(14), Batch(1), ExpectedCredits(653)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(15), Batch(1), ExpectedCredits(699)),
|
||||
(Some(CommonResolution::FourK), Seconds(4), Batch(1), ExpectedCredits(358)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(1), ExpectedCredits(447)),
|
||||
(Some(CommonResolution::FourK), Seconds(6), Batch(1), ExpectedCredits(536)),
|
||||
(Some(CommonResolution::FourK), Seconds(7), Batch(1), ExpectedCredits(626)),
|
||||
(Some(CommonResolution::FourK), Seconds(8), Batch(1), ExpectedCredits(715)),
|
||||
(Some(CommonResolution::FourK), Seconds(9), Batch(1), ExpectedCredits(804)),
|
||||
(Some(CommonResolution::FourK), Seconds(10), Batch(1), ExpectedCredits(893)),
|
||||
(Some(CommonResolution::FourK), Seconds(11), Batch(1), ExpectedCredits(983)),
|
||||
(Some(CommonResolution::FourK), Seconds(12), Batch(1), ExpectedCredits(1072)),
|
||||
(Some(CommonResolution::FourK), Seconds(13), Batch(1), ExpectedCredits(1161)),
|
||||
(Some(CommonResolution::FourK), Seconds(14), Batch(1), ExpectedCredits(1251)),
|
||||
(Some(CommonResolution::FourK), Seconds(15), Batch(1), ExpectedCredits(1340)),
|
||||
(Some(CommonResolution::FourK), Seconds(4), Batch(1), ExpectedCredits(370)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(1), ExpectedCredits(463)),
|
||||
(Some(CommonResolution::FourK), Seconds(6), Batch(1), ExpectedCredits(555)),
|
||||
(Some(CommonResolution::FourK), Seconds(7), Batch(1), ExpectedCredits(648)),
|
||||
(Some(CommonResolution::FourK), Seconds(8), Batch(1), ExpectedCredits(740)),
|
||||
(Some(CommonResolution::FourK), Seconds(9), Batch(1), ExpectedCredits(833)),
|
||||
(Some(CommonResolution::FourK), Seconds(10), Batch(1), ExpectedCredits(925)),
|
||||
(Some(CommonResolution::FourK), Seconds(11), Batch(1), ExpectedCredits(1018)),
|
||||
(Some(CommonResolution::FourK), Seconds(12), Batch(1), ExpectedCredits(1110)),
|
||||
(Some(CommonResolution::FourK), Seconds(13), Batch(1), ExpectedCredits(1203)),
|
||||
(Some(CommonResolution::FourK), Seconds(14), Batch(1), ExpectedCredits(1295)),
|
||||
(Some(CommonResolution::FourK), Seconds(15), Batch(1), ExpectedCredits(1388)),
|
||||
];
|
||||
|
||||
for (resolution, seconds, batch, expected) in cases {
|
||||
@@ -109,16 +109,16 @@ mod seedance_2p0 {
|
||||
(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(80)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(2), ExpectedCredits(160)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(3), ExpectedCredits(240)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(4), ExpectedCredits(320)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(5), ExpectedCredits(320)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(6), ExpectedCredits(320)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(7), ExpectedCredits(320)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(8), ExpectedCredits(320)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(9), ExpectedCredits(320)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(10), ExpectedCredits(320)),
|
||||
(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)),
|
||||
@@ -129,16 +129,16 @@ mod seedance_2p0 {
|
||||
(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(447)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(2), ExpectedCredits(893)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(3), ExpectedCredits(1340)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(4), ExpectedCredits(1786)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(5), ExpectedCredits(1786)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(6), ExpectedCredits(1786)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(7), ExpectedCredits(1786)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(8), ExpectedCredits(1786)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(9), ExpectedCredits(1786)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(10), ExpectedCredits(1786)),
|
||||
(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 {
|
||||
@@ -161,24 +161,24 @@ mod seedance_2p0 {
|
||||
(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(192)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(7), Batch(3), ExpectedCredits(336)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(9), Batch(4), ExpectedCredits(576)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(11), Batch(5), ExpectedCredits(704)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(13), Batch(8), ExpectedCredits(832)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(15), Batch(10), ExpectedCredits(960)),
|
||||
(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(1072)),
|
||||
(Some(CommonResolution::FourK), Seconds(7), Batch(3), ExpectedCredits(1876)),
|
||||
(Some(CommonResolution::FourK), Seconds(9), Batch(4), ExpectedCredits(3215)),
|
||||
(Some(CommonResolution::FourK), Seconds(11), Batch(5), ExpectedCredits(3930)),
|
||||
(Some(CommonResolution::FourK), Seconds(13), Batch(8), ExpectedCredits(4644)),
|
||||
(Some(CommonResolution::FourK), Seconds(15), Batch(10), ExpectedCredits(5358)),
|
||||
(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 {
|
||||
@@ -394,7 +394,7 @@ mod seedance_2p0 {
|
||||
// ── Seedance 2.0 BytePlus (+ its PreviewModel alias) ──
|
||||
// THE collapse-bug regression pins. These variants are FULFILLED by the
|
||||
// base Seedance 2.0 request but must be PRICED as themselves: 480p 10 ¢/s,
|
||||
// 720p 25 ¢/s, 1080p 50 ¢/s, 4K 86.6 ¢/s. The shipped bug billed them at
|
||||
// 720p 25 ¢/s, 1080p 50 ¢/s, 4K 95 ¢/s. The shipped bug billed them at
|
||||
// the base rate (720p 5s: 80 instead of 125).
|
||||
mod seedance_2p0_bp {
|
||||
use super::*;
|
||||
@@ -560,7 +560,7 @@ mod seedance_2p0_bp {
|
||||
}
|
||||
}
|
||||
|
||||
/// The BytePlus rate cards are flat below 4K: reference videos only change the 4K price (a 17.20 cents/s surcharge). Charges are asserted on the refunded ledger entry (the fixture media is unreachable).
|
||||
/// 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 duration 4-15s at every resolution, single video.
|
||||
#[tokio::test]
|
||||
#[cfg_attr(feature = "skip_database_tests", ignore)]
|
||||
@@ -625,7 +625,7 @@ mod seedance_2p0_bp {
|
||||
}
|
||||
}
|
||||
|
||||
/// The BytePlus rate cards are flat below 4K: reference videos only change the 4K price (a 17.20 cents/s surcharge). Charges are asserted on the refunded ledger entry (the fixture media is unreachable).
|
||||
/// 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.
|
||||
#[tokio::test]
|
||||
@@ -683,7 +683,7 @@ mod seedance_2p0_bp {
|
||||
}
|
||||
}
|
||||
|
||||
/// The BytePlus rate cards are flat below 4K: reference videos only change the 4K price (a 17.20 cents/s surcharge). Charges are asserted on the refunded ledger entry (the fixture media is unreachable).
|
||||
/// 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).
|
||||
/// Spot checks across duration x batch x resolution.
|
||||
/// References must be CHARGED more than the identical no-reference
|
||||
/// request, at every resolution and across durations.
|
||||
@@ -925,7 +925,7 @@ mod seedance_2p0_bpu {
|
||||
}
|
||||
}
|
||||
|
||||
/// The BytePlus rate cards are flat below 4K: reference videos only change the 4K price (a 17.20 cents/s surcharge). Charges are asserted on the refunded ledger entry (the fixture media is unreachable).
|
||||
/// 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 duration 4-15s at every resolution, single video.
|
||||
#[tokio::test]
|
||||
#[cfg_attr(feature = "skip_database_tests", ignore)]
|
||||
@@ -990,7 +990,7 @@ mod seedance_2p0_bpu {
|
||||
}
|
||||
}
|
||||
|
||||
/// The BytePlus rate cards are flat below 4K: reference videos only change the 4K price (a 17.20 cents/s surcharge). Charges are asserted on the refunded ledger entry (the fixture media is unreachable).
|
||||
/// 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.
|
||||
#[tokio::test]
|
||||
@@ -1048,7 +1048,7 @@ mod seedance_2p0_bpu {
|
||||
}
|
||||
}
|
||||
|
||||
/// The BytePlus rate cards are flat below 4K: reference videos only change the 4K price (a 17.20 cents/s surcharge). Charges are asserted on the refunded ledger entry (the fixture media is unreachable).
|
||||
/// 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).
|
||||
/// Spot checks across duration x batch x resolution.
|
||||
/// References must be CHARGED more than the identical no-reference
|
||||
/// request, at every resolution and across durations.
|
||||
@@ -1139,18 +1139,18 @@ mod premium {
|
||||
|
||||
let cases: &[(Option<CommonResolution>, Seconds, Batch, ExpectedCredits, ExpectedCredits, CreditsDelta)] = &[
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(1), ExpectedCredits(39), ExpectedCredits(50), CreditsDelta(11)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(1), ExpectedCredits(80), ExpectedCredits(125), CreditsDelta(45)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(10), Batch(1), ExpectedCredits(160), ExpectedCredits(250), CreditsDelta(90)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(1), ExpectedCredits(93), ExpectedCredits(125), CreditsDelta(32)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(10), Batch(1), ExpectedCredits(185), ExpectedCredits(250), CreditsDelta(65)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(1), ExpectedCredits(233), ExpectedCredits(250), CreditsDelta(17)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(15), Batch(1), ExpectedCredits(240), ExpectedCredits(375), CreditsDelta(135)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(4), ExpectedCredits(320), ExpectedCredits(500), CreditsDelta(180)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(15), Batch(1), ExpectedCredits(278), ExpectedCredits(375), CreditsDelta(97)),
|
||||
(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(320), ExpectedCredits(500), CreditsDelta(180)),
|
||||
// 4K premium: the base tier prices at 89.3 cents/s while the BytePlus
|
||||
(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(447), ExpectedCredits(475), CreditsDelta(28)),
|
||||
(Some(CommonResolution::FourK), Seconds(15), Batch(4), ExpectedCredits(5358), ExpectedCredits(5700), CreditsDelta(342)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(1), ExpectedCredits(463), ExpectedCredits(475), CreditsDelta(12)),
|
||||
(Some(CommonResolution::FourK), Seconds(15), Batch(4), ExpectedCredits(5550), ExpectedCredits(5700), CreditsDelta(150)),
|
||||
];
|
||||
|
||||
for (resolution, seconds, batch, base, variant, delta) in cases {
|
||||
@@ -1170,18 +1170,18 @@ mod premium {
|
||||
|
||||
let cases: &[(Option<CommonResolution>, Seconds, Batch, ExpectedCredits, ExpectedCredits, CreditsDelta)] = &[
|
||||
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(1), ExpectedCredits(39), ExpectedCredits(50), CreditsDelta(11)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(1), ExpectedCredits(80), ExpectedCredits(125), CreditsDelta(45)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(10), Batch(1), ExpectedCredits(160), ExpectedCredits(250), CreditsDelta(90)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(1), ExpectedCredits(93), ExpectedCredits(125), CreditsDelta(32)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(10), Batch(1), ExpectedCredits(185), ExpectedCredits(250), CreditsDelta(65)),
|
||||
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(1), ExpectedCredits(233), ExpectedCredits(250), CreditsDelta(17)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(15), Batch(1), ExpectedCredits(240), ExpectedCredits(375), CreditsDelta(135)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(4), ExpectedCredits(320), ExpectedCredits(500), CreditsDelta(180)),
|
||||
(Some(CommonResolution::SevenTwentyP), Seconds(15), Batch(1), ExpectedCredits(278), ExpectedCredits(375), CreditsDelta(97)),
|
||||
(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(320), ExpectedCredits(500), CreditsDelta(180)),
|
||||
// 4K premium: the base tier prices at 89.3 cents/s while the BytePlus
|
||||
(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(447), ExpectedCredits(475), CreditsDelta(28)),
|
||||
(Some(CommonResolution::FourK), Seconds(15), Batch(4), ExpectedCredits(5358), ExpectedCredits(5700), CreditsDelta(342)),
|
||||
(Some(CommonResolution::FourK), Seconds(5), Batch(1), ExpectedCredits(463), ExpectedCredits(475), CreditsDelta(12)),
|
||||
(Some(CommonResolution::FourK), Seconds(15), Batch(4), ExpectedCredits(5550), ExpectedCredits(5700), CreditsDelta(150)),
|
||||
];
|
||||
|
||||
for (resolution, seconds, batch, base, variant, delta) in cases {
|
||||
|
||||
Reference in New Issue
Block a user