mirror of
https://github.com/storytold/artcraft.git
synced 2026-10-25 18:49:38 +00:00
new endpoint
This commit is contained in:
+20
@@ -0,0 +1,20 @@
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use tokens::tokens::generic_inference_jobs::InferenceJobToken;
|
||||
use tokens::tokens::media_files::MediaFileToken;
|
||||
use utoipa::ToSchema;
|
||||
|
||||
pub const BEEBLE_SWITCHX_EDIT_VIDEO_PATH: &str = "/v1/generate/video/edit/beeble_switchx";
|
||||
|
||||
#[derive(Serialize, Deserialize, ToSchema, Debug)]
|
||||
pub struct BeebleSwitchXEditVideoRequest {
|
||||
pub uuid_idempotency_token: String,
|
||||
pub source_video_media_token: Option<MediaFileToken>,
|
||||
pub reference_image_media_token: Option<MediaFileToken>,
|
||||
pub prompt: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, ToSchema)]
|
||||
pub struct BeebleSwitchXEditVideoResponse {
|
||||
pub success: bool,
|
||||
pub inference_job_token: InferenceJobToken,
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
pub mod beeble_switchx_edit_video;
|
||||
@@ -1,3 +1,4 @@
|
||||
pub mod edit;
|
||||
pub mod generate_kling_1_6_pro_image_to_video;
|
||||
pub mod generate_kling_2_1_master_image_to_video;
|
||||
pub mod generate_kling_2_1_pro_image_to_video;
|
||||
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
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::edit::beeble_switchx_edit_video::{
|
||||
BeebleSwitchXEditVideoRequest, BeebleSwitchXEditVideoResponse, BEEBLE_SWITCHX_EDIT_VIDEO_PATH,
|
||||
};
|
||||
|
||||
pub async fn beeble_switchx_edit_video(
|
||||
api_host: &ApiHost,
|
||||
maybe_creds: Option<&StorytellerCredentialSet>,
|
||||
request: BeebleSwitchXEditVideoRequest,
|
||||
) -> Result<BeebleSwitchXEditVideoResponse, StorytellerError> {
|
||||
Ok(basic_json_post_request(
|
||||
api_host,
|
||||
BEEBLE_SWITCHX_EDIT_VIDEO_PATH,
|
||||
maybe_creds,
|
||||
request,
|
||||
).await?)
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
pub mod beeble_switchx_edit_video;
|
||||
@@ -1,3 +1,4 @@
|
||||
pub mod edit;
|
||||
pub mod generate_kling_16_pro_image_to_video;
|
||||
pub mod generate_kling_21_master_image_to_video;
|
||||
pub mod generate_kling_21_pro_image_to_video;
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
pub mod create_upload_url;
|
||||
mod request_types;
|
||||
pub mod upload_bytes;
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
use log::info;
|
||||
|
||||
use crate::creds::beeble_api_key::BeebleApiKey;
|
||||
use crate::error::beeble_client_error::BeebleClientError;
|
||||
use crate::error::beeble_error::BeebleError;
|
||||
use crate::error::beeble_generic_api_error::BeebleGenericApiError;
|
||||
use crate::requests::create_upload_url::create_upload_url::{
|
||||
create_upload_url, CreateUploadUrlArgs, CreateUploadUrlSuccess,
|
||||
};
|
||||
|
||||
/// Upload bytes to Beeble in one call (create presigned URL + PUT bytes).
|
||||
///
|
||||
/// Returns the upload success with the `beeble_uri` to use in generation calls.
|
||||
pub async fn upload_bytes_to_beeble(
|
||||
api_key: &BeebleApiKey,
|
||||
filename: &str,
|
||||
content_type: &str,
|
||||
bytes: Vec<u8>,
|
||||
) -> Result<CreateUploadUrlSuccess, BeebleError> {
|
||||
info!("Uploading {} bytes to Beeble as {}", bytes.len(), filename);
|
||||
|
||||
// 1. Get presigned URL.
|
||||
let upload = create_upload_url(CreateUploadUrlArgs {
|
||||
api_key: api_key.clone(),
|
||||
filename: filename.to_string(),
|
||||
}).await?;
|
||||
|
||||
info!("Beeble upload URL obtained: {}", upload.beeble_uri);
|
||||
|
||||
// 2. PUT bytes to presigned URL.
|
||||
let client = reqwest::Client::new();
|
||||
let put_response = client.put(&upload.upload_url)
|
||||
.header("Content-Type", content_type)
|
||||
.body(bytes)
|
||||
.send()
|
||||
.await
|
||||
.map_err(|err| BeebleGenericApiError::ReqwestError(err))?;
|
||||
|
||||
let status = put_response.status();
|
||||
if !status.is_success() {
|
||||
let body = put_response.text().await.unwrap_or_default();
|
||||
return Err(BeebleGenericApiError::UncategorizedBadResponseWithStatusAndBody {
|
||||
status_code: status,
|
||||
body,
|
||||
}.into());
|
||||
}
|
||||
|
||||
info!("Beeble upload PUT succeeded: {}", upload.beeble_uri);
|
||||
Ok(upload)
|
||||
}
|
||||
+168
@@ -0,0 +1,168 @@
|
||||
use anyhow::anyhow;
|
||||
use log::info;
|
||||
use sqlx::{Executor, MySql};
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use enums::by_table::generic_inference_jobs::frontend_failure_category::FrontendFailureCategory;
|
||||
use enums::by_table::generic_inference_jobs::inference_category::InferenceCategory;
|
||||
use enums::by_table::generic_inference_jobs::inference_job_external_third_party::InferenceJobExternalThirdParty;
|
||||
use enums::by_table::generic_inference_jobs::inference_job_product_category::InferenceJobProductCategory;
|
||||
use enums::by_table::generic_inference_jobs::inference_job_type::InferenceJobType;
|
||||
use enums::common::job_status_plus::JobStatusPlus;
|
||||
use enums::common::visibility::Visibility;
|
||||
use tokens::tokens::anonymous_visitor_tracking::AnonymousVisitorTrackingToken;
|
||||
use tokens::tokens::generic_inference_jobs::InferenceJobToken;
|
||||
use tokens::tokens::non_unique::debug_logs_event_token::DebugLogEventToken;
|
||||
use tokens::tokens::prompts::PromptToken;
|
||||
use tokens::tokens::users::UserToken;
|
||||
|
||||
use crate::errors::database_query_error::DatabaseQueryError;
|
||||
use crate::payloads::generic_inference_args::generic_inference_args::GenericInferenceArgs;
|
||||
|
||||
|
||||
pub struct InsertGenericInferenceForBeebleWithAprioriJobTokenArgs<'e, 'c, E>
|
||||
where E: 'e + Executor<'c, Database = MySql>
|
||||
{
|
||||
pub uuid_idempotency_token: &'e str,
|
||||
|
||||
// NOTE: We'll generate this ahead of time so we can save it with billing info!
|
||||
pub apriori_job_token: &'e InferenceJobToken,
|
||||
|
||||
/// The external primary key identifier for the job.
|
||||
pub maybe_external_third_party_id: &'e str,
|
||||
|
||||
pub maybe_inference_args: Option<GenericInferenceArgs>,
|
||||
|
||||
pub maybe_prompt_token: Option<&'e PromptToken>,
|
||||
|
||||
pub maybe_creator_user_token: Option<&'e UserToken>,
|
||||
pub maybe_avt_token: Option<&'e AnonymousVisitorTrackingToken>,
|
||||
pub creator_ip_address: &'e str,
|
||||
pub creator_set_visibility: Visibility,
|
||||
|
||||
pub maybe_debug_log_event_token: Option<&'e DebugLogEventToken>,
|
||||
|
||||
/// Override the initial job status. Defaults to `Pending` when `None`.
|
||||
/// Set to `Some(JobStatusPlus::CompleteFailure)` for mock/test failure jobs.
|
||||
pub starting_job_status_override: Option<JobStatusPlus>,
|
||||
|
||||
pub maybe_frontend_failure_category: Option<FrontendFailureCategory>,
|
||||
pub maybe_failure_reason: Option<&'e str>,
|
||||
|
||||
pub mysql_executor: E,
|
||||
|
||||
// TODO: Not sure if this works to tell the compiler we need the lifetime annotation.
|
||||
// See: https://doc.rust-lang.org/std/marker/struct.PhantomData.html#unused-lifetime-parameters
|
||||
pub phantom: PhantomData<&'c E>,
|
||||
}
|
||||
|
||||
pub async fn insert_generic_inference_job_for_beeble_queue_with_apriori_job_token<'e, 'c : 'e, E>(
|
||||
args: InsertGenericInferenceForBeebleWithAprioriJobTokenArgs<'e, 'c, E>
|
||||
) -> Result<InferenceJobToken, DatabaseQueryError>
|
||||
where E: 'e + Executor<'c, Database = MySql>
|
||||
{
|
||||
let serialized_args_payload = serde_json::ser::to_string(&args.maybe_inference_args)
|
||||
.map_err(|_e| anyhow!("could not encode inference args"))?;
|
||||
|
||||
const JOB_TYPE: InferenceJobType = InferenceJobType::BeebleQueue;
|
||||
const INFERENCE_CATEGORY: InferenceCategory = InferenceCategory::VideoGeneration;
|
||||
const PRODUCT_CATEGORY: InferenceJobProductCategory = InferenceJobProductCategory::BeebleVideo;
|
||||
const EXTERNAL_THIRD_PARTY: InferenceJobExternalThirdParty = InferenceJobExternalThirdParty::Beeble;
|
||||
|
||||
let status = args.starting_job_status_override.unwrap_or(JobStatusPlus::Pending);
|
||||
|
||||
let maybe_frontend_failure_category_str = args.maybe_frontend_failure_category
|
||||
.map(|c| c.to_str());
|
||||
|
||||
let maybe_truncated_failure_reason = args.maybe_failure_reason
|
||||
.map(|s| if s.len() > 255 { &s[..255] } else { s });
|
||||
|
||||
let query = sqlx::query!(
|
||||
r#"
|
||||
INSERT INTO generic_inference_jobs
|
||||
SET
|
||||
token = ?,
|
||||
uuid_idempotency_token = ?,
|
||||
|
||||
job_type = ?,
|
||||
|
||||
maybe_external_third_party = ?,
|
||||
maybe_external_third_party_id = ?,
|
||||
|
||||
product_category = ?,
|
||||
inference_category = ?,
|
||||
|
||||
maybe_model_type = NULL,
|
||||
maybe_model_token = NULL,
|
||||
|
||||
maybe_input_source_token = NULL,
|
||||
maybe_input_source_token_type = NULL,
|
||||
|
||||
maybe_download_url = NULL,
|
||||
maybe_cover_image_media_file_token = NULL,
|
||||
|
||||
maybe_prompt_token = ?,
|
||||
|
||||
maybe_raw_inference_text = NULL,
|
||||
|
||||
maybe_inference_args = ?,
|
||||
|
||||
maybe_creator_user_token = ?,
|
||||
maybe_creator_anonymous_visitor_token = ?,
|
||||
creator_ip_address = ?,
|
||||
creator_set_visibility = ?,
|
||||
|
||||
priority_level = 0,
|
||||
is_keepalive_required = FALSE,
|
||||
max_duration_seconds = 0,
|
||||
|
||||
is_debug_request = FALSE,
|
||||
maybe_routing_tag = NULL,
|
||||
|
||||
maybe_debug_log_event_token = ?,
|
||||
|
||||
frontend_failure_category = ?,
|
||||
failure_reason = ?,
|
||||
|
||||
status = ?
|
||||
"#,
|
||||
args.apriori_job_token.as_str(),
|
||||
args.uuid_idempotency_token,
|
||||
|
||||
JOB_TYPE.to_str(),
|
||||
|
||||
EXTERNAL_THIRD_PARTY.to_str(),
|
||||
args.maybe_external_third_party_id,
|
||||
|
||||
PRODUCT_CATEGORY.to_str(),
|
||||
INFERENCE_CATEGORY.to_str(),
|
||||
|
||||
args.maybe_prompt_token.map(|t| t.to_string()),
|
||||
|
||||
serialized_args_payload,
|
||||
|
||||
args.maybe_creator_user_token.map(|t| t.to_string()),
|
||||
args.maybe_avt_token.map(|t| t.to_string()),
|
||||
args.creator_ip_address,
|
||||
args.creator_set_visibility.to_str(),
|
||||
|
||||
args.maybe_debug_log_event_token.map(|t| t.as_str()),
|
||||
|
||||
maybe_frontend_failure_category_str,
|
||||
maybe_truncated_failure_reason,
|
||||
|
||||
status.to_str(),
|
||||
);
|
||||
|
||||
let query_result = query.execute(args.mysql_executor)
|
||||
.await;
|
||||
|
||||
let record_id = match query_result {
|
||||
Err(err) => return Err(DatabaseQueryError::from(err)),
|
||||
Ok(res) => res.last_insert_id(),
|
||||
};
|
||||
|
||||
info!("Insert generic inference job for Beeble queue: {} with record ID {}", args.apriori_job_token, record_id);
|
||||
|
||||
Ok(args.apriori_job_token.clone())
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
pub mod insert_generic_inference_job_for_beeble_queue_with_apriori_job_token;
|
||||
@@ -1,3 +1,4 @@
|
||||
pub mod beeble;
|
||||
pub mod fal;
|
||||
pub mod job;
|
||||
pub mod moderation;
|
||||
|
||||
+11
-1
@@ -12,6 +12,10 @@ use strum::EnumIter;
|
||||
#[derive(Clone, Copy, Eq, PartialEq, Hash, Ord, PartialOrd, Deserialize, Serialize, Default, utoipa::ToSchema)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum InferenceJobExternalThirdParty {
|
||||
/// Beeble jobs
|
||||
#[serde(rename = "beeble")]
|
||||
Beeble,
|
||||
|
||||
/// Fal jobs
|
||||
#[serde(rename = "fal")]
|
||||
#[default]
|
||||
@@ -34,6 +38,7 @@ impl_mysql_enum_coders!(InferenceJobExternalThirdParty);
|
||||
impl InferenceJobExternalThirdParty {
|
||||
pub fn to_str(&self) -> &'static str {
|
||||
match self {
|
||||
Self::Beeble => "beeble",
|
||||
Self::Fal => "fal",
|
||||
Self::Seedance2Pro => "seedance2pro",
|
||||
Self::Worldlabs => "worldlabs",
|
||||
@@ -42,6 +47,7 @@ impl InferenceJobExternalThirdParty {
|
||||
|
||||
pub fn from_str(value: &str) -> Result<Self, String> {
|
||||
match value {
|
||||
"beeble" => Ok(Self::Beeble),
|
||||
"fal" => Ok(Self::Fal),
|
||||
"seedance2pro" => Ok(Self::Seedance2Pro),
|
||||
"worldlabs" => Ok(Self::Worldlabs),
|
||||
@@ -53,6 +59,7 @@ impl InferenceJobExternalThirdParty {
|
||||
// NB: BTreeSet is sorted
|
||||
// NB: BTreeSet::from() isn't const, but not worth using LazyStatic, etc.
|
||||
BTreeSet::from([
|
||||
Self::Beeble,
|
||||
Self::Fal,
|
||||
Self::Seedance2Pro,
|
||||
Self::Worldlabs,
|
||||
@@ -70,6 +77,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_serialization() {
|
||||
assert_serialization(InferenceJobExternalThirdParty::Beeble, "beeble");
|
||||
assert_serialization(InferenceJobExternalThirdParty::Fal, "fal");
|
||||
assert_serialization(InferenceJobExternalThirdParty::Seedance2Pro, "seedance2pro");
|
||||
assert_serialization(InferenceJobExternalThirdParty::Worldlabs, "worldlabs");
|
||||
@@ -77,6 +85,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn to_str() {
|
||||
assert_eq!(InferenceJobExternalThirdParty::Beeble.to_str(), "beeble");
|
||||
assert_eq!(InferenceJobExternalThirdParty::Fal.to_str(), "fal");
|
||||
assert_eq!(InferenceJobExternalThirdParty::Seedance2Pro.to_str(), "seedance2pro");
|
||||
assert_eq!(InferenceJobExternalThirdParty::Worldlabs.to_str(), "worldlabs");
|
||||
@@ -84,6 +93,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn from_str() {
|
||||
assert_eq!(InferenceJobExternalThirdParty::from_str("beeble").unwrap(), InferenceJobExternalThirdParty::Beeble);
|
||||
assert_eq!(InferenceJobExternalThirdParty::from_str("fal").unwrap(), InferenceJobExternalThirdParty::Fal);
|
||||
assert_eq!(InferenceJobExternalThirdParty::from_str("seedance2pro").unwrap(), InferenceJobExternalThirdParty::Seedance2Pro);
|
||||
assert_eq!(InferenceJobExternalThirdParty::from_str("worldlabs").unwrap(), InferenceJobExternalThirdParty::Worldlabs);
|
||||
@@ -92,7 +102,7 @@ mod tests {
|
||||
#[test]
|
||||
fn all_variants() {
|
||||
// Static check
|
||||
const EXPECTED_COUNT : usize = 3;
|
||||
const EXPECTED_COUNT : usize = 4;
|
||||
|
||||
assert_eq!(InferenceJobExternalThirdParty::all_variants().len(), EXPECTED_COUNT);
|
||||
assert_eq!(InferenceJobExternalThirdParty::iter().len(), EXPECTED_COUNT);
|
||||
|
||||
+12
-1
@@ -30,6 +30,11 @@ pub enum InferenceJobProductCategory {
|
||||
FalObject,
|
||||
FalBgRemoval,
|
||||
|
||||
// =============== BEEBLE ===============
|
||||
|
||||
#[serde(rename = "beeble_video")]
|
||||
BeebleVideo,
|
||||
|
||||
// =============== SEEDANCE 2 PRO ===============
|
||||
|
||||
#[serde(rename = "seedance2pro_video")]
|
||||
@@ -139,6 +144,7 @@ impl InferenceJobProductCategory {
|
||||
Self::FalVideo => "fal_video",
|
||||
Self::FalObject => "fal_object",
|
||||
Self::FalBgRemoval => "fal_bg_removal",
|
||||
Self::BeebleVideo => "beeble_video",
|
||||
Self::Seedance2ProVideo => "seedance2pro_video",
|
||||
Self::Seedance2ProCharacter => "seedance2pro_character",
|
||||
Self::WorldlabsSplat => "worldlabs_splat",
|
||||
@@ -174,6 +180,7 @@ impl InferenceJobProductCategory {
|
||||
"fal_video" => Ok(Self::FalVideo),
|
||||
"fal_object" => Ok(Self::FalObject),
|
||||
"fal_bg_removal" => Ok(Self::FalBgRemoval),
|
||||
"beeble_video" => Ok(Self::BeebleVideo),
|
||||
"seedance2pro_video" => Ok(Self::Seedance2ProVideo),
|
||||
"seedance2pro_character" => Ok(Self::Seedance2ProCharacter),
|
||||
"worldlabs_splat" => Ok(Self::WorldlabsSplat),
|
||||
@@ -212,6 +219,7 @@ impl InferenceJobProductCategory {
|
||||
Self::FalVideo,
|
||||
Self::FalObject,
|
||||
Self::FalBgRemoval,
|
||||
Self::BeebleVideo,
|
||||
Self::Seedance2ProVideo,
|
||||
Self::Seedance2ProCharacter,
|
||||
Self::WorldlabsSplat,
|
||||
@@ -256,6 +264,7 @@ mod tests {
|
||||
assert_serialization(InferenceJobProductCategory::FalVideo, "fal_video");
|
||||
assert_serialization(InferenceJobProductCategory::FalObject, "fal_object");
|
||||
assert_serialization(InferenceJobProductCategory::FalBgRemoval, "fal_bg_removal");
|
||||
assert_serialization(InferenceJobProductCategory::BeebleVideo, "beeble_video");
|
||||
assert_serialization(InferenceJobProductCategory::Seedance2ProVideo, "seedance2pro_video");
|
||||
assert_serialization(InferenceJobProductCategory::Seedance2ProCharacter, "seedance2pro_character");
|
||||
assert_serialization(InferenceJobProductCategory::WorldlabsSplat, "worldlabs_splat");
|
||||
@@ -290,6 +299,7 @@ mod tests {
|
||||
assert_eq!(InferenceJobProductCategory::FalVideo.to_str(), "fal_video");
|
||||
assert_eq!(InferenceJobProductCategory::FalObject.to_str(), "fal_object");
|
||||
assert_eq!(InferenceJobProductCategory::FalBgRemoval.to_str(), "fal_bg_removal");
|
||||
assert_eq!(InferenceJobProductCategory::BeebleVideo.to_str(), "beeble_video");
|
||||
assert_eq!(InferenceJobProductCategory::Seedance2ProVideo.to_str(), "seedance2pro_video");
|
||||
assert_eq!(InferenceJobProductCategory::Seedance2ProCharacter.to_str(), "seedance2pro_character");
|
||||
assert_eq!(InferenceJobProductCategory::WorldlabsSplat.to_str(), "worldlabs_splat");
|
||||
@@ -323,6 +333,7 @@ mod tests {
|
||||
assert_eq!(InferenceJobProductCategory::from_str("fal_video").unwrap(), InferenceJobProductCategory::FalVideo);
|
||||
assert_eq!(InferenceJobProductCategory::from_str("fal_object").unwrap(), InferenceJobProductCategory::FalObject);
|
||||
assert_eq!(InferenceJobProductCategory::from_str("fal_bg_removal").unwrap(), InferenceJobProductCategory::FalBgRemoval);
|
||||
assert_eq!(InferenceJobProductCategory::from_str("beeble_video").unwrap(), InferenceJobProductCategory::BeebleVideo);
|
||||
assert_eq!(InferenceJobProductCategory::from_str("seedance2pro_video").unwrap(), InferenceJobProductCategory::Seedance2ProVideo);
|
||||
assert_eq!(InferenceJobProductCategory::from_str("seedance2pro_character").unwrap(), InferenceJobProductCategory::Seedance2ProCharacter);
|
||||
assert_eq!(InferenceJobProductCategory::from_str("worldlabs_splat").unwrap(), InferenceJobProductCategory::WorldlabsSplat);
|
||||
@@ -352,7 +363,7 @@ mod tests {
|
||||
#[test]
|
||||
fn all_variants() {
|
||||
// Static check
|
||||
const EXPECTED_COUNT : usize = 30;
|
||||
const EXPECTED_COUNT : usize = 31;
|
||||
|
||||
assert_eq!(InferenceJobProductCategory::all_variants().len(), EXPECTED_COUNT);
|
||||
assert_eq!(InferenceJobProductCategory::iter().len(), EXPECTED_COUNT);
|
||||
|
||||
+11
-1
@@ -27,6 +27,10 @@ pub enum InferenceJobType {
|
||||
#[serde(rename = "seedance2pro_character")]
|
||||
Seedance2ProCharacter,
|
||||
|
||||
/// Beeble jobs. We poll for results.
|
||||
#[serde(rename = "beeble_queue")]
|
||||
BeebleQueue,
|
||||
|
||||
/// World Labs jobs. We poll for results.
|
||||
#[serde(rename = "worldlabs_queue")]
|
||||
WorldlabsQueue,
|
||||
@@ -122,6 +126,7 @@ impl InferenceJobType {
|
||||
pub fn to_str(&self) -> &'static str {
|
||||
match self {
|
||||
Self::FalQueue => "fal_queue",
|
||||
Self::BeebleQueue => "beeble_queue",
|
||||
Self::Seedance2ProQueue => "seedance2pro_queue",
|
||||
Self::Seedance2ProCharacter => "seedance2pro_character",
|
||||
Self::WorldlabsQueue => "worldlabs_queue",
|
||||
@@ -151,6 +156,7 @@ impl InferenceJobType {
|
||||
pub fn from_str(value: &str) -> Result<Self, String> {
|
||||
match value {
|
||||
"fal_queue" => Ok(Self::FalQueue),
|
||||
"beeble_queue" => Ok(Self::BeebleQueue),
|
||||
"seedance2pro_queue" => Ok(Self::Seedance2ProQueue),
|
||||
"seedance2pro_character" => Ok(Self::Seedance2ProCharacter),
|
||||
"worldlabs_queue" => Ok(Self::WorldlabsQueue),
|
||||
@@ -183,6 +189,7 @@ impl InferenceJobType {
|
||||
// NB: BTreeSet::from() isn't const, but not worth using LazyStatic, etc.
|
||||
BTreeSet::from([
|
||||
Self::FalQueue,
|
||||
Self::BeebleQueue,
|
||||
Self::Seedance2ProQueue,
|
||||
Self::Seedance2ProCharacter,
|
||||
Self::WorldlabsQueue,
|
||||
@@ -226,6 +233,7 @@ mod tests {
|
||||
#[test]
|
||||
fn test_serialization() {
|
||||
assert_serialization(InferenceJobType::FalQueue, "fal_queue");
|
||||
assert_serialization(InferenceJobType::BeebleQueue, "beeble_queue");
|
||||
assert_serialization(InferenceJobType::Seedance2ProQueue, "seedance2pro_queue");
|
||||
assert_serialization(InferenceJobType::Seedance2ProCharacter, "seedance2pro_character");
|
||||
assert_serialization(InferenceJobType::WorldlabsQueue, "worldlabs_queue");
|
||||
@@ -254,6 +262,7 @@ mod tests {
|
||||
#[test]
|
||||
fn to_str() {
|
||||
assert_eq!(InferenceJobType::FalQueue.to_str(), "fal_queue");
|
||||
assert_eq!(InferenceJobType::BeebleQueue.to_str(), "beeble_queue");
|
||||
assert_eq!(InferenceJobType::Seedance2ProQueue.to_str(), "seedance2pro_queue");
|
||||
assert_eq!(InferenceJobType::Seedance2ProCharacter.to_str(), "seedance2pro_character");
|
||||
assert_eq!(InferenceJobType::WorldlabsQueue.to_str(), "worldlabs_queue");
|
||||
@@ -282,6 +291,7 @@ mod tests {
|
||||
#[test]
|
||||
fn from_str() {
|
||||
assert_eq!(InferenceJobType::from_str("fal_queue").unwrap(), InferenceJobType::FalQueue);
|
||||
assert_eq!(InferenceJobType::from_str("beeble_queue").unwrap(), InferenceJobType::BeebleQueue);
|
||||
assert_eq!(InferenceJobType::from_str("seedance2pro_queue").unwrap(), InferenceJobType::Seedance2ProQueue);
|
||||
assert_eq!(InferenceJobType::from_str("seedance2pro_character").unwrap(), InferenceJobType::Seedance2ProCharacter);
|
||||
assert_eq!(InferenceJobType::from_str("worldlabs_queue").unwrap(), InferenceJobType::WorldlabsQueue);
|
||||
@@ -310,7 +320,7 @@ mod tests {
|
||||
#[test]
|
||||
fn all_variants() {
|
||||
// Static check
|
||||
const EXPECTED_COUNT : usize = 24;
|
||||
const EXPECTED_COUNT : usize = 25;
|
||||
|
||||
assert_eq!(InferenceJobType::all_variants().len(), EXPECTED_COUNT);
|
||||
assert_eq!(InferenceJobType::iter().len(), EXPECTED_COUNT);
|
||||
|
||||
@@ -144,6 +144,8 @@ pub enum CommonModelType {
|
||||
Veo3p1,
|
||||
#[serde(rename = "veo_3p1_fast")]
|
||||
Veo3p1Fast,
|
||||
#[serde(rename = "switch_x")]
|
||||
SwitchX,
|
||||
|
||||
// 3D Object generation models
|
||||
#[serde(rename = "hunyuan_3d_2p0")]
|
||||
@@ -224,6 +226,7 @@ impl CommonModelType {
|
||||
Self::Veo3Fast => "veo_3_fast",
|
||||
Self::Veo3p1 => "veo_3p1",
|
||||
Self::Veo3p1Fast => "veo_3p1_fast",
|
||||
Self::SwitchX => "switch_x",
|
||||
|
||||
// 3D Object generation models
|
||||
Self::Hunyuan3d2_0 => "hunyuan_3d_2p0",
|
||||
@@ -293,6 +296,7 @@ impl CommonModelType {
|
||||
"veo_3_fast" => Ok(Self::Veo3Fast),
|
||||
"veo_3p1" => Ok(Self::Veo3p1),
|
||||
"veo_3p1_fast" => Ok(Self::Veo3p1Fast),
|
||||
"switch_x" => Ok(Self::SwitchX),
|
||||
|
||||
// 3D Object generation models
|
||||
"hunyuan_3d_2p0" => Ok(Self::Hunyuan3d2_0),
|
||||
@@ -366,6 +370,7 @@ impl CommonModelType {
|
||||
Self::Veo3Fast,
|
||||
Self::Veo3p1,
|
||||
Self::Veo3p1Fast,
|
||||
Self::SwitchX,
|
||||
|
||||
// 3D Object generation models
|
||||
Self::Hunyuan3d2_0,
|
||||
@@ -436,6 +441,7 @@ impl CommonModelType {
|
||||
Self::Veo3Fast => CommonModelClass::Video,
|
||||
Self::Veo3p1 => CommonModelClass::Video,
|
||||
Self::Veo3p1Fast => CommonModelClass::Video,
|
||||
Self::SwitchX => CommonModelClass::Video,
|
||||
|
||||
// 3D Object generation models (mesh)
|
||||
Self::Hunyuan3d2_0 => CommonModelClass::DimensionalMesh,
|
||||
@@ -515,6 +521,7 @@ mod tests {
|
||||
assert_serialization(CommonModelType::Veo3Fast, "veo_3_fast");
|
||||
assert_serialization(CommonModelType::Veo3p1, "veo_3p1");
|
||||
assert_serialization(CommonModelType::Veo3p1Fast, "veo_3p1_fast");
|
||||
assert_serialization(CommonModelType::SwitchX, "switch_x");
|
||||
// 3D Object generation models
|
||||
assert_serialization(CommonModelType::Hunyuan3d2_0, "hunyuan_3d_2p0");
|
||||
assert_serialization(CommonModelType::Hunyuan3d2_1, "hunyuan_3d_2p1");
|
||||
@@ -581,6 +588,7 @@ mod tests {
|
||||
assert_eq!(CommonModelType::Veo3Fast.to_str(), "veo_3_fast");
|
||||
assert_eq!(CommonModelType::Veo3p1.to_str(), "veo_3p1");
|
||||
assert_eq!(CommonModelType::Veo3p1Fast.to_str(), "veo_3p1_fast");
|
||||
assert_eq!(CommonModelType::SwitchX.to_str(), "switch_x");
|
||||
|
||||
// 3D Object generation models
|
||||
assert_eq!(CommonModelType::Hunyuan3d2_0.to_str(), "hunyuan_3d_2p0");
|
||||
@@ -647,7 +655,8 @@ mod tests {
|
||||
assert_eq!(CommonModelType::from_str("veo_3_fast").unwrap(), CommonModelType::Veo3Fast);
|
||||
assert_eq!(CommonModelType::from_str("veo_3p1").unwrap(), CommonModelType::Veo3p1);
|
||||
assert_eq!(CommonModelType::from_str("veo_3p1_fast").unwrap(), CommonModelType::Veo3p1Fast);
|
||||
|
||||
assert_eq!(CommonModelType::from_str("switch_x").unwrap(), CommonModelType::SwitchX);
|
||||
|
||||
// 3D Object generation models
|
||||
assert_eq!(CommonModelType::from_str("hunyuan_3d_2p0").unwrap(), CommonModelType::Hunyuan3d2_0);
|
||||
assert_eq!(CommonModelType::from_str("hunyuan_3d_2p1").unwrap(), CommonModelType::Hunyuan3d2_1);
|
||||
@@ -660,7 +669,7 @@ mod tests {
|
||||
#[test]
|
||||
fn all_variants() {
|
||||
let mut variants = CommonModelType::all_variants();
|
||||
assert_eq!(variants.len(), 57);
|
||||
assert_eq!(variants.len(), 58);
|
||||
// Image models
|
||||
assert_eq!(variants.pop_first(), Some(CommonModelType::Flux1Dev));
|
||||
assert_eq!(variants.pop_first(), Some(CommonModelType::Flux1Schnell));
|
||||
@@ -715,6 +724,7 @@ mod tests {
|
||||
assert_eq!(variants.pop_first(), Some(CommonModelType::Veo3Fast));
|
||||
assert_eq!(variants.pop_first(), Some(CommonModelType::Veo3p1));
|
||||
assert_eq!(variants.pop_first(), Some(CommonModelType::Veo3p1Fast));
|
||||
assert_eq!(variants.pop_first(), Some(CommonModelType::SwitchX));
|
||||
// 3D Object generation models
|
||||
assert_eq!(variants.pop_first(), Some(CommonModelType::Hunyuan3d2_0));
|
||||
assert_eq!(variants.pop_first(), Some(CommonModelType::Hunyuan3d2_1));
|
||||
|
||||
@@ -50,6 +50,7 @@ datetimes = { path = "../../../lib/data/datetimes" }
|
||||
easyenv.workspace = true
|
||||
elasticsearch_schema = { path = "../../../schema/database/elasticsearch_schema" }
|
||||
enums.workspace = true
|
||||
beeble_client.workspace = true
|
||||
error_utils.workspace = true
|
||||
errors.workspace = true
|
||||
fal_client.workspace = true
|
||||
|
||||
@@ -85,6 +85,7 @@ use crate::http_server::endpoints::media_files::upsert_upload::write_engine_asse
|
||||
use crate::http_server::endpoints::media_files::upsert_upload::write_error::MediaFileWriteError;
|
||||
use crate::http_server::endpoints::media_files::upsert_upload::write_scene_file::write_scene_file_media_file_handler::*;
|
||||
use crate::http_server::endpoints::model_download::enqueue_gptsovits_model_download_handler::*;
|
||||
use artcraft_api_defs::generate::video::edit::beeble_switchx_edit_video::*;
|
||||
use crate::http_server::endpoints::moderation::debug_logs::moderation_list_debug_logs_for_token_handler::*;
|
||||
use crate::http_server::endpoints::moderation::jobs::moderation_get_job_by_token_handler::*;
|
||||
use crate::http_server::endpoints::moderation::user_feature_flags::moderator_edit_user_feature_flags_handler::*;
|
||||
@@ -365,6 +366,7 @@ use crate::http_server::endpoints::media_files::list::list_batch_generated_redux
|
||||
crate::http_server::endpoints::generate::video::image::generate_kling_2_1_master_video_handler::generate_kling_2_1_master_video_handler,
|
||||
crate::http_server::endpoints::generate::video::image::generate_kling_2_1_pro_video_handler::generate_kling_2_1_pro_video_handler,
|
||||
crate::http_server::endpoints::generate::video::image::generate_seedance_1_0_lite_image_to_video_handler::generate_seedance_1_0_lite_image_to_video_handler,
|
||||
crate::http_server::endpoints::generate::video::edit::beeble_switchx_edit_video_gen_handler::beeble_switchx_edit_video_gen_handler,
|
||||
crate::http_server::endpoints::generate::video::image::generate_veo_2_image_to_video_handler::generate_veo_2_image_to_video_handler,
|
||||
crate::http_server::endpoints::image_studio::upload::upload_snapshot_media_file_handler::upload_snapshot_media_file_handler,
|
||||
crate::http_server::endpoints::inference_job::delete::dismiss_finished_session_jobs_handler::dismiss_finished_session_jobs_handler,
|
||||
@@ -860,6 +862,8 @@ use crate::http_server::endpoints::media_files::list::list_batch_generated_redux
|
||||
GenerateSeedance10LiteImageToVideoResponse,
|
||||
GenerateSeedance10LiteResolution,
|
||||
GenerateVeo2AspectRatio,
|
||||
BeebleSwitchXEditVideoRequest,
|
||||
BeebleSwitchXEditVideoResponse,
|
||||
GenerateVeo2Duration,
|
||||
GenerateVeo2ImageToVideoRequest,
|
||||
GenerateVeo2ImageToVideoResponse,
|
||||
|
||||
+309
@@ -0,0 +1,309 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use actix_web::web::Json;
|
||||
use actix_web::{web, HttpRequest};
|
||||
use log::{error, info, warn};
|
||||
use sqlx::Acquire;
|
||||
|
||||
use artcraft_api_defs::generate::video::edit::beeble_switchx_edit_video::{
|
||||
BeebleSwitchXEditVideoRequest, BeebleSwitchXEditVideoResponse,
|
||||
};
|
||||
use beeble_client::requests::create_upload_url::upload_bytes::upload_bytes_to_beeble;
|
||||
use beeble_client::requests::start_generation::start_generation::{
|
||||
start_generation, BeebleAlphaMode, BeebleGenerationType, StartGenerationArgs,
|
||||
StartGenerationRequest,
|
||||
};
|
||||
use bucket_paths::legacy::typified_paths::public::media_files::bucket_file_path::MediaFileBucketPath;
|
||||
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 enums::common::visibility::Visibility;
|
||||
use http_server_common::request::get_request_ip::get_request_ip;
|
||||
use mysql_queries::queries::generic_inference::beeble::insert_generic_inference_job_for_beeble_queue_with_apriori_job_token::{
|
||||
insert_generic_inference_job_for_beeble_queue_with_apriori_job_token,
|
||||
InsertGenericInferenceForBeebleWithAprioriJobTokenArgs,
|
||||
};
|
||||
use mysql_queries::queries::idepotency_tokens::insert_idempotency_token::insert_idempotency_token;
|
||||
use mysql_queries::queries::media_files::get::get_media_file::get_media_file_with_connection;
|
||||
use mysql_queries::queries::prompts::insert_prompt::{insert_prompt, InsertPromptArgs};
|
||||
use tokens::tokens::generic_inference_jobs::InferenceJobToken;
|
||||
|
||||
use crate::http_server::common_responses::advanced_common_web_error::AdvancedCommonWebError;
|
||||
use crate::http_server::common_responses::media::media_links_builder::MediaLinksBuilder;
|
||||
use crate::http_server::endpoints::generate::common::payments_error_test::payments_error_test;
|
||||
use crate::http_server::endpoints::media_files::helpers::get_media_domain::get_media_domain;
|
||||
use crate::http_server::validations::validate_idempotency_token_format::validate_idempotency_token_format;
|
||||
use crate::state::server_state::ServerState;
|
||||
use crate::util::http_download_url_to_bytes::http_download_url_to_bytes;
|
||||
|
||||
/// Beeble SwitchX Video Edit
|
||||
#[utoipa::path(
|
||||
post,
|
||||
tag = "Generate Videos",
|
||||
path = "/v1/generate/video/edit/beeble_switchx",
|
||||
request_body = BeebleSwitchXEditVideoRequest,
|
||||
responses(
|
||||
(status = 200, description = "Success", body = BeebleSwitchXEditVideoResponse),
|
||||
(status = 400, description = "Bad input"),
|
||||
(status = 401, description = "Unauthorized"),
|
||||
(status = 402, description = "Payment required"),
|
||||
(status = 500, description = "Server error"),
|
||||
),
|
||||
)]
|
||||
pub async fn beeble_switchx_edit_video_gen_handler(
|
||||
http_request: HttpRequest,
|
||||
request: Json<BeebleSwitchXEditVideoRequest>,
|
||||
server_state: web::Data<Arc<ServerState>>,
|
||||
) -> Result<Json<BeebleSwitchXEditVideoResponse>, AdvancedCommonWebError> {
|
||||
|
||||
payments_error_test(&request.prompt.as_deref().unwrap_or(""))?;
|
||||
|
||||
// ==================== SESSION ==================== //
|
||||
|
||||
let mut mysql_connection = server_state.mysql_pool.acquire().await?;
|
||||
|
||||
let maybe_user_session = server_state
|
||||
.session_checker
|
||||
.maybe_get_user_session_from_connection(&http_request, &mut mysql_connection)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
warn!("Session checker error: {:?}", e);
|
||||
AdvancedCommonWebError::from(e)
|
||||
})?;
|
||||
|
||||
let user_token = match maybe_user_session.as_ref() {
|
||||
Some(session) => &session.user_token,
|
||||
None => return Err(AdvancedCommonWebError::NotAuthorized),
|
||||
};
|
||||
|
||||
let maybe_avt_token = server_state
|
||||
.avt_cookie_manager
|
||||
.get_avt_token_from_request(&http_request);
|
||||
|
||||
// ==================== VALIDATE INPUT ==================== //
|
||||
|
||||
let source_video_media_token = request.source_video_media_token.as_ref()
|
||||
.ok_or_else(|| {
|
||||
AdvancedCommonWebError::BadInputWithSimpleMessage("source_video_media_token is required".to_string())
|
||||
})?;
|
||||
|
||||
if let Err(reason) = validate_idempotency_token_format(&request.uuid_idempotency_token) {
|
||||
return Err(AdvancedCommonWebError::BadInputWithSimpleMessage(reason));
|
||||
}
|
||||
|
||||
insert_idempotency_token(&request.uuid_idempotency_token, &mut *mysql_connection)
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!("Error inserting idempotency token: {:?}", err);
|
||||
AdvancedCommonWebError::BadInputWithSimpleMessage("repeated idempotency token".to_string())
|
||||
})?;
|
||||
|
||||
// ==================== LOOKUP MEDIA FILES ==================== //
|
||||
|
||||
const IS_MOD: bool = false;
|
||||
let media_domain = get_media_domain(&http_request);
|
||||
|
||||
// Source video (required)
|
||||
let source_video_file = get_media_file_with_connection(
|
||||
source_video_media_token, IS_MOD, &mut mysql_connection,
|
||||
).await
|
||||
.map_err(|err| {
|
||||
warn!("Error looking up source video: {:?}", err);
|
||||
AdvancedCommonWebError::from_anyhow_error(err)
|
||||
})?
|
||||
.ok_or(AdvancedCommonWebError::NotFound)?;
|
||||
|
||||
let source_video_cdn_url = {
|
||||
let path = MediaFileBucketPath::from_object_hash(
|
||||
&source_video_file.public_bucket_directory_hash,
|
||||
source_video_file.maybe_public_bucket_prefix.as_deref(),
|
||||
source_video_file.maybe_public_bucket_extension.as_deref(),
|
||||
);
|
||||
MediaLinksBuilder::from_media_path_and_env(media_domain, server_state.server_environment, &path)
|
||||
.cdn_url.to_string()
|
||||
};
|
||||
|
||||
// Reference image (optional)
|
||||
let maybe_reference_image_cdn_url = if let Some(ref_token) = &request.reference_image_media_token {
|
||||
let ref_file = get_media_file_with_connection(ref_token, IS_MOD, &mut mysql_connection)
|
||||
.await
|
||||
.map_err(|err| {
|
||||
warn!("Error looking up reference image: {:?}", err);
|
||||
AdvancedCommonWebError::from_anyhow_error(err)
|
||||
})?
|
||||
.ok_or(AdvancedCommonWebError::NotFound)?;
|
||||
|
||||
let path = MediaFileBucketPath::from_object_hash(
|
||||
&ref_file.public_bucket_directory_hash,
|
||||
ref_file.maybe_public_bucket_prefix.as_deref(),
|
||||
ref_file.maybe_public_bucket_extension.as_deref(),
|
||||
);
|
||||
|
||||
Some(
|
||||
MediaLinksBuilder::from_media_path_and_env(media_domain, server_state.server_environment, &path)
|
||||
.cdn_url.to_string()
|
||||
)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
// ==================== DOWNLOAD & UPLOAD TO BEEBLE ==================== //
|
||||
|
||||
let beeble_api_key = &server_state.beeble.api_key;
|
||||
|
||||
// Download + upload source video
|
||||
info!("Downloading source video from CDN: {}", source_video_cdn_url);
|
||||
let video_bytes = http_download_url_to_bytes(&source_video_cdn_url).await
|
||||
.map_err(|err| {
|
||||
error!("Failed to download source video: {:?}", err);
|
||||
AdvancedCommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
info!("Downloaded source video: {} bytes", video_bytes.len());
|
||||
|
||||
let video_extension = source_video_file.maybe_public_bucket_extension
|
||||
.as_deref().unwrap_or("mp4");
|
||||
let video_filename = format!("{}.{}", source_video_media_token.as_str(), video_extension);
|
||||
let video_content_type = match video_extension {
|
||||
"mp4" => "video/mp4",
|
||||
"mov" => "video/quicktime",
|
||||
_ => "application/octet-stream",
|
||||
};
|
||||
|
||||
let video_upload = upload_bytes_to_beeble(
|
||||
beeble_api_key, &video_filename, video_content_type, video_bytes.to_vec(),
|
||||
).await.map_err(|err| {
|
||||
error!("Beeble video upload failed: {:?}", err);
|
||||
AdvancedCommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
info!("Source video uploaded to Beeble: {}", video_upload.beeble_uri);
|
||||
|
||||
// Download + upload reference image (if provided)
|
||||
let maybe_reference_beeble_uri = if let Some(ref_cdn_url) = &maybe_reference_image_cdn_url {
|
||||
info!("Downloading reference image from CDN: {}", ref_cdn_url);
|
||||
let image_bytes = http_download_url_to_bytes(ref_cdn_url).await
|
||||
.map_err(|err| {
|
||||
error!("Failed to download reference image: {:?}", err);
|
||||
AdvancedCommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
info!("Downloaded reference image: {} bytes", image_bytes.len());
|
||||
|
||||
let ref_token = request.reference_image_media_token.as_ref().unwrap();
|
||||
let image_filename = format!("{}.jpg", ref_token.as_str());
|
||||
|
||||
let image_upload = upload_bytes_to_beeble(
|
||||
beeble_api_key, &image_filename, "image/jpeg", image_bytes.to_vec(),
|
||||
).await.map_err(|err| {
|
||||
error!("Beeble image upload failed: {:?}", err);
|
||||
AdvancedCommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
info!("Reference image uploaded to Beeble: {}", image_upload.beeble_uri);
|
||||
Some(image_upload.beeble_uri)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
// ==================== CALL BEEBLE GENERATE ==================== //
|
||||
|
||||
let apriori_job_token = InferenceJobToken::generate();
|
||||
|
||||
let beeble_result = start_generation(StartGenerationArgs {
|
||||
api_key: beeble_api_key.clone(),
|
||||
request: StartGenerationRequest {
|
||||
generation_type: BeebleGenerationType::Video,
|
||||
source_uri: video_upload.beeble_uri,
|
||||
alpha_mode: BeebleAlphaMode::Auto,
|
||||
prompt: request.prompt.clone(),
|
||||
reference_image_uri: maybe_reference_beeble_uri,
|
||||
alpha_uri: None,
|
||||
max_resolution: Some(1080),
|
||||
callback_url: None,
|
||||
idempotency_key: Some(request.uuid_idempotency_token.clone()),
|
||||
},
|
||||
}).await.map_err(|err| {
|
||||
error!("Beeble start_generation failed: {:?}", err);
|
||||
AdvancedCommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
let external_job_id = &beeble_result.id;
|
||||
info!("Beeble job started: id={}, status={}", external_job_id, beeble_result.status);
|
||||
|
||||
// ==================== DB TRANSACTION ==================== //
|
||||
|
||||
let ip_address = get_request_ip(&http_request);
|
||||
|
||||
let mut transaction = mysql_connection.begin().await.map_err(|err| {
|
||||
error!("Error starting MySQL transaction: {:?}", err);
|
||||
AdvancedCommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
// -- Prompt --
|
||||
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: Some(CommonModelType::SwitchX),
|
||||
maybe_generation_provider: Some(GenerationProvider::Artcraft),
|
||||
maybe_positive_prompt: request.prompt.as_deref(),
|
||||
maybe_negative_prompt: None,
|
||||
maybe_other_args: None,
|
||||
maybe_generation_mode: Some(CommonGenerationMode::Edit),
|
||||
maybe_aspect_ratio: None,
|
||||
maybe_resolution: None,
|
||||
maybe_batch_count: None,
|
||||
maybe_generate_audio: None,
|
||||
maybe_duration_seconds: None,
|
||||
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
|
||||
}
|
||||
};
|
||||
|
||||
// -- Inference job --
|
||||
let db_result = insert_generic_inference_job_for_beeble_queue_with_apriori_job_token(
|
||||
InsertGenericInferenceForBeebleWithAprioriJobTokenArgs {
|
||||
apriori_job_token: &apriori_job_token,
|
||||
uuid_idempotency_token: &request.uuid_idempotency_token,
|
||||
maybe_external_third_party_id: external_job_id,
|
||||
maybe_inference_args: None,
|
||||
maybe_prompt_token: prompt_token.as_ref(),
|
||||
maybe_creator_user_token: Some(user_token),
|
||||
maybe_avt_token: maybe_avt_token.as_ref(),
|
||||
creator_ip_address: &ip_address,
|
||||
creator_set_visibility: Visibility::Public,
|
||||
maybe_debug_log_event_token: None,
|
||||
starting_job_status_override: None,
|
||||
maybe_frontend_failure_category: None,
|
||||
maybe_failure_reason: None,
|
||||
mysql_executor: &mut *transaction,
|
||||
phantom: Default::default(),
|
||||
}
|
||||
).await;
|
||||
|
||||
let job_token = match db_result {
|
||||
Ok(token) => token,
|
||||
Err(err) => {
|
||||
error!("Error inserting inference job: {:?}", err);
|
||||
return Err(AdvancedCommonWebError::from_error(err));
|
||||
}
|
||||
};
|
||||
|
||||
transaction.commit().await.map_err(|err| {
|
||||
error!("Error committing transaction: {:?}", err);
|
||||
AdvancedCommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
Ok(Json(BeebleSwitchXEditVideoResponse {
|
||||
success: true,
|
||||
inference_job_token: job_token,
|
||||
}))
|
||||
}
|
||||
+1
@@ -0,0 +1 @@
|
||||
pub mod beeble_switchx_edit_video_gen_handler;
|
||||
@@ -1,2 +1,3 @@
|
||||
pub mod edit;
|
||||
pub mod image;
|
||||
pub mod multi_function;
|
||||
+7
@@ -1,3 +1,4 @@
|
||||
use crate::http_server::endpoints::generate::video::edit::beeble_switchx_edit_video_gen_handler::beeble_switchx_edit_video_gen_handler;
|
||||
use crate::http_server::endpoints::generate::cost_estimate::image::estimate_image_cost_handler::estimate_image_cost_handler;
|
||||
use crate::http_server::endpoints::generate::cost_estimate::splat::estimate_splat_cost_handler::estimate_splat_cost_handler;
|
||||
use crate::http_server::endpoints::generate::cost_estimate::video::estimate_video_cost_handler::estimate_video_cost_handler;
|
||||
@@ -251,6 +252,12 @@ where
|
||||
.route(web::head().to(|| HttpResponse::Ok()))
|
||||
)
|
||||
)
|
||||
.service(web::scope("/edit")
|
||||
.service(web::resource("/beeble_switchx")
|
||||
.route(web::post().to(beeble_switchx_edit_video_gen_handler))
|
||||
.route(web::head().to(|| HttpResponse::Ok()))
|
||||
)
|
||||
)
|
||||
.service(web::scope("/object")
|
||||
.service(web::scope("/multi_function")
|
||||
.service(web::resource("/hunyuan3d_v3")
|
||||
|
||||
@@ -80,7 +80,7 @@ use crate::http_server::web_utils::scoped_temp_dir_creator::ScopedTempDirCreator
|
||||
use crate::startup::build_pager::build_pager;
|
||||
use crate::state::certs::google_sign_in_cert::GoogleSignInCert;
|
||||
use crate::state::memory_cache::model_token_to_info_cache::ModelTokenToInfoCache;
|
||||
use crate::state::server_state::{DurableInMemoryCaches, EnvConfig, EphemeralInMemoryCaches, FalData, InMemoryCaches, OpenAiData, ResendData, Seedance2ProData, ServerInfo, ServerState, StaticFeatureFlags, StripeSettings, TrollBans, WorldLabsData};
|
||||
use crate::state::server_state::{BeebleData, DurableInMemoryCaches, EnvConfig, EphemeralInMemoryCaches, FalData, InMemoryCaches, OpenAiData, ResendData, Seedance2ProData, ServerInfo, ServerState, StaticFeatureFlags, StripeSettings, TrollBans, WorldLabsData};
|
||||
use crate::threads::db_health_checker_thread::db_health_check_status::HealthCheckStatus;
|
||||
use crate::threads::db_health_checker_thread::db_health_checker_thread::db_health_checker_thread;
|
||||
use crate::threads::poll_ip_banlist_thread::poll_ip_bans;
|
||||
@@ -475,6 +475,11 @@ async fn main() -> AnyhowResult<()> {
|
||||
sort_key_crypto,
|
||||
opaque_cursors: opaque_cursor_encoder,
|
||||
static_api_token_set,
|
||||
beeble: BeebleData {
|
||||
api_key: beeble_client::creds::beeble_api_key::BeebleApiKey::new(
|
||||
easyenv::get_env_string_required("BEEBLE_API_KEY")?,
|
||||
),
|
||||
},
|
||||
fal: FalData {
|
||||
api_key: fal_api_key,
|
||||
webhook_url: fal_webhook_url,
|
||||
|
||||
@@ -25,6 +25,7 @@ use billing_component::stripe::stripe_config::StripeConfig;
|
||||
use chrono::{DateTime, Utc};
|
||||
use cloud_storage::bucket_client::BucketClient;
|
||||
use elasticsearch::Elasticsearch;
|
||||
use beeble_client::creds::beeble_api_key::BeebleApiKey;
|
||||
use fal_client::creds::fal_api_key::FalApiKey;
|
||||
use memory_caching::arc_ttl_sieve::ArcTtlSieve;
|
||||
use memory_caching::single_item_ttl_cache::SingleItemTtlCache;
|
||||
@@ -95,6 +96,8 @@ pub struct ServerState {
|
||||
|
||||
pub fal: FalData,
|
||||
|
||||
pub beeble: BeebleData,
|
||||
|
||||
pub seedance2pro: Seedance2ProData,
|
||||
|
||||
pub openai: OpenAiData,
|
||||
@@ -321,6 +324,12 @@ pub struct FalData {
|
||||
pub webhook_url: String,
|
||||
}
|
||||
|
||||
/// Beeble SwitchX integration
|
||||
#[derive(Clone)]
|
||||
pub struct BeebleData {
|
||||
pub api_key: BeebleApiKey,
|
||||
}
|
||||
|
||||
/// Seedance 2 Pro integration
|
||||
#[derive(Clone)]
|
||||
pub struct Seedance2ProData {
|
||||
|
||||
Reference in New Issue
Block a user