mirror of
https://github.com/storytold/artcraft.git
synced 2026-10-31 15:55:54 -04:00
Compare commits
52
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f08cc3fd4a | ||
|
|
b7853b8914 | ||
|
|
26d5d6d272 | ||
|
|
4e20f63f87 | ||
|
|
c8fa722b3d | ||
|
|
0540d2caba | ||
|
|
449c29e824 | ||
|
|
bf4bd8f21c | ||
|
|
59bcf8b040 | ||
|
|
9785f3292d | ||
|
|
6f7262d0e8 | ||
|
|
ec0b475457 | ||
|
|
47db5d0f3d | ||
|
|
e9de700f2c | ||
|
|
f365f1a02f | ||
|
|
bfebf60289 | ||
|
|
f0f2788e1b | ||
|
|
33b66f388f | ||
|
|
387cb22e65 | ||
|
|
7248425f30 | ||
|
|
cc0e3a2f01 | ||
|
|
aa719ba56f | ||
|
|
1da489a177 | ||
|
|
e777d996bc | ||
|
|
eeb6d486e1 | ||
|
|
57ca10f23d | ||
|
|
7b97c3bfd2 | ||
|
|
111383a0ae | ||
|
|
542a7c7116 | ||
|
|
c6dc35eccb | ||
|
|
d8fbc09867 | ||
|
|
2b0ebd5350 | ||
|
|
8d22697855 | ||
|
|
6cdbe9ea00 | ||
|
|
15db937039 | ||
|
|
a01bde2a4b | ||
|
|
98bf91fa17 | ||
|
|
6f4fe5c542 | ||
|
|
ebae1dbc9f | ||
|
|
39a07b25f5 | ||
|
|
8ba103fd2b | ||
|
|
ef53d96a27 | ||
|
|
ebc6834128 | ||
|
|
ea1090b8ef | ||
|
|
9c9c2b4fff | ||
|
|
7bea2ed724 | ||
|
|
e7f2aed1ef | ||
|
|
13573437fb | ||
|
|
215e958405 | ||
|
|
84a2aa6108 | ||
|
|
aff8d5d310 | ||
|
|
55367e8723 |
Generated
+1
@@ -26,6 +26,7 @@
|
||||
<w>hparams</w>
|
||||
<w>huggingface</w>
|
||||
<w>lipsync</w>
|
||||
<w>mocap</w>
|
||||
<w>mochi</w>
|
||||
<w>nestedness</w>
|
||||
<w>pathbuf</w>
|
||||
|
||||
-12
@@ -1,12 +0,0 @@
|
||||
{
|
||||
"db_name": "MySQL",
|
||||
"query": "\nINSERT INTO generic_inference_jobs\nSET\n token = ?,\n uuid_idempotency_token = ?,\n\n inference_category = ?,\n maybe_model_type = ?,\n maybe_model_token = ?,\n\n maybe_input_source_token = ?,\n maybe_input_source_token_type = ?,\n\n maybe_raw_inference_text = ?,\n\n maybe_inference_args = ?,\n\n maybe_creator_user_token = ?,\n maybe_creator_anonymous_visitor_token = ?,\n creator_ip_address = ?,\n\n creator_set_visibility = ?,\n\n priority_level = ?,\n is_keepalive_required = ?,\n max_duration_seconds = ?,\n\n is_debug_request = ?,\n maybe_routing_tag = ?,\n\n status = \"pending\"\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Right": 18
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "0d780caac7560f84efb50bd2ed876cbdd728b32e26b73dfbb3009ada12985405"
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"db_name": "MySQL",
|
||||
"query": "\n INSERT INTO media_files\n SET\n token = ?,\n media_type = ?,\n\n origin_category = ?, \n origin_product_category = ?, \n maybe_origin_model_type = ?, \n maybe_origin_model_token = ?, \n maybe_origin_filename = ?,\n\n is_batch_generated = ?, \n\n maybe_mime_type = ?,\n file_size_bytes = ?,\n maybe_duration_millis = ?,\n maybe_audio_encoding = ?, \n maybe_video_encoding = ?, \n maybe_frame_width = ?, \n maybe_frame_height = ?, \n checksum_sha2 = ?,\n\n public_bucket_directory_hash = ?, \n maybe_public_bucket_prefix = ?, \n maybe_public_bucket_extension = ?, \n extra_file_modification_info = ?, \n\n maybe_creator_user_token = ?, \n maybe_creator_anonymous_visitor_token = ?, \n\n creator_ip_address = ?, \n creator_set_visibility = ?, \n\n maybe_creator_file_synthetic_id = ?, \n maybe_creator_category_synthetic_id = ?,\n\n maybe_mod_user_token = ?, \n is_generated_on_prem = ?, \n generated_by_worker = ?, \n generated_by_cluster = ?\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Right": 30
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "80900178668fb38ae51dfe19401c1be37f4ef97fda0198f83239507b8000d1ae"
|
||||
}
|
||||
+91
@@ -0,0 +1,91 @@
|
||||
{
|
||||
"db_name": "MySQL",
|
||||
"query": "\nSELECT\n m.token as `token: tokens::tokens::media_files::MediaFileToken`,\n\n m.media_type as `media_type: enums::by_table::media_files::media_file_type::MediaFileType`,\n\n m.maybe_creator_user_token as `maybe_creator_user_token: tokens::tokens::users::UserToken`,\n m.creator_set_visibility as `creator_set_visibility: enums::common::visibility::Visibility`,\n\n m.public_bucket_directory_hash,\n m.maybe_public_bucket_prefix,\n m.maybe_public_bucket_extension\n\nFROM media_files AS m\nWHERE\n m.token = ?\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "token: tokens::tokens::media_files::MediaFileToken",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "NOT_NULL | UNIQUE_KEY | BINARY | NO_DEFAULT_VALUE",
|
||||
"char_set": 224,
|
||||
"max_size": 128
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "media_type: enums::by_table::media_files::media_file_type::MediaFileType",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "NOT_NULL | MULTIPLE_KEY | BINARY | NO_DEFAULT_VALUE",
|
||||
"char_set": 224,
|
||||
"max_size": 64
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"name": "maybe_creator_user_token: tokens::tokens::users::UserToken",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "MULTIPLE_KEY | BINARY",
|
||||
"char_set": 224,
|
||||
"max_size": 128
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 3,
|
||||
"name": "creator_set_visibility: enums::common::visibility::Visibility",
|
||||
"type_info": {
|
||||
"type": "String",
|
||||
"flags": "NOT_NULL | BINARY | ENUM",
|
||||
"char_set": 224,
|
||||
"max_size": 28
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 4,
|
||||
"name": "public_bucket_directory_hash",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "NOT_NULL | BINARY | NO_DEFAULT_VALUE",
|
||||
"char_set": 224,
|
||||
"max_size": 128
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 5,
|
||||
"name": "maybe_public_bucket_prefix",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "BINARY",
|
||||
"char_set": 224,
|
||||
"max_size": 64
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 6,
|
||||
"name": "maybe_public_bucket_extension",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "BINARY",
|
||||
"char_set": 224,
|
||||
"max_size": 64
|
||||
}
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Right": 1
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
true
|
||||
]
|
||||
},
|
||||
"hash": "8ba733c63aff16f144417eaa2004d7de479e59f62688016f0ce7b7c61ec1ec57"
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"db_name": "MySQL",
|
||||
"query": "\nINSERT INTO generic_inference_jobs\nSET\n token = ?,\n uuid_idempotency_token = ?,\n\n job_type = ?,\n\n inference_category = ?,\n maybe_model_type = ?,\n maybe_model_token = ?,\n\n maybe_input_source_token = ?,\n maybe_input_source_token_type = ?,\n\n maybe_raw_inference_text = ?,\n\n maybe_inference_args = ?,\n\n maybe_creator_user_token = ?,\n maybe_creator_anonymous_visitor_token = ?,\n creator_ip_address = ?,\n\n creator_set_visibility = ?,\n\n priority_level = ?,\n is_keepalive_required = ?,\n max_duration_seconds = ?,\n\n is_debug_request = ?,\n maybe_routing_tag = ?,\n\n status = \"pending\"\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Right": 19
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "e97a0974ed643b8a20d4d319b4aa93aba979d5f8fc0bdc44b77ef4f945b03b35"
|
||||
}
|
||||
Generated
+36
-4
@@ -3099,6 +3099,7 @@ dependencies = [
|
||||
"r2d2_redis",
|
||||
"rand 0.8.5",
|
||||
"redis_common",
|
||||
"reqwest",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
@@ -3110,6 +3111,7 @@ dependencies = [
|
||||
"tokens",
|
||||
"tokio",
|
||||
"tts_common",
|
||||
"walkdir",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3632,7 +3634,7 @@ dependencies = [
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"serial_test",
|
||||
"serial_test 2.0.0",
|
||||
"sqlx",
|
||||
"tokens",
|
||||
"tokio",
|
||||
@@ -3905,7 +3907,7 @@ dependencies = [
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"serial_test",
|
||||
"serial_test 2.0.0",
|
||||
"sqlx",
|
||||
"tokens",
|
||||
"tokio",
|
||||
@@ -5526,7 +5528,21 @@ dependencies = [
|
||||
"lazy_static",
|
||||
"log",
|
||||
"parking_lot 0.12.1",
|
||||
"serial_test_derive",
|
||||
"serial_test_derive 2.0.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serial_test"
|
||||
version = "3.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "953ad9342b3aaca7cb43c45c097dd008d4907070394bd0751a0aa8817e5a018d"
|
||||
dependencies = [
|
||||
"dashmap",
|
||||
"futures 0.3.29",
|
||||
"lazy_static",
|
||||
"log",
|
||||
"parking_lot 0.12.1",
|
||||
"serial_test_derive 3.0.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -5540,6 +5556,17 @@ dependencies = [
|
||||
"syn 2.0.39",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serial_test_derive"
|
||||
version = "3.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b93fb4adc70021ac1b47f7d45e8cc4169baaa7ea58483bc5b721d19a26202212"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.39",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "server_environment"
|
||||
version = "0.0.1"
|
||||
@@ -6127,7 +6154,7 @@ name = "storyteller_root"
|
||||
version = "0.0.1"
|
||||
dependencies = [
|
||||
"my-workspace-hack",
|
||||
"serial_test",
|
||||
"serial_test 2.0.0",
|
||||
"tempdir",
|
||||
]
|
||||
|
||||
@@ -6210,7 +6237,12 @@ dependencies = [
|
||||
name = "subprocess_common"
|
||||
version = "0.0.1"
|
||||
dependencies = [
|
||||
"errors",
|
||||
"filesys",
|
||||
"log",
|
||||
"my-workspace-hack",
|
||||
"serial_test 3.0.0",
|
||||
"subprocess",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
-- noinspection SqlDialectInspectionForFile
|
||||
-- noinspection SqlNoDataSourceInspectionForFile
|
||||
-- noinspection SqlResolveForFile
|
||||
|
||||
update model_weights
|
||||
set
|
||||
mod_deleted_at = NOW()
|
||||
where token IN (
|
||||
'weight_tnhacmxb9qwqrygc3pxvk3ds6',
|
||||
'weight_9g1pghkc0xhfy5n9m36b11vds',
|
||||
'weight_5k0kzkftr4x39xyc9yg5p9c86'
|
||||
);
|
||||
|
||||
@@ -375,7 +375,8 @@ where token IN (
|
||||
'vcm_a47wtfbsjrfc',
|
||||
'vcm_kygenhbg5mgj',
|
||||
'vcm_s8gndv19yt7p',
|
||||
'vcm_ny9pj57w5eyh'
|
||||
'vcm_ny9pj57w5eyh',
|
||||
'vcm_jg4rst3j2d7x'
|
||||
);
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
-- noinspection SqlDialectInspectionForFile
|
||||
-- noinspection SqlNoDataSourceInspectionForFile
|
||||
-- noinspection SqlResolveForFile
|
||||
|
||||
|
||||
-- Inventory non-deleted
|
||||
select
|
||||
m.id,
|
||||
m.token,
|
||||
SUBSTRING(m.title, 1, 35) as title,
|
||||
u.username,
|
||||
SUBSTRING(m.original_download_url, 1, 35) as url,
|
||||
m.updated_at
|
||||
from model_weights as m
|
||||
left join users AS u on
|
||||
u.token = m.creator_user_token
|
||||
where
|
||||
m.user_deleted_at IS NULL
|
||||
and m.mod_deleted_at IS NULL
|
||||
order by m.title asc;
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
-- noinspection SqlDialectInspectionForFile
|
||||
-- noinspection SqlNoDataSourceInspectionForFile
|
||||
-- noinspection SqlResolveForFile
|
||||
|
||||
ALTER TABLE generic_inference_jobs DROP COLUMN job_type;
|
||||
@@ -0,0 +1,10 @@
|
||||
-- noinspection SqlDialectInspectionForFile
|
||||
-- noinspection SqlNoDataSourceInspectionForFile
|
||||
-- noinspection SqlResolveForFile
|
||||
|
||||
ALTER TABLE generic_inference_jobs
|
||||
ADD COLUMN job_type VARCHAR(32) DEFAULT NULL
|
||||
AFTER uuid_idempotency_token;
|
||||
|
||||
ALTER TABLE generic_inference_jobs
|
||||
ADD INDEX index_job_type (job_type);
|
||||
@@ -17,12 +17,27 @@ CREATE TABLE generic_inference_jobs (
|
||||
|
||||
-- ========== INFERENCE DETAILS ==========
|
||||
|
||||
-- The new enum for the type of job, which will eventually replace `inference_category` and `maybe_model_type`.
|
||||
-- This isn't fully supported yet in the inference-job, but we'll start populating it and adding logic around it.
|
||||
-- We'll start this out as nullable, then migrate the old rows with a default column value.
|
||||
job_type VARCHAR(32) DEFAULT NULL,
|
||||
|
||||
-- NB: This is becoming a problematic field and is becoming conflated with `maybe_model_type`.
|
||||
-- We're using this to handle job dispatching, but the latter is being used to load container
|
||||
-- dependencies at startup (even if it isn't a real model type or a polymorphic foreign key).
|
||||
-- This is pretty gross and horrible.
|
||||
--
|
||||
-- Broad category of inference
|
||||
-- Examples (may not be up to date):
|
||||
-- * text_to_speech
|
||||
-- * voice_conversion
|
||||
inference_category VARCHAR(32) NOT NULL,
|
||||
|
||||
-- NB: See notes on inference_category. This is becoming a problematic field. We're using
|
||||
-- this to load the container spin-up arguments and dependencies, but the former is being
|
||||
-- used to dispatch the job. `inference_category` is the one that should probably go away,
|
||||
-- and this column should be renamed to `inference_job_type`.
|
||||
--
|
||||
-- Potential part of the composite foreign key to the primary model being used, if any.
|
||||
-- This will normally live in `maybe_inference_args`, but in this case, it's useful for
|
||||
-- running easy database analytical queries.
|
||||
@@ -239,6 +254,7 @@ CREATE TABLE generic_inference_jobs (
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE KEY (token),
|
||||
UNIQUE KEY (uuid_idempotency_token),
|
||||
KEY index_job_type (job_type),
|
||||
KEY index_inference_category (inference_category),
|
||||
KEY index_maybe_model_type_and_maybe_model_token (maybe_model_type, maybe_model_token),
|
||||
KEY index_maybe_model_type (maybe_model_type),
|
||||
|
||||
@@ -73,6 +73,14 @@ impl MediaFileBucketPath {
|
||||
pub fn get_basename(&self) -> &str {
|
||||
&self.filename
|
||||
}
|
||||
|
||||
pub fn get_optional_prefix(&self) -> Option<&str> {
|
||||
self.optional_prefix.as_deref()
|
||||
}
|
||||
|
||||
pub fn get_optional_extension(&self) -> Option<&str> {
|
||||
self.optional_extension.as_deref()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -12,6 +12,7 @@ const ORIGINAL_FILE_BASENAME : &str = "original_upload.bin";
|
||||
/// The original user upload file.
|
||||
/// It may have derivative files (down samples, crops, etc.) that live alongside it.
|
||||
#[derive(Clone)]
|
||||
#[deprecated(note = "Use media_files records and MediaFileBucketPath instead. media_uploads records are DEPRECATED.")]
|
||||
pub struct MediaUploadOriginalFilePath {
|
||||
directory: MediaUploadDirectory,
|
||||
full_object_path: String,
|
||||
@@ -21,11 +22,13 @@ impl PublicPath for MediaUploadOriginalFilePath {}
|
||||
|
||||
impl MediaUploadOriginalFilePath {
|
||||
|
||||
#[deprecated(note = "Use media_files records and MediaFileBucketPath instead. media_uploads records are DEPRECATED.")]
|
||||
pub fn generate_new() -> Self {
|
||||
let entropy = crockford_entropy_lower(32);
|
||||
Self::from_object_hash(&entropy)
|
||||
}
|
||||
|
||||
#[deprecated(note = "Use media_files records and MediaFileBucketPath instead. media_uploads records are DEPRECATED.")]
|
||||
pub fn from_object_hash(hash: &str) -> Self {
|
||||
// TODO: Path construction could be cleaner.
|
||||
let directory = MediaUploadDirectory::from_object_hash(hash);
|
||||
@@ -36,22 +39,27 @@ impl MediaUploadOriginalFilePath {
|
||||
}
|
||||
}
|
||||
|
||||
#[deprecated(note = "Use media_files records and MediaFileBucketPath instead. media_uploads records are DEPRECATED.")]
|
||||
pub fn get_full_object_path_str(&self) -> &str {
|
||||
&self.full_object_path
|
||||
}
|
||||
|
||||
#[deprecated(note = "Use media_files records and MediaFileBucketPath instead. media_uploads records are DEPRECATED.")]
|
||||
pub fn to_full_object_pathbuf(&self) -> PathBuf {
|
||||
PathBuf::from(&self.full_object_path)
|
||||
}
|
||||
|
||||
#[deprecated(note = "Use media_files records and MediaFileBucketPath instead. media_uploads records are DEPRECATED.")]
|
||||
pub fn get_directory(&self) -> &MediaUploadDirectory {
|
||||
&self.directory
|
||||
}
|
||||
|
||||
#[deprecated(note = "Use media_files records and MediaFileBucketPath instead. media_uploads records are DEPRECATED.")]
|
||||
pub fn get_object_hash(&self) -> &str {
|
||||
&self.directory.get_object_hash()
|
||||
}
|
||||
|
||||
#[deprecated(note = "Use media_files records and MediaFileBucketPath instead. media_uploads records are DEPRECATED.")]
|
||||
pub fn get_basename() -> &'static str {
|
||||
ORIGINAL_FILE_BASENAME
|
||||
}
|
||||
|
||||
+1
-1
@@ -18,7 +18,7 @@ use strum::EnumIter;
|
||||
/// Another benefit is that we'll surface all of the various types of failure and perhaps eventually
|
||||
/// come to handle some in a cross-cutting way.
|
||||
///
|
||||
/// DO NOT CHANGE VALUES WITHOUT A MIGRATION STRATEGY.
|
||||
/// YOU CAN ADD NEW VALUES, BUT DO NOT CHANGE EXISTING VALUES WITHOUT A MIGRATION STRATEGY.
|
||||
#[cfg_attr(test, derive(EnumIter, EnumCount))]
|
||||
#[derive(Clone, Copy, Eq, PartialEq, Hash, Ord, PartialOrd, Deserialize, Serialize)]
|
||||
pub enum FrontendFailureCategory {
|
||||
|
||||
@@ -11,7 +11,7 @@ use strum::EnumIter;
|
||||
/// Each "category" of inference is identified by the following enum variants.
|
||||
/// These types are present in the HTTP API and database columns as serialized here.
|
||||
///
|
||||
/// DO NOT CHANGE VALUES WITHOUT A MIGRATION STRATEGY.
|
||||
/// YOU CAN ADD NEW VALUES, BUT DO NOT CHANGE EXISTING VALUES WITHOUT A MIGRATION STRATEGY.
|
||||
#[cfg_attr(test, derive(EnumIter, EnumCount))]
|
||||
#[derive(Clone, Copy, Eq, PartialEq, Hash, Ord, PartialOrd, Deserialize, Serialize, Default)]
|
||||
pub enum InferenceCategory {
|
||||
@@ -37,6 +37,9 @@ pub enum InferenceCategory {
|
||||
|
||||
#[serde(rename = "workflow")]
|
||||
Workflow,
|
||||
|
||||
#[serde(rename = "format_conversion")]
|
||||
FormatConversion,
|
||||
}
|
||||
|
||||
// TODO(bt, 2022-12-21): This desperately needs MySQL integration tests!
|
||||
@@ -54,6 +57,7 @@ impl InferenceCategory {
|
||||
Self::ImageGeneration => "image_generation",
|
||||
Self::Mocap => "mocap",
|
||||
Self::Workflow => "workflow",
|
||||
Self::FormatConversion => "format_conversion",
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,6 +70,7 @@ impl InferenceCategory {
|
||||
"image_generation" => Ok(Self::ImageGeneration),
|
||||
"mocap" => Ok(Self::Mocap),
|
||||
"workflow" => Ok(Self::Workflow),
|
||||
"format_conversion" => Ok(Self::FormatConversion),
|
||||
_ => Err(format!("invalid value: {:?}", value)),
|
||||
}
|
||||
}
|
||||
@@ -81,6 +86,7 @@ impl InferenceCategory {
|
||||
Self::ImageGeneration,
|
||||
Self::Mocap,
|
||||
Self::Workflow,
|
||||
Self::FormatConversion,
|
||||
])
|
||||
}
|
||||
}
|
||||
@@ -90,55 +96,92 @@ mod tests {
|
||||
use crate::by_table::generic_inference_jobs::inference_category::InferenceCategory;
|
||||
use crate::test_helpers::assert_serialization;
|
||||
|
||||
#[test]
|
||||
fn test_serialization() {
|
||||
assert_serialization(InferenceCategory::LipsyncAnimation, "lipsync_animation");
|
||||
assert_serialization(InferenceCategory::TextToSpeech, "text_to_speech");
|
||||
assert_serialization(InferenceCategory::VoiceConversion, "voice_conversion");
|
||||
assert_serialization(InferenceCategory::VideoFilter, "video_filter");
|
||||
assert_serialization(InferenceCategory::ImageGeneration, "image_generation");
|
||||
assert_serialization(InferenceCategory::Mocap, "mocap");
|
||||
assert_serialization(InferenceCategory::Workflow, "workflow")
|
||||
mod explicit_checks {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_serialization() {
|
||||
assert_serialization(InferenceCategory::LipsyncAnimation, "lipsync_animation");
|
||||
assert_serialization(InferenceCategory::TextToSpeech, "text_to_speech");
|
||||
assert_serialization(InferenceCategory::VoiceConversion, "voice_conversion");
|
||||
assert_serialization(InferenceCategory::VideoFilter, "video_filter");
|
||||
assert_serialization(InferenceCategory::ImageGeneration, "image_generation");
|
||||
assert_serialization(InferenceCategory::Mocap, "mocap");
|
||||
assert_serialization(InferenceCategory::Workflow, "workflow");
|
||||
assert_serialization(InferenceCategory::FormatConversion, "format_conversion");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn to_str() {
|
||||
assert_eq!(InferenceCategory::LipsyncAnimation.to_str(), "lipsync_animation");
|
||||
assert_eq!(InferenceCategory::TextToSpeech.to_str(), "text_to_speech");
|
||||
assert_eq!(InferenceCategory::VoiceConversion.to_str(), "voice_conversion");
|
||||
assert_eq!(InferenceCategory::VideoFilter.to_str(), "video_filter");
|
||||
assert_eq!(InferenceCategory::ImageGeneration.to_str(), "image_generation");
|
||||
assert_eq!(InferenceCategory::Mocap.to_str(), "mocap");
|
||||
assert_eq!(InferenceCategory::Workflow.to_str(), "workflow");
|
||||
assert_eq!(InferenceCategory::FormatConversion.to_str(), "format_conversion");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_str() {
|
||||
assert_eq!(InferenceCategory::from_str("lipsync_animation").unwrap(), InferenceCategory::LipsyncAnimation);
|
||||
assert_eq!(InferenceCategory::from_str("text_to_speech").unwrap(), InferenceCategory::TextToSpeech);
|
||||
assert_eq!(InferenceCategory::from_str("voice_conversion").unwrap(), InferenceCategory::VoiceConversion);
|
||||
assert_eq!(InferenceCategory::from_str("video_filter").unwrap(), InferenceCategory::VideoFilter);
|
||||
assert_eq!(InferenceCategory::from_str("image_generation").unwrap(), InferenceCategory::ImageGeneration);
|
||||
assert_eq!(InferenceCategory::from_str("mocap").unwrap(), InferenceCategory::Mocap);
|
||||
assert_eq!(InferenceCategory::from_str("workflow").unwrap(), InferenceCategory::Workflow);
|
||||
assert_eq!(InferenceCategory::from_str("format_conversion").unwrap(), InferenceCategory::FormatConversion);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn all_variants() {
|
||||
// Static check
|
||||
let mut variants = InferenceCategory::all_variants();
|
||||
assert_eq!(variants.len(), 8);
|
||||
assert_eq!(variants.pop_first(), Some(InferenceCategory::LipsyncAnimation));
|
||||
assert_eq!(variants.pop_first(), Some(InferenceCategory::TextToSpeech));
|
||||
assert_eq!(variants.pop_first(), Some(InferenceCategory::VoiceConversion));
|
||||
assert_eq!(variants.pop_first(), Some(InferenceCategory::VideoFilter));
|
||||
assert_eq!(variants.pop_first(), Some(InferenceCategory::ImageGeneration));
|
||||
assert_eq!(variants.pop_first(), Some(InferenceCategory::Mocap));
|
||||
assert_eq!(variants.pop_first(), Some(InferenceCategory::Workflow));
|
||||
assert_eq!(variants.pop_first(), Some(InferenceCategory::FormatConversion));
|
||||
assert_eq!(variants.pop_first(), None);
|
||||
|
||||
// Generated check
|
||||
use strum::IntoEnumIterator;
|
||||
assert_eq!(InferenceCategory::all_variants().len(), InferenceCategory::iter().len());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn to_str() {
|
||||
assert_eq!(InferenceCategory::LipsyncAnimation.to_str(), "lipsync_animation");
|
||||
assert_eq!(InferenceCategory::TextToSpeech.to_str(), "text_to_speech");
|
||||
assert_eq!(InferenceCategory::VoiceConversion.to_str(), "voice_conversion");
|
||||
assert_eq!(InferenceCategory::VideoFilter.to_str(), "video_filter");
|
||||
assert_eq!(InferenceCategory::ImageGeneration.to_str(), "image_generation");
|
||||
assert_eq!(InferenceCategory::Mocap.to_str(), "mocap");
|
||||
assert_eq!(InferenceCategory::Workflow.to_str(), "workflow");
|
||||
}
|
||||
mod mechanical_checks {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn from_str() {
|
||||
assert_eq!(InferenceCategory::from_str("lipsync_animation").unwrap(), InferenceCategory::LipsyncAnimation);
|
||||
assert_eq!(InferenceCategory::from_str("text_to_speech").unwrap(), InferenceCategory::TextToSpeech);
|
||||
assert_eq!(InferenceCategory::from_str("voice_conversion").unwrap(), InferenceCategory::VoiceConversion);
|
||||
assert_eq!(InferenceCategory::from_str("video_filter").unwrap(), InferenceCategory::VideoFilter);
|
||||
assert_eq!(InferenceCategory::from_str("image_generation").unwrap(), InferenceCategory::ImageGeneration);
|
||||
assert_eq!(InferenceCategory::from_str("mocap").unwrap(), InferenceCategory::Mocap);
|
||||
assert_eq!(InferenceCategory::from_str("workflow").unwrap(), InferenceCategory::Workflow);
|
||||
}
|
||||
#[test]
|
||||
fn variant_length() {
|
||||
use strum::IntoEnumIterator;
|
||||
assert_eq!(InferenceCategory::all_variants().len(), InferenceCategory::iter().len());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn all_variants() {
|
||||
// Static check
|
||||
let mut variants = InferenceCategory::all_variants();
|
||||
assert_eq!(variants.len(), 7);
|
||||
assert_eq!(variants.pop_first(), Some(InferenceCategory::LipsyncAnimation));
|
||||
assert_eq!(variants.pop_first(), Some(InferenceCategory::TextToSpeech));
|
||||
assert_eq!(variants.pop_first(), Some(InferenceCategory::VoiceConversion));
|
||||
assert_eq!(variants.pop_first(), Some(InferenceCategory::VideoFilter));
|
||||
assert_eq!(variants.pop_first(), Some(InferenceCategory::ImageGeneration));
|
||||
assert_eq!(variants.pop_first(), Some(InferenceCategory::Mocap));
|
||||
assert_eq!(variants.pop_first(), Some(InferenceCategory::Workflow));
|
||||
assert_eq!(variants.pop_first(), None);
|
||||
#[test]
|
||||
fn round_trip() {
|
||||
for variant in InferenceCategory::all_variants() {
|
||||
assert_eq!(variant, InferenceCategory::from_str(variant.to_str()).unwrap());
|
||||
assert_eq!(variant, InferenceCategory::from_str(&format!("{}", variant)).unwrap());
|
||||
assert_eq!(variant, InferenceCategory::from_str(&format!("{:?}", variant)).unwrap());
|
||||
}
|
||||
}
|
||||
|
||||
// Generated check
|
||||
use strum::IntoEnumIterator;
|
||||
assert_eq!(InferenceCategory::all_variants().len(), InferenceCategory::iter().len());
|
||||
#[test]
|
||||
fn serialized_length_ok_for_database() {
|
||||
const MAX_LENGTH : usize = 32;
|
||||
for variant in InferenceCategory::all_variants() {
|
||||
let serialized = variant.to_str();
|
||||
assert!(serialized.len() > 0, "variant {:?} is too short", variant);
|
||||
assert!(serialized.len() <= MAX_LENGTH, "variant {:?} is too long", variant);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -7,7 +7,7 @@ use strum::EnumIter;
|
||||
|
||||
/// Used in the `generic_inference_jobs` table in `VARCHAR(32)` field `maybe_input_source_token`.
|
||||
///
|
||||
/// DO NOT CHANGE VALUES WITHOUT A MIGRATION STRATEGY.
|
||||
/// YOU CAN ADD NEW VALUES, BUT DO NOT CHANGE EXISTING VALUES WITHOUT A MIGRATION STRATEGY.
|
||||
#[cfg_attr(test, derive(EnumIter, EnumCount))]
|
||||
#[derive(Clone, Copy, Eq, PartialEq, Hash, Ord, PartialOrd, Deserialize, Serialize)]
|
||||
pub enum InferenceInputSourceTokenType {
|
||||
|
||||
@@ -0,0 +1,227 @@
|
||||
use std::collections::BTreeSet;
|
||||
|
||||
#[cfg(test)]
|
||||
use strum::EnumCount;
|
||||
#[cfg(test)]
|
||||
use strum::EnumIter;
|
||||
|
||||
/// Used in the `generic_inference_jobs` table in `VARCHAR(32)` field `job_type`.
|
||||
///
|
||||
/// TODO(bt,2024-02-01): This will replace "inference_category" and "maybe_model_type" for job control and dispatch,
|
||||
/// since those mechanisms are overloaded and inconsistent.
|
||||
///
|
||||
/// YOU CAN ADD NEW VALUES, BUT DO NOT CHANGE EXISTING VALUES WITHOUT A MIGRATION STRATEGY.
|
||||
#[cfg_attr(test, derive(EnumIter, EnumCount))]
|
||||
#[derive(Clone, Copy, Eq, PartialEq, Hash, Ord, PartialOrd, Deserialize, Serialize, Default)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum InferenceJobType {
|
||||
/// Jobs that run ComfyUI workflows
|
||||
ComfyUi,
|
||||
|
||||
/// A job that turns "FBX" game engine files into "GLTF" files (Bevy-compatible).
|
||||
#[serde(rename = "convert_fbx_gltf")]
|
||||
ConvertFbxToGltf,
|
||||
|
||||
/// Process a video into BVH mocap animation data for game engines
|
||||
MocapNet,
|
||||
|
||||
/// Re-render a video is a video style transfer algorithm. We developed code
|
||||
/// around it, but chose to develop AnimateDiff / ComfyUI support instead.
|
||||
RerenderAVideo,
|
||||
|
||||
/// RVC is a voice conversion model. RVCv2 is the most popular such model currently.
|
||||
#[serde(rename = "rvc_v2")]
|
||||
RvcV2,
|
||||
|
||||
/// SadTalker does image-to-video lip-syncing when given an audio file and image.
|
||||
SadTalker,
|
||||
|
||||
/// so-vits-svc voice conversion. This predates RVCv2.
|
||||
SoVitsSvc,
|
||||
|
||||
/// Stable diffusion image generation
|
||||
StableDiffusion,
|
||||
|
||||
/// StyleTTS2 is a zero shot multi-speaker TTS model.
|
||||
/// This job type should handle both speaker vector encoding and inference.
|
||||
#[serde(rename = "styletts2")]
|
||||
StyleTTS2,
|
||||
|
||||
/// TT2 Text to speech
|
||||
Tacotron2,
|
||||
|
||||
/// A value we may use in the future for historical jobs
|
||||
/// (i.e. when we backfill the database column and make it non-nullable)
|
||||
#[default]
|
||||
Unknown,
|
||||
}
|
||||
|
||||
// TODO(bt, 2022-12-21): This desperately needs MySQL integration tests!
|
||||
impl_enum_display_and_debug_using_to_str!(InferenceJobType);
|
||||
impl_mysql_enum_coders!(InferenceJobType);
|
||||
|
||||
/// NB: Legacy API for older code.
|
||||
impl InferenceJobType {
|
||||
pub fn to_str(&self) -> &'static str {
|
||||
match self {
|
||||
Self::ComfyUi => "comfy_ui",
|
||||
Self::ConvertFbxToGltf => "convert_fbx_gltf",
|
||||
Self::MocapNet => "mocap_net",
|
||||
Self::RerenderAVideo => "rerender_a_video",
|
||||
Self::RvcV2 => "rvc_v2",
|
||||
Self::SadTalker => "sad_talker",
|
||||
Self::SoVitsSvc => "so_vits_svc",
|
||||
Self::StableDiffusion => "stable_diffusion",
|
||||
Self::StyleTTS2 => "styletts2",
|
||||
Self::Tacotron2 => "tacotron2",
|
||||
Self::Unknown => "unknown",
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_str(value: &str) -> Result<Self, String> {
|
||||
match value {
|
||||
"comfy_ui" => Ok(Self::ComfyUi),
|
||||
"convert_fbx_gltf" => Ok(Self::ConvertFbxToGltf),
|
||||
"mocap_net" => Ok(Self::MocapNet),
|
||||
"rerender_a_video" => Ok(Self::RerenderAVideo),
|
||||
"rvc_v2" => Ok(Self::RvcV2),
|
||||
"sad_talker" => Ok(Self::SadTalker),
|
||||
"so_vits_svc" => Ok(Self::SoVitsSvc),
|
||||
"stable_diffusion" => Ok(Self::StableDiffusion),
|
||||
"styletts2" => Ok(Self::StyleTTS2),
|
||||
"tacotron2" => Ok(Self::Tacotron2),
|
||||
"unknown" => Ok(Self::Unknown),
|
||||
_ => Err(format!("invalid value: {:?}", value)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn all_variants() -> BTreeSet<Self> {
|
||||
// NB: BTreeSet is sorted
|
||||
// NB: BTreeSet::from() isn't const, but not worth using LazyStatic, etc.
|
||||
BTreeSet::from([
|
||||
Self::ComfyUi,
|
||||
Self::ConvertFbxToGltf,
|
||||
Self::MocapNet,
|
||||
Self::RerenderAVideo,
|
||||
Self::RvcV2,
|
||||
Self::SadTalker,
|
||||
Self::SoVitsSvc,
|
||||
Self::StableDiffusion,
|
||||
Self::StyleTTS2,
|
||||
Self::Tacotron2,
|
||||
Self::Unknown,
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::by_table::generic_inference_jobs::inference_job_type::InferenceJobType;
|
||||
use crate::test_helpers::assert_serialization;
|
||||
|
||||
mod explicit_checks {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn default() {
|
||||
assert_eq!(InferenceJobType::default(), InferenceJobType::Unknown);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_serialization() {
|
||||
assert_serialization(InferenceJobType::ComfyUi, "comfy_ui");
|
||||
assert_serialization(InferenceJobType::ConvertFbxToGltf, "convert_fbx_gltf");
|
||||
assert_serialization(InferenceJobType::MocapNet, "mocap_net");
|
||||
assert_serialization(InferenceJobType::RerenderAVideo, "rerender_a_video");
|
||||
assert_serialization(InferenceJobType::RvcV2, "rvc_v2");
|
||||
assert_serialization(InferenceJobType::SadTalker, "sad_talker");
|
||||
assert_serialization(InferenceJobType::SoVitsSvc, "so_vits_svc");
|
||||
assert_serialization(InferenceJobType::StableDiffusion, "stable_diffusion");
|
||||
assert_serialization(InferenceJobType::StyleTTS2, "styletts2");
|
||||
assert_serialization(InferenceJobType::Tacotron2, "tacotron2");
|
||||
assert_serialization(InferenceJobType::Unknown, "unknown");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn to_str() {
|
||||
assert_eq!(InferenceJobType::ComfyUi.to_str(), "comfy_ui");
|
||||
assert_eq!(InferenceJobType::ConvertFbxToGltf.to_str(), "convert_fbx_gltf");
|
||||
assert_eq!(InferenceJobType::MocapNet.to_str(), "mocap_net");
|
||||
assert_eq!(InferenceJobType::RerenderAVideo.to_str(), "rerender_a_video");
|
||||
assert_eq!(InferenceJobType::RvcV2.to_str(), "rvc_v2");
|
||||
assert_eq!(InferenceJobType::SadTalker.to_str(), "sad_talker");
|
||||
assert_eq!(InferenceJobType::SoVitsSvc.to_str(), "so_vits_svc");
|
||||
assert_eq!(InferenceJobType::StableDiffusion.to_str(), "stable_diffusion");
|
||||
assert_eq!(InferenceJobType::StyleTTS2.to_str(), "styletts2");
|
||||
assert_eq!(InferenceJobType::Tacotron2.to_str(), "tacotron2");
|
||||
assert_eq!(InferenceJobType::Unknown.to_str(), "unknown");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_str() {
|
||||
assert_eq!(InferenceJobType::from_str("comfy_ui").unwrap(), InferenceJobType::ComfyUi);
|
||||
assert_eq!(InferenceJobType::from_str("convert_fbx_gltf").unwrap(), InferenceJobType::ConvertFbxToGltf);
|
||||
assert_eq!(InferenceJobType::from_str("mocap_net").unwrap(), InferenceJobType::MocapNet);
|
||||
assert_eq!(InferenceJobType::from_str("rerender_a_video").unwrap(), InferenceJobType::RerenderAVideo);
|
||||
assert_eq!(InferenceJobType::from_str("rvc_v2").unwrap(), InferenceJobType::RvcV2);
|
||||
assert_eq!(InferenceJobType::from_str("sad_talker").unwrap(), InferenceJobType::SadTalker);
|
||||
assert_eq!(InferenceJobType::from_str("so_vits_svc").unwrap(), InferenceJobType::SoVitsSvc);
|
||||
assert_eq!(InferenceJobType::from_str("stable_diffusion").unwrap(), InferenceJobType::StableDiffusion);
|
||||
assert_eq!(InferenceJobType::from_str("styletts2").unwrap(), InferenceJobType::StyleTTS2);
|
||||
assert_eq!(InferenceJobType::from_str("tacotron2").unwrap(), InferenceJobType::Tacotron2);
|
||||
assert_eq!(InferenceJobType::from_str("unknown").unwrap(), InferenceJobType::Unknown);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn all_variants() {
|
||||
// Static check
|
||||
let mut variants = InferenceJobType::all_variants();
|
||||
assert_eq!(variants.len(), 11);
|
||||
assert_eq!(variants.pop_first(), Some(InferenceJobType::ComfyUi));
|
||||
assert_eq!(variants.pop_first(), Some(InferenceJobType::ConvertFbxToGltf));
|
||||
assert_eq!(variants.pop_first(), Some(InferenceJobType::MocapNet));
|
||||
assert_eq!(variants.pop_first(), Some(InferenceJobType::RerenderAVideo));
|
||||
assert_eq!(variants.pop_first(), Some(InferenceJobType::RvcV2));
|
||||
assert_eq!(variants.pop_first(), Some(InferenceJobType::SadTalker));
|
||||
assert_eq!(variants.pop_first(), Some(InferenceJobType::SoVitsSvc));
|
||||
assert_eq!(variants.pop_first(), Some(InferenceJobType::StableDiffusion));
|
||||
assert_eq!(variants.pop_first(), Some(InferenceJobType::StyleTTS2));
|
||||
assert_eq!(variants.pop_first(), Some(InferenceJobType::Tacotron2));
|
||||
assert_eq!(variants.pop_first(), Some(InferenceJobType::Unknown));
|
||||
assert_eq!(variants.pop_first(), None);
|
||||
|
||||
// Generated check
|
||||
use strum::IntoEnumIterator;
|
||||
assert_eq!(InferenceJobType::all_variants().len(), InferenceJobType::iter().len());
|
||||
}
|
||||
}
|
||||
|
||||
mod mechanical_checks {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn variant_length() {
|
||||
use strum::IntoEnumIterator;
|
||||
assert_eq!(InferenceJobType::all_variants().len(), InferenceJobType::iter().len());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn round_trip() {
|
||||
for variant in InferenceJobType::all_variants() {
|
||||
assert_eq!(variant, InferenceJobType::from_str(variant.to_str()).unwrap());
|
||||
assert_eq!(variant, InferenceJobType::from_str(&format!("{}", variant)).unwrap());
|
||||
assert_eq!(variant, InferenceJobType::from_str(&format!("{:?}", variant)).unwrap());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn serialized_length_ok_for_database() {
|
||||
const MAX_LENGTH : usize = 32;
|
||||
for variant in InferenceJobType::all_variants() {
|
||||
let serialized = variant.to_str();
|
||||
assert!(serialized.len() > 0, "variant {:?} is too short", variant);
|
||||
assert!(serialized.len() <= MAX_LENGTH, "variant {:?} is too long", variant);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -13,7 +13,7 @@ use strum::EnumIter;
|
||||
///
|
||||
/// These types are present in the HTTP API and database columns as serialized here.
|
||||
///
|
||||
/// DO NOT CHANGE VALUES WITHOUT A MIGRATION STRATEGY.
|
||||
/// YOU CAN ADD NEW VALUES, BUT DO NOT CHANGE EXISTING VALUES WITHOUT A MIGRATION STRATEGY.
|
||||
#[cfg_attr(test, derive(EnumIter, EnumCount))]
|
||||
#[derive(Clone, Copy, Eq, PartialEq, Hash, Ord, PartialOrd, Deserialize, Serialize)]
|
||||
pub enum InferenceModelType {
|
||||
@@ -41,6 +41,9 @@ pub enum InferenceModelType {
|
||||
ComfyUi,
|
||||
#[serde(rename = "styletts2")]
|
||||
StyleTTS2,
|
||||
/// A job that turns "FBX" game engine files into "GLTF" files (Bevy-compatible).
|
||||
#[serde(rename = "convert_fbx_gltf")]
|
||||
ConvertFbxToGltf,
|
||||
}
|
||||
|
||||
// TODO(bt, 2022-12-21): This desperately needs MySQL integration tests!
|
||||
@@ -62,6 +65,7 @@ impl InferenceModelType {
|
||||
Self::MocapNet => "mocap_net",
|
||||
Self::StyleTTS2 => "styletts2",
|
||||
Self::ComfyUi => "comfy_ui",
|
||||
Self::ConvertFbxToGltf => "convert_fbx_gltf",
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,25 +82,27 @@ impl InferenceModelType {
|
||||
"mocap_net" => Ok(Self::MocapNet),
|
||||
"styletts2" => Ok(Self::StyleTTS2),
|
||||
"comfy_ui" => Ok(Self::ComfyUi),
|
||||
"convert_fbx_gltf" => Ok(Self::ConvertFbxToGltf),
|
||||
_ => Err(format!("invalid value: {:?}", value)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn all_variants() -> BTreeSet<InferenceModelType> {
|
||||
pub fn all_variants() -> BTreeSet<Self> {
|
||||
// NB: BTreeSet is sorted
|
||||
// NB: BTreeSet::from() isn't const, but not worth using LazyStatic, etc.
|
||||
BTreeSet::from([
|
||||
InferenceModelType::RvcV2,
|
||||
InferenceModelType::SadTalker,
|
||||
InferenceModelType::SoVitsSvc,
|
||||
InferenceModelType::Tacotron2,
|
||||
InferenceModelType::Vits,
|
||||
InferenceModelType::VallEX,
|
||||
InferenceModelType::RerenderAVideo,
|
||||
InferenceModelType::StableDiffusion,
|
||||
InferenceModelType::MocapNet,
|
||||
InferenceModelType::StyleTTS2,
|
||||
InferenceModelType::ComfyUi,
|
||||
Self::RvcV2,
|
||||
Self::SadTalker,
|
||||
Self::SoVitsSvc,
|
||||
Self::Tacotron2,
|
||||
Self::Vits,
|
||||
Self::VallEX,
|
||||
Self::RerenderAVideo,
|
||||
Self::StableDiffusion,
|
||||
Self::MocapNet,
|
||||
Self::StyleTTS2,
|
||||
Self::ComfyUi,
|
||||
Self::ConvertFbxToGltf,
|
||||
])
|
||||
}
|
||||
}
|
||||
@@ -106,71 +112,108 @@ mod tests {
|
||||
use crate::by_table::generic_inference_jobs::inference_model_type::InferenceModelType;
|
||||
use crate::test_helpers::assert_serialization;
|
||||
|
||||
#[test]
|
||||
fn test_serialization() {
|
||||
assert_serialization(InferenceModelType::RvcV2, "rvc_v2");
|
||||
assert_serialization(InferenceModelType::SadTalker, "sad_talker");
|
||||
assert_serialization(InferenceModelType::SoVitsSvc, "so_vits_svc");
|
||||
assert_serialization(InferenceModelType::Tacotron2, "tacotron2");
|
||||
assert_serialization(InferenceModelType::Vits, "vits");
|
||||
assert_serialization(InferenceModelType::VallEX, "vall_e_x");
|
||||
assert_serialization(InferenceModelType::RerenderAVideo, "rerender_a_video");
|
||||
assert_serialization(InferenceModelType::StableDiffusion, "stable_diffusion");
|
||||
assert_serialization(InferenceModelType::MocapNet, "mocap_net");
|
||||
assert_serialization(InferenceModelType::ComfyUi, "comfy_ui");
|
||||
assert_serialization(InferenceModelType::StyleTTS2, "styletts2");
|
||||
mod explicit_checks {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_serialization() {
|
||||
assert_serialization(InferenceModelType::RvcV2, "rvc_v2");
|
||||
assert_serialization(InferenceModelType::SadTalker, "sad_talker");
|
||||
assert_serialization(InferenceModelType::SoVitsSvc, "so_vits_svc");
|
||||
assert_serialization(InferenceModelType::Tacotron2, "tacotron2");
|
||||
assert_serialization(InferenceModelType::Vits, "vits");
|
||||
assert_serialization(InferenceModelType::VallEX, "vall_e_x");
|
||||
assert_serialization(InferenceModelType::RerenderAVideo, "rerender_a_video");
|
||||
assert_serialization(InferenceModelType::StableDiffusion, "stable_diffusion");
|
||||
assert_serialization(InferenceModelType::MocapNet, "mocap_net");
|
||||
assert_serialization(InferenceModelType::ComfyUi, "comfy_ui");
|
||||
assert_serialization(InferenceModelType::StyleTTS2, "styletts2");
|
||||
assert_serialization(InferenceModelType::ConvertFbxToGltf, "convert_fbx_gltf");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn to_str() {
|
||||
assert_eq!(InferenceModelType::RvcV2.to_str(), "rvc_v2");
|
||||
assert_eq!(InferenceModelType::SadTalker.to_str(), "sad_talker");
|
||||
assert_eq!(InferenceModelType::SoVitsSvc.to_str(), "so_vits_svc");
|
||||
assert_eq!(InferenceModelType::Tacotron2.to_str(), "tacotron2");
|
||||
assert_eq!(InferenceModelType::Vits.to_str(), "vits");
|
||||
assert_eq!(InferenceModelType::VallEX.to_str(), "vall_e_x");
|
||||
assert_eq!(InferenceModelType::RerenderAVideo.to_str(), "rerender_a_video");
|
||||
assert_eq!(InferenceModelType::StableDiffusion.to_str(), "stable_diffusion");
|
||||
assert_eq!(InferenceModelType::MocapNet.to_str(), "mocap_net");
|
||||
assert_eq!(InferenceModelType::StyleTTS2.to_str(), "styletts2");
|
||||
assert_eq!(InferenceModelType::ComfyUi.to_str(), "comfy_ui");
|
||||
assert_eq!(InferenceModelType::ConvertFbxToGltf.to_str(), "convert_fbx_gltf");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_str() {
|
||||
assert_eq!(InferenceModelType::from_str("rvc_v2").unwrap(), InferenceModelType::RvcV2);
|
||||
assert_eq!(InferenceModelType::from_str("sad_talker").unwrap(), InferenceModelType::SadTalker);
|
||||
assert_eq!(InferenceModelType::from_str("so_vits_svc").unwrap(), InferenceModelType::SoVitsSvc);
|
||||
assert_eq!(InferenceModelType::from_str("tacotron2").unwrap(), InferenceModelType::Tacotron2);
|
||||
assert_eq!(InferenceModelType::from_str("vits").unwrap(), InferenceModelType::Vits);
|
||||
assert_eq!(InferenceModelType::from_str("vall_e_x").unwrap(), InferenceModelType::VallEX);
|
||||
assert_eq!(InferenceModelType::from_str("rerender_a_video").unwrap(), InferenceModelType::RerenderAVideo);
|
||||
assert_eq!(InferenceModelType::from_str("stable_diffusion").unwrap(), InferenceModelType::StableDiffusion);
|
||||
assert_eq!(InferenceModelType::from_str("mocap_net").unwrap(), InferenceModelType::MocapNet);
|
||||
assert_eq!(InferenceModelType::from_str("styletts2").unwrap(), InferenceModelType::StyleTTS2);
|
||||
assert_eq!(InferenceModelType::from_str("comfy_ui").unwrap(), InferenceModelType::ComfyUi);
|
||||
assert_eq!(InferenceModelType::from_str("convert_fbx_gltf").unwrap(), InferenceModelType::ConvertFbxToGltf);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn all_variants() {
|
||||
// Static check
|
||||
let mut variants = InferenceModelType::all_variants();
|
||||
assert_eq!(variants.len(), 12);
|
||||
assert_eq!(variants.pop_first(), Some(InferenceModelType::RvcV2));
|
||||
assert_eq!(variants.pop_first(), Some(InferenceModelType::SadTalker));
|
||||
assert_eq!(variants.pop_first(), Some(InferenceModelType::SoVitsSvc));
|
||||
assert_eq!(variants.pop_first(), Some(InferenceModelType::Tacotron2));
|
||||
assert_eq!(variants.pop_first(), Some(InferenceModelType::Vits));
|
||||
assert_eq!(variants.pop_first(), Some(InferenceModelType::VallEX));
|
||||
assert_eq!(variants.pop_first(), Some(InferenceModelType::RerenderAVideo));
|
||||
assert_eq!(variants.pop_first(), Some(InferenceModelType::StableDiffusion));
|
||||
assert_eq!(variants.pop_first(), Some(InferenceModelType::MocapNet));
|
||||
assert_eq!(variants.pop_first(), Some(InferenceModelType::ComfyUi));
|
||||
assert_eq!(variants.pop_first(), Some(InferenceModelType::StyleTTS2));
|
||||
assert_eq!(variants.pop_first(), Some(InferenceModelType::ConvertFbxToGltf));
|
||||
assert_eq!(variants.pop_first(), None);
|
||||
|
||||
// Generated check
|
||||
use strum::IntoEnumIterator;
|
||||
assert_eq!(InferenceModelType::all_variants().len(), InferenceModelType::iter().len());
|
||||
}
|
||||
}
|
||||
|
||||
mod mechanical_checks {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn to_str() {
|
||||
assert_eq!(InferenceModelType::RvcV2.to_str(), "rvc_v2");
|
||||
assert_eq!(InferenceModelType::SadTalker.to_str(), "sad_talker");
|
||||
assert_eq!(InferenceModelType::SoVitsSvc.to_str(), "so_vits_svc");
|
||||
assert_eq!(InferenceModelType::Tacotron2.to_str(), "tacotron2");
|
||||
assert_eq!(InferenceModelType::Vits.to_str(), "vits");
|
||||
assert_eq!(InferenceModelType::VallEX.to_str(), "vall_e_x");
|
||||
assert_eq!(InferenceModelType::RerenderAVideo.to_str(), "rerender_a_video");
|
||||
assert_eq!(InferenceModelType::StableDiffusion.to_str(), "stable_diffusion");
|
||||
assert_eq!(InferenceModelType::MocapNet.to_str(), "mocap_net");
|
||||
assert_eq!(InferenceModelType::StyleTTS2.to_str(), "styletts2");
|
||||
assert_eq!(InferenceModelType::ComfyUi.to_str(), "comfy_ui");
|
||||
}
|
||||
#[test]
|
||||
fn variant_length() {
|
||||
use strum::IntoEnumIterator;
|
||||
assert_eq!(InferenceModelType::all_variants().len(), InferenceModelType::iter().len());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_str() {
|
||||
assert_eq!(InferenceModelType::from_str("rvc_v2").unwrap(), InferenceModelType::RvcV2);
|
||||
assert_eq!(InferenceModelType::from_str("sad_talker").unwrap(), InferenceModelType::SadTalker);
|
||||
assert_eq!(InferenceModelType::from_str("so_vits_svc").unwrap(), InferenceModelType::SoVitsSvc);
|
||||
assert_eq!(InferenceModelType::from_str("tacotron2").unwrap(), InferenceModelType::Tacotron2);
|
||||
assert_eq!(InferenceModelType::from_str("vits").unwrap(), InferenceModelType::Vits);
|
||||
assert_eq!(InferenceModelType::from_str("vall_e_x").unwrap(), InferenceModelType::VallEX);
|
||||
assert_eq!(InferenceModelType::from_str("rerender_a_video").unwrap(), InferenceModelType::RerenderAVideo);
|
||||
assert_eq!(InferenceModelType::from_str("stable_diffusion").unwrap(), InferenceModelType::StableDiffusion);
|
||||
assert_eq!(InferenceModelType::from_str("mocap_net").unwrap(), InferenceModelType::MocapNet);
|
||||
assert_eq!(InferenceModelType::from_str("styletts2").unwrap(), InferenceModelType::StyleTTS2);
|
||||
assert_eq!(InferenceModelType::from_str("comfy_ui").unwrap(), InferenceModelType::ComfyUi);
|
||||
}
|
||||
#[test]
|
||||
fn round_trip() {
|
||||
for variant in InferenceModelType::all_variants() {
|
||||
assert_eq!(variant, InferenceModelType::from_str(variant.to_str()).unwrap());
|
||||
assert_eq!(variant, InferenceModelType::from_str(&format!("{}", variant)).unwrap());
|
||||
assert_eq!(variant, InferenceModelType::from_str(&format!("{:?}", variant)).unwrap());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn all_variants() {
|
||||
// Static check
|
||||
let mut variants = InferenceModelType::all_variants();
|
||||
assert_eq!(variants.len(), 11);
|
||||
assert_eq!(variants.pop_first(), Some(InferenceModelType::RvcV2));
|
||||
assert_eq!(variants.pop_first(), Some(InferenceModelType::SadTalker));
|
||||
assert_eq!(variants.pop_first(), Some(InferenceModelType::SoVitsSvc));
|
||||
assert_eq!(variants.pop_first(), Some(InferenceModelType::Tacotron2));
|
||||
assert_eq!(variants.pop_first(), Some(InferenceModelType::Vits));
|
||||
assert_eq!(variants.pop_first(), Some(InferenceModelType::VallEX));
|
||||
assert_eq!(variants.pop_first(), Some(InferenceModelType::RerenderAVideo));
|
||||
assert_eq!(variants.pop_first(), Some(InferenceModelType::StableDiffusion));
|
||||
assert_eq!(variants.pop_first(), Some(InferenceModelType::MocapNet));
|
||||
assert_eq!(variants.pop_first(), Some(InferenceModelType::ComfyUi));
|
||||
assert_eq!(variants.pop_first(), Some(InferenceModelType::StyleTTS2));
|
||||
assert_eq!(variants.pop_first(), None);
|
||||
|
||||
// Generated check
|
||||
use strum::IntoEnumIterator;
|
||||
assert_eq!(InferenceModelType::all_variants().len(), InferenceModelType::iter().len());
|
||||
#[test]
|
||||
fn serialized_length_ok_for_database() {
|
||||
const MAX_LENGTH : usize = 32;
|
||||
for variant in InferenceModelType::all_variants() {
|
||||
let serialized = variant.to_str();
|
||||
assert!(serialized.len() > 0, "variant {:?} is too short", variant);
|
||||
assert!(serialized.len() <= MAX_LENGTH, "variant {:?} is too long", variant);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ use strum::EnumIter;
|
||||
///
|
||||
/// These types are present in the HTTP API and database columns as serialized here.
|
||||
///
|
||||
/// DO NOT CHANGE VALUES WITHOUT A MIGRATION STRATEGY.
|
||||
/// YOU CAN ADD NEW VALUES, BUT DO NOT CHANGE EXISTING VALUES WITHOUT A MIGRATION STRATEGY.
|
||||
#[cfg_attr(test, derive(EnumIter, EnumCount))]
|
||||
#[derive(Clone, Copy, Eq, PartialEq, Hash, Ord, PartialOrd, Deserialize, Serialize)]
|
||||
pub enum InferenceResultType {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
pub mod frontend_failure_category;
|
||||
pub mod inference_category;
|
||||
pub mod inference_input_source_token_type;
|
||||
pub mod inference_job_type;
|
||||
pub mod inference_model_type;
|
||||
pub mod inference_result_type;
|
||||
|
||||
@@ -143,14 +143,14 @@ mod tests {
|
||||
assert_eq!(MediaFileOriginModelType::from_str("mocap_net").unwrap(), MediaFileOriginModelType::MocapNet);
|
||||
assert_eq!(MediaFileOriginModelType::from_str("comfy_ui").unwrap(), MediaFileOriginModelType::ComfyUi);
|
||||
assert_eq!(MediaFileOriginModelType::from_str("styletts2").unwrap(), MediaFileOriginModelType::StyleTTS2);
|
||||
assert_eq!(MediaFileOriginModelType::from_str("stable_diffusion_1_5").unwrap(), MediaFileOriginModelType::StyleTTS2);
|
||||
assert_eq!(MediaFileOriginModelType::from_str("stable_diffusion_1_5").unwrap(), MediaFileOriginModelType::StableDiffusion15);
|
||||
assert!(MediaFileOriginModelType::from_str("foo").is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn all_variants() {
|
||||
let mut variants = MediaFileOriginModelType::all_variants();
|
||||
assert_eq!(variants.len(), 9);
|
||||
assert_eq!(variants.len(), 10);
|
||||
assert_eq!(variants.pop_first(), Some(MediaFileOriginModelType::RvcV2));
|
||||
assert_eq!(variants.pop_first(), Some(MediaFileOriginModelType::SadTalker));
|
||||
assert_eq!(variants.pop_first(), Some(MediaFileOriginModelType::SoVitsSvc));
|
||||
|
||||
@@ -124,7 +124,7 @@ mod tests {
|
||||
assert_serialization(MediaFileOriginProductCategory::ZeroShotVoice, "zs_voice");
|
||||
assert_serialization(MediaFileOriginProductCategory::VideoFilter, "video_filter");
|
||||
assert_serialization(MediaFileOriginProductCategory::Mocap, "mocap");
|
||||
assert_serialization(MediaFileOriginProductCategory::Mocap, "image_gen");
|
||||
assert_serialization(MediaFileOriginProductCategory::ImageGeneration, "image_gen");
|
||||
assert_serialization(MediaFileOriginProductCategory::Workflow, "workflow");
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ use strum::EnumCount;
|
||||
use strum::EnumIter;
|
||||
use utoipa::ToSchema;
|
||||
|
||||
/// Used in the `media_files` table in a `VARCHAR` field.
|
||||
/// Used in the `media_files` table in a `VARCHAR(16)` field.
|
||||
///
|
||||
/// DO NOT CHANGE VALUES WITHOUT A MIGRATION STRATEGY.
|
||||
#[cfg_attr(test, derive(EnumIter, EnumCount))]
|
||||
@@ -23,7 +23,22 @@ pub enum MediaFileType {
|
||||
Video,
|
||||
|
||||
/// Mocap files: BVH, etc.
|
||||
/// NB: In actuality, this is just the BVH file type.
|
||||
/// NB: This is the old type to migrate from.
|
||||
Mocap,
|
||||
|
||||
/// BVH files (for Bevy)
|
||||
/// NB: This is the new type to migrate to.
|
||||
Bvh,
|
||||
|
||||
/// FBX files (for Bevy)
|
||||
Fbx,
|
||||
|
||||
/// glTF binary files (for Bevy)
|
||||
Glb,
|
||||
|
||||
/// glTF files (for Bevy)
|
||||
Gltf,
|
||||
}
|
||||
|
||||
// TODO(bt, 2022-12-21): This desperately needs MySQL integration tests!
|
||||
@@ -39,6 +54,10 @@ impl MediaFileType {
|
||||
Self::Image => "image",
|
||||
Self::Video => "video",
|
||||
Self::Mocap => "mocap",
|
||||
Self::Bvh => "bvh",
|
||||
Self::Fbx => "fbx",
|
||||
Self::Glb => "glb",
|
||||
Self::Gltf => "gltf",
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,6 +67,10 @@ impl MediaFileType {
|
||||
"image" => Ok(Self::Image),
|
||||
"video" => Ok(Self::Video),
|
||||
"mocap" => Ok(Self::Mocap),
|
||||
"bvh" => Ok(Self::Bvh),
|
||||
"fbx" => Ok(Self::Fbx),
|
||||
"glb" => Ok(Self::Glb),
|
||||
"gltf" => Ok(Self::Gltf),
|
||||
_ => Err(format!("invalid value: {:?}", value)),
|
||||
}
|
||||
}
|
||||
@@ -60,6 +83,10 @@ impl MediaFileType {
|
||||
Self::Image,
|
||||
Self::Video,
|
||||
Self::Mocap,
|
||||
Self::Bvh,
|
||||
Self::Fbx,
|
||||
Self::Glb,
|
||||
Self::Gltf,
|
||||
])
|
||||
}
|
||||
}
|
||||
@@ -78,6 +105,10 @@ mod tests {
|
||||
assert_serialization(MediaFileType::Image, "image");
|
||||
assert_serialization(MediaFileType::Video, "video");
|
||||
assert_serialization(MediaFileType::Mocap, "mocap");
|
||||
assert_serialization(MediaFileType::Bvh, "bvh");
|
||||
assert_serialization(MediaFileType::Fbx, "fbx");
|
||||
assert_serialization(MediaFileType::Glb, "glb");
|
||||
assert_serialization(MediaFileType::Gltf, "gltf");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,7 +120,10 @@ mod tests {
|
||||
assert_eq!(MediaFileType::Audio.to_str(), "audio");
|
||||
assert_eq!(MediaFileType::Image.to_str(), "image");
|
||||
assert_eq!(MediaFileType::Video.to_str(), "video");
|
||||
assert_eq!(MediaFileType::Mocap.to_str(), "mocap");
|
||||
assert_eq!(MediaFileType::Bvh.to_str(), "bvh");
|
||||
assert_eq!(MediaFileType::Fbx.to_str(), "fbx");
|
||||
assert_eq!(MediaFileType::Glb.to_str(), "glb");
|
||||
assert_eq!(MediaFileType::Gltf.to_str(), "gltf");
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -98,6 +132,10 @@ mod tests {
|
||||
assert_eq!(MediaFileType::from_str("image").unwrap(), MediaFileType::Image);
|
||||
assert_eq!(MediaFileType::from_str("video").unwrap(), MediaFileType::Video);
|
||||
assert_eq!(MediaFileType::from_str("mocap").unwrap(), MediaFileType::Mocap);
|
||||
assert_eq!(MediaFileType::from_str("bvh").unwrap(), MediaFileType::Bvh);
|
||||
assert_eq!(MediaFileType::from_str("fbx").unwrap(), MediaFileType::Fbx);
|
||||
assert_eq!(MediaFileType::from_str("glb").unwrap(), MediaFileType::Glb);
|
||||
assert_eq!(MediaFileType::from_str("gltf").unwrap(), MediaFileType::Gltf);
|
||||
assert!(MediaFileType::from_str("foo").is_err());
|
||||
}
|
||||
}
|
||||
@@ -108,11 +146,15 @@ mod tests {
|
||||
#[test]
|
||||
fn all_variants() {
|
||||
let mut variants = MediaFileType::all_variants();
|
||||
assert_eq!(variants.len(), 4);
|
||||
assert_eq!(variants.len(), 8);
|
||||
assert_eq!(variants.pop_first(), Some(MediaFileType::Audio));
|
||||
assert_eq!(variants.pop_first(), Some(MediaFileType::Image));
|
||||
assert_eq!(variants.pop_first(), Some(MediaFileType::Video));
|
||||
assert_eq!(variants.pop_first(), Some(MediaFileType::Mocap));
|
||||
assert_eq!(variants.pop_first(), Some(MediaFileType::Bvh));
|
||||
assert_eq!(variants.pop_first(), Some(MediaFileType::Fbx));
|
||||
assert_eq!(variants.pop_first(), Some(MediaFileType::Glb));
|
||||
assert_eq!(variants.pop_first(), Some(MediaFileType::Gltf));
|
||||
assert_eq!(variants.pop_first(), None);
|
||||
}
|
||||
}
|
||||
@@ -134,5 +176,15 @@ mod tests {
|
||||
assert_eq!(variant, MediaFileType::from_str(&format!("{:?}", variant)).unwrap());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn serialized_length_ok_for_database() {
|
||||
const MAX_LENGTH : usize = 16;
|
||||
for variant in MediaFileType::all_variants() {
|
||||
let serialized = variant.to_str();
|
||||
assert!(serialized.len() > 0, "variant {:?} is too short", variant);
|
||||
assert!(serialized.len() <= MAX_LENGTH, "variant {:?} is too long", variant);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,11 +8,6 @@ use errors::AnyhowResult;
|
||||
use crate::bucket_clients::get_bucket_clients;
|
||||
use crate::cli_args::parse_cli_args;
|
||||
use crate::seeding::model_weights::seed_weights;
|
||||
use crate::seeding::media_files::seed_media_seedtool;
|
||||
use crate::seeding::seed_media_files::seed_media_files;
|
||||
use crate::seeding::tts_tacotron2::seed_tts_tacotron2;
|
||||
use crate::seeding::users::seed_user_accounts;
|
||||
use crate::seeding::voice_conversion::seed_voice_conversion;
|
||||
|
||||
pub mod bucket_clients;
|
||||
|
||||
@@ -52,13 +47,13 @@ pub async fn main() -> AnyhowResult<()> {
|
||||
// maybe_elasticsearch = Some(get_elasticsearch_client());
|
||||
// }
|
||||
|
||||
seed_user_accounts(&pool).await?;
|
||||
seed_media_files(&pool, maybe_bucket_clients.as_ref()).await?;
|
||||
// seed_user_accounts(&pool).await?;
|
||||
// seed_media_files(&pool, maybe_bucket_clients.as_ref()).await?;
|
||||
//seed_zero_shot_tts(&pool, maybe_bucket_clients.as_ref()).await?;
|
||||
seed_voice_conversion(&pool).await?;
|
||||
// seed_voice_conversion(&pool).await?;
|
||||
seed_weights(&pool).await?;
|
||||
seed_media_seedtool(&pool).await?;
|
||||
seed_tts_tacotron2(&pool, maybe_bucket_clients.as_ref()).await?;
|
||||
// seed_media_seedtool(&pool).await?;
|
||||
// seed_tts_tacotron2(&pool, maybe_bucket_clients.as_ref()).await?;
|
||||
|
||||
// should seed the weights with a few files for hanashi
|
||||
//seed_weights_files(&pool, maybe_bucket_clients.as_ref()).await?;
|
||||
|
||||
@@ -43,7 +43,7 @@ pub async fn seed_test_videos(mysql_pool: &Pool<MySql>, user_token: UserToken) -
|
||||
}
|
||||
|
||||
pub async fn seed_media_seedtool(mysql_pool: &Pool<MySql>) -> AnyhowResult<()> {
|
||||
info!("Seeding weights...");
|
||||
info!("Seeding media...");
|
||||
|
||||
let user_token = match get_user_token_by_username(HANASHI_USERNAME, mysql_pool).await? {
|
||||
None => {
|
||||
|
||||
@@ -6,7 +6,7 @@ use sqlx::{MySql, Pool};
|
||||
|
||||
use cloud_storage::remote_file_manager::remote_cloud_bucket_details::RemoteCloudBucketDetails;
|
||||
use cloud_storage::remote_file_manager::remote_cloud_file_manager::RemoteCloudFileClient;
|
||||
use cloud_storage::remote_file_manager::weights_descriptor::{WeightsLoRADescriptor, WeightsSD15Descriptor, WeightsWorkflowDescriptor};
|
||||
use cloud_storage::remote_file_manager::weights_descriptor::{WeightsLoRADescriptor, WeightsSD15Descriptor, WeightsSD15CkptDescriptor, WeightsWorkflowDescriptor};
|
||||
use enums::by_table::model_weights::{
|
||||
weights_category::WeightsCategory,
|
||||
weights_types::WeightsType,
|
||||
@@ -363,7 +363,7 @@ pub async fn seed_weights_for_paging(mysql_pool: &Pool<MySql>, user_token: UserT
|
||||
//cached_user_ratings_positive_count,
|
||||
//cached_user_ratings_negative_count,
|
||||
//maybe_cached_user_ratings_ratio: Some(cached_user_ratings_ratio as f32),
|
||||
version,
|
||||
version: version as u32,
|
||||
mysql_pool: &mysql_pool, // replace with actual MySqlPool
|
||||
};
|
||||
|
||||
@@ -623,7 +623,7 @@ Noosphere by skumerz + dalcefoPainting + 饭特稀V08 by zhazhahui345 + GhostMix
|
||||
maybe_last_update_user_token: Some("Last Update User Token".to_string()),
|
||||
original_download_url: Some("https://civitai.com/models/41865/majicmix-fantasy".to_string()),
|
||||
original_filename: Some("majicmixFantasy_v30Vae.safetensors".to_string()),
|
||||
file_size_bytes: metadata1.file_size_bytes as i32,
|
||||
file_size_bytes: metadata1.file_size_bytes,
|
||||
file_checksum_sha2: metadata1.sha256_checksum.to_string(),
|
||||
public_bucket_hash: metadata1.bucket_details.clone().unwrap().object_hash,
|
||||
maybe_public_bucket_prefix: Some(metadata1.bucket_details.clone().unwrap().prefix),
|
||||
@@ -645,7 +645,7 @@ Noosphere by skumerz + dalcefoPainting + 饭特稀V08 by zhazhahui345 + GhostMix
|
||||
maybe_last_update_user_token: Some("<p> Honkai <p>".to_string()),
|
||||
original_download_url: Some("https://civitai.com/models/95243/mihoyo-collection-honkai-impact-3rd-or-honkai-star-rail-or-genshin-impact-or-zenless-zone-zero".to_string()),
|
||||
original_filename: Some("xiawolei-v100-000019.safetensors".to_string()),
|
||||
file_size_bytes: metadata2.file_size_bytes as i32,
|
||||
file_size_bytes: metadata2.file_size_bytes,
|
||||
file_checksum_sha2: metadata2.sha256_checksum.to_string(),
|
||||
public_bucket_hash: metadata2.bucket_details.clone().unwrap().object_hash.clone(),
|
||||
maybe_public_bucket_prefix: Some(metadata2.bucket_details.clone().unwrap().prefix),
|
||||
@@ -667,7 +667,7 @@ Noosphere by skumerz + dalcefoPainting + 饭特稀V08 by zhazhahui345 + GhostMix
|
||||
maybe_last_update_user_token: Some("Last Update User Token".to_string()),
|
||||
original_download_url: Some("https://civitai.com/models/97653/z-vae".to_string()),
|
||||
original_filename: Some("zVae_v20.safetensors".to_string()),
|
||||
file_size_bytes: metadata3.file_size_bytes as i32,
|
||||
file_size_bytes: metadata3.file_size_bytes,
|
||||
file_checksum_sha2: metadata3.sha256_checksum.to_string(),
|
||||
public_bucket_hash: metadata3.bucket_details.clone().unwrap().object_hash,
|
||||
maybe_public_bucket_prefix: Some(metadata3.bucket_details.clone().unwrap().prefix),
|
||||
@@ -686,32 +686,44 @@ Noosphere by skumerz + dalcefoPainting + 饭特稀V08 by zhazhahui345 + GhostMix
|
||||
pub async fn seed_workflows_for_testing_inference(mysql_pool: &Pool<MySql>, user_token: UserToken) -> AnyhowResult<()>{
|
||||
let model_weight_token1 = ModelWeightToken::generate_for_testing_and_dev_seeding_never_use_in_production_seriously();
|
||||
let model_weight_token2 = ModelWeightToken::generate_for_testing_and_dev_seeding_never_use_in_production_seriously();
|
||||
let model_weight_token3 = ModelWeightToken::generate_for_testing_and_dev_seeding_never_use_in_production_seriously();
|
||||
let model_weight_token4 = ModelWeightToken::new_from_str("weight_n8sz47gmfw2zx02snrbz88ns9");
|
||||
|
||||
let mut path_to_comfy = get_seed_tool_data_root();
|
||||
path_to_comfy.push("models/workflows/comfyui/workflow_api.json");
|
||||
path_to_comfy.push("models/workflows/comfyui/yae_video_prod.json");
|
||||
let remote_cloud_file_client = RemoteCloudFileClient::get_remote_cloud_file_client().await?;
|
||||
let comfy_weights_descriptor = Box::new(WeightsWorkflowDescriptor {});
|
||||
let metadata1 = remote_cloud_file_client.upload_file(comfy_weights_descriptor, path_to_comfy.as_path().to_str().unwrap()).await?;
|
||||
|
||||
let mut path_to_comfy2 = get_seed_tool_data_root();
|
||||
path_to_comfy2.push("models/workflows/comfyui/majicmixRealistic_v7.safetensors");
|
||||
let comfy_weights_descriptor2 = Box::new(WeightsSD15Descriptor {});
|
||||
let metadata2 = remote_cloud_file_client.upload_file(comfy_weights_descriptor2, path_to_comfy2.as_path().to_str().unwrap()).await?;
|
||||
// let mut path_to_comfy2 = get_seed_tool_data_root();
|
||||
// path_to_comfy2.push("models/workflows/comfyui/majicmixRealistic_v7.safetensors");
|
||||
// let comfy_weights_descriptor2 = Box::new(WeightsSD15Descriptor {});
|
||||
// let metadata2 = remote_cloud_file_client.upload_file(comfy_weights_descriptor2, path_to_comfy2.as_path().to_str().unwrap()).await?;
|
||||
|
||||
// let mut path_to_comfy3 = get_seed_tool_data_root();
|
||||
// path_to_comfy3.push("models/workflows/comfyui/yae_video_test.json");
|
||||
// let comfy_weights_descriptor3 = Box::new(WeightsWorkflowDescriptor {});
|
||||
// let metadata3 = remote_cloud_file_client.upload_file(comfy_weights_descriptor3, path_to_comfy3.as_path().to_str().unwrap()).await?;
|
||||
|
||||
/* let mut path_to_comfy4 = get_seed_tool_data_root();
|
||||
path_to_comfy4.push("models/workflows/comfyui/v3_sd15_mm.ckpt");
|
||||
let comfy_weights_descriptor4 = Box::new(WeightsSD15CkptDescriptor {});
|
||||
let metadata4 = remote_cloud_file_client.upload_file(comfy_weights_descriptor4, path_to_comfy4.as_path().to_str().unwrap()).await?;*/
|
||||
|
||||
let weights1 = CreateModelWeightsArgs {
|
||||
token: &model_weight_token1, // replace with actual ModelWeightToken
|
||||
weights_type: WeightsType::ComfyUi, // replace with actual WeightsType
|
||||
weights_category: WeightsCategory::WorkflowConfig, // replace with actual WeightsCategory
|
||||
title: "comfy-test-workflow".to_string(),
|
||||
maybe_description_markdown: Some("Test workflow for ComfyUI".to_string()),
|
||||
title: "yae-video-prod".to_string(),
|
||||
maybe_description_markdown: Some("ComfyUI Workflow".to_string()),
|
||||
maybe_description_rendered_html: Some("<p>Description</p>".to_string()),
|
||||
creator_user_token: Some(&user_token), // replace with actual UserToken
|
||||
creator_ip_address: "192.168.1.1",
|
||||
creator_set_visibility: Visibility::Public,
|
||||
maybe_last_update_user_token: Some("Last Update User Token".to_string()),
|
||||
original_download_url: Some("https://github.com/comfyanonymous/ComfyUI".to_string()),
|
||||
original_filename: Some("test-workflow.json".to_string()),
|
||||
file_size_bytes: metadata1.file_size_bytes as i32,
|
||||
original_filename: Some("yae_video_prod.json".to_string()),
|
||||
file_size_bytes: metadata1.file_size_bytes,
|
||||
file_checksum_sha2: metadata1.sha256_checksum.to_string(),
|
||||
public_bucket_hash: metadata1.bucket_details.clone().unwrap().object_hash,
|
||||
maybe_public_bucket_prefix: Some(metadata1.bucket_details.clone().unwrap().prefix),
|
||||
@@ -719,30 +731,76 @@ pub async fn seed_workflows_for_testing_inference(mysql_pool: &Pool<MySql>, user
|
||||
version: 1,
|
||||
mysql_pool: &mysql_pool, // replace with actual MySqlPool
|
||||
};
|
||||
let weights2 = CreateModelWeightsArgs {
|
||||
token: &model_weight_token2, // replace with actual ModelWeightToken
|
||||
weights_type: WeightsType::ComfyUi, // replace with actual WeightsType
|
||||
weights_category: WeightsCategory::WorkflowConfig, // replace with actual WeightsCategory
|
||||
title: "v1-5-pruned-emaonly".to_string(),
|
||||
maybe_description_markdown: Some("Test model for ComfyUI".to_string()),
|
||||
maybe_description_rendered_html: Some("<p>Description</p>".to_string()),
|
||||
creator_user_token: Some(&user_token), // replace with actual UserToken
|
||||
creator_ip_address: "192.168.1.1",
|
||||
creator_set_visibility: Visibility::Public,
|
||||
maybe_last_update_user_token: Some("Last Update User Token".to_string()),
|
||||
original_download_url: Some("https://huggingface.co/runwayml/stable-diffusion-v1-5".to_string()),
|
||||
original_filename: Some("v1-5-pruned-emaonly.ckpt".to_string()),
|
||||
file_size_bytes: metadata2.file_size_bytes as i32,
|
||||
file_checksum_sha2: metadata2.sha256_checksum.to_string(),
|
||||
public_bucket_hash: metadata2.bucket_details.clone().unwrap().object_hash,
|
||||
maybe_public_bucket_prefix: Some(metadata2.bucket_details.clone().unwrap().prefix),
|
||||
maybe_public_bucket_extension: Some(metadata2.bucket_details.clone().unwrap().suffix),
|
||||
version: 1,
|
||||
mysql_pool: &mysql_pool, // replace with actual MySqlPool
|
||||
};
|
||||
// let weights2 = CreateModelWeightsArgs {
|
||||
// token: &model_weight_token2, // replace with actual ModelWeightToken
|
||||
// weights_type: WeightsType::ComfyUi, // replace with actual WeightsType
|
||||
// weights_category: WeightsCategory::WorkflowConfig, // replace with actual WeightsCategory
|
||||
// title: "v1-5-pruned-emaonly".to_string(),
|
||||
// maybe_description_markdown: Some("Test model for ComfyUI".to_string()),
|
||||
// maybe_description_rendered_html: Some("<p>Description</p>".to_string()),
|
||||
// creator_user_token: Some(&user_token), // replace with actual UserToken
|
||||
// creator_ip_address: "192.168.1.1",
|
||||
// creator_set_visibility: Visibility::Public,
|
||||
// maybe_last_update_user_token: Some("Last Update User Token".to_string()),
|
||||
// original_download_url: Some("https://huggingface.co/runwayml/stable-diffusion-v1-5".to_string()),
|
||||
// original_filename: Some("v1-5-pruned-emaonly.ckpt".to_string()),
|
||||
// file_size_bytes: metadata2.file_size_bytes as i32,
|
||||
// file_checksum_sha2: metadata2.sha256_checksum.to_string(),
|
||||
// public_bucket_hash: metadata2.bucket_details.clone().unwrap().object_hash,
|
||||
// maybe_public_bucket_prefix: Some(metadata2.bucket_details.clone().unwrap().prefix),
|
||||
// maybe_public_bucket_extension: Some(metadata2.bucket_details.clone().unwrap().suffix),
|
||||
// version: 1,
|
||||
// mysql_pool: &mysql_pool, // replace with actual MySqlPool
|
||||
// };
|
||||
|
||||
// let weights3 = CreateModelWeightsArgs {
|
||||
// token: &model_weight_token3, // replace with actual ModelWeightToken
|
||||
// weights_type: WeightsType::ComfyUi, // replace with actual WeightsType
|
||||
// weights_category: WeightsCategory::WorkflowConfig, // replace with actual WeightsCategory
|
||||
// title: "yae-video-test".to_string(),
|
||||
// maybe_description_markdown: Some("Test workflow for ComfyUI".to_string()),
|
||||
// maybe_description_rendered_html: Some("<p>Description</p>".to_string()),
|
||||
// creator_user_token: Some(&user_token), // replace with actual UserToken
|
||||
// creator_ip_address: "192.168.1.1",
|
||||
// creator_set_visibility: Visibility::Public,
|
||||
// maybe_last_update_user_token: Some("Last Update User Token".to_string()),
|
||||
// original_download_url: Some("https://github.com/comfyanonymous/ComfyUI".to_string()),
|
||||
// original_filename: Some("yae-video-test.json".to_string()),
|
||||
// file_size_bytes: metadata3.file_size_bytes as i32,
|
||||
// file_checksum_sha2: metadata3.sha256_checksum.to_string(),
|
||||
// public_bucket_hash: metadata3.bucket_details.clone().unwrap().object_hash,
|
||||
// maybe_public_bucket_prefix: Some(metadata3.bucket_details.clone().unwrap().prefix),
|
||||
// maybe_public_bucket_extension: Some(metadata3.bucket_details.clone().unwrap().suffix),
|
||||
// version: 1,
|
||||
// mysql_pool: &mysql_pool, // replace with actual MySqlPool
|
||||
// };
|
||||
|
||||
// let weights4 = CreateModelWeightsArgs {
|
||||
// token: &model_weight_token4, // replace with actual ModelWeightToken
|
||||
// weights_type: WeightsType::ComfyUi, // replace with actual WeightsType
|
||||
// weights_category: WeightsCategory::WorkflowConfig, // replace with actual WeightsCategory
|
||||
// title: "v3_sd15_mm".to_string(),
|
||||
// maybe_description_markdown: Some("ComfyUI motion module".to_string()),
|
||||
// maybe_description_rendered_html: Some("<p>Description</p>".to_string()),
|
||||
// creator_user_token: Some(&user_token), // replace with actual UserToken
|
||||
// creator_ip_address: "192.168.1.1",
|
||||
// creator_set_visibility: Visibility::Public,
|
||||
// maybe_last_update_user_token: Some("Last Update User Token".to_string()),
|
||||
// original_download_url: Some("https://huggingface.co/guoyww/animatediff/blob/main/v3_sd15_mm.ckpt".to_string()),
|
||||
// original_filename: Some("v3_sd15_mm.ckpt".to_string()),
|
||||
// file_size_bytes: metadata4.file_size_bytes as i32,
|
||||
// file_checksum_sha2: metadata4.sha256_checksum.to_string(),
|
||||
// public_bucket_hash: metadata4.bucket_details.clone().unwrap().object_hash,
|
||||
// maybe_public_bucket_prefix: Some(metadata4.bucket_details.clone().unwrap().prefix),
|
||||
// maybe_public_bucket_extension: Some(metadata4.bucket_details.clone().unwrap().suffix),
|
||||
// version: 1,
|
||||
// mysql_pool: &mysql_pool, // replace with actual MySqlPool
|
||||
// };
|
||||
|
||||
create_weight(weights1).await?;
|
||||
create_weight(weights2).await?;
|
||||
// create_weight(weights2).await?;
|
||||
// create_weight(weights3).await?;
|
||||
// create_weight(weights4).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -1061,9 +1119,9 @@ pub async fn seed_weights(mysql_pool: &Pool<MySql>) -> AnyhowResult<()> {
|
||||
// original_seed_weights(mysql_pool,user_token).await?;
|
||||
// seed_weights_for_user_token(mysql_pool, user_token).await?;
|
||||
// seed_weights_for_paging(mysql_pool,user_token).await?;
|
||||
seed_weights_for_testing_inference(mysql_pool,user_token.clone()).await?;
|
||||
println!("TESTING DOWLOAD");
|
||||
test_seed_weights_files().await?;
|
||||
// seed_weights_for_testing_inference(mysql_pool,user_token.clone()).await?;
|
||||
// println!("TESTING DOWLOAD");
|
||||
// test_seed_weights_files().await?;
|
||||
seed_workflows_for_testing_inference(mysql_pool,user_token.clone()).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -137,7 +137,7 @@ pub async fn seed_weights_files(
|
||||
maybe_last_update_user_token: None, // Assuming there is no last update user token
|
||||
original_download_url: Some(original_download_url.to_string()),
|
||||
original_filename: Some(original_filename.to_string()),
|
||||
file_size_bytes: file_size_bytes as i32, // Assuming the file size is 0
|
||||
file_size_bytes, // Assuming the file size is 0
|
||||
file_checksum_sha2: sha256_checksum.to_string(), // Assuming the file checksum is an empty string
|
||||
public_bucket_hash: bucket_hash,
|
||||
maybe_public_bucket_prefix: Some(maybe_bucket_prefix.to_string()),
|
||||
|
||||
+6
-1
@@ -31,7 +31,12 @@ impl RemoteCloudBucketDetails {
|
||||
match self.prefix.as_str() {
|
||||
// Weights
|
||||
"loRA" => Box::new(weights_descriptor::WeightsLoRADescriptor {}),
|
||||
"sd15" => Box::new(weights_descriptor::WeightsSD15Descriptor {}),
|
||||
"sd15" =>
|
||||
match self.suffix.as_str() {
|
||||
"safetensors" => Box::new(weights_descriptor::WeightsSD15Descriptor {}),
|
||||
"ckpt" => Box::new(weights_descriptor::WeightsSD15CkptDescriptor {}),
|
||||
_ => panic!("Unknown suffix: {}",self.suffix)
|
||||
},
|
||||
"sdxl" => Box::new(weights_descriptor::WeightsSDXLDescriptor {}),
|
||||
"valle_prompt" => Box::new(weights_descriptor::WeightsVallePromptDescriptor {}),
|
||||
"rvc" => {
|
||||
|
||||
@@ -50,6 +50,30 @@ impl FileDescriptor for WeightsSD15Descriptor {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct WeightsSD15CkptDescriptor;
|
||||
|
||||
impl FileDescriptor for WeightsSD15CkptDescriptor {
|
||||
fn remote_directory_path(&self) -> &str {
|
||||
REMOTE_FILE_DIRECTORY
|
||||
}
|
||||
// this will be the type of file peroid is handled by the file formatter
|
||||
// e.g safetensors bin jpg
|
||||
fn get_suffix(&self)->String {
|
||||
"ckpt".to_string()
|
||||
}
|
||||
// This will be the prefix of the media type or the weights type.
|
||||
// name of the weights or the name of the media type
|
||||
// vall-e_prompt, SD15, sd15, sdxl when implmenting add to the end
|
||||
fn get_prefix(&self)->String {
|
||||
"sd15".to_string()
|
||||
}
|
||||
|
||||
// This will be ensure that the right bucket is picked
|
||||
fn is_public(&self) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub struct WeightsSDXLDescriptor;
|
||||
|
||||
@@ -189,4 +213,4 @@ impl FileDescriptor for WeightsWorkflowDescriptor {
|
||||
fn is_public(&self) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,10 @@ pub enum EnvError {
|
||||
reason: String
|
||||
},
|
||||
/// The required environment variable wasn't present.
|
||||
RequiredNotPresent,
|
||||
RequiredNotPresent {
|
||||
/// The name of the missing environment variable.
|
||||
name: String
|
||||
},
|
||||
}
|
||||
|
||||
impl Display for EnvError {
|
||||
@@ -21,7 +24,19 @@ impl Display for EnvError {
|
||||
let reason = match self {
|
||||
EnvError::NotUnicode => "EnvError::NotUnicode",
|
||||
EnvError::ParseError { .. } => "EnvError::ParseError",
|
||||
EnvError::RequiredNotPresent => "EnvError::RequiredNotPresent",
|
||||
EnvError::RequiredNotPresent { name } =>
|
||||
return write!(f, r#"
|
||||
EnvError::RequiredNotPresent: the following environment variable was not present:
|
||||
|
||||
---> {:?}
|
||||
|
||||
In development, setting it in the environment config files: .env, .env-secrets,
|
||||
cargo/service/../{{app_name}}/config/{{app_name}}.development.env, etc.
|
||||
|
||||
If it looks like it might be a secret, ask for help in our company Discord.
|
||||
|
||||
In production, make sure these environment variables are set in Kubernetes.
|
||||
"#, name),
|
||||
};
|
||||
write!(f, "{:?}", reason)
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@ pub fn get_env_bool_required(env_name: &str) -> Result<bool, EnvError> {
|
||||
.and_then(|maybe| match maybe {
|
||||
None => {
|
||||
warn!("Env var '{}' not supplied.", env_name);
|
||||
Err(EnvError::RequiredNotPresent)
|
||||
Err(EnvError::RequiredNotPresent { name: env_name.to_string() })
|
||||
},
|
||||
Some(val) => Ok(val),
|
||||
})
|
||||
|
||||
@@ -12,7 +12,7 @@ pub fn get_env_duration_seconds_required(env_name: &str) -> Result<Duration, Env
|
||||
.and_then(|maybe| match maybe {
|
||||
None => {
|
||||
warn!("Env var '{}' not supplied.", env_name);
|
||||
Err(EnvError::RequiredNotPresent)
|
||||
Err(EnvError::RequiredNotPresent { name: env_name.to_string() })
|
||||
},
|
||||
Some(val) => Ok(val),
|
||||
})
|
||||
|
||||
@@ -15,7 +15,7 @@ pub fn get_env_pathbuf_required(env_name: &str) -> Result<PathBuf, EnvError> {
|
||||
.and_then(|maybe| match maybe {
|
||||
None => {
|
||||
warn!("Env var '{}' not supplied.", env_name);
|
||||
Err(EnvError::RequiredNotPresent)
|
||||
Err(EnvError::RequiredNotPresent { name: env_name.to_string() })
|
||||
},
|
||||
Some(val) => Ok(val),
|
||||
})
|
||||
|
||||
@@ -32,7 +32,7 @@ pub fn get_env_string_required(env_name: &str) -> Result<String, EnvError> {
|
||||
Some(s) => Ok(s.to_string()),
|
||||
None => {
|
||||
warn!("Required env var '{}' not supplied.", env_name);
|
||||
Err(EnvError::RequiredNotPresent)
|
||||
Err(EnvError::RequiredNotPresent { name: env_name.to_string() })
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
+7
-1
@@ -54,6 +54,10 @@ pub enum InferenceCategoryAbbreviated {
|
||||
#[serde(rename = "wf")] // NB: DO NOT CHANGE. It could break live jobs. Renamed to be fewer bytes.
|
||||
#[serde(alias = "workflow")]
|
||||
Workflow,
|
||||
|
||||
#[serde(rename = "fc")] // NB: DO NOT CHANGE. It could break live jobs. Renamed to be fewer bytes.
|
||||
#[serde(alias = "format_conversion")]
|
||||
FormatConversion,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Copy, Debug)]
|
||||
@@ -138,6 +142,7 @@ impl InferenceCategoryAbbreviated {
|
||||
InferenceCategory::ImageGeneration => Self::ImageGeneration,
|
||||
InferenceCategory::Mocap => Self::Mocap,
|
||||
InferenceCategory::Workflow => Self::Workflow,
|
||||
InferenceCategory::FormatConversion => Self::FormatConversion,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -150,6 +155,7 @@ impl InferenceCategoryAbbreviated {
|
||||
Self::ImageGeneration =>InferenceCategory::ImageGeneration,
|
||||
Self::Mocap => InferenceCategory::Mocap,
|
||||
Self::Workflow => InferenceCategory::Workflow,
|
||||
Self::FormatConversion => InferenceCategory::FormatConversion,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -250,7 +256,7 @@ mod tests {
|
||||
};
|
||||
|
||||
let json = serde_json::ser::to_string(&args).unwrap();
|
||||
assert_eq!(json, r#"{"cat":"ig","args":{"Ig":{"sd":"sd_model_token","lm":"lora_model_token","w":512,"h":512,"s":"sampler","p":"prompt","np":"n_prompt","se":1,"mu":"upload_path","cf":7,"lu":"lora_upload_path","sa":25,"bc":1,"t":"type_of_inference","de":"Option","na":"Model Name"}}}"#.to_string());
|
||||
assert_eq!(json, r#"{"cat":"ig","args":{"Ig":{"sd":"sd_model_token","lm":"lora_model_token","w":512,"h":512,"s":"sampler","p":"prompt","np":"n_prompt","se":1,"mu":"upload_path","cf":7,"lu":"lora_upload_path","sa":25,"bc":1,"t":"type_of_inference","de":"Option","na":"Model Name","ve":0}}}"#.to_string());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use std::collections::HashMap;
|
||||
use tokens::tokens::media_files::MediaFileToken;
|
||||
use tokens::tokens::model_weights::ModelWeightToken;
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
@@ -27,4 +28,12 @@ pub struct WorkflowArgs {
|
||||
#[serde(rename = "json_modifications")]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub maybe_json_modifications: Option<HashMap<String, NewValue>>,
|
||||
|
||||
#[serde(rename = "in")]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub maybe_input_file: Option<MediaFileToken>,
|
||||
|
||||
#[serde(rename = "out")]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub maybe_output_path: Option<String>,
|
||||
}
|
||||
|
||||
+57
-135
@@ -88,18 +88,10 @@ pub async fn list_available_generic_inference_jobs(
|
||||
)
|
||||
-> AnyhowResult<Vec<AvailableInferenceJob>>
|
||||
{
|
||||
let model_types = args.maybe_scope_by_model_type
|
||||
.map(|types| types.clone())
|
||||
.unwrap_or(InferenceModelType::all_variants()); // NB: All model types
|
||||
|
||||
let inference_categories = args.maybe_scope_by_job_category
|
||||
.map(|types| types.clone())
|
||||
.unwrap_or(InferenceCategory::all_variants()); // NB: All categories
|
||||
|
||||
let query = if args.sort_by_priority {
|
||||
list_sorted_by_priority(args, model_types, inference_categories).await
|
||||
list_sorted_by_priority(args).await
|
||||
} else {
|
||||
list_sorted_by_id(args, model_types, inference_categories).await
|
||||
list_sorted_by_id(args).await
|
||||
};
|
||||
|
||||
let job_records = query?;
|
||||
@@ -156,16 +148,43 @@ pub async fn list_available_generic_inference_jobs(
|
||||
Ok(job_records)
|
||||
}
|
||||
|
||||
async fn list_sorted_by_id(args: ListAvailableGenericInferenceJobArgs<'_>, model_types: BTreeSet<InferenceModelType>, inference_categories: BTreeSet<InferenceCategory>) -> Result<Vec<AvailableInferenceJobRawInternal>, sqlx::Error> {
|
||||
async fn list_sorted_by_id(args: ListAvailableGenericInferenceJobArgs<'_>) -> Result<Vec<AvailableInferenceJobRawInternal>, sqlx::Error> {
|
||||
// NB: Can't be type checked because of WHERE IN clause with dynamic contents
|
||||
let mut query = core_query(&args);
|
||||
|
||||
// Also had to remove the following typing:
|
||||
//id as `id: crate::queries::generic_inference::job::_keys::GenericInferenceJobId`,
|
||||
//token AS `inference_job_token: tokens::jobs::inference::InferenceJobToken`,
|
||||
//inference_type as `inference_type: enums::workers::generic_inference_type::GenericInferenceType`,
|
||||
//creator_set_visibility as `creator_set_visibility: enums::common::visibility::Visibility`,
|
||||
//status as `status: crate::column_types::job_status::JobStatus`,
|
||||
query.push_str(r#"
|
||||
ORDER BY id ASC
|
||||
LIMIT ?
|
||||
"#);
|
||||
|
||||
let query = sqlx::query_as::<_, AvailableInferenceJobRawInternal>(&query)
|
||||
.bind(args.is_debug_worker)
|
||||
.bind(args.num_records);
|
||||
|
||||
query.fetch_all(args.mysql_pool)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn list_sorted_by_priority(args: ListAvailableGenericInferenceJobArgs<'_>) -> Result<Vec<AvailableInferenceJobRawInternal>, sqlx::Error> {
|
||||
// NB: Can't be type checked because of WHERE IN clause with dynamic contents
|
||||
let mut query = core_query(&args);
|
||||
|
||||
query.push_str(r#"
|
||||
ORDER BY priority_level DESC, id ASC
|
||||
LIMIT ?
|
||||
"#);
|
||||
|
||||
let query = sqlx::query_as::<_, AvailableInferenceJobRawInternal>(&query)
|
||||
.bind(args.is_debug_worker)
|
||||
.bind(args.num_records);
|
||||
|
||||
query.fetch_all(args.mysql_pool)
|
||||
.await
|
||||
}
|
||||
|
||||
// TODO(bt,2024-01-25): Make QueryBuilder
|
||||
fn core_query(args: &ListAvailableGenericInferenceJobArgs<'_>) -> String {
|
||||
// NB: Can't be type checked because of WHERE IN clause with dynamic contents
|
||||
let mut query = r#"
|
||||
SELECT
|
||||
id,
|
||||
@@ -207,24 +226,9 @@ SELECT
|
||||
retry_at,
|
||||
NOW() as database_clock
|
||||
|
||||
FROM generic_inference_jobs"#.to_string();
|
||||
FROM generic_inference_jobs
|
||||
|
||||
query.push_str(&format!(r#"
|
||||
WHERE
|
||||
(
|
||||
maybe_model_type IN ({})
|
||||
)
|
||||
"#, model_type_predicate(&model_types)));
|
||||
|
||||
query.push_str(&format!(r#"
|
||||
and
|
||||
(
|
||||
inference_category IN ({})
|
||||
)
|
||||
"#, inference_category_predicate(&inference_categories)));
|
||||
|
||||
query.push_str(r#"
|
||||
AND
|
||||
WHERE
|
||||
(
|
||||
status IN ("pending", "attempt_failed")
|
||||
)
|
||||
@@ -238,111 +242,29 @@ FROM generic_inference_jobs"#.to_string();
|
||||
(
|
||||
is_debug_request = ?
|
||||
)
|
||||
ORDER BY id ASC
|
||||
LIMIT ?
|
||||
"#);
|
||||
"#.to_string();
|
||||
|
||||
let query = sqlx::query_as::<_, AvailableInferenceJobRawInternal>(&query)
|
||||
.bind(args.is_debug_worker)
|
||||
.bind(args.num_records);
|
||||
if let Some(model_types) = args.maybe_scope_by_model_type {
|
||||
query.push_str(&format!(r#"
|
||||
AND
|
||||
(
|
||||
maybe_model_type IN ({})
|
||||
)
|
||||
"#, model_type_predicate(model_types)));
|
||||
}
|
||||
|
||||
query.fetch_all(args.mysql_pool)
|
||||
.await
|
||||
if let Some(inference_categories) = args.maybe_scope_by_job_category {
|
||||
query.push_str(&format!(r#"
|
||||
AND
|
||||
(
|
||||
inference_category IN ({})
|
||||
)
|
||||
"#, inference_category_predicate(&inference_categories)));
|
||||
}
|
||||
|
||||
query
|
||||
}
|
||||
|
||||
async fn list_sorted_by_priority(args: ListAvailableGenericInferenceJobArgs<'_>, model_types: BTreeSet<InferenceModelType>, inference_categories: BTreeSet<InferenceCategory>) -> Result<Vec<AvailableInferenceJobRawInternal>, sqlx::Error> {
|
||||
// NB: Can't be type checked because of WHERE IN clause with dynamic contents
|
||||
|
||||
// Also had to remove the following typing:
|
||||
//id as `id: crate::queries::generic_inference::job::_keys::GenericInferenceJobId`,
|
||||
//token AS `inference_job_token: tokens::jobs::inference::InferenceJobToken`,
|
||||
//inference_category as `inference_category: enums::workers::generic_inference_type::GenericInferenceType`,
|
||||
//creator_set_visibility as `creator_set_visibility: enums::common::visibility::Visibility`,
|
||||
//status as `status: crate::column_types::job_status::JobStatus`,
|
||||
|
||||
let mut query = r#"
|
||||
SELECT
|
||||
id,
|
||||
token as inference_job_token,
|
||||
uuid_idempotency_token,
|
||||
|
||||
inference_category,
|
||||
maybe_model_type,
|
||||
maybe_model_token,
|
||||
|
||||
maybe_input_source_token,
|
||||
maybe_input_source_token_type,
|
||||
|
||||
maybe_inference_args,
|
||||
maybe_raw_inference_text,
|
||||
|
||||
maybe_creator_user_token,
|
||||
maybe_creator_anonymous_visitor_token,
|
||||
creator_ip_address,
|
||||
creator_set_visibility,
|
||||
|
||||
status,
|
||||
attempt_count,
|
||||
|
||||
priority_level,
|
||||
is_keepalive_required,
|
||||
|
||||
is_from_premium_user,
|
||||
is_from_api_user,
|
||||
is_for_twitch,
|
||||
|
||||
max_duration_seconds,
|
||||
|
||||
is_debug_request,
|
||||
maybe_routing_tag,
|
||||
|
||||
created_at,
|
||||
updated_at,
|
||||
retry_at,
|
||||
NOW() as database_clock
|
||||
|
||||
FROM generic_inference_jobs"#.to_string();
|
||||
|
||||
query.push_str(&format!(r#"
|
||||
WHERE
|
||||
(
|
||||
maybe_model_type IN ({})
|
||||
)
|
||||
"#, model_type_predicate(&model_types)));
|
||||
|
||||
query.push_str(&format!(r#"
|
||||
and
|
||||
(
|
||||
inference_category IN ({})
|
||||
)
|
||||
"#, inference_category_predicate(&inference_categories)));
|
||||
|
||||
query.push_str(r#"
|
||||
AND
|
||||
(
|
||||
status IN ("pending", "attempt_failed")
|
||||
)
|
||||
AND
|
||||
(
|
||||
retry_at IS NULL
|
||||
OR
|
||||
retry_at < CURRENT_TIMESTAMP
|
||||
)
|
||||
AND
|
||||
(
|
||||
is_debug_request = ?
|
||||
)
|
||||
ORDER BY priority_level DESC, id ASC
|
||||
LIMIT ?
|
||||
"#);
|
||||
|
||||
let query = sqlx::query_as::<_, AvailableInferenceJobRawInternal>(&query)
|
||||
.bind(args.is_debug_worker)
|
||||
.bind(args.num_records);
|
||||
|
||||
query.fetch_all(args.mysql_pool)
|
||||
.await
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(sqlx::FromRow)]
|
||||
|
||||
@@ -182,6 +182,7 @@ fn raw_record_to_public_result(record: RawGenericInferenceJobStatus) -> GenericI
|
||||
InferenceCategory::ImageGeneration => Some("Image Generation"),
|
||||
InferenceCategory::Mocap => Some("Mocap"),
|
||||
InferenceCategory::Workflow => Some("Workflow"),
|
||||
InferenceCategory::FormatConversion => Some("format conversion"),
|
||||
};
|
||||
|
||||
// NB: A bit of a hack. We store TTS results with a full path.
|
||||
@@ -194,6 +195,7 @@ fn raw_record_to_public_result(record: RawGenericInferenceJobStatus) -> GenericI
|
||||
InferenceCategory::ImageGeneration => (true, record.maybe_media_file_public_bucket_directory_hash.as_deref()),
|
||||
InferenceCategory::Mocap => (true, record.maybe_media_file_public_bucket_directory_hash.as_deref()),
|
||||
InferenceCategory::Workflow => (true, record.maybe_media_file_public_bucket_directory_hash.as_deref()),
|
||||
InferenceCategory::FormatConversion => (true, record.maybe_media_file_public_bucket_directory_hash.as_deref()),
|
||||
};
|
||||
|
||||
// NB: We've moved voice conversion out of their own table and into media_files
|
||||
|
||||
+10
@@ -3,6 +3,7 @@ use sqlx::MySqlPool;
|
||||
|
||||
use enums::by_table::generic_inference_jobs::inference_category::InferenceCategory;
|
||||
use enums::by_table::generic_inference_jobs::inference_input_source_token_type::InferenceInputSourceTokenType;
|
||||
use enums::by_table::generic_inference_jobs::inference_job_type::InferenceJobType;
|
||||
use enums::by_table::generic_inference_jobs::inference_model_type::InferenceModelType;
|
||||
use enums::common::visibility::Visibility;
|
||||
use errors::AnyhowResult;
|
||||
@@ -15,6 +16,11 @@ use crate::payloads::generic_inference_args::generic_inference_args::GenericInfe
|
||||
pub struct InsertGenericInferenceArgs<'a> {
|
||||
pub uuid_idempotency_token: &'a str,
|
||||
|
||||
// NB: This will eventually take the place of "inference category" and "maybe model type", since the latter two are
|
||||
// used entirely inconsistently for job dispatching (especially "inference category"). This should always be 1:1 with
|
||||
// a concrete job type.
|
||||
pub job_type: InferenceJobType,
|
||||
|
||||
pub inference_category: InferenceCategory,
|
||||
pub maybe_model_type: Option<InferenceModelType>,
|
||||
pub maybe_model_token: Option<&'a str>,
|
||||
@@ -68,6 +74,8 @@ SET
|
||||
token = ?,
|
||||
uuid_idempotency_token = ?,
|
||||
|
||||
job_type = ?,
|
||||
|
||||
inference_category = ?,
|
||||
maybe_model_type = ?,
|
||||
maybe_model_token = ?,
|
||||
@@ -97,6 +105,8 @@ SET
|
||||
job_token.as_str(),
|
||||
args.uuid_idempotency_token,
|
||||
|
||||
args.job_type.to_str(),
|
||||
|
||||
args.inference_category.to_str(),
|
||||
|
||||
args.maybe_model_type.map(|t| t.to_str()),
|
||||
|
||||
+7
-19
@@ -1,23 +1,20 @@
|
||||
use anyhow::anyhow;
|
||||
use enums::common::visibility::Visibility;
|
||||
use sqlx;
|
||||
use sqlx::MySqlPool;
|
||||
|
||||
use enums::by_table::generic_synthetic_ids::id_category::IdCategory;
|
||||
use enums::by_table::media_files::media_file_origin_category::{MediaFileOriginCategory, self};
|
||||
use enums::by_table::media_files::media_file_origin_category::MediaFileOriginCategory;
|
||||
use enums::by_table::media_files::media_file_origin_model_type::MediaFileOriginModelType;
|
||||
use enums::by_table::media_files::media_file_origin_product_category::MediaFileOriginProductCategory;
|
||||
use enums::by_table::media_files::media_file_type::MediaFileType;
|
||||
|
||||
use errors::AnyhowResult;
|
||||
use tokens::tokens::anonymous_visitor_tracking::AnonymousVisitorTrackingToken;
|
||||
use tokens::tokens::media_files::MediaFileToken;
|
||||
use tokens::tokens::model_weights::ModelWeightToken;
|
||||
use tokens::tokens::users::UserToken;
|
||||
|
||||
use crate::queries::generic_inference::job::list_available_generic_inference_jobs::AvailableInferenceJob;
|
||||
use crate::queries::generic_synthetic_ids::transactional_increment_generic_synthetic_id::transactional_increment_generic_synthetic_id;
|
||||
use crate::queries::media_files::create::insert_media_file_from_file_upload::UploadType;
|
||||
|
||||
// thought about this it seems like this can be a bit more geneneric instead of having this we can ...
|
||||
pub struct InsertArgs<'a> {
|
||||
pub pool: &'a MySqlPool,
|
||||
@@ -51,13 +48,6 @@ pub struct InsertArgs<'a> {
|
||||
pub maybe_public_bucket_extension: Option<&'a str>,
|
||||
pub extra_file_modification_info: Option<&'a str>, // Assuming TEXT type can be represented by &str
|
||||
|
||||
// Creator information
|
||||
pub maybe_creator_user_token: Option<&'a UserToken>,
|
||||
pub maybe_creator_anonymous_visitor_token: Option<&'a AnonymousVisitorTrackingToken>,
|
||||
|
||||
pub creator_ip_address: &'a str,
|
||||
pub creator_set_visibility: Visibility,
|
||||
|
||||
pub maybe_creator_file_synthetic_id_category: IdCategory,
|
||||
pub maybe_creator_category_synthetic_id_category: IdCategory,
|
||||
|
||||
@@ -167,11 +157,11 @@ pub async fn insert_media_file_generic(
|
||||
args.maybe_public_bucket_extension,
|
||||
args.extra_file_modification_info,
|
||||
|
||||
args.maybe_creator_user_token.map(|e| e.to_string()),
|
||||
args.maybe_creator_anonymous_visitor_token.map(|e| e.to_string()),
|
||||
args.job.maybe_creator_user_token.as_deref(),
|
||||
args.job.maybe_creator_anonymous_visitor_token.as_deref(),
|
||||
|
||||
args.creator_ip_address,
|
||||
args.creator_set_visibility.to_str(),
|
||||
args.job.creator_ip_address,
|
||||
args.job.creator_set_visibility.to_str(),
|
||||
|
||||
maybe_creator_file_synthetic_id,
|
||||
maybe_creator_category_synthetic_id,
|
||||
@@ -183,9 +173,7 @@ pub async fn insert_media_file_generic(
|
||||
).execute(&mut *transaction).await;
|
||||
|
||||
let record_id = match query_result {
|
||||
Ok(res) => {
|
||||
res.last_insert_id()
|
||||
},
|
||||
Ok(res) => res.last_insert_id(),
|
||||
Err(err) => {
|
||||
// TODO: handle better
|
||||
//transaction.rollback().await?;
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
// NB: Incrementally getting rid of build warnings...
|
||||
#![forbid(unused_imports)]
|
||||
#![forbid(unused_mut)]
|
||||
#![forbid(unused_variables)]
|
||||
|
||||
use anyhow::anyhow;
|
||||
use sqlx::MySqlPool;
|
||||
|
||||
use enums::by_table::media_files::media_file_type::MediaFileType;
|
||||
use enums::common::visibility::Visibility;
|
||||
use errors::AnyhowResult;
|
||||
use tokens::tokens::media_files::MediaFileToken;
|
||||
use tokens::tokens::users::UserToken;
|
||||
|
||||
#[derive(Serialize, Debug)]
|
||||
pub struct MediaFileForInference {
|
||||
pub token: MediaFileToken,
|
||||
|
||||
pub media_type: MediaFileType,
|
||||
|
||||
pub maybe_creator_user_token: Option<UserToken>,
|
||||
pub creator_set_visibility: Visibility,
|
||||
|
||||
pub public_bucket_directory_hash: String,
|
||||
pub maybe_public_bucket_prefix: Option<String>,
|
||||
pub maybe_public_bucket_extension: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct MediaFileRaw {
|
||||
pub token: MediaFileToken,
|
||||
|
||||
pub media_type: MediaFileType,
|
||||
|
||||
pub maybe_creator_user_token: Option<UserToken>,
|
||||
pub creator_set_visibility: Visibility,
|
||||
|
||||
pub public_bucket_directory_hash: String,
|
||||
pub maybe_public_bucket_prefix: Option<String>,
|
||||
pub maybe_public_bucket_extension: Option<String>,
|
||||
}
|
||||
|
||||
pub async fn get_media_file_for_inference(
|
||||
media_file_token: &MediaFileToken,
|
||||
mysql_pool: &MySqlPool
|
||||
) -> AnyhowResult<Option<MediaFileForInference>> {
|
||||
|
||||
let record = select_record(media_file_token, mysql_pool).await;
|
||||
|
||||
let record = match record {
|
||||
Ok(record) => record,
|
||||
Err(ref err) => {
|
||||
return match err {
|
||||
sqlx::Error::RowNotFound => Ok(None),
|
||||
_ => Err(anyhow!("database error: {:?}", err)),
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Ok(Some(MediaFileForInference {
|
||||
token: record.token,
|
||||
media_type: record.media_type,
|
||||
maybe_creator_user_token: record.maybe_creator_user_token,
|
||||
creator_set_visibility: record.creator_set_visibility,
|
||||
public_bucket_directory_hash: record.public_bucket_directory_hash,
|
||||
maybe_public_bucket_prefix: record.maybe_public_bucket_prefix,
|
||||
maybe_public_bucket_extension: record.maybe_public_bucket_extension,
|
||||
}))
|
||||
}
|
||||
|
||||
async fn select_record(
|
||||
media_file_token: &MediaFileToken,
|
||||
mysql_pool: &MySqlPool
|
||||
) -> Result<MediaFileRaw, sqlx::Error> {
|
||||
sqlx::query_as!(
|
||||
MediaFileRaw,
|
||||
r#"
|
||||
SELECT
|
||||
m.token as `token: tokens::tokens::media_files::MediaFileToken`,
|
||||
|
||||
m.media_type as `media_type: enums::by_table::media_files::media_file_type::MediaFileType`,
|
||||
|
||||
m.maybe_creator_user_token as `maybe_creator_user_token: tokens::tokens::users::UserToken`,
|
||||
m.creator_set_visibility as `creator_set_visibility: enums::common::visibility::Visibility`,
|
||||
|
||||
m.public_bucket_directory_hash,
|
||||
m.maybe_public_bucket_prefix,
|
||||
m.maybe_public_bucket_extension
|
||||
|
||||
FROM media_files AS m
|
||||
WHERE
|
||||
m.token = ?
|
||||
"#,
|
||||
media_file_token
|
||||
)
|
||||
.fetch_one(mysql_pool)
|
||||
.await
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
pub mod create;
|
||||
pub mod delete_media_file;
|
||||
pub mod get_media_file;
|
||||
pub mod get_media_file_for_inference;
|
||||
pub mod list;
|
||||
pub mod update_media_file;
|
||||
|
||||
+5
-1
@@ -149,7 +149,11 @@ impl RawUserBookmarkRecord {
|
||||
MediaFileType::Audio => Some("audio media file".to_string()),
|
||||
MediaFileType::Image => Some("image media file".to_string()),
|
||||
MediaFileType::Video => Some("video media file".to_string()),
|
||||
MediaFileType::Mocap => Some("mocap da file".to_string()),
|
||||
MediaFileType::Mocap => Some("mocap file".to_string()),
|
||||
MediaFileType::Bvh => Some("3d file".to_string()),
|
||||
MediaFileType::Fbx => Some("3d file".to_string()),
|
||||
MediaFileType::Glb => Some("3d file".to_string()),
|
||||
MediaFileType::Gltf => Some("3d file".to_string()),
|
||||
}),
|
||||
UserBookmarkEntityType::VoiceConversionModel => self.maybe_descriptive_text_voice_conversion_model_title,
|
||||
UserBookmarkEntityType::ZsVoice => self.maybe_descriptive_text_zs_voice_title,
|
||||
|
||||
@@ -14,11 +14,21 @@ path = "src/lib.rs"
|
||||
|
||||
[dependencies]
|
||||
|
||||
# External
|
||||
# Internal
|
||||
errors = { path = "../errors" }
|
||||
filesys = { path = "../files/filesys" }
|
||||
|
||||
my-workspace-hack = { version = "0.1", path = "../../my-workspace-hack" }
|
||||
|
||||
# External
|
||||
log = "0.4.14"
|
||||
subprocess = "0.2.9"
|
||||
|
||||
# Serialization
|
||||
#serde = "1.0.125"
|
||||
#serde_derive = "1.0.125"
|
||||
#serde_json = "1.0.64"
|
||||
#toml = "0.5.8"
|
||||
|
||||
[dev-dependencies]
|
||||
serial_test = "3.0.0"
|
||||
|
||||
+1
-1
@@ -46,7 +46,7 @@ impl CommandExitStatus {
|
||||
mod tests {
|
||||
use subprocess::ExitStatus;
|
||||
|
||||
use crate::job::job_loop::command_exit_status::CommandExitStatus;
|
||||
use crate::command_exit_status::CommandExitStatus;
|
||||
|
||||
#[test]
|
||||
fn is_success() {
|
||||
@@ -0,0 +1,7 @@
|
||||
|
||||
/// Trait for arguments that can be turned into a command line string.
|
||||
pub trait CommandArgs {
|
||||
/// Convert to the call signature.
|
||||
/// NB: We're not returning a HashMap since positions may need to be respected, or some args may have no values.
|
||||
fn to_command_string(&self) -> String;
|
||||
}
|
||||
@@ -0,0 +1,291 @@
|
||||
use std::env;
|
||||
use std::ffi::OsString;
|
||||
use std::fs::File;
|
||||
use std::path::PathBuf;
|
||||
use std::time::Duration;
|
||||
|
||||
use log::info;
|
||||
use subprocess::{Popen, PopenConfig, Redirection};
|
||||
|
||||
use errors::AnyhowResult;
|
||||
use filesys::path_to_string::path_to_string;
|
||||
|
||||
use crate::command_exit_status::CommandExitStatus;
|
||||
use crate::command_runner::command_args::CommandArgs;
|
||||
use crate::command_runner::command_runner_args::{FileOrCreate, RunAsSubprocessArgs};
|
||||
use crate::command_runner::env_var_policy::EnvVarPolicy;
|
||||
use crate::docker_options::DockerOptions;
|
||||
use crate::executable_or_command::ExecutableOrShellCommand;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct CommandRunner {
|
||||
/// The script, executable, or command to run.
|
||||
pub executable_or_command: ExecutableOrShellCommand,
|
||||
|
||||
/// The directory to change to before running the command.
|
||||
/// The parent process won't change directory here - only the subprocess will.
|
||||
pub maybe_execution_directory: Option<PathBuf>,
|
||||
|
||||
/// How to treat the env vars when spinning up the subprocess.
|
||||
pub env_var_policy: EnvVarPolicy,
|
||||
|
||||
/// If the command needs to run with a Python virtual env, this can be run.
|
||||
/// eg. `source python/bin/activate`
|
||||
pub maybe_virtual_env_activation_command: Option<String>,
|
||||
|
||||
/// If this is run under Docker (eg. in development), these are the options.
|
||||
/// Typically, this is not done in production.
|
||||
pub maybe_docker_options: Option<DockerOptions>,
|
||||
|
||||
/// If the execution should be ended after a certain point.
|
||||
pub maybe_execution_timeout: Option<Duration>,
|
||||
}
|
||||
|
||||
|
||||
impl CommandRunner {
|
||||
|
||||
/// Run the command with the `subprocess` crate utilities
|
||||
pub fn run_with_subprocess(&self, args: RunAsSubprocessArgs<'_>) -> CommandExitStatus {
|
||||
self.do_run_with_subprocess(args)
|
||||
.unwrap_or_else(|err| {
|
||||
CommandExitStatus::FailureWithReason { reason: format!("error: {:?}", err) }
|
||||
})
|
||||
}
|
||||
|
||||
fn do_run_with_subprocess(&self, args: RunAsSubprocessArgs<'_>) -> AnyhowResult<CommandExitStatus> {
|
||||
let command = self.build_command_string(&args.args);
|
||||
|
||||
info!("Command: {:?}", command);
|
||||
|
||||
let shell = match self.executable_or_command {
|
||||
ExecutableOrShellCommand::BashShellCommand(_) | ExecutableOrShellCommand::Executable(_) => "bash",
|
||||
ExecutableOrShellCommand::ShShellCommand(_) => "sh",
|
||||
};
|
||||
|
||||
let command_parts = [
|
||||
shell,
|
||||
"-c",
|
||||
&command
|
||||
];
|
||||
|
||||
let mut env_vars = Vec::new();
|
||||
|
||||
// Copy all environment variables from the parent process.
|
||||
// This is necessary to send all the kubernetes settings for Nvidia / CUDA.
|
||||
for (env_key, env_value) in env::vars() {
|
||||
// TODO(bt,2024-01-27): Env var sanitization
|
||||
//if IGNORED_ENVIRONMENT_VARS.contains(&env_key) {
|
||||
// continue;
|
||||
//}
|
||||
env_vars.push((
|
||||
OsString::from(env_key),
|
||||
OsString::from(env_value),
|
||||
));
|
||||
}
|
||||
|
||||
let mut config = PopenConfig::default();
|
||||
|
||||
if !env_vars.is_empty() {
|
||||
config.env = Some(env_vars);
|
||||
}
|
||||
|
||||
match args.maybe_stderr_output_file {
|
||||
Some(FileOrCreate::NewFileWithName(stderr_output_file)) => {
|
||||
info!("stderr will be written to file: {:?}", stderr_output_file);
|
||||
|
||||
let stderr_file = File::create(stderr_output_file)?;
|
||||
config.stderr = Redirection::File(stderr_file);
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
|
||||
match args.maybe_stdout_output_file {
|
||||
Some(FileOrCreate::NewFileWithName(stdout_output_file)) => {
|
||||
info!("stdout will be written to file: {:?}", stdout_output_file);
|
||||
|
||||
let stdout_file = File::create(stdout_output_file)?;
|
||||
config.stdout = Redirection::File(stdout_file);
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
|
||||
let mut popen_handle = Popen::create(&command_parts, config)?;
|
||||
|
||||
info!("Subprocess PID: {:?}", popen_handle.pid());
|
||||
|
||||
let maybe_exit_status = match self.maybe_execution_timeout {
|
||||
None => {
|
||||
let exit_status = popen_handle.wait()?;
|
||||
info!("Subprocess exit status: {:?}", exit_status);
|
||||
return Ok(CommandExitStatus::from_exit_status(exit_status));
|
||||
}
|
||||
Some(timeout) => {
|
||||
info!("Executing with timeout: {:?}", &timeout);
|
||||
popen_handle.wait_timeout(timeout.clone())?
|
||||
}
|
||||
};
|
||||
|
||||
match maybe_exit_status {
|
||||
None => {
|
||||
// NB: If the program didn't successfully terminate, kill it.
|
||||
info!("Subprocess didn't end after timeout; terminating...");
|
||||
let _r = popen_handle.terminate()?;
|
||||
Ok(CommandExitStatus::Timeout)
|
||||
}
|
||||
Some(exit_status) => {
|
||||
info!("Subprocess timed wait exit status: {:?}", exit_status);
|
||||
Ok(CommandExitStatus::from_exit_status(exit_status))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn build_command_string(&self, command_args: &Box<&dyn CommandArgs>) -> String {
|
||||
let mut command = String::new();
|
||||
|
||||
if let Some(execution_directory) = self.maybe_execution_directory.as_deref() {
|
||||
command.push_str("cd ");
|
||||
command.push_str(&path_to_string(execution_directory));
|
||||
command.push_str(" && ");
|
||||
}
|
||||
|
||||
if let Some(venv_command) = self.maybe_virtual_env_activation_command.as_deref() {
|
||||
command.push_str(venv_command);
|
||||
command.push_str(" && ");
|
||||
}
|
||||
|
||||
match self.executable_or_command {
|
||||
ExecutableOrShellCommand::Executable(ref executable) => {
|
||||
command.push_str(&path_to_string(executable));
|
||||
command.push_str(" ");
|
||||
}
|
||||
ExecutableOrShellCommand::ShShellCommand(ref cmd) => {
|
||||
command.push_str(cmd);
|
||||
command.push_str(" ");
|
||||
}
|
||||
ExecutableOrShellCommand::BashShellCommand(ref cmd) => {
|
||||
command.push_str(cmd);
|
||||
command.push_str(" ");
|
||||
}
|
||||
}
|
||||
|
||||
let command_arguments = command_args.to_command_string();
|
||||
command.push_str(&command_arguments);
|
||||
|
||||
if let Some(docker_options) = self.maybe_docker_options.as_ref() {
|
||||
command = docker_options.to_command_string(&command);
|
||||
}
|
||||
|
||||
command
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::path::PathBuf;
|
||||
|
||||
use crate::command_runner::command_args::CommandArgs;
|
||||
use crate::command_runner::command_runner::CommandRunner;
|
||||
use crate::command_runner::env_var_policy::EnvVarPolicy;
|
||||
use crate::docker_options::{DockerGpu, DockerOptions};
|
||||
use crate::executable_or_command::ExecutableOrShellCommand;
|
||||
|
||||
struct BlenderArgs;
|
||||
|
||||
impl CommandArgs for BlenderArgs {
|
||||
fn to_command_string(&self) -> String {
|
||||
"-a --foo --bar=baz --bin=\"blah blah\"".to_string()
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn simple_executable() {
|
||||
let runner = CommandRunner {
|
||||
executable_or_command: ExecutableOrShellCommand::Executable(PathBuf::from("blender")),
|
||||
maybe_execution_directory: None,
|
||||
env_var_policy: EnvVarPolicy::CopyNone,
|
||||
maybe_virtual_env_activation_command: None,
|
||||
maybe_docker_options: None,
|
||||
maybe_execution_timeout: None,
|
||||
};
|
||||
|
||||
let ptr : Box<&dyn CommandArgs> = Box::new(&BlenderArgs {});
|
||||
let result = runner.build_command_string(&ptr);
|
||||
|
||||
assert_eq!(&result, r#"blender -a --foo --bar=baz --bin="blah blah""#);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn simple_command() {
|
||||
let runner = CommandRunner {
|
||||
executable_or_command: ExecutableOrShellCommand::BashShellCommand("python3.6 inference.py".to_string()),
|
||||
maybe_execution_directory: None,
|
||||
env_var_policy: EnvVarPolicy::CopyNone,
|
||||
maybe_virtual_env_activation_command: None,
|
||||
maybe_docker_options: None,
|
||||
maybe_execution_timeout: None,
|
||||
};
|
||||
|
||||
let ptr : Box<&dyn CommandArgs> = Box::new(&BlenderArgs {});
|
||||
let result = runner.build_command_string(&ptr);
|
||||
|
||||
assert_eq!(&result, r#"python3.6 inference.py -a --foo --bar=baz --bin="blah blah""#);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn executable_with_directory() {
|
||||
let runner = CommandRunner {
|
||||
executable_or_command: ExecutableOrShellCommand::Executable(PathBuf::from("blender")),
|
||||
maybe_execution_directory: Some(PathBuf::from("/usr/local/bin")),
|
||||
env_var_policy: EnvVarPolicy::CopyNone,
|
||||
maybe_virtual_env_activation_command: None,
|
||||
maybe_docker_options: None,
|
||||
maybe_execution_timeout: None,
|
||||
};
|
||||
|
||||
let ptr : Box<&dyn CommandArgs> = Box::new(&BlenderArgs {});
|
||||
let result = runner.build_command_string(&ptr);
|
||||
|
||||
assert_eq!(&result, r#"cd /usr/local/bin && blender -a --foo --bar=baz --bin="blah blah""#);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn command_with_venv() {
|
||||
let runner = CommandRunner {
|
||||
executable_or_command: ExecutableOrShellCommand::BashShellCommand("python3.6 inference.py".to_string()),
|
||||
maybe_execution_directory: None,
|
||||
env_var_policy: EnvVarPolicy::CopyNone,
|
||||
maybe_virtual_env_activation_command: Some("source venv/bin/activate".to_string()),
|
||||
maybe_docker_options: None,
|
||||
maybe_execution_timeout: None,
|
||||
};
|
||||
|
||||
let ptr : Box<&dyn CommandArgs> = Box::new(&BlenderArgs {});
|
||||
let result = runner.build_command_string(&ptr);
|
||||
|
||||
assert_eq!(&result, r#"source venv/bin/activate && python3.6 inference.py -a --foo --bar=baz --bin="blah blah""#);
|
||||
}
|
||||
|
||||
// TODO(bt, 2024-01-27): The quotes are broken. Fix this!!
|
||||
#[test]
|
||||
fn execute_with_docker() {
|
||||
let runner = CommandRunner {
|
||||
executable_or_command: ExecutableOrShellCommand::Executable(PathBuf::from("blender")),
|
||||
maybe_execution_directory: None,
|
||||
env_var_policy: EnvVarPolicy::CopyNone,
|
||||
maybe_virtual_env_activation_command: None,
|
||||
maybe_docker_options: Some(DockerOptions {
|
||||
image_name: "ABCDE".to_string(),
|
||||
maybe_bind_mount: None,
|
||||
maybe_environment_variables: None,
|
||||
maybe_gpu: Some(DockerGpu::All),
|
||||
}),
|
||||
maybe_execution_timeout: None,
|
||||
};
|
||||
|
||||
let ptr : Box<&dyn CommandArgs> = Box::new(&BlenderArgs {});
|
||||
let result = runner.build_command_string(&ptr);
|
||||
|
||||
// TODO(bt, 2024-01-27): The quotes are broken. Fix this!!
|
||||
assert_eq!(&result, r#"docker run --rm --gpus all ABCDE /bin/bash -c "blender -a --foo --bar=baz --bin="blah blah"""#);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
use std::path::Path;
|
||||
|
||||
use crate::command_runner::command_args::CommandArgs;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum FileOrCreate<'a> {
|
||||
/// Open the file at the path. Overwrite if it already exists.
|
||||
NewFileWithName(&'a Path),
|
||||
|
||||
// Future option(1): new file
|
||||
// /// Create a new file (and presumably return the name)
|
||||
// NewFile,
|
||||
|
||||
// Future option(2): append-only file
|
||||
// Future option(3): already open file handle
|
||||
}
|
||||
|
||||
pub struct RunAsSubprocessArgs<'a> {
|
||||
/// The args for the process we're going to call
|
||||
pub args: Box<&'a dyn CommandArgs>,
|
||||
|
||||
/// If set, write stderr to this file.
|
||||
pub maybe_stderr_output_file: Option<FileOrCreate<'a>>,
|
||||
|
||||
/// If set, write stdout to this file.
|
||||
pub maybe_stdout_output_file: Option<FileOrCreate<'a>>,
|
||||
}
|
||||
@@ -0,0 +1,138 @@
|
||||
use std::collections::HashSet;
|
||||
use std::env;
|
||||
use std::ffi::OsString;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum EnvVarPolicy {
|
||||
/// Copy no env vars to the subprocess
|
||||
CopyNone,
|
||||
/// Copy all env vars to the subprocess
|
||||
CopyAll,
|
||||
/// Copy only the following env vars to the subprocess
|
||||
CopyOnly(HashSet<String>),
|
||||
/// Copy all except the following env vars to the subprocess
|
||||
CopyExcept(HashSet<String>),
|
||||
}
|
||||
|
||||
impl EnvVarPolicy {
|
||||
/// Build the list of env vars to send to the subprocess, if any, based on the policy.
|
||||
pub fn get_env_vars_for_subprocess(&self) -> Option<Vec<(OsString, OsString)>> {
|
||||
match self {
|
||||
EnvVarPolicy::CopyNone => {
|
||||
None
|
||||
}
|
||||
EnvVarPolicy::CopyAll => {
|
||||
Some(env::vars().into_iter()
|
||||
.map(|(key, value)| (OsString::from(key), OsString::from(value)))
|
||||
.collect::<Vec<_>>())
|
||||
}
|
||||
EnvVarPolicy::CopyOnly(names) => {
|
||||
Some(env::vars().into_iter()
|
||||
.filter(|(key, _value)| names.contains(key))
|
||||
.map(|(key, value)| (OsString::from(key), OsString::from(value)))
|
||||
.collect::<Vec<_>>())
|
||||
|
||||
}
|
||||
EnvVarPolicy::CopyExcept(names) => {
|
||||
Some(env::vars().into_iter()
|
||||
.filter(|(key, _value)| !names.contains(key))
|
||||
.map(|(key, value)| (OsString::from(key), OsString::from(value)))
|
||||
.collect::<Vec<_>>())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::collections::HashSet;
|
||||
|
||||
use crate::command_runner::env_var_policy::EnvVarPolicy;
|
||||
|
||||
// NB: Careful with `set_var` as it escapes into *all* other tests! To make matters
|
||||
// more complicated, tests run in parallel and in any order. We're using a special
|
||||
// test variable so we never infect other tests.
|
||||
fn set_env_vars() {
|
||||
std::env::set_var("STORYTELLER_TEST_VAR_FOO", "foo");
|
||||
std::env::set_var("STORYTELLER_TEST_VAR_BAR", "bar");
|
||||
std::env::set_var("STORYTELLER_TEST_VAR_BAZ", "baz");
|
||||
std::env::set_var("STORYTELLER_TEST_VAR_BIN", "bin");
|
||||
}
|
||||
|
||||
// NB: Careful with `set_var` as it escapes into *all* other tests! To make matters
|
||||
// more complicated, tests run in parallel and in any order. We're using a special
|
||||
// test variable so we never infect other tests.
|
||||
fn unset_env_vars() {
|
||||
std::env::remove_var("STORYTELLER_TEST_VAR_FOO");
|
||||
std::env::remove_var("STORYTELLER_TEST_VAR_BAR");
|
||||
std::env::remove_var("STORYTELLER_TEST_VAR_BAZ");
|
||||
std::env::remove_var("STORYTELLER_TEST_VAR_BIN");
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[serial_test::serial]
|
||||
fn copy_none() {
|
||||
let policy = EnvVarPolicy::CopyNone;
|
||||
assert_eq!(policy.get_env_vars_for_subprocess(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[serial_test::serial]
|
||||
fn copy_all() {
|
||||
let policy = EnvVarPolicy::CopyAll;
|
||||
|
||||
set_env_vars();
|
||||
|
||||
let args = policy.get_env_vars_for_subprocess().expect("should contain args");
|
||||
|
||||
// NB: All variables contained
|
||||
assert!(args.iter().find(|(key, _value)| key == "STORYTELLER_TEST_VAR_FOO").is_some());
|
||||
assert!(args.iter().find(|(key, _value)| key == "STORYTELLER_TEST_VAR_BAR").is_some());
|
||||
assert!(args.iter().find(|(key, _value)| key == "STORYTELLER_TEST_VAR_BAZ").is_some());
|
||||
assert!(args.iter().find(|(key, _value)| key == "STORYTELLER_TEST_VAR_BIN").is_some());
|
||||
|
||||
unset_env_vars();
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[serial_test::serial]
|
||||
fn copy_except() {
|
||||
let policy = EnvVarPolicy::CopyExcept(HashSet::from([
|
||||
"STORYTELLER_TEST_VAR_FOO".to_string(),
|
||||
"STORYTELLER_TEST_VAR_BIN".to_string(),
|
||||
]));
|
||||
|
||||
set_env_vars();
|
||||
|
||||
let args = policy.get_env_vars_for_subprocess().expect("should contain args");
|
||||
|
||||
// NB: Only two contained
|
||||
assert!(args.iter().find(|(key, _value)| key == "STORYTELLER_TEST_VAR_FOO").is_none());
|
||||
assert!(args.iter().find(|(key, _value)| key == "STORYTELLER_TEST_VAR_BAR").is_some());
|
||||
assert!(args.iter().find(|(key, _value)| key == "STORYTELLER_TEST_VAR_BAZ").is_some());
|
||||
assert!(args.iter().find(|(key, _value)| key == "STORYTELLER_TEST_VAR_BIN").is_none());
|
||||
|
||||
unset_env_vars();
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[serial_test::serial]
|
||||
fn copy_only() {
|
||||
let policy = EnvVarPolicy::CopyOnly(HashSet::from([
|
||||
"STORYTELLER_TEST_VAR_FOO".to_string(),
|
||||
"STORYTELLER_TEST_VAR_BIN".to_string(),
|
||||
]));
|
||||
|
||||
set_env_vars();
|
||||
|
||||
let args = policy.get_env_vars_for_subprocess().expect("should contain args");
|
||||
|
||||
// NB: Only two contained
|
||||
assert!(args.iter().find(|(key, _value)| key == "STORYTELLER_TEST_VAR_FOO").is_some());
|
||||
assert!(args.iter().find(|(key, _value)| key == "STORYTELLER_TEST_VAR_BAR").is_none());
|
||||
assert!(args.iter().find(|(key, _value)| key == "STORYTELLER_TEST_VAR_BAZ").is_none());
|
||||
assert!(args.iter().find(|(key, _value)| key == "STORYTELLER_TEST_VAR_BIN").is_some());
|
||||
|
||||
unset_env_vars();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
pub mod command_args;
|
||||
pub mod command_runner;
|
||||
pub mod command_runner_args;
|
||||
pub mod env_var_policy;
|
||||
@@ -0,0 +1,17 @@
|
||||
use std::path::PathBuf;
|
||||
|
||||
/// A single executable script or a string to run as a shell command.
|
||||
#[derive(Clone)]
|
||||
pub enum ExecutableOrShellCommand {
|
||||
/// A system path to an executable.
|
||||
/// Eg. `inference.py`
|
||||
Executable(PathBuf),
|
||||
|
||||
/// A string to run as `sh` shell command.
|
||||
/// Eg. `python3 inference.py`
|
||||
ShShellCommand(String),
|
||||
|
||||
/// A string to run as a `bash` shell command.
|
||||
/// Eg. `python3 inference.py`
|
||||
BashShellCommand(String),
|
||||
}
|
||||
@@ -13,4 +13,7 @@
|
||||
#![allow(dead_code)]
|
||||
#![allow(non_snake_case)]
|
||||
|
||||
pub mod docker_options;
|
||||
pub mod command_runner;
|
||||
pub mod command_exit_status;
|
||||
pub mod docker_options;
|
||||
pub mod executable_or_command;
|
||||
|
||||
@@ -82,4 +82,6 @@ my-workspace-hack = { version = "0.1", path = "../../../my-workspace-hack" }
|
||||
time = "0.3.30"
|
||||
mime = "0.3.17"
|
||||
rand = { version = "0.8.5", features = [] }
|
||||
jsonpath_lib = "0.3.0"
|
||||
jsonpath_lib = "0.3.0"
|
||||
walkdir = "2.4.0"
|
||||
reqwest = "0.11.22"
|
||||
@@ -133,6 +133,15 @@ COMFY_INFERENCE_EXECUTABLE_OR_COMMAND="venv/bin/python ../ComfyLauncher/ComfyRun
|
||||
STYLE_TTS2_INFERENCE_ROOT_DIRECTORY="/home/salt/clone/storyteller-ml/tts/style-tts2"
|
||||
STYLE_TTS2_INFERENCE_COMMAND="venv/bin/python3.8 main.py"
|
||||
|
||||
# ================================= #
|
||||
# #
|
||||
# Fbx2Gltf #
|
||||
# #
|
||||
# ================================= #
|
||||
#FBX2GLTF_DIRECTORY="%storyteller_root%/FBX2glTF"
|
||||
FBX2GLTF_DIRECTORY="/home/bt/dev/storyteller/FBX2glTF"
|
||||
FBX2GLTF_EXECUTABLE_OR_COMMAND="./bin/FBX2glTF-linux-x64"
|
||||
|
||||
# ================================= #
|
||||
# #
|
||||
# CACHE SETTINGS #
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
pub mod clear_full_filesystem;
|
||||
pub mod command_exit_status;
|
||||
pub mod determine_dependency_status;
|
||||
pub mod job_success_result;
|
||||
pub mod main_loop;
|
||||
|
||||
@@ -13,12 +13,13 @@ use redis_common::redis_keys::RedisKeys;
|
||||
use crate::job::job_loop::determine_dependency_status::determine_dependency_status;
|
||||
use crate::job::job_loop::process_single_job_error::ProcessSingleJobError;
|
||||
use crate::job::job_loop::process_single_job_success_case::ProcessSingleJobSuccessCase;
|
||||
use crate::job::job_types::format_conversion::process_single_format_conversion_job::process_single_format_conversion_job;
|
||||
use crate::job::job_types::image_generation::process_single_ig_job::process_single_ig_job;
|
||||
use crate::job::job_types::lipsync::process_single_lipsync_job::process_single_lipsync_job;
|
||||
use crate::job::job_types::mocap::process_single_mc_job::process_single_mc_job;
|
||||
use crate::job::job_types::tts::process_single_tts_job::process_single_tts_job;
|
||||
use crate::job::job_types::vc::process_single_vc_job::process_single_vc_job;
|
||||
use crate::job::job_types::videofilter::process_single_vf_job::process_single_vf_job;
|
||||
use crate::job::job_types::image_generation::process_single_ig_job::process_single_ig_job;
|
||||
use crate::job::job_types::mocap::process_single_mc_job::process_single_mc_job;
|
||||
use crate::job::job_types::workflow::process_single_wf_job::process_single_wf_job;
|
||||
use crate::job_dependencies::JobDependencies;
|
||||
|
||||
@@ -198,6 +199,9 @@ async fn do_process_single_job(
|
||||
InferenceCategory::Workflow => {
|
||||
process_single_wf_job(job_dependencies, job).await?
|
||||
}
|
||||
InferenceCategory::FormatConversion => {
|
||||
process_single_format_conversion_job(job_dependencies, job).await?
|
||||
}
|
||||
};
|
||||
|
||||
let maybe_entity_type = job_success_result.maybe_result_entity
|
||||
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
use std::path::Path;
|
||||
|
||||
use filesys::path_to_string::path_to_string;
|
||||
use subprocess_common::command_runner::command_args::CommandArgs;
|
||||
|
||||
pub struct FbxToGltfCommandArgs<'a> {
|
||||
pub input_file: &'a Path,
|
||||
pub output_directory: &'a Path,
|
||||
|
||||
/// If true, output as one `.glb` binary file. If false, output two files: one `.gltf` and one `.bin` file.
|
||||
pub binary: bool,
|
||||
}
|
||||
|
||||
impl CommandArgs for FbxToGltfCommandArgs<'_> {
|
||||
fn to_command_string(&self) -> String {
|
||||
let mut command = String::new();
|
||||
|
||||
// Verbose flag
|
||||
command.push_str(" -v ");
|
||||
|
||||
command.push_str(" -i ");
|
||||
command.push_str(&path_to_string(self.input_file));
|
||||
|
||||
command.push_str(" -o ");
|
||||
command.push_str(&path_to_string(self.output_directory));
|
||||
|
||||
if self.binary {
|
||||
command.push_str(" --binary ");
|
||||
}
|
||||
|
||||
command
|
||||
}
|
||||
}
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
use std::path::PathBuf;
|
||||
|
||||
use errors::AnyhowResult;
|
||||
use subprocess_common::command_runner::command_runner::CommandRunner;
|
||||
use subprocess_common::command_runner::env_var_policy::EnvVarPolicy;
|
||||
use subprocess_common::docker_options::{DockerFilesystemMount, DockerOptions};
|
||||
use subprocess_common::executable_or_command::ExecutableOrShellCommand;
|
||||
|
||||
pub struct FbxToGltfDependencies {
|
||||
pub command_runner: CommandRunner,
|
||||
}
|
||||
|
||||
impl FbxToGltfDependencies {
|
||||
pub fn setup() -> AnyhowResult<Self> {
|
||||
let maybe_execution_directory = easyenv::get_env_pathbuf_optional("FBX2GLTF_DIRECTORY");
|
||||
|
||||
let executable_or_command = easyenv::get_env_string_required("FBX2GLTF_EXECUTABLE_OR_COMMAND")?;
|
||||
|
||||
let executable_or_command = if executable_or_command.contains(" ") {
|
||||
ExecutableOrShellCommand::BashShellCommand(executable_or_command)
|
||||
} else {
|
||||
ExecutableOrShellCommand::Executable(PathBuf::from(executable_or_command))
|
||||
};
|
||||
|
||||
let maybe_execution_timeout = easyenv::get_env_duration_seconds_optional("FBX2GLTF_TIMEOUT_SECONDS");
|
||||
|
||||
let maybe_docker_options = easyenv::get_env_string_optional("FBX2GLTF_DOCKER_IMAGE")
|
||||
.map(|image_name| DockerOptions {
|
||||
image_name,
|
||||
maybe_bind_mount: Some(DockerFilesystemMount::tmp_to_tmp()),
|
||||
maybe_environment_variables: None,
|
||||
maybe_gpu: None,
|
||||
});
|
||||
|
||||
Ok(Self {
|
||||
command_runner: CommandRunner {
|
||||
executable_or_command,
|
||||
maybe_execution_directory,
|
||||
env_var_policy: EnvVarPolicy::CopyNone,
|
||||
maybe_virtual_env_activation_command: None,
|
||||
maybe_docker_options,
|
||||
maybe_execution_timeout,
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
pub mod command_args;
|
||||
pub mod dependencies;
|
||||
pub mod process_job;
|
||||
+405
@@ -0,0 +1,405 @@
|
||||
use std::fs::read_to_string;
|
||||
use std::path::PathBuf;
|
||||
use std::time::Instant;
|
||||
|
||||
use anyhow::anyhow;
|
||||
use log::{error, info, warn};
|
||||
|
||||
use buckets::public::media_files::bucket_file_path::MediaFileBucketPath;
|
||||
use enums::by_table::generic_inference_jobs::inference_result_type::InferenceResultType;
|
||||
use enums::by_table::generic_synthetic_ids::id_category::IdCategory;
|
||||
use enums::by_table::media_files::media_file_origin_category::MediaFileOriginCategory;
|
||||
use enums::by_table::media_files::media_file_origin_product_category::MediaFileOriginProductCategory;
|
||||
use enums::by_table::media_files::media_file_type::MediaFileType;
|
||||
use filesys::check_file_exists::check_file_exists;
|
||||
use filesys::file_size::file_size;
|
||||
use filesys::safe_delete_temp_directory::safe_delete_temp_directory;
|
||||
use filesys::safe_delete_temp_file::safe_delete_temp_file;
|
||||
use hashing::sha256::sha256_hash_file::sha256_hash_file;
|
||||
use jobs_common::job_progress_reporter::job_progress_reporter::JobProgressReporter;
|
||||
use mimetypes::mimetype_for_file::get_mimetype_for_file;
|
||||
use mysql_queries::queries::generic_inference::job::list_available_generic_inference_jobs::AvailableInferenceJob;
|
||||
use mysql_queries::queries::media_files::create::insert_media_file_generic::{insert_media_file_generic, InsertArgs};
|
||||
use mysql_queries::queries::media_files::get_media_file_for_inference::MediaFileForInference;
|
||||
use subprocess_common::command_runner::command_runner_args::{FileOrCreate, RunAsSubprocessArgs};
|
||||
|
||||
use crate::job::job_loop::job_success_result::{JobSuccessResult, ResultEntity};
|
||||
use crate::job::job_loop::process_single_job_error::ProcessSingleJobError;
|
||||
use crate::job::job_types::format_conversion::fbx_to_gltf::command_args::FbxToGltfCommandArgs;
|
||||
use crate::job_dependencies::JobDependencies;
|
||||
use crate::util::maybe_download_file_from_bucket::{maybe_download_file_from_bucket, MaybeDownloadArgs};
|
||||
|
||||
// Flag to control GLB vs GLTF uploading
|
||||
const UPLOAD_AS_GLB : bool = true;
|
||||
|
||||
// GLTF is two files - one json, one binary
|
||||
const BUCKET_GLTF_FILE_EXTENSION: &str = ".gltf";
|
||||
const BUFFER_BIN_FILE_EXTENSION : &str = ".bin";
|
||||
|
||||
// GLB is one binary file
|
||||
const BUCKET_GLB_FILE_EXTENSION: &str = ".glb";
|
||||
|
||||
pub struct FbxToGltfJobArgs<'a> {
|
||||
pub job_dependencies: &'a JobDependencies,
|
||||
pub job: &'a AvailableInferenceJob,
|
||||
pub media_file: &'a MediaFileForInference,
|
||||
}
|
||||
|
||||
//noinspection RsConstantConditionIf
|
||||
pub async fn process_job(args: FbxToGltfJobArgs<'_>) -> Result<JobSuccessResult, ProcessSingleJobError> {
|
||||
let job = args.job;
|
||||
let media_file = args.media_file;
|
||||
|
||||
let mut job_progress_reporter = args.job_dependencies
|
||||
.clients
|
||||
.job_progress_reporter
|
||||
.new_generic_inference(job.inference_job_token.as_str())
|
||||
.map_err(|e| ProcessSingleJobError::Other(anyhow!(e)))?;
|
||||
|
||||
let model_dependencies = args
|
||||
.job_dependencies
|
||||
.job
|
||||
.job_specific_dependencies
|
||||
.maybe_convert_fbx_to_gltf_dependencies
|
||||
.as_ref()
|
||||
.ok_or_else(|| ProcessSingleJobError::JobSystemMisconfiguration(Some("missing ConvertFbx2Gltf dependencies".to_string())))?;
|
||||
|
||||
// ==================== TEMP DIR ==================== //
|
||||
|
||||
let work_temp_dir = format!("temp_file_convert_{}", job.id.0);
|
||||
|
||||
// NB: TempDir exists until it goes out of scope, at which point it should delete from filesystem.
|
||||
let work_temp_dir = args.job_dependencies
|
||||
.fs
|
||||
.scoped_temp_dir_creator_for_work
|
||||
.new_tempdir(&work_temp_dir)
|
||||
.map_err(|e| ProcessSingleJobError::from_io_error(e))?;
|
||||
|
||||
// ==================== DOWNLOAD MEDIA FILE ==================== //
|
||||
|
||||
info!("Download media for conversion...");
|
||||
|
||||
let original_media_upload_fs_path = {
|
||||
let original_media_file_fs_path = work_temp_dir.path().join("original.fbx");
|
||||
|
||||
let media_file_bucket_path = MediaFileBucketPath::from_object_hash(
|
||||
&media_file.public_bucket_directory_hash,
|
||||
media_file.maybe_public_bucket_prefix.as_deref(),
|
||||
media_file.maybe_public_bucket_extension.as_deref());
|
||||
|
||||
let bucket_object_path = media_file_bucket_path.to_full_object_pathbuf();
|
||||
|
||||
info!("Downloading media to bucket path: {:?}", &bucket_object_path);
|
||||
|
||||
maybe_download_file_from_bucket(MaybeDownloadArgs {
|
||||
name_or_description_of_file: "media upload (original file)",
|
||||
final_filesystem_file_path: &original_media_file_fs_path,
|
||||
bucket_object_path: &bucket_object_path,
|
||||
bucket_client: &args.job_dependencies.buckets.public_bucket_client,
|
||||
job_progress_reporter: &mut job_progress_reporter,
|
||||
job_progress_update_description: "downloading",
|
||||
job_id: job.id.0,
|
||||
scoped_tempdir_creator: &args.job_dependencies.fs.scoped_temp_dir_creator_for_work,
|
||||
maybe_existing_file_minimum_size_required: None,
|
||||
}).await?;
|
||||
|
||||
original_media_file_fs_path
|
||||
};
|
||||
|
||||
// ==================== SETUP FOR CONVERSION ==================== //
|
||||
|
||||
job_progress_reporter.log_status("running conversion")
|
||||
.map_err(|e| ProcessSingleJobError::Other(e))?;
|
||||
|
||||
|
||||
// ==================== RUN INFERENCE SCRIPT ==================== //
|
||||
|
||||
let stderr_output_file = work_temp_dir.path().join("stderr.txt");
|
||||
let output_directory = work_temp_dir.path().join("output");
|
||||
|
||||
// NB: gltf gets put into a subdirectory with a name based on the passed argument, whereas glb gets dropped into
|
||||
// the same directory
|
||||
let output_directory_actual = match UPLOAD_AS_GLB {
|
||||
true => work_temp_dir.path().to_path_buf(),
|
||||
false => work_temp_dir.path().join("output_out"),
|
||||
};
|
||||
|
||||
let execution_start_time = Instant::now();
|
||||
|
||||
let command_exit_status = {
|
||||
model_dependencies
|
||||
.command_runner
|
||||
.run_with_subprocess(RunAsSubprocessArgs {
|
||||
args: Box::new(&FbxToGltfCommandArgs {
|
||||
input_file: &original_media_upload_fs_path,
|
||||
output_directory: &output_directory,
|
||||
binary: true,
|
||||
}),
|
||||
maybe_stderr_output_file: Some(FileOrCreate::NewFileWithName(&stderr_output_file)),
|
||||
maybe_stdout_output_file: None,
|
||||
})
|
||||
};
|
||||
|
||||
let execution_duration = Instant::now().duration_since(execution_start_time);
|
||||
|
||||
info!("Execution took duration to complete: {:?}", &execution_duration);
|
||||
|
||||
if !command_exit_status.is_success() {
|
||||
error!("Execution failed: {:?}", command_exit_status);
|
||||
|
||||
let error = ProcessSingleJobError::Other(anyhow!("CommandExitStatus: {:?}", command_exit_status));
|
||||
|
||||
if let Ok(contents) = read_to_string(&stderr_output_file) {
|
||||
warn!("Captured stderr output: {}", contents);
|
||||
|
||||
//match categorize_error(&contents) {
|
||||
// Some(ProcessSingleJobError::FaceDetectionFailure) => {
|
||||
// warn!("Face not detected in source image");
|
||||
// error = ProcessSingleJobError::FaceDetectionFailure;
|
||||
// }
|
||||
// _ => {}
|
||||
//}
|
||||
}
|
||||
|
||||
safe_delete_temp_file(&original_media_upload_fs_path);
|
||||
safe_delete_temp_directory(&output_directory_actual);
|
||||
safe_delete_temp_directory(&work_temp_dir);
|
||||
|
||||
return Err(error);
|
||||
}
|
||||
|
||||
let upload_details = if UPLOAD_AS_GLB {
|
||||
validate_and_upload_glb(&args, &mut job_progress_reporter, &output_directory_actual).await?
|
||||
} else {
|
||||
validate_and_upload_gltf(&args, &mut job_progress_reporter, &output_directory_actual).await?
|
||||
};
|
||||
|
||||
// ==================== CLEANUP FILES ==================== //
|
||||
|
||||
safe_delete_temp_directory(&output_directory_actual);
|
||||
safe_delete_temp_directory(&work_temp_dir);
|
||||
|
||||
// ==================== SAVE RECORDS ==================== //
|
||||
|
||||
info!("Saving record (only gltf or glb, not the buffer.bin) ...");
|
||||
|
||||
let (inference_result_token, id) = insert_media_file_generic(InsertArgs {
|
||||
pool: &args.job_dependencies.db.mysql_pool,
|
||||
job: &job,
|
||||
media_type: upload_details.media_type,
|
||||
origin_category: MediaFileOriginCategory::Processed,
|
||||
origin_product_category: MediaFileOriginProductCategory::Mocap,
|
||||
maybe_origin_model_type: None,
|
||||
maybe_origin_model_token: None,
|
||||
maybe_origin_filename: None,
|
||||
maybe_mime_type: upload_details.maybe_mimetype.as_deref(),
|
||||
file_size_bytes: upload_details.size_bytes,
|
||||
maybe_duration_millis: None,
|
||||
maybe_audio_encoding: None,
|
||||
maybe_video_encoding: None,
|
||||
maybe_frame_width: None,
|
||||
maybe_frame_height: None,
|
||||
public_bucket_directory_hash: upload_details.bucket_location.get_object_hash(),
|
||||
maybe_public_bucket_prefix: upload_details.bucket_location.get_optional_prefix(),
|
||||
maybe_public_bucket_extension: upload_details.bucket_location.get_optional_extension(),
|
||||
extra_file_modification_info: None,
|
||||
maybe_creator_file_synthetic_id_category: IdCategory::MediaFile,
|
||||
maybe_creator_category_synthetic_id_category: IdCategory::MocapResult,
|
||||
maybe_mod_user_token: None,
|
||||
is_batch_generated: false,
|
||||
is_generated_on_prem: false,
|
||||
checksum_sha2: &upload_details.checksum,
|
||||
generated_by_worker: Some(&args.job_dependencies.job.info.container.hostname),
|
||||
generated_by_cluster: Some(&args.job_dependencies.job.info.container.cluster_name),
|
||||
})
|
||||
.await
|
||||
.map_err(|e| ProcessSingleJobError::Other(e))?;
|
||||
|
||||
info!("Fbx2Gltf Done.");
|
||||
|
||||
//args.job_dependencies.clients.firehose_publisher.vc_inference_finished(
|
||||
// maybe_user_token.as_ref(),
|
||||
// &job.inference_job_token,
|
||||
// inference_result_token.as_str())
|
||||
// .await
|
||||
// .map_err(|e| {
|
||||
// error!("error publishing event: {:?}", e);
|
||||
// ProcessSingleJobError::Other(anyhow!("error publishing event"))
|
||||
// })?;
|
||||
|
||||
job_progress_reporter.log_status("done")
|
||||
.map_err(|e| ProcessSingleJobError::Other(e))?;
|
||||
|
||||
info!("Job {:?} complete success! Downloaded, executed successfully, and uploaded. Saved record: {}, Result Token: {}",
|
||||
job.id, id, &inference_result_token);
|
||||
|
||||
Ok(JobSuccessResult {
|
||||
maybe_result_entity: Some(ResultEntity {
|
||||
entity_type: InferenceResultType::MediaFile,
|
||||
entity_token: inference_result_token.to_string(),
|
||||
}),
|
||||
inference_duration: execution_duration,
|
||||
})
|
||||
}
|
||||
|
||||
struct UploadDetails {
|
||||
media_type: MediaFileType,
|
||||
size_bytes: u64,
|
||||
maybe_mimetype: Option<String>,
|
||||
checksum: String,
|
||||
bucket_location: MediaFileBucketPath,
|
||||
}
|
||||
|
||||
|
||||
async fn validate_and_upload_glb(args: &FbxToGltfJobArgs<'_>, job_progress_reporter: &mut Box<dyn JobProgressReporter>,
|
||||
output_directory_actual: &PathBuf) -> Result<UploadDetails, ProcessSingleJobError>
|
||||
{
|
||||
// ==================== CHECK ALL FILES EXIST AND GET METADATA ==================== //
|
||||
|
||||
// NB: FBX2GLTF creates multiple files in the directory
|
||||
// NB: The actual name of the file will be these:
|
||||
let output_file = output_directory_actual.join("output.glb");
|
||||
|
||||
info!("Checking that output files exist...");
|
||||
|
||||
check_file_exists(&output_file).map_err(|e| ProcessSingleJobError::Other(e))?;
|
||||
|
||||
info!("Interrogating result file properties...");
|
||||
|
||||
let file_size_bytes = file_size(&output_file)
|
||||
.map_err(|err| ProcessSingleJobError::Other(err))?;
|
||||
|
||||
let maybe_mimetype = get_mimetype_for_file(&output_file)
|
||||
.map_err(|err| ProcessSingleJobError::from_io_error(err))?
|
||||
.map(|mime| mime.to_string());
|
||||
|
||||
info!("Calculating sha256...");
|
||||
|
||||
let file_checksum = sha256_hash_file(&output_file)
|
||||
.map_err(|err| {
|
||||
ProcessSingleJobError::Other(anyhow!("Error hashing file: {:?}", err))
|
||||
})?;
|
||||
|
||||
// ==================== UPLOAD GLB TO BUCKET ==================== //
|
||||
|
||||
job_progress_reporter.log_status("uploading output.glb")
|
||||
.map_err(|e| ProcessSingleJobError::Other(e))?;
|
||||
|
||||
let bucket_location = MediaFileBucketPath::generate_new(
|
||||
None,
|
||||
Some(BUCKET_GLTF_FILE_EXTENSION));
|
||||
|
||||
let bucket_object_pathbuf = bucket_location.to_full_object_pathbuf();
|
||||
|
||||
info!("Destination bucket path: {:?}", &bucket_object_pathbuf);
|
||||
|
||||
info!("Uploading...");
|
||||
|
||||
args.job_dependencies.buckets.public_bucket_client.upload_filename_with_content_type(
|
||||
&bucket_object_pathbuf,
|
||||
&output_file,
|
||||
"model/gltf+binary")
|
||||
.await
|
||||
.map_err(|e| ProcessSingleJobError::Other(e))?;
|
||||
|
||||
// Cleanup
|
||||
safe_delete_temp_file(&output_file);
|
||||
|
||||
Ok(UploadDetails {
|
||||
media_type: MediaFileType::Glb,
|
||||
size_bytes: file_size_bytes,
|
||||
maybe_mimetype,
|
||||
checksum: file_checksum,
|
||||
bucket_location,
|
||||
})
|
||||
}
|
||||
|
||||
async fn validate_and_upload_gltf(args: &FbxToGltfJobArgs<'_>, job_progress_reporter: &mut Box<dyn JobProgressReporter>,
|
||||
output_directory_actual: &PathBuf) -> Result<UploadDetails, ProcessSingleJobError>
|
||||
{
|
||||
// ==================== CHECK ALL FILES EXIST AND GET METADATA ==================== //
|
||||
|
||||
// NB: FBX2GLTF creates multiple files in the directory
|
||||
// NB: The actual name of the file will be these:
|
||||
let output_gltf_file = output_directory_actual.join("output.gltf");
|
||||
let output_buffer_file = output_directory_actual.join("buffer.bin");
|
||||
|
||||
info!("Checking that output files exist...");
|
||||
|
||||
check_file_exists(&output_gltf_file).map_err(|e| ProcessSingleJobError::Other(e))?;
|
||||
check_file_exists(&output_buffer_file).map_err(|e| ProcessSingleJobError::Other(e))?;
|
||||
|
||||
info!("Interrogating result file properties...");
|
||||
|
||||
let gltf_file_size_bytes = file_size(&output_gltf_file)
|
||||
.map_err(|err| ProcessSingleJobError::Other(err))?;
|
||||
|
||||
let maybe_gltf_mimetype = get_mimetype_for_file(&output_gltf_file)
|
||||
.map_err(|err| ProcessSingleJobError::from_io_error(err))?
|
||||
.map(|mime| mime.to_string());
|
||||
|
||||
info!("Calculating sha256...");
|
||||
|
||||
let gltf_file_checksum = sha256_hash_file(&output_gltf_file)
|
||||
.map_err(|err| {
|
||||
ProcessSingleJobError::Other(anyhow!("Error hashing file: {:?}", err))
|
||||
})?;
|
||||
|
||||
// ==================== UPLOAD GLTF TO BUCKET ==================== //
|
||||
|
||||
job_progress_reporter.log_status("uploading output.gltf")
|
||||
.map_err(|e| ProcessSingleJobError::Other(e))?;
|
||||
|
||||
let result_gtlf_bucket_location = MediaFileBucketPath::generate_new(
|
||||
None,
|
||||
Some(BUCKET_GLTF_FILE_EXTENSION));
|
||||
|
||||
let result_gltf_bucket_object_pathbuf = result_gtlf_bucket_location.to_full_object_pathbuf();
|
||||
|
||||
info!("Destination bucket path: {:?}", &result_gltf_bucket_object_pathbuf);
|
||||
|
||||
info!("Uploading...");
|
||||
|
||||
args.job_dependencies.buckets.public_bucket_client.upload_filename_with_content_type(
|
||||
&result_gltf_bucket_object_pathbuf,
|
||||
&output_gltf_file,
|
||||
"model/gltf+json")
|
||||
.await
|
||||
.map_err(|e| ProcessSingleJobError::Other(e))?;
|
||||
|
||||
// ==================== UPLOAD BIN TO BUCKET ==================== //
|
||||
|
||||
job_progress_reporter.log_status("uploading buffer.bin")
|
||||
.map_err(|e| ProcessSingleJobError::Other(e))?;
|
||||
|
||||
// NB: Reuse same object hash, just change the extension.
|
||||
let result_buffer_bucket_location = MediaFileBucketPath::from_object_hash(
|
||||
result_gtlf_bucket_location.get_object_hash(),
|
||||
None,
|
||||
Some(BUFFER_BIN_FILE_EXTENSION));
|
||||
|
||||
let result_buffer_bucket_object_pathbuf = result_buffer_bucket_location.to_full_object_pathbuf();
|
||||
|
||||
info!("Destination bucket path: {:?}", &result_buffer_bucket_object_pathbuf);
|
||||
|
||||
info!("Uploading...");
|
||||
|
||||
args.job_dependencies.buckets.public_bucket_client.upload_filename_with_content_type(
|
||||
&result_buffer_bucket_object_pathbuf,
|
||||
&output_buffer_file,
|
||||
"application/octet-stream")
|
||||
.await
|
||||
.map_err(|e| ProcessSingleJobError::Other(e))?;
|
||||
|
||||
// Cleanup
|
||||
safe_delete_temp_file(&output_buffer_file);
|
||||
safe_delete_temp_file(&output_gltf_file);
|
||||
|
||||
Ok(UploadDetails {
|
||||
media_type: MediaFileType::Gltf,
|
||||
size_bytes: gltf_file_size_bytes,
|
||||
maybe_mimetype: maybe_gltf_mimetype,
|
||||
checksum: gltf_file_checksum,
|
||||
bucket_location: result_gtlf_bucket_location,
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
pub mod fbx_to_gltf;
|
||||
pub mod process_single_format_conversion_job;
|
||||
+63
@@ -0,0 +1,63 @@
|
||||
use anyhow::anyhow;
|
||||
use log::error;
|
||||
|
||||
use enums::by_table::generic_inference_jobs::inference_model_type::InferenceModelType;
|
||||
use mysql_queries::queries::generic_inference::job::list_available_generic_inference_jobs::AvailableInferenceJob;
|
||||
use mysql_queries::queries::media_files::get_media_file_for_inference::get_media_file_for_inference;
|
||||
use tokens::tokens::media_files::MediaFileToken;
|
||||
|
||||
use crate::job::job_loop::job_success_result::JobSuccessResult;
|
||||
use crate::job::job_loop::process_single_job_error::ProcessSingleJobError;
|
||||
use crate::job::job_types::format_conversion::fbx_to_gltf;
|
||||
use crate::job::job_types::format_conversion::fbx_to_gltf::process_job::FbxToGltfJobArgs;
|
||||
use crate::job_dependencies::JobDependencies;
|
||||
|
||||
// // NB: There will likely be lots of other format conversions for us to run, and there's probably lots of shared code between them.
|
||||
// // This enum lets us reuse the shared portions in this function before dispatching to the special parts.
|
||||
// #[derive(Debug, Copy, Clone)]
|
||||
// pub enum FormatConversionType {
|
||||
// FbxToGltf,
|
||||
// }
|
||||
|
||||
pub async fn process_single_format_conversion_job(job_dependencies: &JobDependencies, job: &AvailableInferenceJob) -> Result<JobSuccessResult, ProcessSingleJobError> {
|
||||
|
||||
let maybe_media_file_token = job.maybe_input_source_token
|
||||
.as_deref()
|
||||
.map(|token| MediaFileToken::new_from_str(token));
|
||||
|
||||
let media_file_token = match maybe_media_file_token {
|
||||
None => return Err(ProcessSingleJobError::Other(anyhow!("no associated media file for format conversion job: {:?}", job.inference_job_token))),
|
||||
Some(token) => token,
|
||||
};
|
||||
|
||||
let maybe_media_file_result =
|
||||
get_media_file_for_inference(&media_file_token, &job_dependencies.db.mysql_pool).await;
|
||||
|
||||
let media_file = match maybe_media_file_result {
|
||||
Ok(Some(media_file)) => media_file,
|
||||
Ok(None) => {
|
||||
error!("no media file record found for token: {:?}", media_file_token);
|
||||
return Err(ProcessSingleJobError::Other(anyhow!("no media file record found for token: {:?}", media_file_token)));
|
||||
}
|
||||
Err(err) => {
|
||||
error!("error fetching media file record from db: {:?}", err);
|
||||
return Err(ProcessSingleJobError::Other(err));
|
||||
}
|
||||
};
|
||||
|
||||
let job_success_result = match job.maybe_model_type {
|
||||
Some(InferenceModelType::ConvertFbxToGltf) => {
|
||||
fbx_to_gltf::process_job::process_job(FbxToGltfJobArgs {
|
||||
job_dependencies,
|
||||
job,
|
||||
media_file: &media_file,
|
||||
}).await?
|
||||
},
|
||||
Some(model_type) => return Err(ProcessSingleJobError::Other(anyhow!("wrong model type: {:?}", model_type))),
|
||||
None => return Err(ProcessSingleJobError::Other(anyhow!("no model type in record"))),
|
||||
};
|
||||
|
||||
Ok(job_success_result)
|
||||
}
|
||||
|
||||
|
||||
+236
-167
@@ -1,59 +1,47 @@
|
||||
use std::convert::TryInto;
|
||||
use std::fs::read_to_string;
|
||||
use std::path::PathBuf;
|
||||
use std::{ fs, thread };
|
||||
use std::time::{ Duration, Instant };
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
use actix_web::dev::ResourcePath;
|
||||
use cloud_storage::remote_file_manager::weights_descriptor::{WeightsLoRADescriptor, WeightsSD15Descriptor};
|
||||
use filesys::file_exists::file_exists;
|
||||
use serde_json;
|
||||
use anyhow::anyhow;
|
||||
use log::{error, info, warn};
|
||||
use serde_json;
|
||||
|
||||
use cloud_storage::remote_file_manager::media_descriptor::MediaImagePngDescriptor;
|
||||
use cloud_storage::remote_file_manager::remote_cloud_bucket_details::RemoteCloudBucketDetails;
|
||||
use cloud_storage::remote_file_manager::remote_cloud_file_manager::RemoteCloudFileClient;
|
||||
use cloud_storage::remote_file_manager::weights_descriptor::{WeightsLoRADescriptor, WeightsSD15Descriptor};
|
||||
use composite_identifiers::by_table::batch_generations::batch_generation_entity::BatchGenerationEntity;
|
||||
use enums::by_table::generic_inference_jobs::inference_result_type::InferenceResultType;
|
||||
use enums::by_table::generic_synthetic_ids::id_category::IdCategory;
|
||||
use enums::by_table::media_files::media_file_origin_category::MediaFileOriginCategory;
|
||||
use enums::by_table::media_files::media_file_origin_model_type::MediaFileOriginModelType;
|
||||
use enums::by_table::media_files::media_file_origin_product_category::MediaFileOriginProductCategory;
|
||||
use log::info;
|
||||
|
||||
use cloud_storage::remote_file_manager::media_descriptor::MediaImagePngDescriptor;
|
||||
use cloud_storage::remote_file_manager::remote_cloud_bucket_details::RemoteCloudBucketDetails;
|
||||
use cloud_storage::remote_file_manager::remote_cloud_file_manager::{self, RemoteCloudFileClient};
|
||||
use enums::by_table::media_files::media_file_type::MediaFileType;
|
||||
|
||||
use enums::by_table::model_weights::weights_category::WeightsCategory;
|
||||
use enums::by_table::model_weights::weights_types::WeightsType;
|
||||
|
||||
use filesys::file_exists::file_exists;
|
||||
use filesys::path_to_string::path_to_string;
|
||||
use filesys::safe_delete_temp_directory::safe_delete_temp_directory;
|
||||
use filesys::safe_delete_temp_file::safe_delete_temp_file;
|
||||
use google_drive_common::google_drive_download_command::GoogleDriveDownloadCommand;
|
||||
use mysql_queries::payloads::generic_inference_args::generic_inference_args::PolymorphicInferenceArgs;
|
||||
use mysql_queries::payloads::generic_inference_args::image_generation_payload::StableDiffusionArgs;
|
||||
use mysql_queries::queries::batch_generations::insert_batch_generation_records::{
|
||||
InsertBatchArgs,
|
||||
self,
|
||||
};
|
||||
use mysql_queries::queries::batch_generations::insert_batch_generation_records::insert_batch_generation_records;
|
||||
use mysql_queries::queries::batch_generations::insert_batch_generation_records::InsertBatchArgs;
|
||||
use mysql_queries::queries::generic_inference::job::list_available_generic_inference_jobs::AvailableInferenceJob;
|
||||
use mysql_queries::queries::media_files::create::insert_media_file_from_file_upload::{
|
||||
insert_media_file_from_file_upload,
|
||||
InsertMediaFileFromUploadArgs,
|
||||
UploadType,
|
||||
};
|
||||
|
||||
use mysql_queries::queries::media_files::create::insert_media_file_generic::insert_media_file_generic;
|
||||
use mysql_queries::queries::media_files::create::insert_media_file_generic::InsertArgs;
|
||||
|
||||
use mysql_queries::queries::model_categories::create_category;
|
||||
use mysql_queries::queries::model_weights::create::create_weight::{
|
||||
create_weight,
|
||||
CreateModelWeightsArgs,
|
||||
};
|
||||
use mysql_queries::queries::model_weights::get::get_weight::get_weight_by_token;
|
||||
use tokens::tokens::anonymous_visitor_tracking::AnonymousVisitorTrackingToken;
|
||||
use tokens::tokens::media_files::MediaFileToken;
|
||||
use tokens::tokens::model_weights::ModelWeightToken;
|
||||
use tokens::tokens::users::UserToken;
|
||||
|
||||
use crate::job::job_loop::job_success_result::{ JobSuccessResult, ResultEntity };
|
||||
use crate::job::job_loop::job_success_result::{JobSuccessResult, ResultEntity};
|
||||
use crate::job::job_loop::process_single_job_error::ProcessSingleJobError;
|
||||
use crate::job::job_types::image_generation::sd::sd_inference_command::InferenceArgs;
|
||||
use crate::job_dependencies::JobDependencies;
|
||||
@@ -90,6 +78,7 @@ pub async fn sd_args_from_job(
|
||||
| args.args.as_ref()
|
||||
)
|
||||
.flatten();
|
||||
|
||||
let polymorphic_args = match inference_args {
|
||||
Some(args) => args,
|
||||
None => {
|
||||
@@ -107,6 +96,7 @@ pub async fn sd_args_from_job(
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
let stable_diffusion_args: StableDiffusionArgs = StableDiffusionArgs::from(sd_args.clone());
|
||||
Ok(stable_diffusion_args)
|
||||
}
|
||||
@@ -118,11 +108,11 @@ pub async fn process_job_selection(
|
||||
let sd_args = sd_args_from_job(&args).await?;
|
||||
|
||||
if sd_args.type_of_inference == "inference" {
|
||||
return process_job_inference(&args).await
|
||||
process_job_inference(&args).await
|
||||
} else if sd_args.type_of_inference == "lora" {
|
||||
return process_job_lora(&args).await
|
||||
process_job_lora(&args).await
|
||||
} else if sd_args.type_of_inference == "model" {
|
||||
return process_job_sd(&args).await
|
||||
process_job_sd(&args).await
|
||||
} else {
|
||||
Err(ProcessSingleJobError::Other(anyhow!("inference type doesn't exist!")))
|
||||
}
|
||||
@@ -168,13 +158,14 @@ pub async fn process_job_sd(
|
||||
.map_err(|e| ProcessSingleJobError::from_io_error(e))?;
|
||||
|
||||
let sd_checkpoint_path = work_temp_dir.path().join("sd_checkpoint.safetensors");
|
||||
let vae_path = work_temp_dir.path().join("vae.safetensors");
|
||||
//let vae_path = work_temp_dir.path().join("vae.safetensors");
|
||||
let vae_path = work_temp_dir.path().join("vae.pt"); // TODO: Should this be `.safetensors` or `.pt`?
|
||||
let output_path = work_temp_dir.path().join("output");
|
||||
|
||||
info!("Paths to download to:");
|
||||
info!("sd_checkpoint_path:{}", sd_checkpoint_path.display());
|
||||
info!("vae_path:{}", vae_path.display());
|
||||
info!("output_path:{}", output_path.display());
|
||||
info!("sd_checkpoint_path: {:?}", sd_checkpoint_path);
|
||||
info!("vae_path: {:?}", vae_path);
|
||||
info!("output_path: {:?}", output_path);
|
||||
|
||||
let download_url = match sd_args.maybe_upload_path {
|
||||
Some(val) => { val }
|
||||
@@ -192,12 +183,12 @@ pub async fn process_job_sd(
|
||||
"DOWNLOAD_SCRIPT",
|
||||
"download_internet_file.py"
|
||||
);
|
||||
// Download
|
||||
|
||||
let google_drive_downloader = GoogleDriveDownloadCommand::new(&download_script,
|
||||
None,
|
||||
None,
|
||||
None);
|
||||
None, None, None);
|
||||
|
||||
info!("Downloading {}", download_url);
|
||||
|
||||
let download_filename = match
|
||||
google_drive_downloader.download_file_with_file_name(
|
||||
&download_url,
|
||||
@@ -206,59 +197,69 @@ pub async fn process_job_sd(
|
||||
).await
|
||||
{
|
||||
Ok(filename) => filename,
|
||||
Err(_e) => {
|
||||
return Err(ProcessSingleJobError::from_anyhow_error(anyhow!("Failed to Download")));
|
||||
}
|
||||
Err(_e) => return Err(ProcessSingleJobError::from_anyhow_error(anyhow!("Failed to Download"))),
|
||||
};
|
||||
|
||||
let download_file_path = work_temp_dir.path().join(download_filename);
|
||||
|
||||
if file_exists(download_file_path.as_path()) == false {
|
||||
return Err(
|
||||
ProcessSingleJobError::from_anyhow_error(anyhow!("Failed to Download SD Model from Google"))
|
||||
);
|
||||
return Err(ProcessSingleJobError::from_anyhow_error(anyhow!("Failed to Download SD Model from Google")));
|
||||
}
|
||||
|
||||
info!("File Retrieved at {}", download_file_path.display());
|
||||
|
||||
// download vae for model
|
||||
|
||||
// use this vae doesn't matter though
|
||||
// VAE token for now
|
||||
let vae_token = String::from("weight_rb0959wfzjhk3d1k93hr3s0qw");
|
||||
let model_weight_vae = ModelWeightToken(vae_token);
|
||||
let vae_weight_record = get_weight_by_token(
|
||||
&model_weight_vae,
|
||||
false,
|
||||
&deps.db.mysql_pool
|
||||
).await?;
|
||||
let vae_weight_record = match vae_weight_record {
|
||||
Some(val) => val,
|
||||
None => {
|
||||
return Err(
|
||||
ProcessSingleJobError::from_anyhow_error(anyhow!("no VAE? thats a problem."))
|
||||
);
|
||||
}
|
||||
};
|
||||
args.job_dependencies
|
||||
.buckets
|
||||
.public_bucket_client
|
||||
.download_file_to_disk(&sd_deps.vae_bucket_path, &vae_path)
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!("could not download VAE: {:?}", err);
|
||||
ProcessSingleJobError::from_anyhow_error(anyhow!("could not download VAE: {:?}", err))
|
||||
})?;
|
||||
|
||||
let vae_details = RemoteCloudBucketDetails::new(
|
||||
vae_weight_record.public_bucket_hash.clone(),
|
||||
vae_weight_record.maybe_public_bucket_prefix.clone().unwrap_or_else(|| "".to_string()),
|
||||
vae_weight_record.maybe_public_bucket_extension.clone().unwrap_or_else(|| "".to_string())
|
||||
);
|
||||
info!("VAE downloaded to: {:?}", &vae_path);
|
||||
|
||||
// // use this vae doesn't matter though
|
||||
// // VAE token for now
|
||||
// let vae_token = String::from("REPLACE_ME");
|
||||
// let model_weight_vae = ModelWeightToken(vae_token);
|
||||
// let vae_weight_record = get_weight_by_token(
|
||||
// &model_weight_vae,
|
||||
// false,
|
||||
// &deps.db.mysql_pool
|
||||
// ).await?;
|
||||
// let vae_weight_record = match vae_weight_record {
|
||||
// Some(val) => val,
|
||||
// None => {
|
||||
// return Err(
|
||||
// ProcessSingleJobError::from_anyhow_error(anyhow!("no VAE? thats a problem."))
|
||||
// );
|
||||
// }
|
||||
// };
|
||||
//
|
||||
// let vae_details = RemoteCloudBucketDetails::new(
|
||||
// vae_weight_record.public_bucket_hash.clone(),
|
||||
// vae_weight_record.maybe_public_bucket_prefix.clone().unwrap_or_else(|| "".to_string()),
|
||||
// vae_weight_record.maybe_public_bucket_extension.clone().unwrap_or_else(|| "".to_string())
|
||||
// );
|
||||
//
|
||||
// let remote_cloud_file_client = RemoteCloudFileClient::get_remote_cloud_file_client().await?;
|
||||
// remote_cloud_file_client.download_file(vae_details, path_to_string(vae_path.clone())).await?;
|
||||
|
||||
let remote_cloud_file_client = RemoteCloudFileClient::get_remote_cloud_file_client().await?;
|
||||
remote_cloud_file_client.download_file(vae_details, path_to_string(vae_path.clone())).await?;
|
||||
|
||||
let stderr_output_file = work_temp_dir.path().join("sd_err.txt");
|
||||
let stdout_output_file = work_temp_dir.path().join("sd_out.txt");
|
||||
|
||||
// run inference on loRA downloaded
|
||||
sd_deps.inference_command.execute_inference(InferenceArgs {
|
||||
let exit_status = sd_deps.inference_command.execute_inference(InferenceArgs {
|
||||
work_dir: work_temp_dir.path().to_path_buf(),
|
||||
output_file: output_path.clone(),
|
||||
stderr_output_file,
|
||||
stdout_output_file,
|
||||
stderr_output_file: &stderr_output_file,
|
||||
stdout_output_file: &stdout_output_file,
|
||||
prompt:String::from("This is a green sign that says go this is a test prompt to test the model."),
|
||||
negative_prompt: sd_args.maybe_n_prompt.clone().unwrap_or_default(),
|
||||
number_of_samples:20,
|
||||
@@ -273,14 +274,38 @@ pub async fn process_job_sd(
|
||||
batch_count: sd_args.maybe_batch_count.unwrap_or(1),
|
||||
});
|
||||
|
||||
if !exit_status.is_success() {
|
||||
error!("SD inference failed: {:?}", exit_status);
|
||||
|
||||
let error = ProcessSingleJobError::Other(anyhow!("CommandExitStatus: {:?}", exit_status));
|
||||
|
||||
if let Ok(contents) = read_to_string(&stderr_output_file) {
|
||||
warn!("Captured stderr output: {}", contents);
|
||||
|
||||
//match categorize_error(&contents) {
|
||||
// Some(ProcessSingleJobError::FaceDetectionFailure) => {
|
||||
// warn!("Face not detected in source image");
|
||||
// error = ProcessSingleJobError::FaceDetectionFailure;
|
||||
// }
|
||||
// _ => {}
|
||||
//}
|
||||
}
|
||||
return Err(error);
|
||||
}
|
||||
|
||||
|
||||
// check if file exists if it does not error out....
|
||||
let path = output_path.clone();
|
||||
let file_path = format!("{}_{}.png", path_to_string(path), 0);
|
||||
|
||||
if file_exists(file_path.path()) == false {
|
||||
return Err(
|
||||
ProcessSingleJobError::from_anyhow_error(anyhow!("Failed to Upload Not a SD Model"))
|
||||
);
|
||||
if !file_exists(file_path.path()) {
|
||||
error!("Output file did not exist: {:?}", file_path);
|
||||
|
||||
if let Ok(contents) = read_to_string(&stderr_output_file) {
|
||||
error!("Captured stderr output: {}", contents);
|
||||
}
|
||||
|
||||
return Err(ProcessSingleJobError::from_anyhow_error(anyhow!("Failed to Upload Not a SD Model")));
|
||||
}
|
||||
|
||||
// If it worked and didn't fail! then we should save and create the weight.
|
||||
@@ -340,23 +365,15 @@ pub async fn process_job_lora(
|
||||
let sd_deps = match
|
||||
&args.job_dependencies.job.job_specific_dependencies.maybe_stable_diffusion_dependencies
|
||||
{
|
||||
None => {
|
||||
return Err(ProcessSingleJobError::Other(anyhow!("Missing Job Specific Dependencies")));
|
||||
}
|
||||
Some(val) => { val }
|
||||
Some(val) => val,
|
||||
None => return Err(ProcessSingleJobError::Other(anyhow!("Missing Job Specific Dependencies"))),
|
||||
};
|
||||
|
||||
let creator_ip_address = &job.creator_ip_address;
|
||||
let creator_user_token: UserToken;
|
||||
|
||||
match &job.maybe_creator_user_token {
|
||||
Some(token) => {
|
||||
creator_user_token = UserToken::new_from_str(token);
|
||||
}
|
||||
None => {
|
||||
return Err(ProcessSingleJobError::InvalidJob(anyhow!("Missing Creator User Token")));
|
||||
}
|
||||
}
|
||||
let creator_user_token = match job.maybe_creator_user_token.as_deref() {
|
||||
Some(token) => UserToken::new_from_str(token),
|
||||
None => return Err(ProcessSingleJobError::InvalidJob(anyhow!("Missing Creator User Token"))),
|
||||
};
|
||||
|
||||
|
||||
let work_temp_dir = format!("temp_stable_diffusion_inference_{}", job.id.0);
|
||||
@@ -425,7 +442,10 @@ pub async fn process_job_lora(
|
||||
|
||||
// download model + vae for lora
|
||||
// use lora with whatever checkpoint make it fixed id if it works submit it
|
||||
let weight_token_sd_model_token = ModelWeightToken(String::from("weight_dmmthavhawqc2hj7yqyemcbf8")); // any generic model.
|
||||
let weight_token_sd_model_token = ModelWeightToken::new_from_str(&sd_deps.predefined_sd_weight_token);
|
||||
|
||||
info!("Using predefined SD weight token: {:?}", &weight_token_sd_model_token);
|
||||
|
||||
let sd_weight_record = get_weight_by_token(
|
||||
&weight_token_sd_model_token,
|
||||
false,
|
||||
@@ -450,42 +470,52 @@ pub async fn process_job_lora(
|
||||
remote_cloud_file_client.download_file(sd_weight_details, path_to_string(sd_checkpoint_path.clone())).await?;
|
||||
|
||||
|
||||
// use this vae doesn't matter though
|
||||
// VAE token for now
|
||||
let vae_token = String::from("weight_rb0959wfzjhk3d1k93hr3s0qw");
|
||||
let model_weight_vae = ModelWeightToken(vae_token);
|
||||
let vae_weight_record = get_weight_by_token(
|
||||
&model_weight_vae,
|
||||
false,
|
||||
&deps.db.mysql_pool
|
||||
).await?;
|
||||
let vae_weight_record = match vae_weight_record {
|
||||
Some(val) => val,
|
||||
None => {
|
||||
return Err(
|
||||
ProcessSingleJobError::from_anyhow_error(anyhow!("no VAE? thats a problem."))
|
||||
);
|
||||
}
|
||||
};
|
||||
args.job_dependencies
|
||||
.buckets
|
||||
.public_bucket_client
|
||||
.download_file_to_disk(&sd_deps.vae_bucket_path, &vae_path)
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!("could not download VAE: {:?}", err);
|
||||
ProcessSingleJobError::from_anyhow_error(anyhow!("could not download VAE: {:?}", err))
|
||||
})?;
|
||||
|
||||
let vae_details = RemoteCloudBucketDetails::new(
|
||||
vae_weight_record.public_bucket_hash.clone(),
|
||||
vae_weight_record.maybe_public_bucket_prefix.clone().unwrap_or_else(|| "".to_string()),
|
||||
vae_weight_record.maybe_public_bucket_extension.clone().unwrap_or_else(|| "".to_string())
|
||||
);
|
||||
|
||||
let remote_cloud_file_client = RemoteCloudFileClient::get_remote_cloud_file_client().await?;
|
||||
remote_cloud_file_client.download_file(vae_details, path_to_string(vae_path.clone())).await?;
|
||||
// // use this vae doesn't matter though
|
||||
// // VAE token for now
|
||||
// let vae_token = String::from("REPLACE_ME");
|
||||
// let model_weight_vae = ModelWeightToken(vae_token);
|
||||
// let vae_weight_record = get_weight_by_token(
|
||||
// &model_weight_vae,
|
||||
// false,
|
||||
// &deps.db.mysql_pool
|
||||
// ).await?;
|
||||
// let vae_weight_record = match vae_weight_record {
|
||||
// Some(val) => val,
|
||||
// None => {
|
||||
// return Err(
|
||||
// ProcessSingleJobError::from_anyhow_error(anyhow!("no VAE? thats a problem."))
|
||||
// );
|
||||
// }
|
||||
// };
|
||||
//
|
||||
// let vae_details = RemoteCloudBucketDetails::new(
|
||||
// vae_weight_record.public_bucket_hash.clone(),
|
||||
// vae_weight_record.maybe_public_bucket_prefix.clone().unwrap_or_else(|| "".to_string()),
|
||||
// vae_weight_record.maybe_public_bucket_extension.clone().unwrap_or_else(|| "".to_string())
|
||||
// );
|
||||
//
|
||||
// let remote_cloud_file_client = RemoteCloudFileClient::get_remote_cloud_file_client().await?;
|
||||
// remote_cloud_file_client.download_file(vae_details, path_to_string(vae_path.clone())).await?;
|
||||
|
||||
let stderr_output_file = work_temp_dir.path().join("sd_err.txt");
|
||||
let stdout_output_file = work_temp_dir.path().join("sd_out.txt");
|
||||
|
||||
// run inference on loRA downloaded
|
||||
sd_deps.inference_command.execute_inference(InferenceArgs {
|
||||
let exit_status = sd_deps.inference_command.execute_inference(InferenceArgs {
|
||||
work_dir: work_temp_dir.path().to_path_buf(),
|
||||
output_file: output_path.clone(),
|
||||
stderr_output_file,
|
||||
stdout_output_file,
|
||||
stderr_output_file: &stderr_output_file,
|
||||
stdout_output_file: &stdout_output_file,
|
||||
prompt:String::from("This is a green sign that says go this is a test prompt to test the model."),
|
||||
negative_prompt: sd_args.maybe_n_prompt.clone().unwrap_or_default(),
|
||||
number_of_samples:20,
|
||||
@@ -500,6 +530,25 @@ pub async fn process_job_lora(
|
||||
batch_count: sd_args.maybe_batch_count.unwrap_or(1),
|
||||
});
|
||||
|
||||
if !exit_status.is_success() {
|
||||
error!("SD inference failed: {:?}", exit_status);
|
||||
|
||||
let error = ProcessSingleJobError::Other(anyhow!("CommandExitStatus: {:?}", exit_status));
|
||||
|
||||
if let Ok(contents) = read_to_string(&stderr_output_file) {
|
||||
warn!("Captured stderr output: {}", contents);
|
||||
|
||||
//match categorize_error(&contents) {
|
||||
// Some(ProcessSingleJobError::FaceDetectionFailure) => {
|
||||
// warn!("Face not detected in source image");
|
||||
// error = ProcessSingleJobError::FaceDetectionFailure;
|
||||
// }
|
||||
// _ => {}
|
||||
//}
|
||||
}
|
||||
return Err(error);
|
||||
}
|
||||
|
||||
// check if file exists if it does not error out....
|
||||
let path = output_path.clone();
|
||||
let file_path = format!("{}_{}.png", path_to_string(path), 0);
|
||||
@@ -573,22 +622,22 @@ pub async fn process_job_inference(
|
||||
Some(val) => { val }
|
||||
};
|
||||
|
||||
let creator_user_token: UserToken;
|
||||
let anon_user_token: Option<AnonymousVisitorTrackingToken>;
|
||||
let _creator_user_token: UserToken;
|
||||
let _anon_user_token: Option<AnonymousVisitorTrackingToken>;
|
||||
|
||||
match &job.maybe_creator_anonymous_visitor_token {
|
||||
Some(token) => {
|
||||
let anonymous_visitor_token = AnonymousVisitorTrackingToken::new_from_str(token);
|
||||
anon_user_token = Some(anonymous_visitor_token);
|
||||
_anon_user_token = Some(anonymous_visitor_token);
|
||||
}
|
||||
None => {
|
||||
anon_user_token = None;
|
||||
_anon_user_token = None;
|
||||
}
|
||||
}
|
||||
|
||||
match &job.maybe_creator_user_token {
|
||||
Some(token) => {
|
||||
creator_user_token = UserToken::new_from_str(token);
|
||||
_creator_user_token = UserToken::new_from_str(token);
|
||||
}
|
||||
None => {
|
||||
return Err(ProcessSingleJobError::InvalidJob(anyhow!("Missing Creator User Token")));
|
||||
@@ -711,32 +760,42 @@ pub async fn process_job_inference(
|
||||
}
|
||||
}
|
||||
|
||||
// VAE token for now
|
||||
let vae_token = String::from("weight_rb0959wfzjhk3d1k93hr3s0qw");
|
||||
let model_weight_vae = ModelWeightToken(vae_token);
|
||||
args.job_dependencies
|
||||
.buckets
|
||||
.public_bucket_client
|
||||
.download_file_to_disk(&sd_deps.vae_bucket_path, &vae_path)
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!("could not download VAE: {:?}", err);
|
||||
ProcessSingleJobError::from_anyhow_error(anyhow!("could not download VAE: {:?}", err))
|
||||
})?;
|
||||
|
||||
let vae_weight_record = get_weight_by_token(
|
||||
&model_weight_vae,
|
||||
false,
|
||||
&deps.db.mysql_pool
|
||||
).await?;
|
||||
|
||||
let vae_weight_record = match vae_weight_record {
|
||||
Some(val) => val,
|
||||
None => {
|
||||
return Err(
|
||||
ProcessSingleJobError::from_anyhow_error(anyhow!("no VAE? thats a problem."))
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
let vae_details = RemoteCloudBucketDetails::new(
|
||||
vae_weight_record.public_bucket_hash.clone(),
|
||||
vae_weight_record.maybe_public_bucket_prefix.clone().unwrap_or_else(|| "".to_string()),
|
||||
vae_weight_record.maybe_public_bucket_extension.clone().unwrap_or_else(|| "".to_string())
|
||||
);
|
||||
|
||||
remote_cloud_file_client.download_file(vae_details, path_to_string(vae_path.clone())).await?;
|
||||
// // VAE token for now
|
||||
// let vae_token = String::from("REPLACE_ME");
|
||||
// let model_weight_vae = ModelWeightToken(vae_token);
|
||||
//
|
||||
// let vae_weight_record = get_weight_by_token(
|
||||
// &model_weight_vae,
|
||||
// false,
|
||||
// &deps.db.mysql_pool
|
||||
// ).await?;
|
||||
//
|
||||
// let vae_weight_record = match vae_weight_record {
|
||||
// Some(val) => val,
|
||||
// None => {
|
||||
// return Err(
|
||||
// ProcessSingleJobError::from_anyhow_error(anyhow!("no VAE? thats a problem."))
|
||||
// );
|
||||
// }
|
||||
// };
|
||||
//
|
||||
// let vae_details = RemoteCloudBucketDetails::new(
|
||||
// vae_weight_record.public_bucket_hash.clone(),
|
||||
// vae_weight_record.maybe_public_bucket_prefix.clone().unwrap_or_else(|| "".to_string()),
|
||||
// vae_weight_record.maybe_public_bucket_extension.clone().unwrap_or_else(|| "".to_string())
|
||||
// );
|
||||
//
|
||||
// remote_cloud_file_client.download_file(vae_details, path_to_string(vae_path.clone())).await?;
|
||||
|
||||
let prompt = match sd_args.maybe_prompt {
|
||||
Some(val) => val,
|
||||
@@ -755,11 +814,11 @@ pub async fn process_job_inference(
|
||||
|
||||
let inference_start_time = Instant::now();
|
||||
|
||||
sd_deps.inference_command.execute_inference(InferenceArgs {
|
||||
let exit_status = sd_deps.inference_command.execute_inference(InferenceArgs {
|
||||
work_dir: work_temp_dir.path().to_path_buf(),
|
||||
output_file: output_path.clone(),
|
||||
stderr_output_file,
|
||||
stdout_output_file,
|
||||
stderr_output_file: &stderr_output_file,
|
||||
stdout_output_file: &stdout_output_file,
|
||||
prompt: prompt.clone(),
|
||||
negative_prompt: sd_args.maybe_n_prompt.clone().unwrap_or_default(),
|
||||
number_of_samples,
|
||||
@@ -774,16 +833,33 @@ pub async fn process_job_inference(
|
||||
batch_count: sd_args.maybe_batch_count.unwrap_or(1),
|
||||
});
|
||||
|
||||
if !exit_status.is_success() {
|
||||
error!("SD inference failed: {:?}", exit_status);
|
||||
|
||||
|
||||
let error = ProcessSingleJobError::Other(anyhow!("CommandExitStatus: {:?}", exit_status));
|
||||
|
||||
// hack to check the directory before clean up.
|
||||
if let Ok(contents) = read_to_string(&stderr_output_file) {
|
||||
warn!("Captured stderr output: {}", contents);
|
||||
|
||||
//match categorize_error(&contents) {
|
||||
// Some(ProcessSingleJobError::FaceDetectionFailure) => {
|
||||
// warn!("Face not detected in source image");
|
||||
// error = ProcessSingleJobError::FaceDetectionFailure;
|
||||
// }
|
||||
// _ => {}
|
||||
//}
|
||||
}
|
||||
return Err(error);
|
||||
}
|
||||
|
||||
|
||||
// hack to check the directory before clean up.
|
||||
// let thirtyMinutes = 1800;
|
||||
// thread::sleep(Duration::from_secs(thirtyMinutes));
|
||||
// upload media and create a record.
|
||||
|
||||
let inference_duration = Instant::now().duration_since(inference_start_time);
|
||||
let creator_ip_address = &job.creator_ip_address;
|
||||
|
||||
// run a for loop for output images output_0 in the folder then use upload media.
|
||||
// pngs
|
||||
|
||||
@@ -799,12 +875,12 @@ pub async fn process_job_inference(
|
||||
width: sd_args.maybe_width.unwrap_or(512),
|
||||
height: sd_args.maybe_height.unwrap_or(512),
|
||||
seed: sd_args.maybe_seed.unwrap_or(1),
|
||||
number_of_samples: number_of_samples,
|
||||
number_of_samples,
|
||||
};
|
||||
|
||||
let inputs = match serde_json::to_string(&inputs) {
|
||||
Ok(result) => result,
|
||||
Err(err) => {
|
||||
Err(_err) => {
|
||||
return Err(
|
||||
ProcessSingleJobError::from_anyhow_error(anyhow!("couldn't serialize metadata."))
|
||||
);
|
||||
@@ -836,7 +912,7 @@ pub async fn process_job_inference(
|
||||
// extra_file_modification_info: todo!(), // JSON ENCODED STRUCT
|
||||
let media_file_token = insert_media_file_generic(InsertArgs {
|
||||
pool: mysql_pool,
|
||||
job: job,
|
||||
job,
|
||||
media_type: MediaFileType::Image,
|
||||
origin_category: MediaFileOriginCategory::Upload,
|
||||
origin_product_category: MediaFileOriginProductCategory::ImageGeneration,
|
||||
@@ -849,17 +925,13 @@ pub async fn process_job_inference(
|
||||
maybe_duration_millis: Some(inference_duration.as_millis() as u64),
|
||||
maybe_audio_encoding: None,
|
||||
maybe_video_encoding: None,
|
||||
maybe_frame_width: Some(sd_args.maybe_width.unwrap_or(512) as u32),
|
||||
maybe_frame_height: Some(sd_args.maybe_height.unwrap_or(512) as u32),
|
||||
maybe_frame_width: Some(sd_args.maybe_width.unwrap_or(512)),
|
||||
maybe_frame_height: Some(sd_args.maybe_height.unwrap_or(512)),
|
||||
checksum_sha2: metadata.sha256_checksum.as_str(),
|
||||
public_bucket_directory_hash: bucket_details.object_hash.as_str(),
|
||||
maybe_public_bucket_prefix: Some(bucket_details.prefix.as_str()),
|
||||
maybe_public_bucket_extension: Some(bucket_details.suffix.as_str()),
|
||||
extra_file_modification_info: Some(&inputs),
|
||||
maybe_creator_user_token: Some(&creator_user_token),
|
||||
maybe_creator_anonymous_visitor_token: anon_user_token.as_ref(),
|
||||
creator_ip_address: creator_ip_address,
|
||||
creator_set_visibility: args.job.creator_set_visibility,
|
||||
maybe_creator_file_synthetic_id_category: IdCategory::MediaFile,
|
||||
maybe_creator_category_synthetic_id_category: IdCategory::ModelWeights,
|
||||
maybe_mod_user_token: None,
|
||||
@@ -883,7 +955,7 @@ pub async fn process_job_inference(
|
||||
|
||||
let batch_token = match batch_token_result {
|
||||
Ok(v) => { v.to_string() }
|
||||
Err(err) => {
|
||||
Err(_err) => {
|
||||
return Err(
|
||||
ProcessSingleJobError::from_anyhow_error(
|
||||
anyhow!("No batch token? something has failed.")
|
||||
@@ -897,7 +969,7 @@ pub async fn process_job_inference(
|
||||
entity_type: InferenceResultType::MediaFile,
|
||||
entity_token: batch_token,
|
||||
}),
|
||||
inference_duration: inference_duration,
|
||||
inference_duration,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -914,9 +986,6 @@ mod tests {
|
||||
};
|
||||
use errors::AnyhowResult;
|
||||
|
||||
#[ignore]
|
||||
#[tokio::test]
|
||||
async fn test_json_extra_info() -> AnyhowResult<()> {}
|
||||
#[ignore]
|
||||
#[tokio::test]
|
||||
async fn test_seed_weights_files() -> AnyhowResult<()> {
|
||||
|
||||
+8
-9
@@ -1,4 +1,3 @@
|
||||
|
||||
use std::collections::HashSet;
|
||||
use std::env;
|
||||
use std::ffi::OsString;
|
||||
@@ -13,10 +12,9 @@ use subprocess::{Popen, PopenConfig, Redirection};
|
||||
|
||||
use errors::AnyhowResult;
|
||||
use filesys::path_to_string::path_to_string;
|
||||
use subprocess_common::command_exit_status::CommandExitStatus;
|
||||
use subprocess_common::docker_options::{DockerFilesystemMount, DockerGpu, DockerOptions};
|
||||
|
||||
use crate::job::job_loop::command_exit_status::CommandExitStatus;
|
||||
|
||||
// These environment vars are not copied over to the subprocess
|
||||
// TODO/FIXME(bt, 2023-05-28): This is horrific security!
|
||||
static IGNORED_ENVIRONMENT_VARS : Lazy<HashSet<String>> = Lazy::new(|| {
|
||||
@@ -66,14 +64,14 @@ pub enum ExecutableOrCommand {
|
||||
Command(String),
|
||||
}
|
||||
|
||||
pub struct InferenceArgs {
|
||||
pub struct InferenceArgs<'a> {
|
||||
/// --source_image: path to the input image (or video)
|
||||
/// --result_dir: path to directory work is performed
|
||||
pub work_dir: PathBuf,
|
||||
/// --result_file: path to final file output
|
||||
pub output_file: PathBuf,
|
||||
pub stdout_output_file: PathBuf,
|
||||
pub stderr_output_file: PathBuf,
|
||||
pub stdout_output_file: &'a PathBuf,
|
||||
pub stderr_output_file: &'a PathBuf,
|
||||
pub prompt: String,
|
||||
pub negative_prompt:String,
|
||||
pub number_of_samples:u32,
|
||||
@@ -274,10 +272,11 @@ impl StableDiffusionInferenceCommand {
|
||||
|
||||
info!("stderr will be written to file: {}", path_to_string(args.stderr_output_file.clone()));
|
||||
|
||||
let stderr_file = File::create(&args.stderr_output_file)?;
|
||||
let stdout_file = File::create(&args.stdout_output_file)?;
|
||||
//let stdout_file = File::create(args.stdout_output_file)?;
|
||||
//config.stdout = Redirection::File(stdout_file);
|
||||
|
||||
let stderr_file = File::create(args.stderr_output_file)?;
|
||||
config.stderr = Redirection::File(stderr_file);
|
||||
config.stdout = Redirection::File(stdout_file);
|
||||
|
||||
if !env_vars.is_empty() {
|
||||
config.env = Some(env_vars);
|
||||
|
||||
+6
@@ -7,6 +7,9 @@ use crate::job::job_types::image_generation::sd::sd_inference_command::StableDif
|
||||
pub struct StableDiffusionDependencies {
|
||||
pub downloaders: StableDiffusionDownloaders,
|
||||
pub inference_command: StableDiffusionInferenceCommand,
|
||||
pub vae_bucket_path: String,
|
||||
pub predefined_sd_weight_token: String,
|
||||
pub predefined_lora_weight_token: String,
|
||||
}
|
||||
|
||||
impl StableDiffusionDependencies {
|
||||
@@ -14,6 +17,9 @@ impl StableDiffusionDependencies {
|
||||
Ok(Self {
|
||||
downloaders: StableDiffusionDownloaders::build_all_from_env(),
|
||||
inference_command: StableDiffusionInferenceCommand::from_env()?,
|
||||
vae_bucket_path: easyenv::get_env_string_required("SD_VAE_BUCKET_PATH")?,
|
||||
predefined_sd_weight_token: easyenv::get_env_string_required("SD_PREDEFINED_SD_WEIGHT_TOKEN")?,
|
||||
predefined_lora_weight_token: easyenv::get_env_string_required("SD_PREDEFINED_LORA_WEIGHT_TOKEN")?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
+1
-2
@@ -12,10 +12,9 @@ use subprocess::{Popen, PopenConfig, Redirection};
|
||||
|
||||
use errors::AnyhowResult;
|
||||
use filesys::path_to_string::path_to_string;
|
||||
use subprocess_common::command_exit_status::CommandExitStatus;
|
||||
use subprocess_common::docker_options::{DockerFilesystemMount, DockerGpu, DockerOptions};
|
||||
|
||||
use crate::job::job_loop::command_exit_status::CommandExitStatus;
|
||||
|
||||
// These environment vars are not copied over to the subprocess
|
||||
// TODO/FIXME(bt, 2023-05-28): This is horrific security!
|
||||
static IGNORED_ENVIRONMENT_VARS : Lazy<HashSet<String>> = Lazy::new(|| {
|
||||
|
||||
+3
-2
@@ -4,15 +4,16 @@ use std::ffi::OsString;
|
||||
use std::fs::File;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::time::Duration;
|
||||
|
||||
use anyhow::anyhow;
|
||||
use log::info;
|
||||
use once_cell::sync::Lazy;
|
||||
use subprocess::{Popen, PopenConfig, Redirection};
|
||||
|
||||
use errors::AnyhowResult;
|
||||
use filesys::path_to_string::path_to_string;
|
||||
use subprocess_common::command_exit_status::CommandExitStatus;
|
||||
use subprocess_common::docker_options::{DockerFilesystemMount, DockerGpu, DockerOptions};
|
||||
use crate::job::job_loop::command_exit_status::CommandExitStatus;
|
||||
|
||||
|
||||
// These environment vars are not copied over to the subprocess
|
||||
// TODO/FIXME(bt, 2023-05-28): This is horrific security!
|
||||
|
||||
@@ -5,3 +5,4 @@ pub(crate) mod videofilter;
|
||||
pub mod image_generation;
|
||||
pub(crate) mod mocap;
|
||||
pub(crate) mod workflow;
|
||||
pub mod format_conversion;
|
||||
|
||||
+1
-2
@@ -12,10 +12,9 @@ use subprocess::{Popen, PopenConfig, Redirection};
|
||||
|
||||
use errors::AnyhowResult;
|
||||
use filesys::path_to_string::path_to_string;
|
||||
use subprocess_common::command_exit_status::CommandExitStatus;
|
||||
use subprocess_common::docker_options::{DockerFilesystemMount, DockerGpu, DockerOptions};
|
||||
|
||||
use crate::job::job_loop::command_exit_status::CommandExitStatus;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum ExecutableOrCommand {
|
||||
/// Eg. `inference.py`
|
||||
|
||||
+1
-2
@@ -12,10 +12,9 @@ use subprocess::{Popen, PopenConfig, Redirection};
|
||||
|
||||
use errors::AnyhowResult;
|
||||
use filesys::path_to_string::path_to_string;
|
||||
use subprocess_common::command_exit_status::CommandExitStatus;
|
||||
use subprocess_common::docker_options::{DockerFilesystemMount, DockerGpu, DockerOptions};
|
||||
|
||||
use crate::job::job_loop::command_exit_status::CommandExitStatus;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum ExecutableOrCommand {
|
||||
/// Eg. `inference.py`
|
||||
|
||||
+1
-2
@@ -13,10 +13,9 @@ use subprocess::{Popen, PopenConfig, Redirection};
|
||||
use errors::AnyhowResult;
|
||||
use filesys::path_to_string::path_to_string;
|
||||
use mysql_queries::payloads::generic_inference_args::generic_inference_args::FundamentalFrequencyMethodForJob;
|
||||
use subprocess_common::command_exit_status::CommandExitStatus;
|
||||
use subprocess_common::docker_options::{DockerFilesystemMount, DockerGpu, DockerOptions};
|
||||
|
||||
use crate::job::job_loop::command_exit_status::CommandExitStatus;
|
||||
|
||||
// These environment vars are not copied over to the subprocess
|
||||
// TODO/FIXME(bt, 2023-05-28): This is horrific security!
|
||||
static IGNORED_ENVIRONMENT_VARS : Lazy<HashSet<String>> = Lazy::new(|| {
|
||||
|
||||
+1
-2
@@ -12,10 +12,9 @@ use subprocess::{Popen, PopenConfig, Redirection};
|
||||
|
||||
use errors::AnyhowResult;
|
||||
use filesys::path_to_string::path_to_string;
|
||||
use subprocess_common::command_exit_status::CommandExitStatus;
|
||||
use subprocess_common::docker_options::{DockerEnvVar, DockerFilesystemMount, DockerGpu, DockerOptions};
|
||||
|
||||
use crate::job::job_loop::command_exit_status::CommandExitStatus;
|
||||
|
||||
// These environment vars are not copied over to the subprocess
|
||||
// TODO/FIXME(bt, 2023-05-28): This is horrific security!
|
||||
static IGNORED_ENVIRONMENT_VARS : Lazy<HashSet<String>> = Lazy::new(|| {
|
||||
|
||||
+1
-2
@@ -12,10 +12,9 @@ use subprocess::{Popen, PopenConfig, Redirection};
|
||||
|
||||
use errors::AnyhowResult;
|
||||
use filesys::path_to_string::path_to_string;
|
||||
use subprocess_common::command_exit_status::CommandExitStatus;
|
||||
use subprocess_common::docker_options::{DockerFilesystemMount, DockerGpu, DockerOptions};
|
||||
|
||||
use crate::job::job_loop::command_exit_status::CommandExitStatus;
|
||||
|
||||
// These environment vars are not copied over to the subprocess
|
||||
// TODO/FIXME(bt, 2023-05-28): This is horrific security!
|
||||
static IGNORED_ENVIRONMENT_VARS : Lazy<HashSet<String>> = Lazy::new(|| {
|
||||
|
||||
+122
@@ -1,15 +1,137 @@
|
||||
use std::path::PathBuf;
|
||||
use errors::AnyhowResult;
|
||||
|
||||
use crate::job::job_types::workflow::comfy_ui::comfy_ui_inference_command::ComfyInferenceCommand;
|
||||
|
||||
pub struct ComfyDependencies {
|
||||
pub inference_command: ComfyInferenceCommand,
|
||||
pub dependency_tokens: RequiredModels,
|
||||
}
|
||||
|
||||
impl ComfyDependencies {
|
||||
pub fn setup() -> AnyhowResult<Self> {
|
||||
Ok(Self {
|
||||
inference_command: ComfyInferenceCommand::from_env()?,
|
||||
dependency_tokens: RequiredModels::init(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub struct ComfyDependency {
|
||||
pub(crate) location: PathBuf,
|
||||
pub(crate) url: String,
|
||||
}
|
||||
|
||||
pub struct RequiredModels {
|
||||
pub(crate) comfy: Vec<ComfyDependency>
|
||||
}
|
||||
|
||||
impl RequiredModels {
|
||||
pub fn init() -> Self {
|
||||
Self {
|
||||
comfy: vec![
|
||||
ComfyDependency {
|
||||
location: PathBuf::from("models/loras/v3_sd15_adapter.ckpt"),
|
||||
url: "https://storage.googleapis.com/vocodes-public/comfyui-deps/v3_sd15_adapter.ckpt".to_string(),
|
||||
},
|
||||
ComfyDependency {
|
||||
location: PathBuf::from("models/embeddings/badhandv4.pt"),
|
||||
url: "https://storage.googleapis.com/vocodes-public/comfyui-deps/badhandv4.pt".to_string(),
|
||||
},
|
||||
ComfyDependency {
|
||||
location: PathBuf::from("models/embeddings/easynegative.safetensors"),
|
||||
url: "https://storage.googleapis.com/vocodes-public/comfyui-deps/easynegative.safetensors".to_string(),
|
||||
},
|
||||
ComfyDependency {
|
||||
location: PathBuf::from("models/controlnet/v3_sd15_sparsectrl_scribble.ckpt"),
|
||||
url: "https://storage.googleapis.com/vocodes-public/comfyui-deps/v3_sd15_sparsectrl_scribble.ckpt".to_string(),
|
||||
},
|
||||
ComfyDependency {
|
||||
location: PathBuf::from("models/controlnet/control_v11f1e_sd15_tile.bin"),
|
||||
url: "https://storage.googleapis.com/vocodes-public/comfyui-deps/control_v11f1e_sd15_tile.bin".to_string(),
|
||||
},
|
||||
ComfyDependency {
|
||||
location: PathBuf::from("models/controlnet/control_v11p_sd15_softedge.safetensors"),
|
||||
url: "https://storage.googleapis.com/vocodes-public/comfyui-deps/control_v11p_sd15_softedge.safetensors".to_string(),
|
||||
},
|
||||
ComfyDependency {
|
||||
location: PathBuf::from("models/controlnet/control_v11p_sd15s2_lineart_anime.safetensors"),
|
||||
url: "https://storage.googleapis.com/vocodes-public/comfyui-deps/control_v11p_sd15s2_lineart_anime.safetensors".to_string(),
|
||||
},
|
||||
ComfyDependency {
|
||||
location: PathBuf::from("models/controlnet/control_v11p_sd15_lineart.safetensors"),
|
||||
url: "https://storage.googleapis.com/vocodes-public/comfyui-deps/control_v11p_sd15_lineart.safetensors".to_string(),
|
||||
},
|
||||
ComfyDependency {
|
||||
location: PathBuf::from("models/controlnet/control_v11f1p_sd15_depth.safetensors"),
|
||||
url: "https://storage.googleapis.com/vocodes-public/comfyui-deps/control_v11f1p_sd15_depth.safetensors".to_string(),
|
||||
},
|
||||
ComfyDependency {
|
||||
location: PathBuf::from("models/controlnet/control_v11p_sd15_openpose.safetensors"),
|
||||
url: "https://storage.googleapis.com/vocodes-public/comfyui-deps/control_v11p_sd15_openpose.safetensors".to_string(),
|
||||
},
|
||||
ComfyDependency {
|
||||
location: PathBuf::from("models/controlnet/animatediff_controlnet.ckpt"),
|
||||
url: "https://storage.googleapis.com/vocodes-public/comfyui-deps/animatediff_controlnet.ckpt".to_string(),
|
||||
},
|
||||
ComfyDependency {
|
||||
location: PathBuf::from("models/controlnet/control_v2p_sd15_mediapipe_face.safetensors"),
|
||||
url: "https://storage.googleapis.com/vocodes-public/comfyui-deps/control_v2p_sd15_mediapipe_face.safetensors".to_string(),
|
||||
},
|
||||
ComfyDependency {
|
||||
location: PathBuf::from("models/controlnet/ControlNetHED.pth"),
|
||||
url: "https://storage.googleapis.com/vocodes-public/comfyui-deps/ControlNetHED.pth".to_string(),
|
||||
},
|
||||
ComfyDependency {
|
||||
location: PathBuf::from("models/ipadapter/ip-adapter_sd15_light.safetensors"),
|
||||
url: "https://storage.googleapis.com/vocodes-public/comfyui-deps/ip-adapter_sd15_light.safetensors".to_string(),
|
||||
},
|
||||
ComfyDependency {
|
||||
location: PathBuf::from("models/clip_vision/CLIP-ViT-H-14-laion2B-s32B-b79K.safetensors"),
|
||||
url: "https://storage.googleapis.com/vocodes-public/comfyui-deps/model.safetensors".to_string(),
|
||||
},
|
||||
ComfyDependency {
|
||||
location: PathBuf::from("custom_nodes/ComfyUI-AnimateDiff-Evolved/models/v3_sd15_mm.ckpt"),
|
||||
url: "https://storage.googleapis.com/vocodes-public/comfyui-deps/v3_sd15_mm.ckpt".to_string(),
|
||||
},
|
||||
ComfyDependency {
|
||||
location: PathBuf::from("custom_nodes/comfyui_controlnet_aux/ckpts/hr16/DWPose-TorchScript-BatchSize5/dw-ll_ucoco_384_bs5.torchscript.pt"),
|
||||
url: "https://storage.googleapis.com/vocodes-public/comfyui-deps/dw-ll_ucoco_384_bs5.torchscript.pt".to_string(),
|
||||
},
|
||||
ComfyDependency {
|
||||
location: PathBuf::from("custom_nodes/comfyui_controlnet_aux/ckpts/yzd-v/DWPose/yolox_l.onnx"),
|
||||
url: "https://storage.googleapis.com/vocodes-public/comfyui-deps/yolox_l.onnx".to_string(),
|
||||
},
|
||||
ComfyDependency {
|
||||
location: PathBuf::from("custom_nodes/comfyui_controlnet_aux/ckpts/LiheYoung/Depth-Anything/checkpoints/depth_anything_vitl14.pth"),
|
||||
url: "https://storage.googleapis.com/vocodes-public/comfyui-deps/depth_anything_vitl14.pth".to_string(),
|
||||
},
|
||||
ComfyDependency {
|
||||
location: PathBuf::from("custom_nodes/comfyui_controlnet_aux/ckpts/lllyasviel/Annotators/sk_model2.pth"),
|
||||
url: "https://storage.googleapis.com/vocodes-public/comfyui-deps/sk_model2.pth".to_string(),
|
||||
},
|
||||
ComfyDependency {
|
||||
location: PathBuf::from("custom_nodes/comfyui_controlnet_aux/ckpts/lllyasviel/Annotators/sk_model.pth"),
|
||||
url: "https://storage.googleapis.com/vocodes-public/comfyui-deps/sk_model.pth".to_string(),
|
||||
},
|
||||
ComfyDependency {
|
||||
location: PathBuf::from("custom_nodes/comfyui_controlnet_aux/ckpts/lllyasviel/Annotators/netG.pth"),
|
||||
url: "https://storage.googleapis.com/vocodes-public/comfyui-deps/netG.pth".to_string(),
|
||||
},
|
||||
ComfyDependency {
|
||||
location: PathBuf::from("custom_nodes/comfyui_controlnet_aux/ckpts/lllyasviel/Annotators/ControlNetHED.pth"),
|
||||
url: "https://storage.googleapis.com/vocodes-public/comfyui-deps/ControlNetHED.pth".to_string(),
|
||||
},
|
||||
ComfyDependency {
|
||||
location: PathBuf::from("custom_nodes/ComfyUI-Frame-Interpolation/ckpts/rife/rife47.pth"),
|
||||
url: "https://storage.googleapis.com/vocodes-public/comfyui-deps/rife47.pth".to_string(),
|
||||
},
|
||||
ComfyDependency {
|
||||
location: PathBuf::from("input/example.png"),
|
||||
url: "https://storage.googleapis.com/vocodes-public/comfyui-deps/example.png".to_string(),
|
||||
},
|
||||
],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+11
-2
@@ -12,10 +12,9 @@ use subprocess::{Popen, PopenConfig, Redirection};
|
||||
|
||||
use errors::AnyhowResult;
|
||||
use filesys::path_to_string::path_to_string;
|
||||
use subprocess_common::command_exit_status::CommandExitStatus;
|
||||
use subprocess_common::docker_options::{DockerFilesystemMount, DockerGpu, DockerOptions};
|
||||
|
||||
use crate::job::job_loop::command_exit_status::CommandExitStatus;
|
||||
|
||||
// These environment vars are not copied over to the subprocess
|
||||
// TODO/FIXME(bt, 2023-05-28): This is horrific security!
|
||||
static IGNORED_ENVIRONMENT_VARS : Lazy<HashSet<String>> = Lazy::new(|| {
|
||||
@@ -39,6 +38,9 @@ pub struct ComfyInferenceCommand {
|
||||
/// A single executable script or a much larger bash command.
|
||||
executable_or_command: ExecutableOrCommand,
|
||||
|
||||
// Where to mount the filesystem
|
||||
pub(crate) mounts_directory: PathBuf,
|
||||
|
||||
/// Config file to use
|
||||
config_path: Option<PathBuf>,
|
||||
|
||||
@@ -64,6 +66,7 @@ pub enum ExecutableOrCommand {
|
||||
|
||||
pub struct InferenceArgs<'s> {
|
||||
pub stderr_output_file: &'s Path,
|
||||
pub stdout_output_file: &'s Path,
|
||||
}
|
||||
|
||||
impl ComfyInferenceCommand {
|
||||
@@ -105,10 +108,14 @@ impl ComfyInferenceCommand {
|
||||
}
|
||||
});
|
||||
|
||||
let mounts_directory = easyenv::get_env_pathbuf_required(
|
||||
"COMFY_MOUNTS_DIRECTORY")?;
|
||||
|
||||
Ok(Self {
|
||||
comfy_root_code_directory,
|
||||
executable_or_command,
|
||||
config_path,
|
||||
mounts_directory,
|
||||
maybe_virtual_env_activation_command,
|
||||
maybe_docker_options,
|
||||
maybe_execution_timeout,
|
||||
@@ -180,7 +187,9 @@ impl ComfyInferenceCommand {
|
||||
info!("stderr will be written to file: {:?}", args.stderr_output_file.as_os_str());
|
||||
|
||||
let stderr_file = File::create(&args.stderr_output_file)?;
|
||||
let stdout_file = File::create(&args.stdout_output_file)?;
|
||||
config.stderr = Redirection::File(stderr_file);
|
||||
config.stdout = Redirection::File(stdout_file);
|
||||
|
||||
if !env_vars.is_empty() {
|
||||
config.env = Some(env_vars);
|
||||
|
||||
+230
-102
@@ -1,12 +1,13 @@
|
||||
use std::fs::read_to_string;
|
||||
use std::path::PathBuf;
|
||||
use std::fs::{File, read_to_string};
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::time::Instant;
|
||||
|
||||
use anyhow::anyhow;
|
||||
use jsonpath_lib::replace_with;
|
||||
use log::{error, info, warn};
|
||||
use serde::de::Error;
|
||||
use serde_json::Value;
|
||||
use tokio::io::AsyncWriteExt;
|
||||
use walkdir::WalkDir;
|
||||
|
||||
use buckets::public::media_files::bucket_file_path::MediaFileBucketPath;
|
||||
use cloud_storage::remote_file_manager::remote_cloud_bucket_details::RemoteCloudBucketDetails;
|
||||
@@ -23,6 +24,7 @@ use mysql_queries::payloads::generic_inference_args::generic_inference_args::Pol
|
||||
use mysql_queries::payloads::generic_inference_args::workflow_payload::NewValue;
|
||||
use mysql_queries::queries::generic_inference::job::list_available_generic_inference_jobs::AvailableInferenceJob;
|
||||
use mysql_queries::queries::media_files::create::insert_media_file_from_comfy_ui::{insert_media_file_from_comfy_ui, InsertArgs};
|
||||
use mysql_queries::queries::media_files::get_media_file::get_media_file;
|
||||
use mysql_queries::queries::model_weights::get::get_weight::get_weight_by_token;
|
||||
|
||||
use crate::job::job_loop::job_success_result::{JobSuccessResult, ResultEntity};
|
||||
@@ -31,13 +33,21 @@ use crate::job::job_types::workflow::comfy_ui::comfy_ui_inference_command::Infer
|
||||
use crate::job::job_types::workflow::comfy_ui::validate_job::validate_job;
|
||||
use crate::job_dependencies::JobDependencies;
|
||||
|
||||
const BUCKET_FILE_PREFIX: &str = "fakeyou_";
|
||||
|
||||
pub struct ComfyProcessJobArgs<'a> {
|
||||
pub job_dependencies: &'a JobDependencies,
|
||||
pub job: &'a AvailableInferenceJob,
|
||||
}
|
||||
|
||||
fn recursively_delete_files_in(path: &Path) -> std::io::Result<()> {
|
||||
for entry in WalkDir::new(path) {
|
||||
let entry = entry?;
|
||||
if entry.path().is_file() {
|
||||
safe_delete_temp_file(entry.path());
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn process_job(args: ComfyProcessJobArgs<'_>) -> Result<JobSuccessResult, ProcessSingleJobError> {
|
||||
let job = args.job;
|
||||
let deps = args.job_dependencies;
|
||||
@@ -71,9 +81,8 @@ pub async fn process_job(args: ComfyProcessJobArgs<'_>) -> Result<JobSuccessResu
|
||||
.new_tempdir(&work_temp_dir)
|
||||
.map_err(|e| ProcessSingleJobError::from_io_error(e))?;
|
||||
|
||||
|
||||
// ==================== QUERY AND DOWNLOAD FILES ==================== //
|
||||
let root_comfy_path = model_dependencies.inference_command.comfy_root_code_directory.clone();
|
||||
// ===================== DOWNLOAD REQUIRED MODELS IF NOT EXIST ===================== //
|
||||
let root_comfy_path = model_dependencies.inference_command.mounts_directory.clone();
|
||||
|
||||
let remote_cloud_file_client = RemoteCloudFileClient::get_remote_cloud_file_client().await;
|
||||
let remote_cloud_file_client = match remote_cloud_file_client {
|
||||
@@ -85,57 +94,57 @@ pub async fn process_job(args: ComfyProcessJobArgs<'_>) -> Result<JobSuccessResu
|
||||
}
|
||||
};
|
||||
|
||||
// Download SD model if specified
|
||||
let mut maybe_sd_path: Option<PathBuf> = None;
|
||||
match job_args.maybe_sd_model {
|
||||
Some(sd_model) => {
|
||||
let sd_dir = root_comfy_path.join("models").join("checkpoints");
|
||||
let retrieved_sd_record = get_weight_by_token(
|
||||
sd_model,
|
||||
false,
|
||||
&deps.db.mysql_pool
|
||||
).await?.unwrap();
|
||||
async fn download_file(file_url: String, dep_path: PathBuf) -> Result<(), anyhow::Error> {
|
||||
// Send a GET request to the file_url
|
||||
let response = reqwest::get(&file_url).await?;
|
||||
|
||||
let bucket_details = RemoteCloudBucketDetails {
|
||||
object_hash: retrieved_sd_record.public_bucket_hash,
|
||||
prefix: retrieved_sd_record.maybe_public_bucket_prefix.unwrap(),
|
||||
suffix: retrieved_sd_record.maybe_public_bucket_extension.unwrap(),
|
||||
};
|
||||
let sd_filename = retrieved_sd_record.original_filename.unwrap_or("sd_model.safetensors".to_string());
|
||||
let sd_path = sd_dir.join(sd_filename).to_str().unwrap().to_string();
|
||||
remote_cloud_file_client.download_file(bucket_details, sd_path.clone()).await?;
|
||||
maybe_sd_path = Some(sd_path.parse().unwrap());
|
||||
info!("Downloaded SD model to {:?}", sd_path);
|
||||
// Ensure the request was successful
|
||||
if response.status().is_success() {
|
||||
// Get the byte stream of the file
|
||||
let bytes = response.bytes().await?;
|
||||
|
||||
// Ensure the parent directory exists
|
||||
if let Some(parent) = dep_path.parent() {
|
||||
// Skip errors (for example if the directory already exists)
|
||||
let _ = tokio::fs::create_dir_all(&parent).await;
|
||||
}
|
||||
|
||||
// Create or open the file at dep_path asynchronously
|
||||
let mut file = tokio::fs::File::create(&dep_path).await?;
|
||||
|
||||
// Write the bytes to the file asynchronously
|
||||
file.write_all(&bytes).await?;
|
||||
println!("File downloaded successfully.");
|
||||
} else {
|
||||
println!("Failed to download the file.");
|
||||
}
|
||||
None => {}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
// Download Lora model if specified
|
||||
let mut maybe_lora_path: Option<PathBuf> = None;
|
||||
match job_args.maybe_lora_model {
|
||||
Some(lora_model) => {
|
||||
let lora_dir = root_comfy_path.join("models").join("checkpoints");
|
||||
let retrieved_lora_record = get_weight_by_token(
|
||||
lora_model,
|
||||
false,
|
||||
&deps.db.mysql_pool
|
||||
).await?.unwrap();
|
||||
let bucket_details = RemoteCloudBucketDetails {
|
||||
object_hash: retrieved_lora_record.public_bucket_hash,
|
||||
prefix: retrieved_lora_record.maybe_public_bucket_prefix.unwrap(),
|
||||
suffix: retrieved_lora_record.maybe_public_bucket_extension.unwrap(),
|
||||
};
|
||||
|
||||
let lora_filename = retrieved_lora_record.original_filename.unwrap_or("lora_model.safetensors".to_string());
|
||||
let lora_path = lora_dir.join(lora_filename).to_str().unwrap().to_string();
|
||||
remote_cloud_file_client.download_file(bucket_details, lora_path.clone()).await?;
|
||||
maybe_lora_path = Some(lora_path.parse().unwrap());
|
||||
info!("Downloaded Lora model to {:?}", lora_path);
|
||||
let all_models = &args.job_dependencies.job.job_specific_dependencies.maybe_comfy_ui_dependencies;
|
||||
match all_models {
|
||||
Some(models) => {
|
||||
for model in &models.dependency_tokens.comfy {
|
||||
let mut dep_path = model_dependencies.inference_command.mounts_directory.clone();
|
||||
dep_path = dep_path.join(model.location.clone());
|
||||
if !dep_path.exists() {
|
||||
download_file(model.url.clone(), dep_path.clone()).await.map_err(|e| ProcessSingleJobError::Other(e))?;
|
||||
info!("Downloaded model to {:?}", dep_path);
|
||||
}
|
||||
}
|
||||
}
|
||||
None => {
|
||||
info!("No models specified to download")
|
||||
}
|
||||
None => {}
|
||||
}
|
||||
|
||||
// Download workflow to ComfyRunner
|
||||
let workflow_dir = root_comfy_path.join("../ComfyLauncher");
|
||||
let workflow_dir = root_comfy_path.join("prompt");
|
||||
// make folder if not exist
|
||||
if !workflow_dir.exists() {
|
||||
std::fs::create_dir_all(&workflow_dir).unwrap();
|
||||
}
|
||||
let retrieved_workflow_record = get_weight_by_token(
|
||||
job_args.workflow_source,
|
||||
false,
|
||||
@@ -168,19 +177,113 @@ pub async fn process_job(args: ComfyProcessJobArgs<'_>) -> Result<JobSuccessResu
|
||||
// Apply modifications if they exist
|
||||
if let Some(modifications) = comfy_args.maybe_json_modifications.clone() {
|
||||
// Load prompt.json
|
||||
let prompt_file = std::fs::File::open(&workflow_path).unwrap();
|
||||
let prompt_file = File::open(&workflow_path).unwrap();
|
||||
let mut prompt_json: Value = serde_json::from_reader(prompt_file).unwrap();
|
||||
// Modify json
|
||||
for (path, new_value) in modifications {
|
||||
prompt_json = replace_json_value(prompt_json, &path, new_value).expect(
|
||||
format!("Failed to replace json value at path {}", path).as_str()
|
||||
);
|
||||
prompt_json = replace_json_value(prompt_json, &path, new_value).map_err(|e| ProcessSingleJobError::Other(e))?;
|
||||
}
|
||||
// Save prompt.json
|
||||
let prompt_file = std::fs::File::create(&workflow_path).unwrap();
|
||||
let prompt_file = File::create(&workflow_path).unwrap();
|
||||
serde_json::to_writer(prompt_file, &prompt_json).unwrap();
|
||||
}
|
||||
|
||||
|
||||
// ==================== QUERY AND DOWNLOAD FILES ==================== //
|
||||
|
||||
// Download SD model if specified
|
||||
let mut maybe_sd_path: Option<PathBuf> = None;
|
||||
match job_args.maybe_sd_model {
|
||||
Some(sd_model) => {
|
||||
let sd_dir = root_comfy_path.join("models").join("checkpoints");
|
||||
// make if not exist
|
||||
if !sd_dir.exists() {
|
||||
std::fs::create_dir_all(&sd_dir).unwrap();
|
||||
}
|
||||
let retrieved_sd_record = get_weight_by_token(
|
||||
sd_model,
|
||||
false,
|
||||
&deps.db.mysql_pool
|
||||
).await?.ok_or_else(|| ProcessSingleJobError::Other(anyhow!("SD model not found")))?;
|
||||
|
||||
let bucket_details = RemoteCloudBucketDetails {
|
||||
object_hash: retrieved_sd_record.public_bucket_hash,
|
||||
prefix: retrieved_sd_record.maybe_public_bucket_prefix.unwrap(),
|
||||
suffix: retrieved_sd_record.maybe_public_bucket_extension.unwrap(),
|
||||
};
|
||||
let sd_filename = "model.safetensors";
|
||||
let sd_path = sd_dir.join(sd_filename).to_str().unwrap().to_string();
|
||||
remote_cloud_file_client.download_file(bucket_details, sd_path.clone()).await?;
|
||||
maybe_sd_path = Some(sd_path.parse().unwrap());
|
||||
info!("Downloaded SD model to {:?}", sd_path);
|
||||
maybe_sd_path = Some(sd_path.parse().unwrap());
|
||||
}
|
||||
None => {}
|
||||
}
|
||||
// Download Lora model if specified
|
||||
let mut maybe_lora_path: Option<PathBuf> = None;
|
||||
match job_args.maybe_lora_model {
|
||||
Some(lora_model) => {
|
||||
let lora_dir = root_comfy_path.join("models").join("loras");
|
||||
// make if not exist
|
||||
if !lora_dir.exists() {
|
||||
std::fs::create_dir_all(&lora_dir).unwrap();
|
||||
}
|
||||
let retrieved_lora_record = get_weight_by_token(
|
||||
lora_model,
|
||||
false,
|
||||
&deps.db.mysql_pool
|
||||
).await?.ok_or_else(|| ProcessSingleJobError::Other(anyhow!("Lora model not found")))?;
|
||||
let bucket_details = RemoteCloudBucketDetails {
|
||||
object_hash: retrieved_lora_record.public_bucket_hash,
|
||||
prefix: retrieved_lora_record.maybe_public_bucket_prefix.unwrap(),
|
||||
suffix: retrieved_lora_record.maybe_public_bucket_extension.unwrap(),
|
||||
};
|
||||
|
||||
let lora_filename = "lora.safetensors";
|
||||
let lora_path = lora_dir.join(lora_filename).to_str().unwrap().to_string();
|
||||
remote_cloud_file_client.download_file(bucket_details, lora_path.clone()).await?;
|
||||
maybe_lora_path = Some(lora_path.parse().unwrap());
|
||||
info!("Downloaded Lora model to {:?}", lora_path);
|
||||
maybe_lora_path = Some(lora_path.parse().unwrap());
|
||||
}
|
||||
None => {}
|
||||
}
|
||||
// Download Input file if specified
|
||||
let mut maybe_input_path: Option<PathBuf> = None;
|
||||
match job_args.maybe_input_file {
|
||||
Some(input_file) => {
|
||||
let input_dir = root_comfy_path.join("input");
|
||||
// make if not exist
|
||||
if !input_dir.exists() {
|
||||
std::fs::create_dir_all(&input_dir).unwrap();
|
||||
}
|
||||
let retrieved_input_record = get_media_file(
|
||||
input_file,
|
||||
false,
|
||||
&deps.db.mysql_pool
|
||||
).await?.ok_or_else(|| ProcessSingleJobError::Other(anyhow!("Input file not found")))?;
|
||||
let bucket_details = RemoteCloudBucketDetails {
|
||||
object_hash: retrieved_input_record.public_bucket_directory_hash,
|
||||
prefix: retrieved_input_record.maybe_public_bucket_prefix.unwrap(),
|
||||
suffix: retrieved_input_record.maybe_public_bucket_extension.clone().unwrap(),
|
||||
};
|
||||
// Download to "input.EXTENSION"
|
||||
let input_filename = format!("input.{}", retrieved_input_record.maybe_public_bucket_extension.unwrap());
|
||||
let input_path = input_dir.join(input_filename).to_str().unwrap().to_string();
|
||||
remote_cloud_file_client.download_file(bucket_details, input_path.clone()).await?;
|
||||
info!("Downloaded input file to {:?}", input_path);
|
||||
maybe_input_path = Some(input_path.parse().unwrap());
|
||||
}
|
||||
None => {}
|
||||
}
|
||||
|
||||
// make outputs dir if not exist
|
||||
let output_dir = root_comfy_path.join("output");
|
||||
if !output_dir.exists() {
|
||||
std::fs::create_dir_all(&output_dir).unwrap();
|
||||
}
|
||||
|
||||
// ==================== SETUP FOR INFERENCE ==================== //
|
||||
|
||||
info!("Ready for ComfyUI inference...");
|
||||
@@ -192,21 +295,36 @@ pub async fn process_job(args: ComfyProcessJobArgs<'_>) -> Result<JobSuccessResu
|
||||
|
||||
// ==================== RUN INFERENCE SCRIPT ==================== //
|
||||
let stderr_output_file = work_temp_dir.path().join("stderr.txt");
|
||||
let stdout_output_file = work_temp_dir.path().join("stdout.txt");
|
||||
|
||||
let inference_start_time = Instant::now();
|
||||
|
||||
let command_exit_status = model_dependencies
|
||||
.inference_command
|
||||
.execute_inference(InferenceArgs {
|
||||
stderr_output_file: &stderr_output_file,
|
||||
stdout_output_file: &stdout_output_file,
|
||||
});
|
||||
|
||||
let inference_duration = Instant::now().duration_since(inference_start_time);
|
||||
|
||||
info!("Inference took duration to complete: {:?}", &inference_duration);
|
||||
|
||||
if !command_exit_status.is_success() {
|
||||
// ==================== GET OUTPUT FILE ======================== //
|
||||
let mut output_file = root_comfy_path.join("output");
|
||||
output_file = output_file.join(job_args.output_path);
|
||||
|
||||
// ==================== CHECK ALL FILES EXIST AND GET METADATA ==================== //
|
||||
|
||||
// check stdout for success and check if file exists
|
||||
let stdout_output = read_to_string(&stdout_output_file).unwrap();
|
||||
// check for "Prompt executed" in stdout (comfyui only outputs this for success)
|
||||
if !stdout_output.contains("Prompt executed") || check_file_exists(&output_file).is_err() {
|
||||
error!("Inference failed: {:?}", command_exit_status);
|
||||
|
||||
error!("Captured stdout output: {}", stdout_output);
|
||||
error!("Captured stderr output: {}", read_to_string(&stderr_output_file).unwrap());
|
||||
|
||||
let error = ProcessSingleJobError::Other(anyhow!("CommandExitStatus: {:?}", command_exit_status));
|
||||
|
||||
if let Ok(contents) = read_to_string(&stderr_output_file) {
|
||||
@@ -214,6 +332,7 @@ pub async fn process_job(args: ComfyProcessJobArgs<'_>) -> Result<JobSuccessResu
|
||||
}
|
||||
|
||||
safe_delete_temp_file(&stderr_output_file);
|
||||
safe_delete_temp_file(&stdout_output_file);
|
||||
safe_delete_temp_directory(&work_temp_dir);
|
||||
safe_delete_temp_file(&workflow_path);
|
||||
if let Some(sd_path) = maybe_sd_path {
|
||||
@@ -222,49 +341,40 @@ pub async fn process_job(args: ComfyProcessJobArgs<'_>) -> Result<JobSuccessResu
|
||||
if let Some(lora_path) = maybe_lora_path {
|
||||
safe_delete_temp_file(&lora_path);
|
||||
}
|
||||
if let Some(input_path) = maybe_input_path {
|
||||
safe_delete_temp_file(&input_path);
|
||||
}
|
||||
let output_dir = root_comfy_path.join("output");
|
||||
recursively_delete_files_in(&output_dir).unwrap();
|
||||
|
||||
return Err(error);
|
||||
}
|
||||
|
||||
// ==================== GET OUTPUT FILE ======================== //
|
||||
|
||||
// take the latest file in the output directory
|
||||
let output_dir = root_comfy_path.join("output");
|
||||
let mut entries: Vec<PathBuf> = std::fs::read_dir(output_dir).unwrap()
|
||||
.map(|res| res.map(|e| e.path()))
|
||||
.collect::<Result<Vec<_>, std::io::Error>>()
|
||||
.unwrap();
|
||||
|
||||
entries.sort_by_key(|path| std::fs::metadata(path).unwrap().modified().unwrap());
|
||||
|
||||
|
||||
// ==================== CHECK ALL FILES EXIST AND GET METADATA ==================== //
|
||||
|
||||
// check if entries is empty
|
||||
if entries.is_empty() {
|
||||
return Err(ProcessSingleJobError::Other(anyhow!("No output files found")));
|
||||
}
|
||||
let finished_file = entries.last().unwrap();
|
||||
info!("Checking that file exists: {:?} ...", finished_file);
|
||||
check_file_exists(&finished_file).map_err(|e| ProcessSingleJobError::Other(e))?;
|
||||
|
||||
info!("Interrogating result file size ...");
|
||||
|
||||
let file_size_bytes = file_size(&finished_file)
|
||||
let file_size_bytes = file_size(&output_file)
|
||||
.map_err(|err| ProcessSingleJobError::Other(err))?;
|
||||
|
||||
info!("Interrogating result mimetype ...");
|
||||
|
||||
let mimetype = get_mimetype_for_file(&finished_file)
|
||||
let mimetype = get_mimetype_for_file(&output_file)
|
||||
.map_err(|err| ProcessSingleJobError::from_io_error(err))?
|
||||
.map(|mime| mime.to_string())
|
||||
.ok_or(ProcessSingleJobError::Other(anyhow!("Mimetype could not be determined")))?;
|
||||
|
||||
// create ext from mimetype
|
||||
let ext = match mimetype.as_str() {
|
||||
"video/mp4" => ".mp4",
|
||||
"image/png" => ".png",
|
||||
"image/jpeg" => ".jpg",
|
||||
"video/mp4" => "mp4",
|
||||
"image/png" => "png",
|
||||
"image/jpeg" => "jpg",
|
||||
_ => return Err(ProcessSingleJobError::Other(anyhow!("Mimetype not supported: {}", mimetype))),
|
||||
};
|
||||
|
||||
// create prefix from mimetype
|
||||
let prefix = match mimetype.as_str() {
|
||||
"video/mp4" => "video",
|
||||
"image/png" => "image",
|
||||
"image/jpeg" => "image",
|
||||
_ => return Err(ProcessSingleJobError::Other(anyhow!("Mimetype not supported: {}", mimetype))),
|
||||
};
|
||||
|
||||
@@ -278,7 +388,7 @@ pub async fn process_job(args: ComfyProcessJobArgs<'_>) -> Result<JobSuccessResu
|
||||
|
||||
info!("Calculating sha256...");
|
||||
|
||||
let file_checksum = sha256_hash_file(&finished_file)
|
||||
let file_checksum = sha256_hash_file(&output_file)
|
||||
.map_err(|err| {
|
||||
ProcessSingleJobError::Other(anyhow!("Error hashing file: {:?}", err))
|
||||
})?;
|
||||
@@ -289,7 +399,7 @@ pub async fn process_job(args: ComfyProcessJobArgs<'_>) -> Result<JobSuccessResu
|
||||
.map_err(|e| ProcessSingleJobError::Other(e))?;
|
||||
|
||||
let result_bucket_location = MediaFileBucketPath::generate_new(
|
||||
Some(BUCKET_FILE_PREFIX),
|
||||
Some(prefix),
|
||||
Some(ext));
|
||||
|
||||
let result_bucket_object_pathbuf = result_bucket_location.to_full_object_pathbuf();
|
||||
@@ -300,7 +410,7 @@ pub async fn process_job(args: ComfyProcessJobArgs<'_>) -> Result<JobSuccessResu
|
||||
|
||||
args.job_dependencies.buckets.public_bucket_client.upload_filename_with_content_type(
|
||||
&result_bucket_object_pathbuf,
|
||||
&finished_file,
|
||||
&output_file,
|
||||
&mimetype) // TODO: We should check the mimetype to make sure bad payloads can't get uploaded
|
||||
.await
|
||||
.map_err(|e| ProcessSingleJobError::Other(e))?;
|
||||
@@ -308,16 +418,20 @@ pub async fn process_job(args: ComfyProcessJobArgs<'_>) -> Result<JobSuccessResu
|
||||
// ==================== DELETE TEMP FILES ==================== //
|
||||
|
||||
safe_delete_temp_file(&stderr_output_file);
|
||||
|
||||
// delete all files in output directory
|
||||
let output_dir = root_comfy_path.join("output");
|
||||
let entries: Vec<PathBuf> = std::fs::read_dir(output_dir).unwrap()
|
||||
.map(|res| res.map(|e| e.path()))
|
||||
.collect::<Result<Vec<_>, std::io::Error>>()
|
||||
.unwrap();
|
||||
for entry in entries {
|
||||
safe_delete_temp_file(&entry);
|
||||
safe_delete_temp_file(&stdout_output_file);
|
||||
safe_delete_temp_file(&workflow_path);
|
||||
if let Some(sd_path) = maybe_sd_path {
|
||||
safe_delete_temp_file(&sd_path);
|
||||
}
|
||||
if let Some(lora_path) = maybe_lora_path {
|
||||
safe_delete_temp_file(&lora_path);
|
||||
}
|
||||
if let Some(input_path) = maybe_input_path {
|
||||
safe_delete_temp_file(&input_path);
|
||||
}
|
||||
|
||||
let output_dir = root_comfy_path.join("output");
|
||||
recursively_delete_files_in(&output_dir).unwrap();
|
||||
|
||||
// NB: We should be using a tempdir, but to make absolutely certain we don't overflow the disk...
|
||||
safe_delete_temp_directory(&work_temp_dir);
|
||||
@@ -333,7 +447,7 @@ pub async fn process_job(args: ComfyProcessJobArgs<'_>) -> Result<JobSuccessResu
|
||||
file_size_bytes,
|
||||
sha256_checksum: &file_checksum,
|
||||
public_bucket_directory_hash: result_bucket_location.get_object_hash(),
|
||||
maybe_public_bucket_prefix: Some(BUCKET_FILE_PREFIX),
|
||||
maybe_public_bucket_prefix: Some(prefix),
|
||||
maybe_public_bucket_extension: Some(ext),
|
||||
is_on_prem: args.job_dependencies.job.info.container.is_on_prem,
|
||||
worker_hostname: &args.job_dependencies.job.info.container.hostname,
|
||||
@@ -360,15 +474,29 @@ pub async fn process_job(args: ComfyProcessJobArgs<'_>) -> Result<JobSuccessResu
|
||||
})
|
||||
}
|
||||
|
||||
fn replace_json_value(json: Value, path: &str, new_value: NewValue) -> Result<Value, serde_json::Error> {
|
||||
replace_with(json, path, &mut |_| {
|
||||
fn replace_json_value(json: Value, path: &str, new_value: NewValue) -> anyhow::Result<Value> {
|
||||
// First, attempt to read the value at the specified path
|
||||
let results = jsonpath_lib::select(&json, path).map_err(|err| {
|
||||
anyhow!("Invalid jsonpath '{}': {:?}", path, err)
|
||||
})?;
|
||||
|
||||
// If the path does not exist or returns no results, return an error
|
||||
if results.is_empty() {
|
||||
return Err(anyhow!("Path '{}' does not exist in the provided JSON.", path));
|
||||
}
|
||||
|
||||
// If the path exists, proceed with the replacement
|
||||
// Assuming replace_with returns a Result, handle it appropriately
|
||||
jsonpath_lib::replace_with(json, path, &mut |_| {
|
||||
match &new_value {
|
||||
NewValue::String(s) => Some(Value::String(s.clone())),
|
||||
NewValue::Float(f) => Some(Value::Number(serde_json::Number::from_f64(*f as f64).unwrap())),
|
||||
NewValue::Float(f) => serde_json::Number::from_f64(*f as f64)
|
||||
.map(Value::Number) // Convert Option to Some(Value::Number) if Some, else None
|
||||
.or_else(|| Some(Value::Null)), // If None, use Value::Null instead
|
||||
NewValue::Int(i) => Some(Value::Number(serde_json::Number::from(*i))),
|
||||
NewValue::Bool(b) => Some(Value::Bool(*b)),
|
||||
}
|
||||
}).map_err(|err| {
|
||||
serde_json::Error::custom(format!("Failed to replace json value at path {}: {:?}", path, err))
|
||||
anyhow!("Failed to replace json value at path '{}': {:?}", path, err)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
+12
-1
@@ -4,16 +4,18 @@ use anyhow::anyhow;
|
||||
use mysql_queries::payloads::generic_inference_args::generic_inference_args::{InferenceCategoryAbbreviated, PolymorphicInferenceArgs};
|
||||
use mysql_queries::payloads::generic_inference_args::workflow_payload::{NewValue};
|
||||
use mysql_queries::queries::generic_inference::job::list_available_generic_inference_jobs::AvailableInferenceJob;
|
||||
use tokens::tokens::media_files::MediaFileToken;
|
||||
use tokens::tokens::model_weights::ModelWeightToken;
|
||||
|
||||
use crate::job::job_loop::process_single_job_error::ProcessSingleJobError;
|
||||
|
||||
pub struct JobArgs<'a> {
|
||||
pub workflow_source: &'a ModelWeightToken,
|
||||
pub output_path: &'a String,
|
||||
pub maybe_json_modifications: &'a Option<HashMap<String, NewValue>>,
|
||||
pub maybe_sd_model: &'a Option<ModelWeightToken>,
|
||||
pub maybe_lora_model: &'a Option<ModelWeightToken>,
|
||||
|
||||
pub maybe_input_file: &'a Option<MediaFileToken>,
|
||||
}
|
||||
|
||||
pub fn validate_job(job: &AvailableInferenceJob) -> Result<JobArgs, ProcessSingleJobError> {
|
||||
@@ -58,11 +60,20 @@ pub fn validate_job(job: &AvailableInferenceJob) -> Result<JobArgs, ProcessSingl
|
||||
}
|
||||
};
|
||||
|
||||
let output_path = match &inference_args.maybe_output_path {
|
||||
Some(args) => args,
|
||||
None => {
|
||||
return Err(ProcessSingleJobError::from_anyhow_error(anyhow!("No output file provided!")));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Ok(JobArgs {
|
||||
workflow_source,
|
||||
output_path,
|
||||
maybe_sd_model: &inference_args.maybe_sd_model,
|
||||
maybe_lora_model: &inference_args.maybe_lora_model,
|
||||
maybe_json_modifications: &inference_args.maybe_json_modifications,
|
||||
maybe_input_file: &inference_args.maybe_input_file,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use enums::by_table::generic_inference_jobs::inference_model_type::InferenceModelType;
|
||||
use errors::AnyhowResult;
|
||||
|
||||
use crate::job::job_types::format_conversion::fbx_to_gltf::dependencies::FbxToGltfDependencies;
|
||||
use crate::job::job_types::image_generation::sd::stable_diffusion_dependencies::StableDiffusionDependencies;
|
||||
use crate::job::job_types::lipsync::sad_talker::sad_talker_dependencies::SadTalkerDependencies;
|
||||
use crate::job::job_types::mocap::mocap_net::mocapnet_dependencies::MocapNetDependencies;
|
||||
@@ -26,6 +27,7 @@ pub struct JobSpecificDependencies {
|
||||
pub maybe_mocapnet_dependencies: Option<MocapNetDependencies>,
|
||||
pub maybe_styletts2_dependencies: Option<StyleTTS2Dependencies>,
|
||||
pub maybe_comfy_ui_dependencies: Option<ComfyDependencies>,
|
||||
pub maybe_convert_fbx_to_gltf_dependencies: Option<FbxToGltfDependencies>,
|
||||
}
|
||||
|
||||
impl JobSpecificDependencies {
|
||||
@@ -42,6 +44,7 @@ impl JobSpecificDependencies {
|
||||
let mut maybe_mocapnet_dependencies = None;
|
||||
let mut maybe_styletts2_dependencies = None;
|
||||
let mut maybe_comfy_ui_dependencies = None;
|
||||
let mut maybe_convert_fbx_to_gltf_dependencies = None;
|
||||
|
||||
if scoped_execution.can_run_job(InferenceModelType::RvcV2) {
|
||||
print_with_space("Setting RVCv2 dependencies...");
|
||||
@@ -98,6 +101,11 @@ impl JobSpecificDependencies {
|
||||
maybe_comfy_ui_dependencies = Some(ComfyDependencies::setup()?);
|
||||
}
|
||||
|
||||
if scoped_execution.can_run_job(InferenceModelType::ConvertFbxToGltf) {
|
||||
print_with_space("Setting ConvertFbxToGltf dependencies...");
|
||||
maybe_convert_fbx_to_gltf_dependencies = Some(FbxToGltfDependencies::setup()?);
|
||||
}
|
||||
|
||||
Ok(JobSpecificDependencies {
|
||||
maybe_rvc_v2_dependencies,
|
||||
maybe_sad_talker_dependencies,
|
||||
@@ -110,6 +118,7 @@ impl JobSpecificDependencies {
|
||||
maybe_mocapnet_dependencies,
|
||||
maybe_styletts2_dependencies,
|
||||
maybe_comfy_ui_dependencies,
|
||||
maybe_convert_fbx_to_gltf_dependencies,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
+1
-2
@@ -9,8 +9,7 @@ use subprocess::{Popen, PopenConfig};
|
||||
|
||||
use errors::AnyhowResult;
|
||||
use filesys::path_to_string::path_to_string;
|
||||
|
||||
use crate::job::job_loop::command_exit_status::CommandExitStatus;
|
||||
use subprocess_common::command_exit_status::CommandExitStatus;
|
||||
|
||||
// These environment vars are not copied over to the subprocess
|
||||
// TODO/FIXME(bt, 2023-05-28): This is horrific security!
|
||||
|
||||
@@ -22,6 +22,7 @@ use crate::http_server::common_responses::pagination_cursors::PaginationCursors;
|
||||
use crate::http_server::common_responses::pagination_page::PaginationPage;
|
||||
use crate::http_server::common_responses::simple_entity_stats::SimpleEntityStats;
|
||||
use crate::http_server::common_responses::user_details_lite::{DefaultAvatarInfo, UserDetailsLight};
|
||||
use crate::http_server::endpoints::conversion::enqueue_fbx_to_gltf_handler::*;
|
||||
use crate::http_server::endpoints::media_files::get_media_file::*;
|
||||
use crate::http_server::endpoints::media_files::list_featured_media_files::*;
|
||||
use crate::http_server::endpoints::media_files::list_media_files::*;
|
||||
@@ -49,6 +50,7 @@ use crate::http_server::web_utils::response_success_helpers::*;
|
||||
#[derive(OpenApi)]
|
||||
#[openapi(
|
||||
paths(
|
||||
crate::http_server::endpoints::conversion::enqueue_fbx_to_gltf_handler::enqueue_fbx_to_gltf_handler,
|
||||
crate::http_server::endpoints::media_files::get_media_file::get_media_file_handler,
|
||||
crate::http_server::endpoints::media_files::list_featured_media_files::list_featured_media_files_handler,
|
||||
crate::http_server::endpoints::media_files::list_media_files::list_media_files_handler,
|
||||
@@ -103,6 +105,7 @@ use crate::http_server::web_utils::response_success_helpers::*;
|
||||
|
||||
// Inference
|
||||
EnqueueTTSRequest,EnqueueTTSRequestSuccessResponse,EnqueueTTSRequestError,
|
||||
EnqueueFbxToGltfRequest,EnqueueFbxToGltfRequestSuccessResponse,EnqueueFbxToGltfRequestError,
|
||||
// Media Files
|
||||
GetMediaFilePathInfo,GetMediaFileError,GetMediaFileSuccessResponse,GetMediaFileModelInfo,MediaFileInfo,
|
||||
// Model Weights
|
||||
|
||||
+2
@@ -11,6 +11,7 @@ use actix_web::http::StatusCode;
|
||||
use log::{info, warn};
|
||||
|
||||
use enums::by_table::generic_inference_jobs::inference_category::InferenceCategory;
|
||||
use enums::by_table::generic_inference_jobs::inference_job_type::InferenceJobType;
|
||||
use enums::by_table::generic_inference_jobs::inference_model_type::InferenceModelType;
|
||||
use enums::common::visibility::Visibility;
|
||||
use http_server_common::request::get_request_header_optional::get_request_header_optional;
|
||||
@@ -307,6 +308,7 @@ pub async fn enqueue_face_animation_handler(
|
||||
|
||||
let query_result = insert_generic_inference_job(InsertGenericInferenceArgs {
|
||||
uuid_idempotency_token: &request.uuid_idempotency_token,
|
||||
job_type: InferenceJobType::SadTalker,
|
||||
inference_category: InferenceCategory::LipsyncAnimation,
|
||||
maybe_model_type: Some(InferenceModelType::SadTalker), // NB: Model is static during inference
|
||||
maybe_model_token: None, // NB: Model is static during inference
|
||||
|
||||
+2
@@ -11,6 +11,7 @@ use actix_web::http::StatusCode;
|
||||
use log::{info, warn};
|
||||
|
||||
use enums::by_table::generic_inference_jobs::inference_category::InferenceCategory;
|
||||
use enums::by_table::generic_inference_jobs::inference_job_type::InferenceJobType;
|
||||
use enums::by_table::generic_inference_jobs::inference_model_type::InferenceModelType;
|
||||
use enums::common::visibility::Visibility;
|
||||
use http_server_common::request::get_request_header_optional::get_request_header_optional;
|
||||
@@ -198,6 +199,7 @@ pub async fn enqueue_rerender_animation_handler(
|
||||
|
||||
let query_result = insert_generic_inference_job(InsertGenericInferenceArgs {
|
||||
uuid_idempotency_token: &request.uuid_idempotency_token,
|
||||
job_type: InferenceJobType::RerenderAVideo,
|
||||
inference_category: InferenceCategory::VideoFilter,
|
||||
maybe_model_type: Some(InferenceModelType::RerenderAVideo), // NB: Model is static during inference
|
||||
maybe_model_token: None, // NB: Model is static during inference
|
||||
|
||||
+218
@@ -0,0 +1,218 @@
|
||||
#![forbid(unused_mut)]
|
||||
|
||||
use std::fmt::Debug;
|
||||
use std::sync::Arc;
|
||||
|
||||
use actix_web::{HttpRequest, HttpResponse, web};
|
||||
use actix_web::error::ResponseError;
|
||||
use actix_web::http::StatusCode;
|
||||
use log::warn;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
use utoipa::ToSchema;
|
||||
|
||||
use enums::by_table::generic_inference_jobs::inference_category::InferenceCategory;
|
||||
use enums::by_table::generic_inference_jobs::inference_job_type::InferenceJobType;
|
||||
use enums::by_table::generic_inference_jobs::inference_model_type::InferenceModelType;
|
||||
use enums::common::visibility::Visibility;
|
||||
use http_server_common::request::get_request_header_optional::get_request_header_optional;
|
||||
use http_server_common::request::get_request_ip::get_request_ip;
|
||||
use mysql_queries::payloads::generic_inference_args::generic_inference_args::{
|
||||
GenericInferenceArgs,
|
||||
InferenceCategoryAbbreviated,
|
||||
};
|
||||
use mysql_queries::queries::generic_inference::web::insert_generic_inference_job::{
|
||||
insert_generic_inference_job,
|
||||
InsertGenericInferenceArgs,
|
||||
};
|
||||
use tokens::tokens::generic_inference_jobs::InferenceJobToken;
|
||||
use tokens::tokens::media_files::MediaFileToken;
|
||||
use tokens::tokens::users::UserToken;
|
||||
|
||||
use crate::configs::plans::get_correct_plan_for_session::get_correct_plan_for_session;
|
||||
use crate::http_server::web_utils::response_error_helpers::to_simple_json_error;
|
||||
use crate::server_state::ServerState;
|
||||
|
||||
/// Debug requests can get routed to special "debug-only" workers, which can
|
||||
/// be used to trial new code, run debugging, etc.
|
||||
const DEBUG_HEADER_NAME: &str = "enable-debug-mode";
|
||||
|
||||
/// The routing tag header can send workloads to particular k8s hosts.
|
||||
/// This is useful for catching the live logs or intercepting the job.
|
||||
const ROUTING_TAG_HEADER_NAME: &str = "routing-tag";
|
||||
|
||||
#[derive(Deserialize, ToSchema)]
|
||||
pub struct EnqueueFbxToGltfRequest {
|
||||
// Entropy for idempotency
|
||||
uuid_idempotency_token: String,
|
||||
// The existing FBX media file token
|
||||
media_file_token: MediaFileToken,
|
||||
}
|
||||
|
||||
#[derive(Serialize, ToSchema)]
|
||||
pub struct EnqueueFbxToGltfRequestSuccessResponse {
|
||||
pub success: bool,
|
||||
pub inference_job_token: InferenceJobToken,
|
||||
}
|
||||
|
||||
#[derive(Debug, ToSchema)]
|
||||
pub enum EnqueueFbxToGltfRequestError {
|
||||
BadInput(String),
|
||||
NotAuthorized,
|
||||
ServerError,
|
||||
RateLimited,
|
||||
}
|
||||
|
||||
impl ResponseError for EnqueueFbxToGltfRequestError {
|
||||
fn status_code(&self) -> StatusCode {
|
||||
match *self {
|
||||
EnqueueFbxToGltfRequestError::BadInput(_) => StatusCode::BAD_REQUEST,
|
||||
EnqueueFbxToGltfRequestError::NotAuthorized => StatusCode::UNAUTHORIZED,
|
||||
EnqueueFbxToGltfRequestError::ServerError => StatusCode::INTERNAL_SERVER_ERROR,
|
||||
EnqueueFbxToGltfRequestError::RateLimited => StatusCode::TOO_MANY_REQUESTS,
|
||||
}
|
||||
}
|
||||
|
||||
fn error_response(&self) -> HttpResponse {
|
||||
let error_reason = match self {
|
||||
EnqueueFbxToGltfRequestError::BadInput(reason) => reason.to_string(),
|
||||
EnqueueFbxToGltfRequestError::NotAuthorized => "unauthorized".to_string(),
|
||||
EnqueueFbxToGltfRequestError::ServerError => "server error".to_string(),
|
||||
EnqueueFbxToGltfRequestError::RateLimited => "rate limited".to_string(),
|
||||
};
|
||||
|
||||
to_simple_json_error(&error_reason, self.status_code())
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for EnqueueFbxToGltfRequestError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{:?}", self)
|
||||
}
|
||||
}
|
||||
|
||||
#[utoipa::path(
|
||||
post,
|
||||
path = "/v1/conversion/enqueue_fbx_to_gltf",
|
||||
responses(
|
||||
(
|
||||
status = 200,
|
||||
description = "Enqueue FBX to GLTF",
|
||||
body = EnqueueFbxToGltfRequestSuccessResponse,
|
||||
),
|
||||
(status = 400, description = "Bad input", body = EnqueueFbxToGltfRequestError),
|
||||
(status = 401, description = "Not authorized", body = EnqueueFbxToGltfRequestError),
|
||||
(status = 429, description = "Rate limited", body = EnqueueFbxToGltfRequestError),
|
||||
(status = 500, description = "Server error", body = EnqueueFbxToGltfRequestError)
|
||||
),
|
||||
params(("request" = EnqueueFbxToGltfRequest, description = "Payload for FBX to GLTF Request"))
|
||||
)]
|
||||
pub async fn enqueue_fbx_to_gltf_handler(
|
||||
http_request: HttpRequest,
|
||||
request: web::Json<EnqueueFbxToGltfRequest>,
|
||||
server_state: web::Data<Arc<ServerState>>
|
||||
) -> Result<HttpResponse, EnqueueFbxToGltfRequestError> {
|
||||
let mut maybe_user_token: Option<UserToken> = None;
|
||||
|
||||
let mut mysql_connection = server_state.mysql_pool.acquire().await.map_err(|err| {
|
||||
warn!("MySql pool error: {:?}", err);
|
||||
EnqueueFbxToGltfRequestError::ServerError
|
||||
})?;
|
||||
|
||||
// ==================== USER SESSION ==================== //
|
||||
|
||||
let maybe_user_session = server_state.session_checker
|
||||
.maybe_get_user_session_extended_from_connection(&http_request, &mut mysql_connection).await
|
||||
.map_err(|e| {
|
||||
warn!("Session checker error: {:?}", e);
|
||||
EnqueueFbxToGltfRequestError::ServerError
|
||||
})?;
|
||||
|
||||
if let Some(user_session) = maybe_user_session.as_ref() {
|
||||
maybe_user_token = Some(UserToken::new_from_str(&user_session.user_token));
|
||||
}
|
||||
|
||||
// Plan should handle "first anonymous use" and "investor" cases.
|
||||
let plan = get_correct_plan_for_session(
|
||||
server_state.server_environment,
|
||||
maybe_user_session.as_ref()
|
||||
);
|
||||
|
||||
// Separate priority for animation.
|
||||
let priority_level = plan.web_vc_base_priority_level();
|
||||
|
||||
// ==================== DEBUG MODE + ROUTING TAG ==================== //
|
||||
|
||||
let is_debug_request = get_request_header_optional(&http_request, DEBUG_HEADER_NAME).is_some();
|
||||
|
||||
let maybe_routing_tag = get_request_header_optional(&http_request, ROUTING_TAG_HEADER_NAME).map(
|
||||
|routing_tag| routing_tag.trim().to_string()
|
||||
);
|
||||
|
||||
// ==================== BANNED USERS ==================== //
|
||||
|
||||
if let Some(ref user) = maybe_user_session {
|
||||
if user.role.is_banned {
|
||||
return Err(EnqueueFbxToGltfRequestError::NotAuthorized);
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== RATE LIMIT ==================== //
|
||||
|
||||
let rate_limiter = match maybe_user_session {
|
||||
None => &server_state.redis_rate_limiters.logged_out,
|
||||
Some(ref _user) => &server_state.redis_rate_limiters.logged_in,
|
||||
};
|
||||
|
||||
if let Err(_err) = rate_limiter.rate_limit_request(&http_request) {
|
||||
return Err(EnqueueFbxToGltfRequestError::RateLimited);
|
||||
}
|
||||
|
||||
let ip_address = get_request_ip(&http_request);
|
||||
|
||||
let maybe_avt_token = server_state.avt_cookie_manager
|
||||
.get_avt_token_from_request(&http_request);
|
||||
|
||||
let query_result = insert_generic_inference_job(InsertGenericInferenceArgs {
|
||||
uuid_idempotency_token: &request.uuid_idempotency_token,
|
||||
job_type: InferenceJobType::ConvertFbxToGltf,
|
||||
inference_category: InferenceCategory::FormatConversion,
|
||||
maybe_model_type: Some(InferenceModelType::ConvertFbxToGltf),
|
||||
maybe_model_token: None,
|
||||
maybe_input_source_token: Some(&request.media_file_token.as_str()),
|
||||
maybe_input_source_token_type: None,
|
||||
maybe_raw_inference_text: None,
|
||||
maybe_max_duration_seconds: None,
|
||||
maybe_inference_args: Some(GenericInferenceArgs {
|
||||
inference_category: Some(InferenceCategoryAbbreviated::FormatConversion),
|
||||
args: None, // NB: no format conversion args for now
|
||||
}),
|
||||
maybe_creator_user_token: maybe_user_token.as_ref(),
|
||||
maybe_avt_token: maybe_avt_token.as_ref(),
|
||||
creator_ip_address: &ip_address,
|
||||
creator_set_visibility: Visibility::Public,
|
||||
priority_level,
|
||||
requires_keepalive: true,
|
||||
is_debug_request,
|
||||
maybe_routing_tag: maybe_routing_tag.as_deref(),
|
||||
mysql_pool: &server_state.mysql_pool,
|
||||
}).await;
|
||||
|
||||
let job_token = match query_result {
|
||||
Ok((job_token, _id)) => job_token,
|
||||
Err(err) => {
|
||||
warn!("New generic inference job creation DB error: {:?}", err);
|
||||
return Err(EnqueueFbxToGltfRequestError::ServerError);
|
||||
}
|
||||
};
|
||||
|
||||
let response: EnqueueFbxToGltfRequestSuccessResponse = EnqueueFbxToGltfRequestSuccessResponse {
|
||||
success: true,
|
||||
inference_job_token: job_token,
|
||||
};
|
||||
|
||||
let body = serde_json::to_string(&response)
|
||||
.map_err(|_e| EnqueueFbxToGltfRequestError::ServerError)?;
|
||||
|
||||
Ok(HttpResponse::Ok().content_type("application/json").body(body))
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
pub mod enqueue_fbx_to_gltf_handler;
|
||||
+8
-5
@@ -15,6 +15,7 @@ use serde::Serialize;
|
||||
use utoipa::ToSchema;
|
||||
|
||||
use enums::by_table::generic_inference_jobs::inference_category::InferenceCategory;
|
||||
use enums::by_table::generic_inference_jobs::inference_job_type::InferenceJobType;
|
||||
use enums::by_table::generic_inference_jobs::inference_model_type::InferenceModelType;
|
||||
use http_server_common::request::get_request_header_optional::get_request_header_optional;
|
||||
use http_server_common::request::get_request_ip::get_request_ip;
|
||||
@@ -188,15 +189,16 @@ pub async fn enqueue_image_generation_request(
|
||||
// TODO:I know ill fix this later
|
||||
let path = http_request.path();
|
||||
let segments: Vec<&str> = path.split('/').collect();
|
||||
let last_segment = segments.last().unwrap_or(&"");
|
||||
let last_segment = segments.last().map(|s| *s).unwrap_or("");
|
||||
|
||||
println!("Last segment: {}", last_segment);
|
||||
|
||||
// use segment to determine what to do.
|
||||
let mode;
|
||||
if last_segment.to_string() == "lora" {
|
||||
let mode;
|
||||
|
||||
if last_segment == "lora" {
|
||||
mode = "lora";
|
||||
} else if last_segment.to_string() == "model" {
|
||||
} else if last_segment == "model" {
|
||||
mode = "model";
|
||||
} else {
|
||||
mode = "inference";
|
||||
@@ -338,7 +340,7 @@ pub async fn enqueue_image_generation_request(
|
||||
};
|
||||
|
||||
let number_of_samples = match request.maybe_number_of_samples {
|
||||
Some(val) => if val < 0 { 20 } else if val > 128 { 128 } else { val }
|
||||
Some(val) => if val > 128 { 128 } else { val }
|
||||
None => 20,
|
||||
};
|
||||
|
||||
@@ -405,6 +407,7 @@ pub async fn enqueue_image_generation_request(
|
||||
// create the job record here!
|
||||
let query_result = insert_generic_inference_job(InsertGenericInferenceArgs {
|
||||
uuid_idempotency_token: &request.uuid_idempotency_token,
|
||||
job_type: InferenceJobType::StableDiffusion,
|
||||
inference_category: InferenceCategory::ImageGeneration,
|
||||
maybe_model_type: Some(InferenceModelType::StableDiffusion), // NB: Model is static during inference
|
||||
maybe_model_token: None, // NB: Model is static during inference
|
||||
|
||||
+3
@@ -321,6 +321,9 @@ fn record_to_payload(
|
||||
InferenceCategory::ImageGeneration => {
|
||||
"".to_string()
|
||||
}
|
||||
InferenceCategory::FormatConversion => {
|
||||
"".to_string()
|
||||
}
|
||||
};
|
||||
|
||||
ResultDetailsResponse {
|
||||
|
||||
+23
-3
@@ -260,9 +260,23 @@ pub async fn process_upload_media_file(
|
||||
// https://research.cs.wisc.edu/graphics/Courses/cs-838-1999/Jeff/BVH.html
|
||||
const BVH_HEADER : &[u8] = "HIERARCHY".as_bytes();
|
||||
|
||||
// https://code.blender.org/2013/08/fbx-binary-file-format-specification/
|
||||
const FBX_HEADER : &[u8] = "Kaydara FBX Binary".as_bytes();
|
||||
|
||||
// https://github.com/KhronosGroup/glTF-Tutorials/blob/master/gltfTutorial/gltfTutorial_002_BasicGltfStructure.md
|
||||
// TODO(bt,2024-01-28): Fix this ASAP
|
||||
const GLTF_CONTENTS_1 : &[u8] = "{".as_bytes();
|
||||
|
||||
if bytes.starts_with(BVH_HEADER) {
|
||||
media_file_type = Some(MediaFileType::Mocap);
|
||||
media_file_type = Some(MediaFileType::Bvh);
|
||||
maybe_mimetype = Some("application/octet-stream");
|
||||
} else if bytes.starts_with(FBX_HEADER) {
|
||||
media_file_type = Some(MediaFileType::Fbx);
|
||||
maybe_mimetype = Some("application/octet-stream");
|
||||
} else if bytes.starts_with(GLTF_CONTENTS_1) {
|
||||
// TODO(bt,2024-01-28): This is a horrible check!
|
||||
media_file_type = Some(MediaFileType::Gltf);
|
||||
maybe_mimetype = Some("application/json");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -292,8 +306,14 @@ pub async fn process_upload_media_file(
|
||||
let mut extension = mimetype_to_extension(mime_type)
|
||||
.map(|extension| format!(".{extension}"));
|
||||
|
||||
if extension.is_none() && media_file_type == MediaFileType::Mocap {
|
||||
extension = Some(".bvh".to_string());
|
||||
if extension.is_none() {
|
||||
extension = match media_file_type {
|
||||
MediaFileType::Mocap => Some(".bvh".to_string()),
|
||||
MediaFileType::Bvh => Some(".bvh".to_string()),
|
||||
MediaFileType::Fbx => Some(".fbx".to_string()),
|
||||
MediaFileType::Gltf => Some(".gltf".to_string()),
|
||||
_ => None,
|
||||
};
|
||||
}
|
||||
|
||||
const PREFIX : Option<&str> = Some("upload_");
|
||||
|
||||
@@ -11,6 +11,7 @@ use actix_web::http::StatusCode;
|
||||
use log::{info, warn};
|
||||
|
||||
use enums::by_table::generic_inference_jobs::inference_category::InferenceCategory;
|
||||
use enums::by_table::generic_inference_jobs::inference_job_type::InferenceJobType;
|
||||
use enums::by_table::generic_inference_jobs::inference_model_type::InferenceModelType;
|
||||
use enums::common::visibility::Visibility;
|
||||
use http_server_common::request::get_request_header_optional::get_request_header_optional;
|
||||
@@ -187,6 +188,7 @@ pub async fn enqueue_mocapnet_handler(
|
||||
|
||||
let query_result = insert_generic_inference_job(InsertGenericInferenceArgs {
|
||||
uuid_idempotency_token: &request.uuid_idempotency_token,
|
||||
job_type: InferenceJobType::MocapNet,
|
||||
inference_category: InferenceCategory::Mocap,
|
||||
maybe_model_type: Some(InferenceModelType::MocapNet), // NB: Model is static during inference
|
||||
maybe_model_token: None, // NB: Model is static during inference
|
||||
|
||||
@@ -2,9 +2,11 @@ pub mod animation;
|
||||
pub mod api_tokens;
|
||||
pub mod categories;
|
||||
pub mod comments;
|
||||
pub mod conversion;
|
||||
pub mod download_job;
|
||||
pub mod events;
|
||||
pub mod flags;
|
||||
pub mod image_gen;
|
||||
pub mod inference_job;
|
||||
pub mod investor_demo;
|
||||
pub mod leaderboard;
|
||||
@@ -19,14 +21,13 @@ pub mod subscriptions;
|
||||
pub mod trending;
|
||||
pub mod tts;
|
||||
pub mod twitch;
|
||||
pub mod user_bookmarks;
|
||||
pub mod user_ratings;
|
||||
pub mod vocoders;
|
||||
pub mod voice_clone_requests;
|
||||
pub mod voice_conversion;
|
||||
pub mod voice_designer;
|
||||
pub mod w2l;
|
||||
pub mod user_bookmarks;
|
||||
pub mod weights;
|
||||
pub mod image_gen;
|
||||
pub(crate) mod mocap;
|
||||
pub(crate) mod workflows;
|
||||
|
||||
+2
@@ -15,6 +15,7 @@ use rand::Rng;
|
||||
use rand::seq::SliceRandom;
|
||||
|
||||
use enums::by_table::generic_inference_jobs::inference_category::InferenceCategory;
|
||||
use enums::by_table::generic_inference_jobs::inference_job_type::InferenceJobType;
|
||||
use enums::by_table::generic_inference_jobs::inference_model_type::InferenceModelType;
|
||||
use enums::common::visibility::Visibility;
|
||||
use http_server_common::request::get_request_api_token::get_request_api_token;
|
||||
@@ -397,6 +398,7 @@ pub async fn enqueue_infer_tts_handler(
|
||||
|
||||
let query_result = insert_generic_inference_job(InsertGenericInferenceArgs {
|
||||
uuid_idempotency_token: &request.uuid_idempotency_token,
|
||||
job_type: InferenceJobType::Tacotron2,
|
||||
inference_category: InferenceCategory::TextToSpeech,
|
||||
maybe_model_type: Some(model_type),
|
||||
maybe_model_token: Some(request.tts_model_token.as_str()),
|
||||
|
||||
+12
@@ -16,6 +16,7 @@ use sqlx::pool::PoolConnection;
|
||||
|
||||
use enums::by_table::generic_inference_jobs::inference_category::InferenceCategory;
|
||||
use enums::by_table::generic_inference_jobs::inference_input_source_token_type::InferenceInputSourceTokenType;
|
||||
use enums::by_table::generic_inference_jobs::inference_job_type::InferenceJobType;
|
||||
use enums::by_table::generic_inference_jobs::inference_model_type::InferenceModelType;
|
||||
use enums::common::visibility::Visibility;
|
||||
use http_server_common::request::get_request_header_optional::get_request_header_optional;
|
||||
@@ -289,8 +290,19 @@ pub async fn enqueue_voice_conversion_inference_handler(
|
||||
});
|
||||
}
|
||||
|
||||
let job_type = match model_inference_info.job_model_type {
|
||||
InferenceModelType::SoVitsSvc => InferenceJobType::SoVitsSvc,
|
||||
InferenceModelType::RvcV2 => InferenceJobType::RvcV2,
|
||||
_ => {
|
||||
// In theory, this shouldn't catch anything.
|
||||
error!("wrong model type for voice conversion: {:?}", model_inference_info.job_model_type);
|
||||
return Err(EnqueueVoiceConversionInferenceError::ServerError)
|
||||
}
|
||||
};
|
||||
|
||||
let query_result = insert_generic_inference_job(InsertGenericInferenceArgs {
|
||||
uuid_idempotency_token: &request.uuid_idempotency_token,
|
||||
job_type,
|
||||
inference_category: InferenceCategory::VoiceConversion,
|
||||
maybe_model_type: Some(model_inference_info.job_model_type),
|
||||
maybe_model_token: Some(&model_token),
|
||||
|
||||
+4
-1
@@ -14,7 +14,9 @@ use serde::Serialize;
|
||||
use utoipa::ToSchema;
|
||||
|
||||
use enums::by_table::generic_inference_jobs::inference_category::InferenceCategory;
|
||||
use enums::by_table::generic_inference_jobs::inference_job_type::InferenceJobType;
|
||||
use enums::by_table::generic_inference_jobs::inference_model_type::InferenceModelType;
|
||||
use enums::common::visibility::Visibility;
|
||||
use http_server_common::request::get_request_header_optional::get_request_header_optional;
|
||||
use http_server_common::request::get_request_ip::get_request_ip;
|
||||
use mysql_queries::payloads::generic_inference_args::generic_inference_args::{
|
||||
@@ -188,6 +190,7 @@ pub async fn enqueue_tts_request(
|
||||
// create the job record here!
|
||||
let query_result = insert_generic_inference_job(InsertGenericInferenceArgs {
|
||||
uuid_idempotency_token: &request.uuid_idempotency_token,
|
||||
job_type: InferenceJobType::StyleTTS2,
|
||||
inference_category: InferenceCategory::TextToSpeech,
|
||||
maybe_model_type: Some(InferenceModelType::StyleTTS2), // NB: Model is static during inference
|
||||
maybe_model_token: None, // NB: Model is static during inference
|
||||
@@ -202,7 +205,7 @@ pub async fn enqueue_tts_request(
|
||||
maybe_creator_user_token: maybe_user_token.as_ref(),
|
||||
maybe_avt_token: maybe_avt_token.as_ref(),
|
||||
creator_ip_address: &ip_address,
|
||||
creator_set_visibility: enums::common::visibility::Visibility::Public,
|
||||
creator_set_visibility: Visibility::Public,
|
||||
priority_level,
|
||||
requires_keepalive: true, // do we need this? I think so
|
||||
is_debug_request,
|
||||
|
||||
+2
@@ -13,6 +13,7 @@ use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
|
||||
use enums::by_table::generic_inference_jobs::inference_category::InferenceCategory;
|
||||
use enums::by_table::generic_inference_jobs::inference_job_type::InferenceJobType;
|
||||
use enums::by_table::generic_inference_jobs::inference_model_type::InferenceModelType;
|
||||
use http_server_common::request::get_request_header_optional::get_request_header_optional;
|
||||
use http_server_common::request::get_request_ip::get_request_ip;
|
||||
@@ -176,6 +177,7 @@ pub async fn create_voice_handler(
|
||||
// create the job record here! explore the table insert the inference args in here as json! keep it short
|
||||
let query_result = insert_generic_inference_job(InsertGenericInferenceArgs {
|
||||
uuid_idempotency_token: &request.uuid_idempotency_token,
|
||||
job_type: InferenceJobType::StyleTTS2,
|
||||
inference_category: InferenceCategory::TextToSpeech,
|
||||
maybe_model_type: Some(InferenceModelType::StyleTTS2), // NB: Model is static during inference
|
||||
maybe_model_token: None, // NB: Model is static during inference
|
||||
|
||||
+7
@@ -11,6 +11,7 @@ use actix_web::error::ResponseError;
|
||||
use actix_web::http::StatusCode;
|
||||
use log::{info, warn};
|
||||
use enums::by_table::generic_inference_jobs::inference_category::InferenceCategory;
|
||||
use enums::by_table::generic_inference_jobs::inference_job_type::InferenceJobType;
|
||||
use enums::by_table::generic_inference_jobs::inference_model_type::InferenceModelType;
|
||||
use enums::common::visibility::Visibility;
|
||||
use http_server_common::request::get_request_header_optional::get_request_header_optional;
|
||||
@@ -19,6 +20,7 @@ use mysql_queries::payloads::generic_inference_args::generic_inference_args::{Ge
|
||||
use mysql_queries::payloads::generic_inference_args::workflow_payload::{NewValue, WorkflowArgs};
|
||||
use mysql_queries::queries::generic_inference::web::insert_generic_inference_job::{insert_generic_inference_job, InsertGenericInferenceArgs};
|
||||
use tokens::tokens::generic_inference_jobs::InferenceJobToken;
|
||||
use tokens::tokens::media_files::MediaFileToken;
|
||||
use tokens::tokens::model_weights::ModelWeightToken;
|
||||
use tokens::tokens::users::UserToken;
|
||||
|
||||
@@ -44,6 +46,8 @@ pub struct EnqueueComfyRequest {
|
||||
maybe_negative_prompt: Option<String>,
|
||||
maybe_json_modifications: Option<HashMap<String, NewValue>>,
|
||||
maybe_workflow_config: Option<ModelWeightToken>,
|
||||
maybe_input_file: Option<MediaFileToken>,
|
||||
maybe_output_path: Option<String>,
|
||||
|
||||
creator_set_visibility: Option<Visibility>,
|
||||
}
|
||||
@@ -178,12 +182,15 @@ pub async fn enqueue_comfy_ui_handler(
|
||||
maybe_lora_model: request.maybe_lora_model.clone(),
|
||||
maybe_json_modifications: request.maybe_json_modifications.clone(),
|
||||
maybe_workflow_config: request.maybe_workflow_config.clone(),
|
||||
maybe_input_file: request.maybe_input_file.clone(),
|
||||
maybe_output_path: request.maybe_output_path.clone(),
|
||||
};
|
||||
|
||||
info!("Creating ComfyUI job record...");
|
||||
|
||||
let query_result = insert_generic_inference_job(InsertGenericInferenceArgs {
|
||||
uuid_idempotency_token: &request.uuid_idempotency_token,
|
||||
job_type: InferenceJobType::ComfyUi,
|
||||
inference_category: InferenceCategory::Workflow,
|
||||
maybe_model_type: Some(InferenceModelType::ComfyUi), // NB: Model is static during inference
|
||||
maybe_model_token: None, // NB: Model is static during inference
|
||||
|
||||
@@ -26,11 +26,13 @@ use crate::http_server::endpoints::categories::tts::list_tts_model_assigned_cate
|
||||
use crate::http_server::endpoints::comments::create_comment_handler::create_comment_handler;
|
||||
use crate::http_server::endpoints::comments::delete_comment_handler::delete_comment_handler;
|
||||
use crate::http_server::endpoints::comments::list_comments_handler::list_comments_handler;
|
||||
use crate::http_server::endpoints::conversion::enqueue_fbx_to_gltf_handler::enqueue_fbx_to_gltf_handler;
|
||||
use crate::http_server::endpoints::download_job::enqueue_generic_download::enqueue_generic_download_handler;
|
||||
use crate::http_server::endpoints::download_job::get_generic_upload_job_status::get_generic_download_job_status_handler;
|
||||
use crate::http_server::endpoints::events::list_events::list_events_handler;
|
||||
use crate::http_server::endpoints::flags::design_refresh_flag::disable_design_refresh_flag_handler::disable_design_refresh_flag_handler;
|
||||
use crate::http_server::endpoints::flags::design_refresh_flag::enable_design_refresh_flag_handler::enable_design_refresh_flag_handler;
|
||||
use crate::http_server::endpoints::image_gen::enqueue_image_generation::enqueue_image_generation_request;
|
||||
use crate::http_server::endpoints::inference_job::get_inference_job_status::get_inference_job_status_handler;
|
||||
use crate::http_server::endpoints::inference_job::get_pending_inference_job_count::get_pending_inference_job_count_handler;
|
||||
use crate::http_server::endpoints::inference_job::kill_inference_jobs::kill_generic_inference_jobs_handler;
|
||||
@@ -55,7 +57,6 @@ use crate::http_server::endpoints::misc::enable_alpha_easy_handler::enable_alpha
|
||||
use crate::http_server::endpoints::misc::enable_alpha_handler::enable_alpha_handler;
|
||||
use crate::http_server::endpoints::misc::root_index::get_root_index;
|
||||
use crate::http_server::endpoints::mocap::enqueue_mocapnet::enqueue_mocapnet_handler;
|
||||
use crate::http_server::endpoints::workflows::enqueue_comfy_ui::enqueue_comfy_ui_handler;
|
||||
use crate::http_server::endpoints::moderation::approval::pending_w2l_templates::get_pending_w2l_templates_handler;
|
||||
use crate::http_server::endpoints::moderation::categories::delete_category::delete_category_handler;
|
||||
use crate::http_server::endpoints::moderation::categories::edit_category::edit_category_handler;
|
||||
@@ -167,7 +168,7 @@ use crate::http_server::endpoints::weights::list_weights_by_user::list_weights_b
|
||||
use crate::http_server::endpoints::weights::search_model_weights_handler::search_model_weights_handler;
|
||||
use crate::http_server::endpoints::weights::set_model_weight_cover_image::set_model_weight_cover_image_handler;
|
||||
use crate::http_server::endpoints::weights::update_weight::update_weight_handler;
|
||||
use crate::http_server::endpoints::image_gen::enqueue_image_generation::enqueue_image_generation_request;
|
||||
use crate::http_server::endpoints::workflows::enqueue_comfy_ui::enqueue_comfy_ui_handler;
|
||||
|
||||
pub fn add_routes<T, B> (app: App<T>, server_environment: ServerEnvironment) -> App<T>
|
||||
where
|
||||
@@ -250,6 +251,12 @@ pub fn add_routes<T, B> (app: App<T>, server_environment: ServerEnvironment) ->
|
||||
.add_get("/v1/model_inference/queue_length", get_pending_inference_job_count_handler)
|
||||
.into_app();
|
||||
|
||||
// ==================== Format Conversion ====================
|
||||
|
||||
let mut app = RouteBuilder::from_app(app)
|
||||
.add_post("/v1/conversion/enqueue_fbx_to_gltf", enqueue_fbx_to_gltf_handler)
|
||||
.into_app();
|
||||
|
||||
// ==================== Stats ====================
|
||||
|
||||
let mut app = RouteBuilder::from_app(app)
|
||||
|
||||
Reference in New Issue
Block a user