mirror of
https://github.com/storytold/artcraft.git
synced 2026-10-20 17:02:53 -04:00
remove from omni api
This commit is contained in:
-150
@@ -1,150 +0,0 @@
|
||||
//! Enqueue flow for first-party Minimax H3 (Turbo / Ultra) video generation.
|
||||
//!
|
||||
//! These models run on our own GPU inference, so unlike the provider-backed
|
||||
//! models there is no external generation call: we bill (Ultra only), write
|
||||
//! the prompt + job records, and return. A scheduler will pick the pending
|
||||
//! jobs up later.
|
||||
|
||||
use std::convert::TryFrom;
|
||||
|
||||
use actix_web::web::Json;
|
||||
use log::{error, info};
|
||||
use sqlx::Acquire;
|
||||
|
||||
use artcraft_api_defs::omni_api::generate_requests::omni_api_video_generate_request::OmniApiVideoGenerateRequest;
|
||||
use artcraft_api_defs::omni_gen::generate_response::omni_gen_video_generate_response::OmniGenVideoGenerateResponse;
|
||||
use enums::common::generation::common_model_type::CommonModelType;
|
||||
use enums::common::generation::common_video_model::CommonVideoModel;
|
||||
use enums::common::platform_type::PlatformType;
|
||||
use enums::common::visibility::Visibility;
|
||||
use mysql_queries::queries::generic_inference::first_party::minimax_h3::insert_generic_inference_job_for_first_party_minimax_h3_with_apriori_job_token::{
|
||||
insert_generic_inference_job_for_first_party_minimax_h3_with_apriori_job_token,
|
||||
FirstPartyMinimaxH3Model,
|
||||
InsertGenericInferenceForFirstPartyMinimaxH3WithAprioriJobTokenArgs,
|
||||
};
|
||||
use tokens::tokens::non_unique::debug_logs_event_token::DebugLogEventToken;
|
||||
use tokens::tokens::users::UserToken;
|
||||
|
||||
use crate::http_server::common_responses::common_web_error::CommonWebError;
|
||||
use crate::http_server::endpoints::omni_api::generate::video::first_party_minimax_h3::minimax_h3_ultra_cost::estimate_minimax_h3_ultra_cost_usd_cents;
|
||||
use crate::http_server::endpoints::omni_api::generate::video::helpers::bill_wallet::bill_wallet;
|
||||
use crate::http_server::endpoints::omni_api::generate::video::helpers::write_prompt_records::{
|
||||
write_prompt_records, WritePromptRecordsArgs,
|
||||
};
|
||||
|
||||
pub struct EnqueueFirstPartyMinimaxH3JobArgs<'a> {
|
||||
pub minimax_model: FirstPartyMinimaxH3Model,
|
||||
pub request: &'a OmniApiVideoGenerateRequest,
|
||||
pub user_token: &'a UserToken,
|
||||
pub maybe_prompt_model_type: Option<CommonModelType>,
|
||||
pub idempotency_token: &'a str,
|
||||
pub ip_address: &'a str,
|
||||
pub debug_log_event_token: &'a DebugLogEventToken,
|
||||
pub mysql_connection: sqlx::pool::PoolConnection<sqlx::MySql>,
|
||||
}
|
||||
|
||||
/// Map the requested video model to a first-party Minimax H3 tier.
|
||||
///
|
||||
/// Returns `None` for every other model — including the bare `MinimaxH3`,
|
||||
/// which stays on its existing third-party (Fal) route.
|
||||
pub fn first_party_minimax_h3_model(
|
||||
maybe_model: Option<CommonVideoModel>,
|
||||
) -> Option<FirstPartyMinimaxH3Model> {
|
||||
match maybe_model {
|
||||
Some(CommonVideoModel::MinimaxH3Turbo) => Some(FirstPartyMinimaxH3Model::Turbo),
|
||||
Some(CommonVideoModel::MinimaxH3Ultra) => Some(FirstPartyMinimaxH3Model::Ultra),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn enqueue_first_party_minimax_h3_job(
|
||||
args: EnqueueFirstPartyMinimaxH3JobArgs<'_>,
|
||||
) -> Result<Json<OmniGenVideoGenerateResponse>, CommonWebError> {
|
||||
let EnqueueFirstPartyMinimaxH3JobArgs {
|
||||
minimax_model,
|
||||
request,
|
||||
user_token,
|
||||
maybe_prompt_model_type,
|
||||
idempotency_token,
|
||||
ip_address,
|
||||
debug_log_event_token,
|
||||
mut mysql_connection,
|
||||
} = args;
|
||||
|
||||
// System credits = cents (same convention as the v2 pipeline).
|
||||
let maybe_system_cost_usd_cents: Option<u64> = match minimax_model {
|
||||
FirstPartyMinimaxH3Model::Turbo => {
|
||||
// Turbo is free: no wallet charge.
|
||||
// TODO: Gate free Turbo generations on how many jobs the user has
|
||||
// already submitted. The count queries and checks land with the
|
||||
// scheduler work.
|
||||
None
|
||||
}
|
||||
FirstPartyMinimaxH3Model::Ultra => {
|
||||
Some(estimate_minimax_h3_ultra_cost_usd_cents(request))
|
||||
}
|
||||
};
|
||||
|
||||
info!("First-party Minimax H3 ({:?}) estimated cost: {:?} credits",
|
||||
minimax_model, maybe_system_cost_usd_cents);
|
||||
|
||||
// Bill the wallet (no-op deduction for the free Turbo tier) and generate
|
||||
// the apriori job token.
|
||||
let billing = bill_wallet(
|
||||
user_token,
|
||||
maybe_system_cost_usd_cents.unwrap_or(0),
|
||||
&mut mysql_connection,
|
||||
).await?;
|
||||
|
||||
let mut transaction = mysql_connection.begin().await.map_err(|err| {
|
||||
error!("Error starting MySQL transaction: {:?}", err);
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
let prompt_token = write_prompt_records(WritePromptRecordsArgs {
|
||||
request,
|
||||
user_token,
|
||||
maybe_prompt_model_type,
|
||||
ip_address,
|
||||
transaction: &mut transaction,
|
||||
}).await;
|
||||
|
||||
let maybe_system_cost_u32 = maybe_system_cost_usd_cents
|
||||
.and_then(|cents| u32::try_from(cents).ok());
|
||||
|
||||
let job_token = insert_generic_inference_job_for_first_party_minimax_h3_with_apriori_job_token(
|
||||
InsertGenericInferenceForFirstPartyMinimaxH3WithAprioriJobTokenArgs {
|
||||
minimax_model,
|
||||
uuid_idempotency_token: idempotency_token,
|
||||
apriori_job_token: &billing.apriori_job_token,
|
||||
maybe_inference_args: None,
|
||||
maybe_prompt_token: prompt_token.as_ref(),
|
||||
maybe_wallet_ledger_entry_token: billing.maybe_wallet_ledger_entry_token.as_ref(),
|
||||
maybe_system_cost_credits: maybe_system_cost_u32,
|
||||
maybe_system_cost_usd_cents: maybe_system_cost_u32,
|
||||
maybe_creator_user_token: Some(user_token),
|
||||
maybe_avt_token: None, // AVT tokens are web-session only; API-key callers have none.
|
||||
creator_ip_address: ip_address,
|
||||
creator_set_visibility: Visibility::Public,
|
||||
// Omni API requests are always API-key authenticated.
|
||||
maybe_platform_type: Some(PlatformType::ApiKey),
|
||||
maybe_debug_log_event_token: Some(debug_log_event_token),
|
||||
mysql_executor: &mut *transaction,
|
||||
phantom: Default::default(),
|
||||
}
|
||||
).await.map_err(|err| {
|
||||
error!("Error inserting first-party Minimax H3 job: {:?}", err);
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
transaction.commit().await.map_err(|err| {
|
||||
error!("Error committing transaction: {:?}", err);
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
Ok(Json(OmniGenVideoGenerateResponse {
|
||||
success: true,
|
||||
inference_job_token: job_token.clone(),
|
||||
all_job_tokens: vec![job_token],
|
||||
}))
|
||||
}
|
||||
-101
@@ -1,101 +0,0 @@
|
||||
//! Pricing for first-party Minimax H3 Ultra.
|
||||
//!
|
||||
//! TODO(2026-08-06): Placeholder rates copied from the existing Minimax H3
|
||||
//! pricing. Finalize once first-party GPU inference costs are known.
|
||||
|
||||
use artcraft_api_defs::omni_api::generate_requests::omni_api_video_generate_request::OmniApiVideoGenerateRequest;
|
||||
use enums::common::generation::common_resolution::CommonResolution;
|
||||
|
||||
/// Per-second rates in hundredths of a US cent.
|
||||
const LOW_RES_RATE_HUNDREDTH_CENTS_PER_SEC: u64 = 1_840;
|
||||
const HIGH_RES_RATE_HUNDREDTH_CENTS_PER_SEC: u64 = 2_990;
|
||||
|
||||
/// Minimax H3 defaults None → 5s.
|
||||
const DEFAULT_DURATION_SECONDS: u64 = 5;
|
||||
|
||||
/// Estimate the user-facing Minimax H3 Ultra cost in USD cents.
|
||||
/// System credits = cents (same convention as the v2 pipeline).
|
||||
pub fn estimate_minimax_h3_ultra_cost_usd_cents(request: &OmniApiVideoGenerateRequest) -> u64 {
|
||||
let duration_seconds = request.duration_seconds
|
||||
.map(u64::from)
|
||||
.unwrap_or(DEFAULT_DURATION_SECONDS);
|
||||
|
||||
let rate = if is_2k(request.resolution) {
|
||||
HIGH_RES_RATE_HUNDREDTH_CENTS_PER_SEC
|
||||
} else {
|
||||
LOW_RES_RATE_HUNDREDTH_CENTS_PER_SEC
|
||||
};
|
||||
|
||||
// Round up to the next whole cent.
|
||||
(rate * duration_seconds).div_ceil(100)
|
||||
}
|
||||
|
||||
/// Minimax H3 renders 768P or 2K; 1080p and above land on the 2K tier, while
|
||||
/// 720p and below render at 768P. The default (unset) resolution is 2K.
|
||||
fn is_2k(resolution: Option<CommonResolution>) -> bool {
|
||||
match resolution {
|
||||
None => true, // defaults to 2K
|
||||
Some(CommonResolution::TenEightyP)
|
||||
| Some(CommonResolution::OneK)
|
||||
| Some(CommonResolution::TwoK)
|
||||
| Some(CommonResolution::ThreeK)
|
||||
| Some(CommonResolution::FourK) => true,
|
||||
Some(CommonResolution::HalfK)
|
||||
| Some(CommonResolution::FourEightyP)
|
||||
| Some(CommonResolution::SevenTwentyP) => false,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn default_resolution_5s_is_150() {
|
||||
assert_eq!(cost_cents(None, None), 150);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn low_res_5s_is_92() {
|
||||
assert_eq!(cost_cents(Some(5), Some(CommonResolution::SevenTwentyP)), 92);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn high_res_10s_is_299() {
|
||||
assert_eq!(cost_cents(Some(10), Some(CommonResolution::TenEightyP)), 299);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn odd_duration_rounds_up_to_whole_cents() {
|
||||
// 7s high res: 2990 × 7 = 20930 hundredth-cents → 210 cents.
|
||||
assert_eq!(cost_cents(Some(7), None), 210);
|
||||
}
|
||||
|
||||
fn cost_cents(duration_seconds: Option<u16>, resolution: Option<CommonResolution>) -> u64 {
|
||||
let request = OmniApiVideoGenerateRequest {
|
||||
idempotency_token: None,
|
||||
model: None,
|
||||
prompt: None,
|
||||
negative_prompt: None,
|
||||
start_frame_image_media_token: None,
|
||||
start_frame_image_url: None,
|
||||
end_frame_image_media_token: None,
|
||||
end_frame_image_url: None,
|
||||
reference_image_media_tokens: None,
|
||||
reference_image_urls: None,
|
||||
reference_video_media_tokens: None,
|
||||
reference_video_urls: None,
|
||||
reference_audio_media_tokens: None,
|
||||
reference_audio_urls: None,
|
||||
reference_character_tokens: None,
|
||||
resolution,
|
||||
aspect_ratio: None,
|
||||
bitrate: None,
|
||||
quality: None,
|
||||
duration_seconds,
|
||||
video_batch_count: None,
|
||||
generate_audio: None,
|
||||
};
|
||||
estimate_minimax_h3_ultra_cost_usd_cents(&request)
|
||||
}
|
||||
}
|
||||
-8
@@ -1,8 +0,0 @@
|
||||
//! First-party Minimax H3 (Turbo / Ultra) video generation.
|
||||
//!
|
||||
//! These models are fulfilled by our own GPU inference rather than an
|
||||
//! external provider, so enqueueing only writes database records — a
|
||||
//! scheduler runs the jobs later.
|
||||
|
||||
pub mod enqueue_first_party_minimax_h3_job;
|
||||
pub mod minimax_h3_ultra_cost;
|
||||
-1
@@ -4,4 +4,3 @@ pub(crate) mod hydrate_router_request;
|
||||
pub mod pipeline_result;
|
||||
pub mod resolve_kinovi_character_ids;
|
||||
pub mod resolve_media_tokens_to_urls;
|
||||
pub mod write_prompt_records;
|
||||
|
||||
-132
@@ -1,132 +0,0 @@
|
||||
//! Shared prompt bookkeeping for the omni-api video generate flows.
|
||||
//!
|
||||
//! Writes the prompt row and its context items (start/end frames, image and
|
||||
//! video references) inside the caller's transaction. Best-effort: failures
|
||||
//! are logged and yield `None` — job enqueue must not fail because prompt
|
||||
//! bookkeeping did.
|
||||
|
||||
use log::warn;
|
||||
use sqlx::{MySql, Transaction};
|
||||
|
||||
use artcraft_api_defs::omni_api::generate_requests::omni_api_video_generate_request::OmniApiVideoGenerateRequest;
|
||||
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_generation_mode::CommonGenerationMode;
|
||||
use enums::common::generation::common_model_type::CommonModelType;
|
||||
use enums::common::generation_provider::GenerationProvider;
|
||||
use mysql_queries::queries::prompt_context_items::insert_batch_prompt_context_items::{
|
||||
insert_batch_prompt_context_items, InsertBatchArgs, PromptContextItem,
|
||||
};
|
||||
use mysql_queries::queries::prompts::insert_prompt::{insert_prompt, InsertPromptArgs};
|
||||
use tokens::tokens::prompts::PromptToken;
|
||||
use tokens::tokens::users::UserToken;
|
||||
|
||||
pub struct WritePromptRecordsArgs<'a, 'tx> {
|
||||
pub request: &'a OmniApiVideoGenerateRequest,
|
||||
pub user_token: &'a UserToken,
|
||||
pub maybe_prompt_model_type: Option<CommonModelType>,
|
||||
pub ip_address: &'a str,
|
||||
pub transaction: &'a mut Transaction<'tx, MySql>,
|
||||
}
|
||||
|
||||
pub async fn write_prompt_records(args: WritePromptRecordsArgs<'_, '_>) -> Option<PromptToken> {
|
||||
let WritePromptRecordsArgs {
|
||||
request,
|
||||
user_token,
|
||||
maybe_prompt_model_type,
|
||||
ip_address,
|
||||
transaction,
|
||||
} = args;
|
||||
|
||||
let prompt_token = match insert_prompt(InsertPromptArgs {
|
||||
maybe_apriori_prompt_token: None,
|
||||
prompt_type: PromptType::ArtcraftApp,
|
||||
maybe_creator_user_token: Some(user_token),
|
||||
maybe_model_type: maybe_prompt_model_type,
|
||||
maybe_generation_provider: Some(GenerationProvider::Artcraft),
|
||||
maybe_positive_prompt: request.prompt.as_deref(),
|
||||
maybe_negative_prompt: request.negative_prompt.as_deref(),
|
||||
maybe_other_args: None,
|
||||
maybe_generation_mode: Some(determine_generation_mode(request)),
|
||||
maybe_aspect_ratio: request.aspect_ratio,
|
||||
maybe_resolution: request.resolution,
|
||||
maybe_bitrate: request.bitrate,
|
||||
maybe_batch_count: request.video_batch_count.map(|c| c as u8),
|
||||
maybe_generate_audio: request.generate_audio,
|
||||
maybe_duration_seconds: request.duration_seconds.map(|d| d as u32),
|
||||
creator_ip_address: ip_address,
|
||||
mysql_executor: &mut **transaction,
|
||||
phantom: Default::default(),
|
||||
}).await {
|
||||
Ok(token) => Some(token),
|
||||
Err(err) => {
|
||||
warn!("Error inserting prompt: {:?}", err);
|
||||
None
|
||||
}
|
||||
};
|
||||
|
||||
if let Some(token) = prompt_token.as_ref() {
|
||||
let mut context_items = Vec::new();
|
||||
|
||||
if let Some(media_token) = &request.start_frame_image_media_token {
|
||||
context_items.push(PromptContextItem {
|
||||
media_token: media_token.clone(),
|
||||
context_semantic_type: PromptContextSemanticType::VidStartFrame,
|
||||
});
|
||||
}
|
||||
if let Some(media_token) = &request.end_frame_image_media_token {
|
||||
context_items.push(PromptContextItem {
|
||||
media_token: media_token.clone(),
|
||||
context_semantic_type: PromptContextSemanticType::VidEndFrame,
|
||||
});
|
||||
}
|
||||
if let Some(ref_tokens) = &request.reference_image_media_tokens {
|
||||
for media_token in ref_tokens {
|
||||
context_items.push(PromptContextItem {
|
||||
media_token: media_token.clone(),
|
||||
context_semantic_type: PromptContextSemanticType::Imgref,
|
||||
});
|
||||
}
|
||||
}
|
||||
if let Some(ref_tokens) = &request.reference_video_media_tokens {
|
||||
for media_token in ref_tokens {
|
||||
context_items.push(PromptContextItem {
|
||||
media_token: media_token.clone(),
|
||||
context_semantic_type: PromptContextSemanticType::VidRef,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if !context_items.is_empty() {
|
||||
if let Err(err) = insert_batch_prompt_context_items(InsertBatchArgs {
|
||||
prompt_token: token.clone(),
|
||||
items: context_items,
|
||||
transaction,
|
||||
}).await {
|
||||
warn!("Error inserting batch prompt context items: {:?}", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
prompt_token
|
||||
}
|
||||
|
||||
fn determine_generation_mode(request: &OmniApiVideoGenerateRequest) -> CommonGenerationMode {
|
||||
let has_keyframe = request.start_frame_image_media_token.is_some()
|
||||
|| request.end_frame_image_media_token.is_some();
|
||||
|
||||
if has_keyframe {
|
||||
return CommonGenerationMode::Keyframe;
|
||||
}
|
||||
|
||||
let has_reference = request.reference_image_media_tokens.as_ref().is_some_and(|t| !t.is_empty())
|
||||
|| request.reference_video_media_tokens.as_ref().is_some_and(|t| !t.is_empty())
|
||||
|| request.reference_audio_media_tokens.as_ref().is_some_and(|t| !t.is_empty())
|
||||
|| request.reference_character_tokens.as_ref().is_some_and(|t| !t.is_empty());
|
||||
|
||||
if has_reference {
|
||||
return CommonGenerationMode::Reference;
|
||||
}
|
||||
|
||||
CommonGenerationMode::Text
|
||||
}
|
||||
-1
@@ -1,6 +1,5 @@
|
||||
pub mod omni_api_video_generate_handler;
|
||||
pub mod check_request;
|
||||
pub mod first_party_minimax_h3;
|
||||
pub mod helpers;
|
||||
pub mod ingest_url_inputs;
|
||||
pub mod pipeline_v2;
|
||||
|
||||
+100
-32
@@ -10,14 +10,22 @@ use artcraft_api_defs::omni_api::generate_requests::omni_api_video_generate_requ
|
||||
use artcraft_api_defs::omni_gen::generate_response::omni_gen_video_generate_response::OmniGenVideoGenerateResponse;
|
||||
use artcraft_router::generate::generate_video::generate_video_response::GenerateVideoResponse;
|
||||
use enums::by_table::debug_logs::debug_log_type::DebugLogType;
|
||||
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_generation_mode::CommonGenerationMode;
|
||||
use enums::common::generation::common_model_type::CommonModelType;
|
||||
use enums::common::generation::common_video_model::CommonVideoModel;
|
||||
use enums::common::generation_provider::GenerationProvider;
|
||||
use enums::common::platform_type::PlatformType;
|
||||
use http_server_common::request::get_request_ip::get_request_ip;
|
||||
use enums::by_table::debug_logs::debug_log_level::DebugLogLevel;
|
||||
use mysql_queries::queries::debug_logs::insert_debug_log::{insert_debug_log, InsertDebugLogArgs};
|
||||
use mysql_queries::queries::generic_inference::api_providers::kinovi_web::insert_generic_inference_job_for_kinovi_web_queue_with_apriori_job_token::KinoviVersion;
|
||||
use mysql_queries::queries::idepotency_tokens::insert_idempotency_token::insert_idempotency_token;
|
||||
use mysql_queries::queries::prompt_context_items::insert_batch_prompt_context_items::{
|
||||
insert_batch_prompt_context_items, InsertBatchArgs, PromptContextItem,
|
||||
};
|
||||
use mysql_queries::queries::prompts::insert_prompt::{insert_prompt, InsertPromptArgs};
|
||||
use tokens::tokens::characters::CharacterToken;
|
||||
use tokens::tokens::generic_inference_jobs::InferenceJobToken;
|
||||
use tokens::tokens::media_files::MediaFileToken;
|
||||
@@ -27,9 +35,6 @@ use crate::http_server::common_responses::common_web_error::CommonWebError;
|
||||
use crate::http_server::endpoints::generate::common::generation_debug_logs::GenerationDebugLogContext;
|
||||
use crate::http_server::endpoints::generate::common::payments_error_test::payments_error_test;
|
||||
use crate::http_server::endpoints::omni_api::generate::video::check_request::check_request;
|
||||
use crate::http_server::endpoints::omni_api::generate::video::first_party_minimax_h3::enqueue_first_party_minimax_h3_job::{
|
||||
enqueue_first_party_minimax_h3_job, first_party_minimax_h3_model, EnqueueFirstPartyMinimaxH3JobArgs,
|
||||
};
|
||||
use crate::http_server::endpoints::omni_api::generate::video::ingest_url_inputs::ingest_url_inputs;
|
||||
use crate::http_server::endpoints::omni_api::generate::video::helpers::hydrate_router_request::hydrate_to_router_request;
|
||||
use crate::http_server::endpoints::omni_api::generate::video::helpers::resolve_kinovi_character_ids::resolve_kinovi_character_ids;
|
||||
@@ -38,9 +43,6 @@ use crate::http_server::endpoints::omni_api::generate::video::insert_db_job::ins
|
||||
use crate::http_server::endpoints::omni_api::generate::video::insert_db_job::insert_grok_api_job::{insert_grok_api_job, InsertGrokApiJobArgs};
|
||||
use crate::http_server::endpoints::omni_api::generate::video::insert_db_job::insert_kinovi_web_jobs::{insert_kinovi_web_jobs, InsertKinoviWebJobsArgs};
|
||||
use crate::http_server::endpoints::omni_api::generate::video::insert_db_job::shared_job_args::SharedJobArgs;
|
||||
use crate::http_server::endpoints::omni_api::generate::video::helpers::write_prompt_records::{
|
||||
write_prompt_records, WritePromptRecordsArgs,
|
||||
};
|
||||
use crate::http_server::endpoints::omni_api::shared_utils::kinovi_account::KinoviAccount;
|
||||
use crate::http_server::endpoints::omni_api::generate::video::pipeline_v2::run_pipeline_v2::{run_pipeline_v2, RunPipelineV2Args};
|
||||
use crate::http_server::endpoints::omni_api::shared_utils::video::validate_video_request::validate_video_request;
|
||||
@@ -216,24 +218,6 @@ pub async fn omni_api_video_generate_handler(
|
||||
warn!("Failed to insert HTTP request debug log: {:?}", err);
|
||||
}
|
||||
|
||||
// ==================== FIRST-PARTY MINIMAX H3 (TURBO / ULTRA) ==================== //
|
||||
|
||||
// These models run on our own GPU inference — no provider call is made.
|
||||
// The module bills the wallet (Ultra only), writes the prompt + job
|
||||
// records, and returns; a scheduler picks the pending jobs up later.
|
||||
if let Some(minimax_model) = first_party_minimax_h3_model(request.model) {
|
||||
return enqueue_first_party_minimax_h3_job(EnqueueFirstPartyMinimaxH3JobArgs {
|
||||
minimax_model,
|
||||
request: &request,
|
||||
user_token,
|
||||
maybe_prompt_model_type,
|
||||
idempotency_token: &idempotency_token,
|
||||
ip_address: &ip_address,
|
||||
debug_log_event_token: &debug_log_event_token,
|
||||
mysql_connection,
|
||||
}).await;
|
||||
}
|
||||
|
||||
// NB: The pipeline takes over the connection for its remaining pre-request DB writes (billing,
|
||||
// outbound provider request debug log) and releases it before the (slow, external) generation
|
||||
// call — holding a pool slot across that call is what starves the pool and causes PoolTimedOut
|
||||
@@ -297,15 +281,79 @@ pub async fn omni_api_video_generate_handler(
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
// -- Prompt + context items --
|
||||
// -- Prompt --
|
||||
|
||||
let prompt_token = write_prompt_records(WritePromptRecordsArgs {
|
||||
request: &request,
|
||||
user_token,
|
||||
maybe_prompt_model_type,
|
||||
ip_address: &ip_address,
|
||||
transaction: &mut transaction,
|
||||
}).await;
|
||||
let prompt_token = match insert_prompt(InsertPromptArgs {
|
||||
maybe_apriori_prompt_token: None,
|
||||
prompt_type: PromptType::ArtcraftApp,
|
||||
maybe_creator_user_token: Some(user_token),
|
||||
maybe_model_type: maybe_prompt_model_type,
|
||||
maybe_generation_provider: Some(GenerationProvider::Artcraft),
|
||||
maybe_positive_prompt: request.prompt.as_deref(),
|
||||
maybe_negative_prompt: request.negative_prompt.as_deref(),
|
||||
maybe_other_args: None,
|
||||
maybe_generation_mode: Some(determine_generation_mode(&request)),
|
||||
maybe_aspect_ratio: request.aspect_ratio,
|
||||
maybe_resolution: request.resolution,
|
||||
maybe_bitrate: request.bitrate,
|
||||
maybe_batch_count: request.video_batch_count.map(|c| c as u8),
|
||||
maybe_generate_audio: request.generate_audio,
|
||||
maybe_duration_seconds: request.duration_seconds.map(|d| d as u32),
|
||||
creator_ip_address: &ip_address,
|
||||
mysql_executor: &mut *transaction,
|
||||
phantom: Default::default(),
|
||||
}).await {
|
||||
Ok(token) => Some(token),
|
||||
Err(err) => {
|
||||
warn!("Error inserting prompt: {:?}", err);
|
||||
None
|
||||
}
|
||||
};
|
||||
|
||||
// -- Prompt context items --
|
||||
|
||||
if let Some(token) = prompt_token.as_ref() {
|
||||
let mut context_items = Vec::new();
|
||||
|
||||
if let Some(media_token) = &request.start_frame_image_media_token {
|
||||
context_items.push(PromptContextItem {
|
||||
media_token: media_token.clone(),
|
||||
context_semantic_type: PromptContextSemanticType::VidStartFrame,
|
||||
});
|
||||
}
|
||||
if let Some(media_token) = &request.end_frame_image_media_token {
|
||||
context_items.push(PromptContextItem {
|
||||
media_token: media_token.clone(),
|
||||
context_semantic_type: PromptContextSemanticType::VidEndFrame,
|
||||
});
|
||||
}
|
||||
if let Some(ref_tokens) = &request.reference_image_media_tokens {
|
||||
for media_token in ref_tokens {
|
||||
context_items.push(PromptContextItem {
|
||||
media_token: media_token.clone(),
|
||||
context_semantic_type: PromptContextSemanticType::Imgref,
|
||||
});
|
||||
}
|
||||
}
|
||||
if let Some(ref_tokens) = &request.reference_video_media_tokens {
|
||||
for media_token in ref_tokens {
|
||||
context_items.push(PromptContextItem {
|
||||
media_token: media_token.clone(),
|
||||
context_semantic_type: PromptContextSemanticType::VidRef,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if !context_items.is_empty() {
|
||||
if let Err(err) = insert_batch_prompt_context_items(InsertBatchArgs {
|
||||
prompt_token: token.clone(),
|
||||
items: context_items,
|
||||
transaction: &mut transaction,
|
||||
}).await {
|
||||
warn!("Error inserting batch prompt context items: {:?}", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// -- Inference job --
|
||||
|
||||
@@ -439,3 +487,23 @@ pub async fn omni_api_video_generate_handler(
|
||||
all_job_tokens,
|
||||
}))
|
||||
}
|
||||
|
||||
fn determine_generation_mode(request: &OmniApiVideoGenerateRequest) -> CommonGenerationMode {
|
||||
let has_keyframe = request.start_frame_image_media_token.is_some()
|
||||
|| request.end_frame_image_media_token.is_some();
|
||||
|
||||
if has_keyframe {
|
||||
return CommonGenerationMode::Keyframe;
|
||||
}
|
||||
|
||||
let has_reference = request.reference_image_media_tokens.as_ref().is_some_and(|t| !t.is_empty())
|
||||
|| request.reference_video_media_tokens.as_ref().is_some_and(|t| !t.is_empty())
|
||||
|| request.reference_audio_media_tokens.as_ref().is_some_and(|t| !t.is_empty())
|
||||
|| request.reference_character_tokens.as_ref().is_some_and(|t| !t.is_empty());
|
||||
|
||||
if has_reference {
|
||||
return CommonGenerationMode::Reference;
|
||||
}
|
||||
|
||||
CommonGenerationMode::Text
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user