mirror of
https://github.com/storytold/artcraft.git
synced 2026-11-04 09:28:11 +00:00
shouldn't be impacting
This commit is contained in:
1 parent
2a62bcc7f8
commit
625ba41423
5 files changed
+174
No files matched your search
@@ -1,5 +1,6 @@
|
||||
pub mod kling_2_5_turbo_multi_function_video_gen;
|
||||
pub mod kling_2_6_multi_function_video_gen;
|
||||
pub mod seedance_2p0_multi_function_video_gen;
|
||||
pub mod sora_2_multi_function_video_gen;
|
||||
pub mod sora_2_pro_multi_function_video_gen;
|
||||
pub mod veo_3p1_fast_multi_function_video_gen;
|
||||
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tokens::tokens::generic_inference_jobs::InferenceJobToken;
|
||||
use tokens::tokens::media_files::MediaFileToken;
|
||||
use utoipa::ToSchema;
|
||||
|
||||
pub const SEEDANCE_2P0_MULTI_FUNCTION_VIDEO_GEN_PATH: &str = "/v1/generate/video/multi_function/seedance_2p0";
|
||||
|
||||
/// Seedance 2.0 multi-function video generation (text-to-video, image-to-video, and reference-based).
|
||||
#[derive(Serialize, Deserialize, ToSchema)]
|
||||
pub struct Seedance2p0MultiFunctionVideoGenRequest {
|
||||
/// Idempotency token to prevent duplicate requests.
|
||||
pub uuid_idempotency_token: String,
|
||||
|
||||
/// Text prompt.
|
||||
pub prompt: Option<String>,
|
||||
|
||||
/// Optional start frame image.
|
||||
pub start_frame_media_token: Option<MediaFileToken>,
|
||||
|
||||
/// Optional end frame image.
|
||||
pub end_frame_media_token: Option<MediaFileToken>,
|
||||
|
||||
/// Optional reference images (reference mode). When present, takes priority over start/end frames.
|
||||
pub reference_image_media_tokens: Option<Vec<MediaFileToken>>,
|
||||
|
||||
/// Video aspect ratio / resolution.
|
||||
pub aspect_ratio: Option<Seedance2p0AspectRatio>,
|
||||
|
||||
/// Duration in seconds (4–15).
|
||||
pub duration_seconds: Option<u8>,
|
||||
|
||||
/// Number of videos to generate.
|
||||
pub batch_count: Option<Seedance2p0BatchCount>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, ToSchema)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum Seedance2p0AspectRatio {
|
||||
/// 16:9 landscape (1280x720)
|
||||
Landscape16x9,
|
||||
/// 9:16 portrait (720x1280)
|
||||
Portrait9x16,
|
||||
/// 1:1 square (720x720)
|
||||
Square1x1,
|
||||
/// 4:3 standard (960x720)
|
||||
Standard4x3,
|
||||
/// 3:4 portrait (720x960)
|
||||
Portrait3x4,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, ToSchema)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum Seedance2p0BatchCount {
|
||||
One,
|
||||
Two,
|
||||
Four,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, ToSchema)]
|
||||
pub struct Seedance2p0MultiFunctionVideoGenResponse {
|
||||
pub success: bool,
|
||||
|
||||
/// The first inference job token (always present).
|
||||
pub inference_job_token: InferenceJobToken,
|
||||
|
||||
/// All inference job tokens for the batch (includes `inference_job_token` as the first entry).
|
||||
pub all_inference_job_tokens: Vec<InferenceJobToken>,
|
||||
}
|
||||
+1
@@ -1,4 +1,5 @@
|
||||
pub mod kling_2p5_turbo_pro_multi_function_video_gen;
|
||||
pub mod seedance_2p0_multi_function_video_gen;
|
||||
pub mod kling_2p6_pro_multi_function_video_gen;
|
||||
pub mod sora_2_multi_function_video_gen;
|
||||
pub mod sora_2_pro_multi_function_video_gen;
|
||||
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
use crate::credentials::storyteller_credential_set::StorytellerCredentialSet;
|
||||
use crate::error::storyteller_error::StorytellerError;
|
||||
use crate::utils::api_host::ApiHost;
|
||||
use crate::utils::basic_json_post_request::basic_json_post_request;
|
||||
use artcraft_api_defs::generate::video::multi_function::seedance_2p0_multi_function_video_gen::{Seedance2p0MultiFunctionVideoGenRequest, Seedance2p0MultiFunctionVideoGenResponse, SEEDANCE_2P0_MULTI_FUNCTION_VIDEO_GEN_PATH};
|
||||
|
||||
pub async fn seedance_2p0_multi_function_video_gen(
|
||||
api_host: &ApiHost,
|
||||
maybe_creds: Option<&StorytellerCredentialSet>,
|
||||
request: Seedance2p0MultiFunctionVideoGenRequest,
|
||||
) -> Result<Seedance2p0MultiFunctionVideoGenResponse, StorytellerError> {
|
||||
Ok(basic_json_post_request(
|
||||
api_host,
|
||||
SEEDANCE_2P0_MULTI_FUNCTION_VIDEO_GEN_PATH,
|
||||
maybe_creds,
|
||||
request,
|
||||
).await?)
|
||||
}
|
||||
@@ -35,6 +35,33 @@ pub struct GenerateVideoArgs<'a> {
|
||||
pub reference_image_urls: Option<Vec<String>>,
|
||||
}
|
||||
|
||||
impl GenerateVideoArgs<'_> {
|
||||
/// Estimates the credit cost for this generation request.
|
||||
///
|
||||
/// Pricing rules:
|
||||
/// - 40 credits per second of video (4s = 160, 15s = 600)
|
||||
/// - Resolution has no effect on cost
|
||||
/// - Input mode (text, keyframe, reference) has no effect on cost
|
||||
/// - Batch 1 = 1×, Batch 2 = 2×, Batch 4 = 3× (not 4×)
|
||||
pub fn estimate_credits(&self) -> u32 {
|
||||
let per_video = u32::from(self.duration_seconds) * 40;
|
||||
let batch_multiplier = match self.batch_count {
|
||||
BatchCount::One => 1,
|
||||
BatchCount::Two => 2,
|
||||
BatchCount::Four => 3, // NB: 3x, not 4x.
|
||||
};
|
||||
per_video * batch_multiplier
|
||||
}
|
||||
|
||||
pub fn estimate_cost_in_usd_cents(&self) -> u64 {
|
||||
// 25000 Credits costs $99.99 as of 2026-02-20
|
||||
// 250 for $1.
|
||||
let credits = self.estimate_credits() as f64;
|
||||
let cost = credits / 250.0 * 100.0;
|
||||
cost.round() as u64
|
||||
}
|
||||
}
|
||||
|
||||
// --- Public enums ---
|
||||
|
||||
/// Video resolution / aspect ratio.
|
||||
@@ -220,6 +247,65 @@ mod tests {
|
||||
use errors::AnyhowResult;
|
||||
use log::LevelFilter;
|
||||
|
||||
fn dummy_session() -> Seedance2ProSession {
|
||||
Seedance2ProSession::from_cookies_string(String::new())
|
||||
}
|
||||
|
||||
fn args_with(duration_seconds: u8, batch_count: BatchCount) -> GenerateVideoArgs<'static> {
|
||||
// Safety: the dummy session is leaked so the reference is 'static for test purposes.
|
||||
let session = Box::leak(Box::new(dummy_session()));
|
||||
GenerateVideoArgs {
|
||||
session,
|
||||
prompt: String::new(),
|
||||
resolution: Resolution::Square1x1,
|
||||
duration_seconds,
|
||||
batch_count,
|
||||
start_frame_url: None,
|
||||
end_frame_url: None,
|
||||
reference_image_urls: None,
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_estimate_credits() {
|
||||
// 40 credits per second, batch 1
|
||||
assert_eq!(args_with(4, BatchCount::One).estimate_credits(), 160);
|
||||
assert_eq!(args_with(5, BatchCount::One).estimate_credits(), 200);
|
||||
assert_eq!(args_with(6, BatchCount::One).estimate_credits(), 240);
|
||||
assert_eq!(args_with(7, BatchCount::One).estimate_credits(), 280);
|
||||
assert_eq!(args_with(15, BatchCount::One).estimate_credits(), 600);
|
||||
|
||||
// Batch 2 = 2×
|
||||
assert_eq!(args_with(4, BatchCount::Two).estimate_credits(), 320);
|
||||
assert_eq!(args_with(5, BatchCount::Two).estimate_credits(), 400);
|
||||
assert_eq!(args_with(15, BatchCount::Two).estimate_credits(), 1200);
|
||||
|
||||
// Batch 4 = 3× (not 4×)
|
||||
assert_eq!(args_with(4, BatchCount::Four).estimate_credits(), 480);
|
||||
assert_eq!(args_with(5, BatchCount::Four).estimate_credits(), 600);
|
||||
assert_eq!(args_with(15, BatchCount::Four).estimate_credits(), 1800);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_estimate_cost_usd_cents() {
|
||||
// 40 credits per second, batch 1
|
||||
assert_eq!(args_with(4, BatchCount::One).estimate_cost_in_usd_cents(), 64);
|
||||
assert_eq!(args_with(5, BatchCount::One).estimate_cost_in_usd_cents(), 80);
|
||||
assert_eq!(args_with(6, BatchCount::One).estimate_cost_in_usd_cents(), 96);
|
||||
assert_eq!(args_with(7, BatchCount::One).estimate_cost_in_usd_cents(), 112);
|
||||
assert_eq!(args_with(15, BatchCount::One).estimate_cost_in_usd_cents(), 240);
|
||||
|
||||
// Batch 2 = 2×
|
||||
assert_eq!(args_with(4, BatchCount::Two).estimate_cost_in_usd_cents(), 128);
|
||||
assert_eq!(args_with(5, BatchCount::Two).estimate_cost_in_usd_cents(), 160);
|
||||
assert_eq!(args_with(15, BatchCount::Two).estimate_cost_in_usd_cents(), 480);
|
||||
|
||||
// Batch 4 = 3× (not 4×)
|
||||
assert_eq!(args_with(4, BatchCount::Four).estimate_cost_in_usd_cents(), 192);
|
||||
assert_eq!(args_with(5, BatchCount::Four).estimate_cost_in_usd_cents(), 240);
|
||||
assert_eq!(args_with(15, BatchCount::Four).estimate_cost_in_usd_cents(), 720);
|
||||
}
|
||||
|
||||
fn test_session() -> AnyhowResult<Seedance2ProSession> {
|
||||
let cookies = get_test_cookies()?;
|
||||
Ok(Seedance2ProSession::from_cookies_string(cookies))
|
||||
|
||||
Reference in new issue
Block a user