mirror of
https://github.com/storytold/artcraft.git
synced 2026-11-05 17:58:55 +00:00
no longer page on kinovi 400s
This commit is contained in:
1 parent
0a007ca030
commit
bcc2787570
7 files changed
+212
-20
No files matched your search
+85
@@ -0,0 +1,85 @@
|
||||
//! Maps Kinovi (Seedance2Pro) client errors to web errors.
|
||||
//!
|
||||
//! Known bad requests (content violations, over-long prompts, too many urls)
|
||||
//! become 400s with a user-facing message. Everything else is unanticipated
|
||||
//! and stays a 500.
|
||||
|
||||
use seedance2pro_client::error::seedance2pro_bad_request_api_error::Seedance2ProBadRequestApiError;
|
||||
use seedance2pro_client::error::seedance2pro_error::Seedance2ProError;
|
||||
|
||||
use crate::http_server::common_responses::common_web_error::CommonWebError;
|
||||
|
||||
/// Map a raw Kinovi client error to a web error.
|
||||
pub fn map_seedance2pro_error_to_web_error(error: Seedance2ProError) -> CommonWebError {
|
||||
match error {
|
||||
Seedance2ProError::ApiBadRequest(bad_request) => {
|
||||
CommonWebError::BadInputWithSimpleMessage(user_facing_bad_request_message(&bad_request))
|
||||
}
|
||||
other => CommonWebError::from_error(other),
|
||||
}
|
||||
}
|
||||
|
||||
fn user_facing_bad_request_message(error: &Seedance2ProBadRequestApiError) -> String {
|
||||
match error {
|
||||
Seedance2ProBadRequestApiError::VideoGenerationViolation { .. } => {
|
||||
"The generation request was flagged as a content violation. \
|
||||
Please adjust your prompt or input media and try again.".to_string()
|
||||
}
|
||||
Seedance2ProBadRequestApiError::PromptIsTooLong { .. } => {
|
||||
"The prompt is too long. Please shorten it and try again.".to_string()
|
||||
}
|
||||
Seedance2ProBadRequestApiError::TooManyUrls { .. } => {
|
||||
"Too many input files were attached. \
|
||||
Please remove some and try again.".to_string()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use actix_web::ResponseError;
|
||||
use seedance2pro_client::error::seedance2pro_specific_api_error::Seedance2ProSpecificApiError;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn bad_requests_become_400s_with_user_facing_messages() {
|
||||
let cases = [
|
||||
(
|
||||
Seedance2ProBadRequestApiError::VideoGenerationViolation { raw_body: "{}".to_string() },
|
||||
"content violation",
|
||||
),
|
||||
(
|
||||
Seedance2ProBadRequestApiError::PromptIsTooLong { raw_body: "{}".to_string() },
|
||||
"prompt is too long",
|
||||
),
|
||||
(
|
||||
Seedance2ProBadRequestApiError::TooManyUrls { raw_body: "{}".to_string() },
|
||||
"Too many input files",
|
||||
),
|
||||
];
|
||||
|
||||
for (bad_request, expected_message_fragment) in cases {
|
||||
let error = map_seedance2pro_error_to_web_error(
|
||||
Seedance2ProError::ApiBadRequest(bad_request));
|
||||
|
||||
assert_eq!(error.status_code().as_u16(), 400);
|
||||
match error {
|
||||
CommonWebError::BadInputWithSimpleMessage(message) => {
|
||||
assert!(
|
||||
message.contains(expected_message_fragment),
|
||||
"message {:?} should contain {:?}", message, expected_message_fragment);
|
||||
}
|
||||
other => panic!("Expected BadInputWithSimpleMessage, got: {:?}", other),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unanticipated_errors_stay_500s() {
|
||||
let error = map_seedance2pro_error_to_web_error(
|
||||
Seedance2ProError::ApiSpecific(Seedance2ProSpecificApiError::UnauthorizedSessionExpired));
|
||||
|
||||
assert_eq!(error.status_code().as_u16(), 500);
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
pub mod job_failure_test;
|
||||
pub mod map_seedance2pro_web_errors;
|
||||
pub mod payments_error_test;
|
||||
mod test_synthetic_failure_reason;
|
||||
+28
-15
@@ -1,9 +1,10 @@
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
|
||||
use actix_http::StatusCode;
|
||||
use crate::billing::wallets::attempt_wallet_deduction::attempt_wallet_deduction_else_common_web_error;
|
||||
use crate::http_server::common_responses::common_web_error::CommonWebError;
|
||||
use crate::http_server::endpoint_helpers::refund_wallet_after_api_failure::refund_wallet_after_api_failure;
|
||||
use crate::http_server::endpoints::generate::common::map_seedance2pro_web_errors::map_seedance2pro_error_to_web_error;
|
||||
use crate::http_server::endpoints::generate::common::payments_error_test::payments_error_test;
|
||||
use crate::http_server::session::lookup::user_session_feature_flags::UserSessionFeatureFlags;
|
||||
use crate::http_server::validations::validate_idempotency_token_format::validate_idempotency_token_format;
|
||||
@@ -12,7 +13,7 @@ use crate::state::server_state::ServerState;
|
||||
use crate::util::http_download_url_to_bytes::http_download_url_to_bytes;
|
||||
use crate::util::lookup::lookup_media_file_urls_as_map::lookup_media_file_urls_as_map;
|
||||
use actix_web::web::Json;
|
||||
use actix_web::{web, HttpRequest};
|
||||
use actix_web::{web, HttpRequest, ResponseError};
|
||||
use artcraft_api_defs::generate::video::multi_function::seedance_2p0_multi_function_video_gen::{
|
||||
Seedance2p0AspectRatio, Seedance2p0BatchCount, Seedance2p0MultiFunctionVideoGenRequest,
|
||||
Seedance2p0MultiFunctionVideoGenResponse, Seedance2p0OutputResolution,
|
||||
@@ -234,13 +235,19 @@ pub async fn seedance_2p0_multi_function_video_gen_handler(
|
||||
}
|
||||
Err(err) => {
|
||||
warn!("Regular session fallback also failed for user {:?}: {:?}", user_token, err);
|
||||
let notification = NotificationDetailsBuilder::from_boxed_error(err.clone().into())
|
||||
.set_title("Seedance 2.0 generation failed (whitelist + fallback)".to_string())
|
||||
.set_urgency(Some(NotificationUrgency::High))
|
||||
.build();
|
||||
if let Err(page_err) = server_state.pager.enqueue_page(notification) {
|
||||
warn!("Failed to enqueue pager alert: {:?}", page_err);
|
||||
|
||||
// Client-fault errors (eg. content violations, over-long prompts)
|
||||
// surface to the user as 400s and shouldn't page.
|
||||
if err.status_code() != StatusCode::BAD_REQUEST {
|
||||
let notification = NotificationDetailsBuilder::from_boxed_error(err.clone().into())
|
||||
.set_title("Seedance 2.0 generation failed (whitelist + fallback)".to_string())
|
||||
.set_urgency(Some(NotificationUrgency::High))
|
||||
.build();
|
||||
if let Err(page_err) = server_state.pager.enqueue_page(notification) {
|
||||
warn!("Failed to enqueue pager alert: {:?}", page_err);
|
||||
}
|
||||
}
|
||||
|
||||
refund_wallet_after_api_failure(&deduction_result.ledger_entry_token, &mut mysql_connection).await?;
|
||||
return Err(err);
|
||||
}
|
||||
@@ -269,13 +276,19 @@ pub async fn seedance_2p0_multi_function_video_gen_handler(
|
||||
Ok(result) => result,
|
||||
Err(err) => {
|
||||
warn!("Error calling seedance2pro generate_video: {:?}", err);
|
||||
let notification = NotificationDetailsBuilder::from_boxed_error(err.clone().into())
|
||||
.set_title("Seedance 2.0 generation failed".to_string())
|
||||
.set_urgency(Some(NotificationUrgency::High))
|
||||
.build();
|
||||
if let Err(page_err) = server_state.pager.enqueue_page(notification) {
|
||||
warn!("Failed to enqueue pager alert: {:?}", page_err);
|
||||
|
||||
// Client-fault errors (eg. content violations, over-long prompts)
|
||||
// surface to the user as 400s and shouldn't page.
|
||||
if err.status_code() != StatusCode::BAD_REQUEST {
|
||||
let notification = NotificationDetailsBuilder::from_boxed_error(err.clone().into())
|
||||
.set_title("Seedance 2.0 generation failed".to_string())
|
||||
.set_urgency(Some(NotificationUrgency::High))
|
||||
.build();
|
||||
if let Err(page_err) = server_state.pager.enqueue_page(notification) {
|
||||
warn!("Failed to enqueue pager alert: {:?}", page_err);
|
||||
}
|
||||
}
|
||||
|
||||
refund_wallet_after_api_failure(&deduction_result.ledger_entry_token, &mut mysql_connection).await?;
|
||||
return Err(err);
|
||||
}
|
||||
@@ -613,7 +626,7 @@ async fn upload_and_generate(
|
||||
let gen_response = generate_video(video_gen_args).await
|
||||
.map_err(|err| {
|
||||
warn!("Error calling seedance2pro generate_video: {:?}", err);
|
||||
CommonWebError::from_error(err)
|
||||
map_seedance2pro_error_to_web_error(err)
|
||||
})?;
|
||||
|
||||
Ok(SeedanceGenerationResult {
|
||||
|
||||
+3
-2
@@ -18,6 +18,7 @@ use tokens::tokens::users::UserToken;
|
||||
use crate::billing::wallets::attempt_wallet_deduction::attempt_wallet_deduction_else_common_web_error;
|
||||
use crate::http_server::common_responses::common_web_error::CommonWebError;
|
||||
use crate::http_server::endpoints::omni_gen::generate::image::pipeline_result::ImagePipelineResult;
|
||||
use crate::http_server::endpoints::omni_gen::shared_utils::map_seedance2pro_router_error::map_router_error_to_web_error;
|
||||
use crate::state::server_state::ServerState;
|
||||
use crate::util::lookup::lookup_media_files_as_cdn_url_list_and_map::MediaFilesAsCdnUrlListAndMap;
|
||||
|
||||
@@ -155,7 +156,7 @@ async fn finalize_and_generate(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
warn!("Failed to finalize image v2 draft: {:?}", err);
|
||||
CommonWebError::from_error(err)
|
||||
map_router_error_to_web_error(err)
|
||||
})?
|
||||
}
|
||||
};
|
||||
@@ -164,7 +165,7 @@ async fn finalize_and_generate(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
warn!("v2 image generation failed: {:?}", err);
|
||||
CommonWebError::from_error(err)
|
||||
map_router_error_to_web_error(err)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
+3
-2
@@ -19,6 +19,7 @@ use crate::http_server::endpoints::omni_gen::generate::video::helpers::build_rou
|
||||
use crate::http_server::endpoints::omni_gen::generate::video::helpers::pipeline_result::PipelineResult;
|
||||
use crate::http_server::endpoints::omni_gen::generate::video::helpers::resolve_media_tokens_to_urls::resolve_media_tokens_to_urls;
|
||||
use crate::http_server::endpoints::omni_gen::generate::video::kinovi_account::KinoviAccount;
|
||||
use crate::http_server::endpoints::omni_gen::shared_utils::map_seedance2pro_router_error::map_router_error_to_web_error;
|
||||
use crate::state::server_state::ServerState;
|
||||
|
||||
pub struct RunPipelineV2Args<'a> {
|
||||
@@ -170,7 +171,7 @@ async fn upload_and_generate(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
warn!("Failed to finalize v2 draft: {:?}", err);
|
||||
CommonWebError::from_error(err)
|
||||
map_router_error_to_web_error(err)
|
||||
})?
|
||||
}
|
||||
};
|
||||
@@ -179,6 +180,6 @@ async fn upload_and_generate(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
warn!("v2 video generation failed: {:?}", err);
|
||||
CommonWebError::from_error(err)
|
||||
map_router_error_to_web_error(err)
|
||||
})
|
||||
}
|
||||
+90
@@ -0,0 +1,90 @@
|
||||
//! Maps artcraft router errors to web errors for the omni_gen pipelines.
|
||||
//!
|
||||
//! Known Kinovi (Seedance2Pro) bad requests (content violations, over-long
|
||||
//! prompts, too many urls) become 400s with a user-facing message. Everything
|
||||
//! else is unanticipated and stays a 500.
|
||||
|
||||
use artcraft_router::errors::artcraft_router_error::ArtcraftRouterError;
|
||||
use artcraft_router::errors::provider_error::ProviderError;
|
||||
use seedance2pro_client::error::seedance2pro_bad_request_api_error::Seedance2ProBadRequestApiError;
|
||||
use seedance2pro_client::error::seedance2pro_error::Seedance2ProError;
|
||||
|
||||
use crate::http_server::common_responses::common_web_error::CommonWebError;
|
||||
|
||||
/// Map an artcraft router error to a web error, unwrapping Kinovi bad requests.
|
||||
pub fn map_router_error_to_web_error(error: ArtcraftRouterError) -> CommonWebError {
|
||||
match error {
|
||||
ArtcraftRouterError::Provider(
|
||||
ProviderError::Seedance2Pro(Seedance2ProError::ApiBadRequest(bad_request))
|
||||
) => {
|
||||
CommonWebError::BadInputWithSimpleMessage(user_facing_bad_request_message(&bad_request))
|
||||
}
|
||||
other => CommonWebError::from_error(other),
|
||||
}
|
||||
}
|
||||
|
||||
fn user_facing_bad_request_message(error: &Seedance2ProBadRequestApiError) -> String {
|
||||
match error {
|
||||
Seedance2ProBadRequestApiError::VideoGenerationViolation { .. } => {
|
||||
"The generation request was flagged as a content violation. \
|
||||
Please adjust your prompt or input media and try again.".to_string()
|
||||
}
|
||||
Seedance2ProBadRequestApiError::PromptIsTooLong { .. } => {
|
||||
"The prompt is too long. Please shorten it below 10,000 characters and try again.".to_string()
|
||||
}
|
||||
Seedance2ProBadRequestApiError::TooManyUrls { .. } => {
|
||||
"Too many input files were attached. \
|
||||
Please remove some and try again.".to_string()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use actix_web::ResponseError;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn kinovi_bad_requests_become_400s_with_user_facing_messages() {
|
||||
let cases = [
|
||||
(
|
||||
Seedance2ProBadRequestApiError::VideoGenerationViolation { raw_body: "{}".to_string() },
|
||||
"content violation",
|
||||
),
|
||||
(
|
||||
Seedance2ProBadRequestApiError::PromptIsTooLong { raw_body: "{}".to_string() },
|
||||
"prompt is too long",
|
||||
),
|
||||
(
|
||||
Seedance2ProBadRequestApiError::TooManyUrls { raw_body: "{}".to_string() },
|
||||
"Too many input files",
|
||||
),
|
||||
];
|
||||
|
||||
for (bad_request, expected_message_fragment) in cases {
|
||||
let error = map_router_error_to_web_error(
|
||||
ArtcraftRouterError::Provider(
|
||||
ProviderError::Seedance2Pro(
|
||||
Seedance2ProError::ApiBadRequest(bad_request))));
|
||||
|
||||
assert_eq!(error.status_code().as_u16(), 400);
|
||||
match error {
|
||||
CommonWebError::BadInputWithSimpleMessage(message) => {
|
||||
assert!(
|
||||
message.contains(expected_message_fragment),
|
||||
"message {:?} should contain {:?}", message, expected_message_fragment);
|
||||
}
|
||||
other => panic!("Expected BadInputWithSimpleMessage, got: {:?}", other),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn other_router_errors_stay_500s() {
|
||||
let error = map_router_error_to_web_error(
|
||||
ArtcraftRouterError::UnsupportedModel("some_model".to_string()));
|
||||
|
||||
assert_eq!(error.status_code().as_u16(), 500);
|
||||
}
|
||||
}
|
||||
+2
-1
@@ -1 +1,2 @@
|
||||
pub mod video;
|
||||
pub mod map_seedance2pro_router_error;
|
||||
pub mod video;
|
||||
Reference in new issue
Block a user