Compare commits

...
Author SHA1 Message Date
Brandon Thomas f69b935678 quick refunds hack for kinovi 2026-04-19 13:50:25 -04:00
4 changed files with 72 additions and 35 deletions
@@ -11,6 +11,7 @@ use artcraft_api_defs::omni_gen::cost_and_generate_requests::omni_gen_video_cost
use artcraft_router::api::provider::Provider;
use enums::common::generation::common_generation_mode::CommonGenerationMode;
use tokens::tokens::media_files::MediaFileToken;
use tokens::tokens::wallet_ledger_entries::WalletLedgerEntryToken;
use crate::http_server::common_responses::advanced_common_web_error::AdvancedCommonWebError;
use crate::state::server_state::ServerState;
@@ -41,6 +42,8 @@ pub async fn execute_generation(
server_state: &ServerState,
media_file_hydration_map: Option<&HashMap<MediaFileToken, Url>>,
kinovi_character_ids: Option<Vec<String>>,
maybe_wallet_ledger_entry_token: Option<&WalletLedgerEntryToken>,
mysql_connection: &mut sqlx::pool::PoolConnection<sqlx::MySql>,
) -> Result<GenerationResult, AdvancedCommonWebError> {
match distilled.execution_provider {
Provider::Seedance2Pro => {
@@ -50,6 +53,8 @@ pub async fn execute_generation(
server_state,
media_file_hydration_map,
kinovi_character_ids,
maybe_wallet_ledger_entry_token,
mysql_connection,
).await
}
_ => {
@@ -5,7 +5,7 @@
use std::collections::HashMap;
use log::{info, warn};
use log::{error, info, warn};
use url::Url;
use artcraft_api_defs::omni_gen::cost_and_generate_requests::omni_gen_video_cost_and_generate_request::OmniGenVideoCostAndGenerateRequest;
@@ -20,11 +20,13 @@ use seedance2pro_client::requests::prepare_file_upload::prepare_file_upload::{
};
use seedance2pro_client::requests::upload_file::upload_file::{upload_file, UploadFileArgs};
use tokens::tokens::media_files::MediaFileToken;
use tokens::tokens::wallet_ledger_entries::WalletLedgerEntryToken;
use url_utils::extension::extract_extension_from_url::{
extract_extension_from_url, ExtractExtensions,
};
use crate::http_server::common_responses::advanced_common_web_error::AdvancedCommonWebError;
use crate::http_server::endpoint_helpers::refund_wallet_after_api_failure::refund_wallet_after_api_failure;
use crate::state::server_state::ServerState;
use crate::util::http_download_url_to_bytes::http_download_url_to_bytes;
@@ -37,6 +39,33 @@ pub(super) async fn execute_generation_kinovi(
server_state: &ServerState,
media_file_hydration_map: Option<&HashMap<MediaFileToken, Url>>,
kinovi_character_ids: Option<Vec<String>>,
maybe_wallet_ledger_entry_token: Option<&WalletLedgerEntryToken>,
mysql_connection: &mut sqlx::pool::PoolConnection<sqlx::MySql>,
) -> Result<GenerationResult, AdvancedCommonWebError> {
let result = execute_generation_kinovi_inner(
request,
server_state,
media_file_hydration_map,
kinovi_character_ids,
).await;
if let Err(ref err) = result {
if let Some(ledger_entry_token) = maybe_wallet_ledger_entry_token {
warn!("Kinovi generation failed, issuing refund for ledger entry {}: {:?}", ledger_entry_token.as_str(), err);
if let Err(refund_err) = refund_wallet_after_api_failure(ledger_entry_token, mysql_connection).await {
error!("Failed to refund wallet after Kinovi failure: {:?}", refund_err);
}
}
}
result
}
async fn execute_generation_kinovi_inner(
request: &OmniGenVideoCostAndGenerateRequest,
server_state: &ServerState,
media_file_hydration_map: Option<&HashMap<MediaFileToken, Url>>,
kinovi_character_ids: Option<Vec<String>>,
) -> Result<GenerationResult, AdvancedCommonWebError> {
let session = Seedance2ProSession::from_cookies_string(
server_state.seedance2pro.cookies.clone()
@@ -186,14 +186,16 @@ pub async fn omni_gen_video_generate_handler(
let apriori_job_token = InferenceJobToken::generate();
if cost > 0 {
attempt_wallet_deduction_else_common_web_error(
let maybe_deduction_result = if cost > 0 {
Some(attempt_wallet_deduction_else_common_web_error(
user_token,
Some(apriori_job_token.as_str()),
cost,
&mut mysql_connection,
).await?;
}
).await?)
} else {
None
};
// ==================== EXECUTE GENERATION ==================== //
@@ -203,6 +205,8 @@ pub async fn omni_gen_video_generate_handler(
&server_state,
media_file_hydration_map.as_ref(),
kinovi_character_ids,
maybe_deduction_result.as_ref().map(|d| &d.ledger_entry_token),
&mut mysql_connection,
).await?;
// ==================== DB TRANSACTION ==================== //
@@ -244,11 +244,11 @@ mod tests {
fn text_only_has_no_media_urls() {
let d = distill_text(&text_only_request(Some(5), None));
if let VideoGenerationPlan::Seedance2proSeedance2p0(plan) = d.plan() {
assert!(plan.start_frame_url.is_none());
assert!(plan.end_frame_url.is_none());
assert!(plan.reference_image_urls.is_none());
assert!(plan.reference_video_urls.is_none());
assert!(plan.reference_audio_urls.is_none());
assert!(plan.request.start_frame_url.is_none());
assert!(plan.request.end_frame_url.is_none());
assert!(plan.request.reference_image_urls.is_none());
assert!(plan.request.reference_video_urls.is_none());
assert!(plan.request.reference_audio_urls.is_none());
} else {
panic!("wrong plan variant");
}
@@ -258,7 +258,7 @@ mod tests {
fn text_only_prompt_is_passed_through() {
let d = distill_text(&text_only_request(Some(5), None));
if let VideoGenerationPlan::Seedance2proSeedance2p0(plan) = d.plan() {
assert_eq!(plan.prompt, Some("a cat dancing".to_string()));
assert_eq!(plan.request.prompt, "a cat dancing");
} else {
panic!("wrong plan variant");
}
@@ -276,7 +276,7 @@ mod tests {
let d = distill_with_map(&req, &map);
if let VideoGenerationPlan::Seedance2proSeedance2p0(plan) = d.plan() {
let url = plan.start_frame_url.as_ref().expect("start_frame_url should be set");
let url = plan.request.start_frame_url.as_ref().expect("start_frame_url should be set");
assert!(url.starts_with("https://"), "expected URL, got: {}", url);
assert!(!url.contains("m_"), "URL should not look like a media token");
} else {
@@ -296,7 +296,7 @@ mod tests {
let d = distill_with_map(&req, &map);
if let VideoGenerationPlan::Seedance2proSeedance2p0(plan) = d.plan() {
let url = plan.end_frame_url.as_ref().expect("end_frame_url should be set");
let url = plan.request.end_frame_url.as_ref().expect("end_frame_url should be set");
assert!(url.starts_with("https://"), "expected URL, got: {}", url);
} else {
panic!("wrong plan variant");
@@ -316,8 +316,8 @@ mod tests {
let d = distill_with_map(&req, &map);
if let VideoGenerationPlan::Seedance2proSeedance2p0(plan) = d.plan() {
assert!(plan.start_frame_url.is_some());
assert!(plan.end_frame_url.is_some());
assert!(plan.request.start_frame_url.is_some());
assert!(plan.request.end_frame_url.is_some());
} else {
panic!("wrong plan variant");
}
@@ -338,7 +338,7 @@ mod tests {
let d = distill_with_map(&req, &map);
if let VideoGenerationPlan::Seedance2proSeedance2p0(plan) = d.plan() {
let urls = plan.reference_image_urls.as_ref().expect("reference_image_urls should be set");
let urls = plan.request.reference_image_urls.as_ref().expect("reference_image_urls should be set");
assert_eq!(urls.len(), 2);
for url in urls {
assert!(url.starts_with("https://"), "expected URL, got: {}", url);
@@ -362,7 +362,7 @@ mod tests {
let d = distill_with_map(&req, &map);
if let VideoGenerationPlan::Seedance2proSeedance2p0(plan) = d.plan() {
let urls = plan.reference_video_urls.as_ref().expect("reference_video_urls should be set");
let urls = plan.request.reference_video_urls.as_ref().expect("reference_video_urls should be set");
assert_eq!(urls.len(), 1);
assert!(urls[0].starts_with("https://"));
} else {
@@ -383,7 +383,7 @@ mod tests {
let d = distill_with_map(&req, &map);
if let VideoGenerationPlan::Seedance2proSeedance2p0(plan) = d.plan() {
let urls = plan.reference_audio_urls.as_ref().expect("reference_audio_urls should be set");
let urls = plan.request.reference_audio_urls.as_ref().expect("reference_audio_urls should be set");
assert_eq!(urls.len(), 1);
assert!(urls[0].starts_with("https://"));
} else {
@@ -410,12 +410,12 @@ mod tests {
let d = distill_with_map(&req, &map);
if let VideoGenerationPlan::Seedance2proSeedance2p0(plan) = d.plan() {
assert!(plan.start_frame_url.is_some());
assert!(plan.end_frame_url.is_some());
assert_eq!(plan.reference_image_urls.as_ref().map(|v| v.len()), Some(1));
assert_eq!(plan.reference_video_urls.as_ref().map(|v| v.len()), Some(1));
assert_eq!(plan.reference_audio_urls.as_ref().map(|v| v.len()), Some(1));
assert!(plan.prompt.as_deref() == Some("full test"));
assert!(plan.request.start_frame_url.is_some());
assert!(plan.request.end_frame_url.is_some());
assert_eq!(plan.request.reference_image_urls.as_ref().map(|v| v.len()), Some(1));
assert_eq!(plan.request.reference_video_urls.as_ref().map(|v| v.len()), Some(1));
assert_eq!(plan.request.reference_audio_urls.as_ref().map(|v| v.len()), Some(1));
assert_eq!(plan.request.prompt, "full test");
} else {
panic!("wrong plan variant");
}
@@ -426,7 +426,7 @@ mod tests {
// Duration below 4 should clamp to 4.
let d = distill_text(&text_only_request(Some(1), None));
if let VideoGenerationPlan::Seedance2proSeedance2p0(plan) = d.plan() {
assert!(plan.duration_seconds >= 4, "expected >= 4, got {}", plan.duration_seconds);
assert!(plan.request.duration_seconds >= 4, "expected >= 4, got {}", plan.request.duration_seconds);
} else {
panic!("wrong plan variant");
}
@@ -434,7 +434,7 @@ mod tests {
// Duration above 15 should clamp to 15.
let d = distill_text(&text_only_request(Some(99), None));
if let VideoGenerationPlan::Seedance2proSeedance2p0(plan) = d.plan() {
assert!(plan.duration_seconds <= 15, "expected <= 15, got {}", plan.duration_seconds);
assert!(plan.request.duration_seconds <= 15, "expected <= 15, got {}", plan.request.duration_seconds);
} else {
panic!("wrong plan variant");
}
@@ -449,8 +449,7 @@ mod tests {
let d = distill_text(&req);
if let VideoGenerationPlan::Seedance2proSeedance2p0(plan) = d.plan() {
// Prompt should be None or empty string.
let prompt = plan.prompt.as_deref().unwrap_or("");
assert!(prompt.is_empty() || plan.prompt.is_none());
assert!(plan.request.prompt.is_empty());
} else {
panic!("wrong plan variant");
}
@@ -467,7 +466,7 @@ mod tests {
);
let d = distill_text(&req);
if let VideoGenerationPlan::Seedance2proSeedance2p0(plan) = d.plan() {
assert!(matches!(plan.aspect_ratio, KinoviAspectRatio::Landscape16x9));
assert!(matches!(plan.request.aspect_ratio, KinoviAspectRatio::Landscape16x9));
} else { panic!("wrong plan variant"); }
}
@@ -480,7 +479,7 @@ mod tests {
);
let d = distill_text(&req);
if let VideoGenerationPlan::Seedance2proSeedance2p0(plan) = d.plan() {
assert!(matches!(plan.aspect_ratio, KinoviAspectRatio::Portrait9x16));
assert!(matches!(plan.request.aspect_ratio, KinoviAspectRatio::Portrait9x16));
} else { panic!("wrong plan variant"); }
}
@@ -493,7 +492,7 @@ mod tests {
);
let d = distill_text(&req);
if let VideoGenerationPlan::Seedance2proSeedance2p0(plan) = d.plan() {
assert!(matches!(plan.aspect_ratio, KinoviAspectRatio::Square1x1));
assert!(matches!(plan.request.aspect_ratio, KinoviAspectRatio::Square1x1));
} else { panic!("wrong plan variant"); }
}
@@ -506,7 +505,7 @@ mod tests {
);
let d = distill_text(&req);
if let VideoGenerationPlan::Seedance2proSeedance2p0(plan) = d.plan() {
assert!(matches!(plan.aspect_ratio, KinoviAspectRatio::Standard4x3));
assert!(matches!(plan.request.aspect_ratio, KinoviAspectRatio::Standard4x3));
} else { panic!("wrong plan variant"); }
}
@@ -519,7 +518,7 @@ mod tests {
);
let d = distill_text(&req);
if let VideoGenerationPlan::Seedance2proSeedance2p0(plan) = d.plan() {
assert!(matches!(plan.aspect_ratio, KinoviAspectRatio::Portrait3x4));
assert!(matches!(plan.request.aspect_ratio, KinoviAspectRatio::Portrait3x4));
} else { panic!("wrong plan variant"); }
}
@@ -532,7 +531,7 @@ mod tests {
);
let d = distill_text(&req);
if let VideoGenerationPlan::Seedance2proSeedance2p0(plan) = d.plan() {
assert!(matches!(plan.aspect_ratio, KinoviAspectRatio::Landscape16x9));
assert!(matches!(plan.request.aspect_ratio, KinoviAspectRatio::Landscape16x9));
} else { panic!("wrong plan variant"); }
}
@@ -545,7 +544,7 @@ mod tests {
);
let d = distill_text(&req);
if let VideoGenerationPlan::Seedance2proSeedance2p0(plan) = d.plan() {
assert!(matches!(plan.aspect_ratio, KinoviAspectRatio::Landscape16x9));
assert!(matches!(plan.request.aspect_ratio, KinoviAspectRatio::Landscape16x9));
} else { panic!("wrong plan variant"); }
}
}