Compare commits

...
Author SHA1 Message Date
Brandon Thomas 5a938ceed1 WIP / Broken early sleep exit during local dev debug cycle 2025-02-11 18:01:39 -05:00
Brandon Thomas 62d708e357 pass tensor dimensions downstream 2025-02-10 23:44:37 -05:00
Brandon Thomas 72083eb030 enforce file types, debugging 2025-02-10 23:14:53 -05:00
Brandon Thomas 62afa22d8c skip image resizes 2025-02-10 22:43:07 -05:00
Brandon Thomas 418b116469 ctrl-c handler, pathing 2025-02-10 21:31:42 -05:00
Brandon Thomas 42c7c70387 resample fps, duration wip 2025-02-10 20:36:07 -05:00
Kasisnu 39c52d5377 animatex: resize args fix, add sqlx migration 2025-02-10 18:12:06 +05:30
Brandon Thomas cea3668cfc dynamic resize behavior 2025-02-06 01:57:57 -05:00
Brandon Thomas 1caf1f38b8 working end-to-end, need to clean up resampling / dimensions / fps 2025-02-06 00:48:52 -05:00
Brandon Thomas f0b2a07613 inference wip 2025-02-05 23:52:01 -05:00
Brandon Thomas 3cd04414f8 auto-kill jobs in dev 2025-02-05 19:37:02 -05:00
Brandon Thomas f3776e953c run pose frame generation 2025-02-05 18:02:00 -05:00
Brandon Thomas d923699009 run stable animator or animate-x 2025-02-05 17:20:17 -05:00
Brandon Thomas f842d795d6 wip 2025-02-05 16:08:55 -05:00
Brandon Thomas 7a1ce7c9fd animate-x http args, refactor job 2025-02-05 14:35:42 -05:00
Brandon Thomas d0a3c02c00 resample input video 2025-02-02 02:50:35 -05:00
Brandon Thomas 32bc400686 local configurability 2025-02-02 00:58:12 -05:00
Brandon Thomas e3973e4ce1 hack fixes for thumbnails and cdn in dev 2025-02-01 23:36:05 -05:00
Brandon Thomas 1d7bc11be3 hack in support for "local CDN" / buckets 2025-01-31 18:51:27 -05:00
Brandon Thomas 5321eb145d fix api bug 2025-01-31 17:53:46 -05:00
Brandon Thomas 48043fb602 resize image 2025-01-31 13:53:50 -05:00
Brandon Thomas 8af2035ee6 fix cors 2025-01-26 02:35:10 -05:00
Kasisnu 1e2f73e44a stable-animator: minor tweaks 2025-01-25 01:19:35 +05:30
Brandon Thomas b49a7225e4 fix build errors 2025-01-24 04:21:39 -05:00
Brandon Thomas de01700359 fix build errors 2025-01-24 04:12:23 -05:00
Brandon Thomas f7990e2b61 fix build errors, codegen db 2025-01-24 03:40:33 -05:00
Brandon Thomas 081307d735 debug sleep, width height 2025-01-24 01:13:53 -05:00
Brandon Thomas 397241fb84 more debugging, more job params 2025-01-24 00:31:38 -05:00
Brandon Thomas dbc0f5f62e fix pathing, deps, etc. (local) 2025-01-23 23:28:58 -05:00
Brandon Thomas b521fa7676 Enqueue jobs properly in dev, dispatch to StableAnimator 2025-01-23 22:42:42 -05:00
Brandon Thomas a67f60d09e debug r2 client in dev, fix startup 2025-01-23 21:02:36 -05:00
Brandon Thomas 2cdf9ce41f build fix 2025-01-23 14:48:51 -05:00
Brandon Thomas 38fb1a4a32 job wip 2025-01-21 23:39:51 -05:00
Brandon Thomas 984fd1a21f wip (need to work on downstream pathing) 2025-01-17 18:46:02 -05:00
Brandon Thomas 8b92dc4099 stub for studio gen 2 inference job 2025-01-16 23:55:13 -05:00
Brandon Thomas 86677b8f97 fixup api docs 2025-01-16 21:12:26 -05:00
Brandon Thomas ed7a9905ee add studio gen2 enqueue endpoint 2025-01-16 20:45:08 -05:00
Kasisnu 1c25c4085b temp switch ST cdn to FY 2024-11-22 23:31:04 +05:30
Kasisnu 91e739d7c4 update FY bucket paths in places I missed the first time 2024-11-22 13:56:41 +05:30
Kasisnu 40a82142bf update FY bucket paths 2024-11-22 13:40:21 +05:30
Kasisnu f6454035d2 enable cog-video for staff only 2024-11-14 19:42:53 +05:30
Kasisnu e93a8664c1 add f5 and seed vc to list jobs handler length exemptions 2024-10-23 19:13:38 +05:30
Kasisnu 0ef4333013 seed-vc: fix source/reference audio mixup 2024-10-23 18:55:07 +05:30
Kasisnu cd11d0aa4e seed-vc: public model type enum extension 2024-10-23 17:48:25 +05:30
Kasisnu e8644d3b82 seed-vc: minor tweaks in inference 2024-10-23 17:05:54 +05:30
Kasisnu 806f70d7ba fix lots of unused imports 2024-10-23 13:41:03 +05:30
Kasisnu b6eb0e9394 super basic seed_vc inference integration 2024-10-23 13:41:03 +05:30
Kasisnu 02050ef86c build a musl variant of rust-gpu 2024-10-21 21:53:20 +05:30
Kasisnu 9b698d155e previews: some tweaks 2024-10-19 14:59:50 +05:30
Kasisnu ed359164ff sigh, add the f5-tts inference_category to inference job loop 2024-10-18 21:17:05 +05:30
Kasisnu 2da81d47a7 sigh, add an f5-tts inference_category 2024-10-18 21:06:40 +05:30
Kasisnu cb1253f509 some tweaks to f5 inference category 2024-10-18 18:16:11 +05:30
Kasisnu f21da59530 some tweaks to f5 inference 2024-10-18 17:18:03 +05:30
Kasisnu 4ebb7fe282 some tweaks to f5 inference 2024-10-18 16:28:05 +05:30
Kasisnu 83764e2381 disable fast previews while focussing on f5 2024-10-18 13:52:37 +05:30
Kasisnu 475ed299db super basic f5 integration 2024-10-18 13:52:37 +05:30
Kasisnu 53786bf9b9 rust: bump up version 2024-10-17 20:47:07 +05:30
Kasisnu 3a7fcc1595 uploads: reduce max concurrent uploads 2024-10-17 18:40:17 +05:30
Kasisnu 770293af49 chore: update hakari deps 2024-10-17 16:37:26 +05:30
Kasisnu bb19960c02 studio previews: faster concurrent uploads 2024-10-17 16:37:26 +05:30
Brandon Thomas f3cde45f33 storyteller render cors 2024-10-06 12:00:07 -04:00
Brandon Thomas 6adb104e58 cors for board.storyteller.ai 2024-10-04 01:02:45 -04:00
Brandon Thomas 3a1220853d sort tags 2024-10-03 10:19:51 -04:00
Brandon Thomas b1732a0d11 fix builds 2024-10-03 09:08:56 -04:00
Brandon Thomas c767a17da7 fix api 2024-10-03 00:45:07 -04:00
Brandon Thomas a2a7a10d0c clean up queries 2024-10-02 23:59:22 -04:00
Brandon Thomas d6ac0c9d0b fix deletion of tags 2024-10-02 23:54:45 -04:00
Brandon Thomas ea90bb6758 fixup queries 2024-10-02 21:44:24 -04:00
Brandon Thomas 252576b88a document endpoints 2024-10-02 17:25:05 -04:00
Brandon Thomas f7ed250c7c list tags endpoint, mount routes 2024-10-02 17:20:51 -04:00
Brandon Thomas 0b5530c8a3 tag editing endpoint 2024-10-02 13:03:26 -04:00
Brandon Thomas 606a439352 all queries for tags 2024-10-02 02:32:20 -04:00
Brandon Thomas aec5675784 almost all of the queries needed for tags 2024-10-02 01:46:42 -04:00
Brandon Thomas db05b61c51 search algorithm evaluation 2024-10-01 20:27:33 -04:00
Brandon Thomas 7141aa028f assert on popular voices 2024-10-01 20:19:58 -04:00
Brandon Thomas 90bfb2c2bf query testing harness 2024-10-01 20:03:42 -04:00
Brandon Thomas 7f43e98f07 refactor es search 2024-10-01 10:53:47 -04:00
Brandon Thomas 9ebd040d59 check up on models not annotated with language 2024-09-30 23:40:31 -04:00
Kasisnu f890f8781c style transfer: disable cancellation watcher 2024-09-30 20:11:48 +05:30
Kasisnu 3e35db2f8e style transfer: disable keepalive 2024-09-30 19:27:47 +05:30
Brandon Thomas 4b53df69a0 sort and threshold search 2024-09-30 03:35:35 -04:00
Brandon Thomas bca8925f31 codegen 2024-09-29 19:21:35 -04:00
Brandon Thomas d0a51f0344 elasticsearch weights search reads from appropriate field 2024-09-29 19:18:02 -04:00
Brandon Thomas 4397e29886 report language in model weights (get, list endpoints) 2024-09-29 19:02:12 -04:00
Brandon Thomas ac4a7f4612 update model language 2024-09-29 01:51:12 -04:00
Brandon Thomas 87a768890a switch update weights handler to mysql connection 2024-09-29 01:24:56 -04:00
Brandon Thomas 9bfd0330a9 migrations to add language tags to models 2024-09-29 00:54:12 -04:00
Brandon Thomas a8766e9620 signups by source 2024-09-29 00:38:43 -04:00
Brandon Thomas 3b3b218e0c move to databox dir 2024-09-28 23:29:59 -04:00
Brandon Thomas 1f595f3d64 new metric to track 2024-09-28 23:29:32 -04:00
Brandon Thomas 7297f77334 fail faster 2024-09-28 00:45:56 -04:00
Brandon Thomas 7186f37841 handle failures in thread (#374) 2024-09-28 00:42:06 -04:00
252 changed files with 10105 additions and 1325 deletions
+1
View File
@@ -50,6 +50,7 @@
<w>unpermitted</w>
<w>upserting</w>
<w>upserts</w>
<w>upvoted</w>
<w>vits</w>
<w>vocoded</w>
<w>vocoder</w>
@@ -0,0 +1,12 @@
{
"db_name": "MySQL",
"query": "\nINSERT INTO media_files\nSET\n token = ?,\n\n media_class = ?,\n media_type = ?,\n\n origin_category = ?,\n origin_product_category = ?,\n maybe_origin_model_type = ?,\n\n maybe_mime_type = ?,\n file_size_bytes = ?,\n\n checksum_sha2 = ?,\n\n maybe_style_transfer_source_media_file_token = ?,\n maybe_scene_source_media_file_token = ?,\n\n maybe_title = ?,\n maybe_prompt_token = ?,\n\n public_bucket_directory_hash = ?,\n maybe_public_bucket_prefix = ?,\n maybe_public_bucket_extension = ?,\n\n maybe_creator_user_token = ?,\n maybe_creator_anonymous_visitor_token = ?,\n creator_ip_address = ?,\n\n creator_set_visibility = ?,\n\n maybe_creator_file_synthetic_id = ?,\n maybe_creator_category_synthetic_id = ?,\n\n is_generated_on_prem = ?,\n generated_by_worker = ?,\n generated_by_cluster = ?\n\n ",
"describe": {
"columns": [],
"parameters": {
"Right": 25
},
"nullable": []
},
"hash": "039c24023c93deacc0b09fbbfe747821ba9f7e06bc17f651628edf1a82f5feb6"
}
@@ -1,6 +1,6 @@
{
"db_name": "MySQL",
"query": "\n SELECT\n wt.token as `token: tokens::tokens::model_weights::ModelWeightToken`,\n wt.title,\n wt.weights_type as `weights_type: enums::by_table::model_weights::weights_types::WeightsType`,\n wt.weights_category as `weights_category: enums::by_table::model_weights::weights_category::WeightsCategory`,\n wt.maybe_description_markdown,\n wt.maybe_description_rendered_html,\n wt.creator_ip_address,\n wt.creator_set_visibility as `creator_set_visibility: enums::common::visibility::Visibility`,\n\n wt.creator_user_token as `creator_user_token: tokens::tokens::users::UserToken`,\n users.username as creator_username,\n users.display_name as creator_display_name,\n users.email_gravatar_hash AS creator_gravatar_hash,\n\n wt.maybe_last_update_user_token as `maybe_last_update_user_token: tokens::tokens::users::UserToken`,\n wt.original_download_url,\n wt.original_filename,\n wt.file_size_bytes,\n wt.file_checksum_sha2,\n wt.public_bucket_hash,\n wt.maybe_public_bucket_prefix,\n wt.maybe_public_bucket_extension,\n\n cover_image.public_bucket_directory_hash as maybe_cover_image_public_bucket_hash,\n cover_image.maybe_public_bucket_prefix as maybe_cover_image_public_bucket_prefix,\n cover_image.maybe_public_bucket_extension as maybe_cover_image_public_bucket_extension,\n\n entity_stats.ratings_positive_count as maybe_ratings_positive_count,\n entity_stats.ratings_negative_count as maybe_ratings_negative_count,\n entity_stats.bookmark_count as maybe_bookmark_count,\n wt.cached_usage_count,\n\n featured_items.entity_token IS NOT NULL AS is_featured,\n\n wt.version,\n wt.created_at,\n wt.updated_at,\n wt.user_deleted_at,\n wt.mod_deleted_at\n\n FROM model_weights as wt\n JOIN users\n ON users.token = wt.creator_user_token\n LEFT OUTER JOIN media_files as cover_image\n ON cover_image.token = wt.maybe_cover_image_media_file_token\n LEFT OUTER JOIN entity_stats\n ON entity_stats.entity_type = \"model_weight\"\n AND entity_stats.entity_token = wt.token\n LEFT OUTER JOIN featured_items\n ON featured_items.entity_type = \"model_weight\"\n AND featured_items.entity_token = wt.token\n AND featured_items.deleted_at IS NULL\n WHERE\n wt.token = ?\n AND wt.user_deleted_at IS NULL\n AND wt.mod_deleted_at IS NULL\n ",
"query": "\n SELECT\n wt.token as `token: tokens::tokens::model_weights::ModelWeightToken`,\n wt.title,\n wt.weights_type as `weights_type: enums::by_table::model_weights::weights_types::WeightsType`,\n wt.weights_category as `weights_category: enums::by_table::model_weights::weights_category::WeightsCategory`,\n wt.maybe_description_markdown,\n wt.maybe_description_rendered_html,\n\n wt.maybe_ietf_language_tag,\n wt.maybe_ietf_primary_language_subtag,\n\n wt.creator_ip_address,\n wt.creator_set_visibility as `creator_set_visibility: enums::common::visibility::Visibility`,\n\n wt.creator_user_token as `creator_user_token: tokens::tokens::users::UserToken`,\n users.username as creator_username,\n users.display_name as creator_display_name,\n users.email_gravatar_hash AS creator_gravatar_hash,\n\n wt.maybe_last_update_user_token as `maybe_last_update_user_token: tokens::tokens::users::UserToken`,\n wt.original_download_url,\n wt.original_filename,\n wt.file_size_bytes,\n wt.file_checksum_sha2,\n wt.public_bucket_hash,\n wt.maybe_public_bucket_prefix,\n wt.maybe_public_bucket_extension,\n\n cover_image.public_bucket_directory_hash as maybe_cover_image_public_bucket_hash,\n cover_image.maybe_public_bucket_prefix as maybe_cover_image_public_bucket_prefix,\n cover_image.maybe_public_bucket_extension as maybe_cover_image_public_bucket_extension,\n\n entity_stats.ratings_positive_count as maybe_ratings_positive_count,\n entity_stats.ratings_negative_count as maybe_ratings_negative_count,\n entity_stats.bookmark_count as maybe_bookmark_count,\n wt.cached_usage_count,\n\n featured_items.entity_token IS NOT NULL AS is_featured,\n\n wt.version,\n wt.created_at,\n wt.updated_at,\n wt.user_deleted_at,\n wt.mod_deleted_at\n\n FROM model_weights as wt\n JOIN users\n ON users.token = wt.creator_user_token\n LEFT OUTER JOIN media_files as cover_image\n ON cover_image.token = wt.maybe_cover_image_media_file_token\n LEFT OUTER JOIN entity_stats\n ON entity_stats.entity_type = \"model_weight\"\n AND entity_stats.entity_token = wt.token\n LEFT OUTER JOIN featured_items\n ON featured_items.entity_type = \"model_weight\"\n AND featured_items.entity_token = wt.token\n AND featured_items.deleted_at IS NULL\n WHERE\n wt.token = ?\n AND wt.user_deleted_at IS NULL\n AND wt.mod_deleted_at IS NULL\n ",
"describe": {
"columns": [
{
@@ -65,6 +65,26 @@
},
{
"ordinal": 6,
"name": "maybe_ietf_language_tag",
"type_info": {
"type": "VarString",
"flags": "BINARY",
"char_set": 224,
"max_size": 256
}
},
{
"ordinal": 7,
"name": "maybe_ietf_primary_language_subtag",
"type_info": {
"type": "VarString",
"flags": "BINARY",
"char_set": 224,
"max_size": 48
}
},
{
"ordinal": 8,
"name": "creator_ip_address",
"type_info": {
"type": "VarString",
@@ -74,7 +94,7 @@
}
},
{
"ordinal": 7,
"ordinal": 9,
"name": "creator_set_visibility: enums::common::visibility::Visibility",
"type_info": {
"type": "String",
@@ -84,7 +104,7 @@
}
},
{
"ordinal": 8,
"ordinal": 10,
"name": "creator_user_token: tokens::tokens::users::UserToken",
"type_info": {
"type": "VarString",
@@ -94,7 +114,7 @@
}
},
{
"ordinal": 9,
"ordinal": 11,
"name": "creator_username",
"type_info": {
"type": "VarString",
@@ -104,7 +124,7 @@
}
},
{
"ordinal": 10,
"ordinal": 12,
"name": "creator_display_name",
"type_info": {
"type": "VarString",
@@ -114,7 +134,7 @@
}
},
{
"ordinal": 11,
"ordinal": 13,
"name": "creator_gravatar_hash",
"type_info": {
"type": "String",
@@ -124,7 +144,7 @@
}
},
{
"ordinal": 12,
"ordinal": 14,
"name": "maybe_last_update_user_token: tokens::tokens::users::UserToken",
"type_info": {
"type": "VarString",
@@ -134,7 +154,7 @@
}
},
{
"ordinal": 13,
"ordinal": 15,
"name": "original_download_url",
"type_info": {
"type": "VarString",
@@ -144,7 +164,7 @@
}
},
{
"ordinal": 14,
"ordinal": 16,
"name": "original_filename",
"type_info": {
"type": "VarString",
@@ -154,7 +174,7 @@
}
},
{
"ordinal": 15,
"ordinal": 17,
"name": "file_size_bytes",
"type_info": {
"type": "LongLong",
@@ -164,7 +184,7 @@
}
},
{
"ordinal": 16,
"ordinal": 18,
"name": "file_checksum_sha2",
"type_info": {
"type": "String",
@@ -174,7 +194,7 @@
}
},
{
"ordinal": 17,
"ordinal": 19,
"name": "public_bucket_hash",
"type_info": {
"type": "VarString",
@@ -184,7 +204,7 @@
}
},
{
"ordinal": 18,
"ordinal": 20,
"name": "maybe_public_bucket_prefix",
"type_info": {
"type": "VarString",
@@ -194,7 +214,7 @@
}
},
{
"ordinal": 19,
"ordinal": 21,
"name": "maybe_public_bucket_extension",
"type_info": {
"type": "VarString",
@@ -204,7 +224,7 @@
}
},
{
"ordinal": 20,
"ordinal": 22,
"name": "maybe_cover_image_public_bucket_hash",
"type_info": {
"type": "VarString",
@@ -214,7 +234,7 @@
}
},
{
"ordinal": 21,
"ordinal": 23,
"name": "maybe_cover_image_public_bucket_prefix",
"type_info": {
"type": "VarString",
@@ -224,7 +244,7 @@
}
},
{
"ordinal": 22,
"ordinal": 24,
"name": "maybe_cover_image_public_bucket_extension",
"type_info": {
"type": "VarString",
@@ -234,7 +254,7 @@
}
},
{
"ordinal": 23,
"ordinal": 25,
"name": "maybe_ratings_positive_count",
"type_info": {
"type": "Long",
@@ -244,7 +264,7 @@
}
},
{
"ordinal": 24,
"ordinal": 26,
"name": "maybe_ratings_negative_count",
"type_info": {
"type": "Long",
@@ -254,7 +274,7 @@
}
},
{
"ordinal": 25,
"ordinal": 27,
"name": "maybe_bookmark_count",
"type_info": {
"type": "Long",
@@ -264,7 +284,7 @@
}
},
{
"ordinal": 26,
"ordinal": 28,
"name": "cached_usage_count",
"type_info": {
"type": "LongLong",
@@ -274,7 +294,7 @@
}
},
{
"ordinal": 27,
"ordinal": 29,
"name": "is_featured",
"type_info": {
"type": "LongLong",
@@ -284,7 +304,7 @@
}
},
{
"ordinal": 28,
"ordinal": 30,
"name": "version",
"type_info": {
"type": "Long",
@@ -294,7 +314,7 @@
}
},
{
"ordinal": 29,
"ordinal": 31,
"name": "created_at",
"type_info": {
"type": "Timestamp",
@@ -304,7 +324,7 @@
}
},
{
"ordinal": 30,
"ordinal": 32,
"name": "updated_at",
"type_info": {
"type": "Timestamp",
@@ -314,7 +334,7 @@
}
},
{
"ordinal": 31,
"ordinal": 33,
"name": "user_deleted_at",
"type_info": {
"type": "Timestamp",
@@ -324,7 +344,7 @@
}
},
{
"ordinal": 32,
"ordinal": 34,
"name": "mod_deleted_at",
"type_info": {
"type": "Timestamp",
@@ -344,6 +364,8 @@
false,
true,
true,
true,
true,
false,
false,
false,
@@ -373,5 +395,5 @@
true
]
},
"hash": "1053294a21439ddb41fdd1053547b3f5c50457f6ba9450e5167a1db5caf896d1"
"hash": "29fe7f8464257537840d7f095ec01f850d79f970109549d14978fc3bed7cef32"
}
@@ -0,0 +1,36 @@
{
"db_name": "MySQL",
"query": "\nSELECT\n t.token as `token: tokens::tokens::tags::TagToken`,\n t.tag_value\n\nFROM\n tags AS t\nJOIN tag_uses AS u\n ON t.token = u.tag_token\nWHERE\n u.entity_type = ?\n AND u.entity_token = ?\n AND u.maybe_deleted_at IS NULL\nORDER BY t.id DESC\nLIMIT 500\n ",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "token: tokens::tokens::tags::TagToken",
"type_info": {
"type": "VarString",
"flags": "NOT_NULL | UNIQUE_KEY | BINARY | NO_DEFAULT_VALUE",
"char_set": 224,
"max_size": 128
}
},
{
"ordinal": 1,
"name": "tag_value",
"type_info": {
"type": "VarString",
"flags": "NOT_NULL | UNIQUE_KEY | BINARY | NO_DEFAULT_VALUE",
"char_set": 224,
"max_size": 1020
}
}
],
"parameters": {
"Right": 2
},
"nullable": [
false,
false
]
},
"hash": "3929e9fe74e75fad7252681880c6c3bd7e76e96b0bf603f77f6a9b2d4fcda18d"
}
@@ -0,0 +1,12 @@
{
"db_name": "MySQL",
"query": "\nDELETE FROM tag_uses\nWHERE entity_type = ?\nAND entity_token = ?\nLIMIT 1\n ",
"describe": {
"columns": [],
"parameters": {
"Right": 2
},
"nullable": []
},
"hash": "41b16ccf04ef1ffb8b48bc59edf9389f61f54b871f6361f35529706b98d419ec"
}
@@ -1,6 +1,6 @@
{
"db_name": "MySQL",
"query": "\n SELECT\n wt.token as `token: tokens::tokens::model_weights::ModelWeightToken`,\n wt.title,\n wt.weights_type as `weights_type: enums::by_table::model_weights::weights_types::WeightsType`,\n wt.weights_category as `weights_category: enums::by_table::model_weights::weights_category::WeightsCategory`,\n wt.maybe_description_markdown,\n wt.maybe_description_rendered_html,\n\n wt.creator_user_token as `creator_user_token: tokens::tokens::users::UserToken`,\n users.username as creator_username,\n users.display_name as creator_display_name,\n users.email_gravatar_hash AS creator_gravatar_hash,\n\n wt.creator_ip_address,\n wt.creator_set_visibility as `creator_set_visibility: enums::common::visibility::Visibility`,\n wt.maybe_last_update_user_token as `maybe_last_update_user_token: tokens::tokens::users::UserToken`,\n wt.original_download_url,\n wt.original_filename,\n wt.file_size_bytes,\n wt.file_checksum_sha2,\n\n wt.public_bucket_hash,\n wt.maybe_public_bucket_prefix,\n wt.maybe_public_bucket_extension,\n\n cover_image.public_bucket_directory_hash as maybe_cover_image_public_bucket_hash,\n cover_image.maybe_public_bucket_prefix as maybe_cover_image_public_bucket_prefix,\n cover_image.maybe_public_bucket_extension as maybe_cover_image_public_bucket_extension,\n\n entity_stats.ratings_positive_count as maybe_ratings_positive_count,\n entity_stats.ratings_negative_count as maybe_ratings_negative_count,\n entity_stats.bookmark_count as maybe_bookmark_count,\n wt.cached_usage_count,\n\n featured_items.entity_token IS NOT NULL AS is_featured,\n\n wt.version,\n wt.created_at,\n wt.updated_at,\n wt.user_deleted_at,\n wt.mod_deleted_at\n\n FROM model_weights as wt\n JOIN users\n ON users.token = wt.creator_user_token\n LEFT OUTER JOIN media_files as cover_image\n ON cover_image.token = wt.maybe_cover_image_media_file_token\n LEFT OUTER JOIN entity_stats\n ON entity_stats.entity_type = \"model_weight\"\n AND entity_stats.entity_token = wt.token\n LEFT OUTER JOIN featured_items\n ON featured_items.entity_type = \"model_weight\"\n AND featured_items.entity_token = wt.token\n AND featured_items.deleted_at IS NULL\n WHERE\n wt.token = ?\n ",
"query": "\n SELECT\n wt.token as `token: tokens::tokens::model_weights::ModelWeightToken`,\n wt.title,\n wt.weights_type as `weights_type: enums::by_table::model_weights::weights_types::WeightsType`,\n wt.weights_category as `weights_category: enums::by_table::model_weights::weights_category::WeightsCategory`,\n wt.maybe_description_markdown,\n wt.maybe_description_rendered_html,\n\n wt.maybe_ietf_language_tag,\n wt.maybe_ietf_primary_language_subtag,\n\n wt.creator_user_token as `creator_user_token: tokens::tokens::users::UserToken`,\n users.username as creator_username,\n users.display_name as creator_display_name,\n users.email_gravatar_hash AS creator_gravatar_hash,\n\n wt.creator_ip_address,\n wt.creator_set_visibility as `creator_set_visibility: enums::common::visibility::Visibility`,\n wt.maybe_last_update_user_token as `maybe_last_update_user_token: tokens::tokens::users::UserToken`,\n wt.original_download_url,\n wt.original_filename,\n wt.file_size_bytes,\n wt.file_checksum_sha2,\n\n wt.public_bucket_hash,\n wt.maybe_public_bucket_prefix,\n wt.maybe_public_bucket_extension,\n\n cover_image.public_bucket_directory_hash as maybe_cover_image_public_bucket_hash,\n cover_image.maybe_public_bucket_prefix as maybe_cover_image_public_bucket_prefix,\n cover_image.maybe_public_bucket_extension as maybe_cover_image_public_bucket_extension,\n\n entity_stats.ratings_positive_count as maybe_ratings_positive_count,\n entity_stats.ratings_negative_count as maybe_ratings_negative_count,\n entity_stats.bookmark_count as maybe_bookmark_count,\n wt.cached_usage_count,\n\n featured_items.entity_token IS NOT NULL AS is_featured,\n\n wt.version,\n wt.created_at,\n wt.updated_at,\n wt.user_deleted_at,\n wt.mod_deleted_at\n\n FROM model_weights as wt\n JOIN users\n ON users.token = wt.creator_user_token\n LEFT OUTER JOIN media_files as cover_image\n ON cover_image.token = wt.maybe_cover_image_media_file_token\n LEFT OUTER JOIN entity_stats\n ON entity_stats.entity_type = \"model_weight\"\n AND entity_stats.entity_token = wt.token\n LEFT OUTER JOIN featured_items\n ON featured_items.entity_type = \"model_weight\"\n AND featured_items.entity_token = wt.token\n AND featured_items.deleted_at IS NULL\n WHERE\n wt.token = ?\n ",
"describe": {
"columns": [
{
@@ -65,6 +65,26 @@
},
{
"ordinal": 6,
"name": "maybe_ietf_language_tag",
"type_info": {
"type": "VarString",
"flags": "BINARY",
"char_set": 224,
"max_size": 256
}
},
{
"ordinal": 7,
"name": "maybe_ietf_primary_language_subtag",
"type_info": {
"type": "VarString",
"flags": "BINARY",
"char_set": 224,
"max_size": 48
}
},
{
"ordinal": 8,
"name": "creator_user_token: tokens::tokens::users::UserToken",
"type_info": {
"type": "VarString",
@@ -74,7 +94,7 @@
}
},
{
"ordinal": 7,
"ordinal": 9,
"name": "creator_username",
"type_info": {
"type": "VarString",
@@ -84,7 +104,7 @@
}
},
{
"ordinal": 8,
"ordinal": 10,
"name": "creator_display_name",
"type_info": {
"type": "VarString",
@@ -94,7 +114,7 @@
}
},
{
"ordinal": 9,
"ordinal": 11,
"name": "creator_gravatar_hash",
"type_info": {
"type": "String",
@@ -104,7 +124,7 @@
}
},
{
"ordinal": 10,
"ordinal": 12,
"name": "creator_ip_address",
"type_info": {
"type": "VarString",
@@ -114,7 +134,7 @@
}
},
{
"ordinal": 11,
"ordinal": 13,
"name": "creator_set_visibility: enums::common::visibility::Visibility",
"type_info": {
"type": "String",
@@ -124,7 +144,7 @@
}
},
{
"ordinal": 12,
"ordinal": 14,
"name": "maybe_last_update_user_token: tokens::tokens::users::UserToken",
"type_info": {
"type": "VarString",
@@ -134,7 +154,7 @@
}
},
{
"ordinal": 13,
"ordinal": 15,
"name": "original_download_url",
"type_info": {
"type": "VarString",
@@ -144,7 +164,7 @@
}
},
{
"ordinal": 14,
"ordinal": 16,
"name": "original_filename",
"type_info": {
"type": "VarString",
@@ -154,7 +174,7 @@
}
},
{
"ordinal": 15,
"ordinal": 17,
"name": "file_size_bytes",
"type_info": {
"type": "LongLong",
@@ -164,7 +184,7 @@
}
},
{
"ordinal": 16,
"ordinal": 18,
"name": "file_checksum_sha2",
"type_info": {
"type": "String",
@@ -174,7 +194,7 @@
}
},
{
"ordinal": 17,
"ordinal": 19,
"name": "public_bucket_hash",
"type_info": {
"type": "VarString",
@@ -184,7 +204,7 @@
}
},
{
"ordinal": 18,
"ordinal": 20,
"name": "maybe_public_bucket_prefix",
"type_info": {
"type": "VarString",
@@ -194,7 +214,7 @@
}
},
{
"ordinal": 19,
"ordinal": 21,
"name": "maybe_public_bucket_extension",
"type_info": {
"type": "VarString",
@@ -204,7 +224,7 @@
}
},
{
"ordinal": 20,
"ordinal": 22,
"name": "maybe_cover_image_public_bucket_hash",
"type_info": {
"type": "VarString",
@@ -214,7 +234,7 @@
}
},
{
"ordinal": 21,
"ordinal": 23,
"name": "maybe_cover_image_public_bucket_prefix",
"type_info": {
"type": "VarString",
@@ -224,7 +244,7 @@
}
},
{
"ordinal": 22,
"ordinal": 24,
"name": "maybe_cover_image_public_bucket_extension",
"type_info": {
"type": "VarString",
@@ -234,7 +254,7 @@
}
},
{
"ordinal": 23,
"ordinal": 25,
"name": "maybe_ratings_positive_count",
"type_info": {
"type": "Long",
@@ -244,7 +264,7 @@
}
},
{
"ordinal": 24,
"ordinal": 26,
"name": "maybe_ratings_negative_count",
"type_info": {
"type": "Long",
@@ -254,7 +274,7 @@
}
},
{
"ordinal": 25,
"ordinal": 27,
"name": "maybe_bookmark_count",
"type_info": {
"type": "Long",
@@ -264,7 +284,7 @@
}
},
{
"ordinal": 26,
"ordinal": 28,
"name": "cached_usage_count",
"type_info": {
"type": "LongLong",
@@ -274,7 +294,7 @@
}
},
{
"ordinal": 27,
"ordinal": 29,
"name": "is_featured",
"type_info": {
"type": "LongLong",
@@ -284,7 +304,7 @@
}
},
{
"ordinal": 28,
"ordinal": 30,
"name": "version",
"type_info": {
"type": "Long",
@@ -294,7 +314,7 @@
}
},
{
"ordinal": 29,
"ordinal": 31,
"name": "created_at",
"type_info": {
"type": "Timestamp",
@@ -304,7 +324,7 @@
}
},
{
"ordinal": 30,
"ordinal": 32,
"name": "updated_at",
"type_info": {
"type": "Timestamp",
@@ -314,7 +334,7 @@
}
},
{
"ordinal": 31,
"ordinal": 33,
"name": "user_deleted_at",
"type_info": {
"type": "Timestamp",
@@ -324,7 +344,7 @@
}
},
{
"ordinal": 32,
"ordinal": 34,
"name": "mod_deleted_at",
"type_info": {
"type": "Timestamp",
@@ -344,6 +364,8 @@
false,
true,
true,
true,
true,
false,
false,
false,
@@ -373,5 +395,5 @@
true
]
},
"hash": "5e953a6ad7bcaaed0918109593652b03c7ab497cc65b59bbb6add011de6401f0"
"hash": "53c90f5230aa202aead37a224ec70e350172bc0ec4eeaebde22878ca4aa0f695"
}
@@ -0,0 +1,12 @@
{
"db_name": "MySQL",
"query": "\nUPDATE generic_inference_jobs\nSET\n status = 'cancelled_by_user',\n retry_at = NULL\nWHERE status IN (\n 'pending',\n 'started',\n 'attempt_failed'\n )\nLIMIT 100\n ",
"describe": {
"columns": [],
"parameters": {
"Right": 0
},
"nullable": []
},
"hash": "84320887e8157a450ad46ec0843071c60c054eab61958a9148789ed76cd476e3"
}
@@ -0,0 +1,12 @@
{
"db_name": "MySQL",
"query": "\nINSERT INTO tags\nSET\n token = ?,\n tag_value = ?\n ",
"describe": {
"columns": [],
"parameters": {
"Right": 2
},
"nullable": []
},
"hash": "c7e848e4c906a34ac5a4ad8cc73354baa8d5cd81170dd06d66d7f414505d538b"
}
@@ -1,6 +1,6 @@
{
"db_name": "MySQL",
"query": "\nSELECT\n w.id,\n w.token as `token: tokens::tokens::model_weights::ModelWeightToken`,\n\n w.creator_set_visibility as `creator_set_visibility: enums::common::visibility::Visibility`,\n\n w.weights_type as `weights_type: enums::by_table::model_weights::weights_types::WeightsType`,\n w.weights_category as `weights_category: enums::by_table::model_weights::weights_category::WeightsCategory`,\n\n w.title,\n\n cover_image.token as `maybe_cover_image_media_file_token: tokens::tokens::media_files::MediaFileToken`,\n cover_image.public_bucket_directory_hash as maybe_cover_image_public_bucket_hash,\n cover_image.maybe_public_bucket_prefix as maybe_cover_image_public_bucket_prefix,\n cover_image.maybe_public_bucket_extension as maybe_cover_image_public_bucket_extension,\n\n w.creator_user_token as `creator_user_token: tokens::tokens::users::UserToken`,\n users.username as creator_username,\n users.display_name as creator_display_name,\n users.email_gravatar_hash as creator_gravatar_hash,\n\n featured_items.entity_token IS NOT NULL AS is_featured,\n\n entity_stats.ratings_positive_count as maybe_ratings_positive_count,\n entity_stats.ratings_negative_count as maybe_ratings_negative_count,\n entity_stats.bookmark_count as maybe_bookmark_count,\n\n w.cached_usage_count,\n\n extension_tts.ietf_language_tag as maybe_tts_ietf_language_tag,\n extension_tts.ietf_primary_language_subtag as maybe_tts_ietf_primary_language_subtag,\n\n extension_vc.ietf_language_tag as maybe_vc_ietf_language_tag,\n extension_vc.ietf_primary_language_subtag as maybe_vc_ietf_primary_language_subtag,\n\n w.created_at,\n w.updated_at,\n w.user_deleted_at,\n w.mod_deleted_at,\n\n NOW() as database_read_time\n\nFROM model_weights as w\n\nJOIN users\n ON users.token = w.creator_user_token\n\nLEFT OUTER JOIN model_weights_extension_tts_details as extension_tts\n ON extension_tts.model_weights_token = w.token\nLEFT OUTER JOIN model_weights_extension_voice_conversion_details as extension_vc\n ON extension_vc.model_weights_token = w.token\nLEFT OUTER JOIN entity_stats\n ON entity_stats.entity_type = \"model_weight\"\n AND entity_stats.entity_token = w.token\nLEFT OUTER JOIN media_files as cover_image\n ON cover_image.token = w.maybe_cover_image_media_file_token\nLEFT OUTER JOIN featured_items\n ON featured_items.entity_type = \"model_weight\"\n AND featured_items.entity_token = w.token\n AND featured_items.deleted_at IS NULL\n\nWHERE\n w.id > ?\nORDER BY id ASC\nLIMIT ?\n ",
"query": "\nSELECT\n w.id,\n w.token as `token: tokens::tokens::model_weights::ModelWeightToken`,\n\n w.creator_set_visibility as `creator_set_visibility: enums::common::visibility::Visibility`,\n\n w.weights_type as `weights_type: enums::by_table::model_weights::weights_types::WeightsType`,\n w.weights_category as `weights_category: enums::by_table::model_weights::weights_category::WeightsCategory`,\n\n w.title,\n\n w.maybe_ietf_language_tag,\n w.maybe_ietf_primary_language_subtag,\n\n cover_image.token as `maybe_cover_image_media_file_token: tokens::tokens::media_files::MediaFileToken`,\n cover_image.public_bucket_directory_hash as maybe_cover_image_public_bucket_hash,\n cover_image.maybe_public_bucket_prefix as maybe_cover_image_public_bucket_prefix,\n cover_image.maybe_public_bucket_extension as maybe_cover_image_public_bucket_extension,\n\n w.creator_user_token as `creator_user_token: tokens::tokens::users::UserToken`,\n users.username as creator_username,\n users.display_name as creator_display_name,\n users.email_gravatar_hash as creator_gravatar_hash,\n\n featured_items.entity_token IS NOT NULL AS is_featured,\n\n entity_stats.ratings_positive_count as maybe_ratings_positive_count,\n entity_stats.ratings_negative_count as maybe_ratings_negative_count,\n entity_stats.bookmark_count as maybe_bookmark_count,\n\n w.cached_usage_count,\n\n extension_tts.ietf_language_tag as maybe_tts_ietf_language_tag,\n extension_tts.ietf_primary_language_subtag as maybe_tts_ietf_primary_language_subtag,\n\n extension_vc.ietf_language_tag as maybe_vc_ietf_language_tag,\n extension_vc.ietf_primary_language_subtag as maybe_vc_ietf_primary_language_subtag,\n\n w.created_at,\n w.updated_at,\n w.user_deleted_at,\n w.mod_deleted_at,\n\n NOW() as database_read_time\n\nFROM model_weights as w\n\nJOIN users\n ON users.token = w.creator_user_token\n\nLEFT OUTER JOIN model_weights_extension_tts_details as extension_tts\n ON extension_tts.model_weights_token = w.token\nLEFT OUTER JOIN model_weights_extension_voice_conversion_details as extension_vc\n ON extension_vc.model_weights_token = w.token\nLEFT OUTER JOIN entity_stats\n ON entity_stats.entity_type = \"model_weight\"\n AND entity_stats.entity_token = w.token\nLEFT OUTER JOIN media_files as cover_image\n ON cover_image.token = w.maybe_cover_image_media_file_token\nLEFT OUTER JOIN featured_items\n ON featured_items.entity_type = \"model_weight\"\n AND featured_items.entity_token = w.token\n AND featured_items.deleted_at IS NULL\n\nWHERE\n w.id > ?\nORDER BY id ASC\nLIMIT ?\n ",
"describe": {
"columns": [
{
@@ -65,6 +65,26 @@
},
{
"ordinal": 6,
"name": "maybe_ietf_language_tag",
"type_info": {
"type": "VarString",
"flags": "BINARY",
"char_set": 224,
"max_size": 256
}
},
{
"ordinal": 7,
"name": "maybe_ietf_primary_language_subtag",
"type_info": {
"type": "VarString",
"flags": "BINARY",
"char_set": 224,
"max_size": 48
}
},
{
"ordinal": 8,
"name": "maybe_cover_image_media_file_token: tokens::tokens::media_files::MediaFileToken",
"type_info": {
"type": "VarString",
@@ -74,7 +94,7 @@
}
},
{
"ordinal": 7,
"ordinal": 9,
"name": "maybe_cover_image_public_bucket_hash",
"type_info": {
"type": "VarString",
@@ -84,7 +104,7 @@
}
},
{
"ordinal": 8,
"ordinal": 10,
"name": "maybe_cover_image_public_bucket_prefix",
"type_info": {
"type": "VarString",
@@ -94,7 +114,7 @@
}
},
{
"ordinal": 9,
"ordinal": 11,
"name": "maybe_cover_image_public_bucket_extension",
"type_info": {
"type": "VarString",
@@ -104,7 +124,7 @@
}
},
{
"ordinal": 10,
"ordinal": 12,
"name": "creator_user_token: tokens::tokens::users::UserToken",
"type_info": {
"type": "VarString",
@@ -114,7 +134,7 @@
}
},
{
"ordinal": 11,
"ordinal": 13,
"name": "creator_username",
"type_info": {
"type": "VarString",
@@ -124,7 +144,7 @@
}
},
{
"ordinal": 12,
"ordinal": 14,
"name": "creator_display_name",
"type_info": {
"type": "VarString",
@@ -134,7 +154,7 @@
}
},
{
"ordinal": 13,
"ordinal": 15,
"name": "creator_gravatar_hash",
"type_info": {
"type": "String",
@@ -144,7 +164,7 @@
}
},
{
"ordinal": 14,
"ordinal": 16,
"name": "is_featured",
"type_info": {
"type": "LongLong",
@@ -154,7 +174,7 @@
}
},
{
"ordinal": 15,
"ordinal": 17,
"name": "maybe_ratings_positive_count",
"type_info": {
"type": "Long",
@@ -164,7 +184,7 @@
}
},
{
"ordinal": 16,
"ordinal": 18,
"name": "maybe_ratings_negative_count",
"type_info": {
"type": "Long",
@@ -174,7 +194,7 @@
}
},
{
"ordinal": 17,
"ordinal": 19,
"name": "maybe_bookmark_count",
"type_info": {
"type": "Long",
@@ -184,7 +204,7 @@
}
},
{
"ordinal": 18,
"ordinal": 20,
"name": "cached_usage_count",
"type_info": {
"type": "LongLong",
@@ -194,7 +214,7 @@
}
},
{
"ordinal": 19,
"ordinal": 21,
"name": "maybe_tts_ietf_language_tag",
"type_info": {
"type": "VarString",
@@ -204,7 +224,7 @@
}
},
{
"ordinal": 20,
"ordinal": 22,
"name": "maybe_tts_ietf_primary_language_subtag",
"type_info": {
"type": "VarString",
@@ -214,7 +234,7 @@
}
},
{
"ordinal": 21,
"ordinal": 23,
"name": "maybe_vc_ietf_language_tag",
"type_info": {
"type": "VarString",
@@ -224,7 +244,7 @@
}
},
{
"ordinal": 22,
"ordinal": 24,
"name": "maybe_vc_ietf_primary_language_subtag",
"type_info": {
"type": "VarString",
@@ -234,7 +254,7 @@
}
},
{
"ordinal": 23,
"ordinal": 25,
"name": "created_at",
"type_info": {
"type": "Timestamp",
@@ -244,7 +264,7 @@
}
},
{
"ordinal": 24,
"ordinal": 26,
"name": "updated_at",
"type_info": {
"type": "Timestamp",
@@ -254,7 +274,7 @@
}
},
{
"ordinal": 25,
"ordinal": 27,
"name": "user_deleted_at",
"type_info": {
"type": "Timestamp",
@@ -264,7 +284,7 @@
}
},
{
"ordinal": 26,
"ordinal": 28,
"name": "mod_deleted_at",
"type_info": {
"type": "Timestamp",
@@ -274,7 +294,7 @@
}
},
{
"ordinal": 27,
"ordinal": 29,
"name": "database_read_time",
"type_info": {
"type": "Datetime",
@@ -298,6 +318,8 @@
true,
true,
true,
true,
true,
false,
false,
false,
@@ -318,5 +340,5 @@
false
]
},
"hash": "4bce2b10f0510feca7f5705049a5c0eaae6e1a840146628495827468a38c05dd"
"hash": "d0f9d2cf4176c3f7f8fbff66eb6ff9415d989afc2b5944629a7bcea4b5a822f3"
}
Generated
+24 -13
View File
@@ -3703,6 +3703,7 @@ dependencies = [
"time",
"tokens",
"tokio",
"tokio-stream",
"tokio-util",
"tts_common",
"url",
@@ -4454,6 +4455,18 @@ dependencies = [
"windows-sys 0.48.0",
]
[[package]]
name = "mio"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec"
dependencies = [
"hermit-abi",
"libc",
"wasi 0.11.0+wasi-snapshot-preview1",
"windows-sys 0.52.0",
]
[[package]]
name = "mio-uds"
version = "0.6.8"
@@ -4528,6 +4541,7 @@ dependencies = [
"anstream",
"arrayvec",
"base64 0.21.7",
"base64 0.22.1",
"base64ct",
"bitflags 2.5.0",
"block-buffer 0.9.0",
@@ -4568,7 +4582,6 @@ dependencies = [
"md-5 0.10.6",
"memchr",
"miniz_oxide",
"mio 0.8.11",
"native-tls",
"num-bigint",
"num-integer",
@@ -7809,21 +7822,20 @@ dependencies = [
[[package]]
name = "tokio"
version = "1.38.0"
version = "1.40.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba4f4a02a7a80d6f274636f0aa95c7e383b912d41fe721a31f29e29698585a4a"
checksum = "e2b070231665d27ad9ec9b8df639893f46727666c6767db40317fbe920a5d998"
dependencies = [
"backtrace",
"bytes 1.6.0",
"libc",
"mio 0.8.11",
"num_cpus",
"mio 1.0.2",
"parking_lot 0.12.2",
"pin-project-lite",
"signal-hook-registry",
"socket2",
"tokio-macros",
"windows-sys 0.48.0",
"windows-sys 0.52.0",
]
[[package]]
@@ -7870,9 +7882,9 @@ dependencies = [
[[package]]
name = "tokio-macros"
version = "2.3.0"
version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f5ae998a069d4b5aba8ee9dad856af7d520c3699e6159b185c2acd48155d39a"
checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752"
dependencies = [
"proc-macro2",
"quote",
@@ -7921,9 +7933,9 @@ dependencies = [
[[package]]
name = "tokio-stream"
version = "0.1.15"
version = "0.1.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "267ac89e0bec6e691e5813911606935d77c476ff49024f98abcea3e7b15e37af"
checksum = "4f4e6ce100d0eb49a2734f8c0812bcd324cf357d21810932c5df6b96ef2b86f1"
dependencies = [
"futures-core",
"pin-project-lite",
@@ -7974,16 +7986,15 @@ dependencies = [
[[package]]
name = "tokio-util"
version = "0.7.10"
version = "0.7.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15"
checksum = "61e7c3654c13bcd040d4a03abee2c75b1d14a37b423cf5a813ceae1cc903ec6a"
dependencies = [
"bytes 1.6.0",
"futures-core",
"futures-sink",
"pin-project-lite",
"tokio",
"tracing",
]
[[package]]
@@ -0,0 +1,15 @@
-- User sign up histogram by method
-- Since we don't have an index on created_at, we threshold by id
-- Current ID count = 7578799 (September 28, 2024)
select
date(created_at) as date,
case
when maybe_signup_method is null then 'email_password'
else maybe_signup_method
end as method,
count(*) as signup_count
from users
where id > 7200000
and created_at >= NOW() - INTERVAL 60 DAY
group by date(created_at), method
@@ -0,0 +1,14 @@
-- User sign up histogram by method
-- Since we don't have an index on created_at, we threshold by id
-- Current ID count = 7578799 (September 28, 2024)
select
date(created_at) as date,
case
when maybe_source is null then 'unknown'
else maybe_source
end as source,
count(*) as signup_count
from users
where id > 7200000
and created_at >= NOW() - INTERVAL 60 DAY
group by date(created_at), maybe_source
@@ -0,0 +1,69 @@
-- Select popular models without cover images or language
select
mw.token,
mw.title,
mw.maybe_ietf_language_tag,
mw.maybe_cover_image_media_file_token,
count(*) as use_count
from generic_inference_jobs as gij
left outer join model_weights as mw
on gij.maybe_model_token = mw.token
where gij.created_at >= (CURDATE() - INTERVAL 5 DAY)
and (
mw.maybe_ietf_language_tag IS NULL
or mw.maybe_cover_image_media_file_token IS NULL
)
and mw.mod_deleted_at IS NULL
and mw.user_deleted_at IS NULL
group by
mw.token,
mw.title,
mw.maybe_ietf_language_tag,
mw.maybe_cover_image_media_file_token
order by use_count desc
limit 100;
-- Select popular models without streamer usage distorting the totals
select
mw.token,
mw.title,
mw.maybe_ietf_language_tag,
mw.maybe_cover_image_media_file_token,
count(*) as use_count
from generic_inference_jobs as gij
left outer join model_weights as mw
on gij.maybe_model_token = mw.token
where gij.created_at >= (CURDATE() - INTERVAL 5 DAY)
and (
mw.maybe_ietf_language_tag IS NULL
or mw.maybe_cover_image_media_file_token IS NULL
)
and mw.mod_deleted_at IS NULL
and mw.user_deleted_at IS NULL
and gij.maybe_creator_user_token NOT IN (
-- This subquery returns streamers that generated over a certain threshold number of results
select
user_token
from (
select
u.token as user_token,
count(*) as generations
from generic_inference_jobs as gij
join users as u
on gij.maybe_creator_user_token = u.token
where gij.created_at >= (CURDATE() - INTERVAL 5 DAY)
and gij.maybe_creator_user_token is not null
group by user_token
order by generations desc
) as top_users
where top_users.generations > 300
)
group by
mw.token,
mw.title,
mw.maybe_ietf_language_tag,
mw.maybe_cover_image_media_file_token
order by use_count desc
limit 50;
@@ -0,0 +1,5 @@
-- noinspection SqlDialectInspectionForFile
-- noinspection SqlNoDataSourceInspectionForFile
-- noinspection SqlResolveForFile
ALTER TABLE model_weights DROP COLUMN maybe_ietf_language_tag;
@@ -0,0 +1,9 @@
-- noinspection SqlDialectInspectionForFile
-- noinspection SqlNoDataSourceInspectionForFile
-- noinspection SqlResolveForFile
ALTER TABLE model_weights
ADD COLUMN
maybe_ietf_language_tag VARCHAR(64) DEFAULT NULL
AFTER maybe_description_rendered_html;
@@ -0,0 +1,5 @@
-- noinspection SqlDialectInspectionForFile
-- noinspection SqlNoDataSourceInspectionForFile
-- noinspection SqlResolveForFile
ALTER TABLE model_weights DROP COLUMN maybe_ietf_primary_language_subtag;
@@ -0,0 +1,9 @@
-- noinspection SqlDialectInspectionForFile
-- noinspection SqlNoDataSourceInspectionForFile
-- noinspection SqlResolveForFile
ALTER TABLE model_weights
ADD COLUMN
maybe_ietf_primary_language_subtag VARCHAR(12) DEFAULT NULL
AFTER maybe_ietf_language_tag;
@@ -53,6 +53,22 @@ CREATE TABLE model_weights (
-- models don't deserve a description).
maybe_description_rendered_html TEXT DEFAULT NULL,
-- OPTIONAL. Only for TTS and Voice Conversion models.
-- The full IETF BCP47 language tag (eg. en, en-US, es-419, ja-JP, pt, etc.)
-- (Not that it matters apart from categorization, since voice conversion is universal.)
-- NB: There's another set of tables (model_weights_extension_voice_conversion_details, etc.) that
-- may have older post-migration data for some models. We'll want to backfill this field from there
-- and then make this one authoritative.
maybe_ietf_language_tag VARCHAR(64) DEFAULT NULL,
-- OPTIONAL. Only for TTS and Voice Conversion models.
-- The IETF BCP47 language tag's primary language subtag (eg. "es-419" becomes "es")
-- (Not that it matters apart from categorization, since voice conversion is universal.)
-- NB: There's another set of tables (model_weights_extension_voice_conversion_details, etc.) that
-- may have older post-migration data for some models. We'll want to backfill this field from there
-- and then make this one authoritative.
maybe_ietf_primary_language_subtag VARCHAR(12) DEFAULT NULL,
-- ========== CREATOR DETAILS ==========
@@ -1,3 +1,4 @@
pub mod audit_logs;
pub mod batch_generations;
pub mod featured_items;
pub mod tag_uses;
@@ -0,0 +1 @@
pub mod tag_use_entity;
@@ -0,0 +1,40 @@
use enums::by_table::tag_uses::tag_use_entity_type::TagUseEntityType;
use tokens::tokens::media_files::MediaFileToken;
use tokens::tokens::model_weights::ModelWeightToken;
// TODO(bt, 2024-01-16): A ref type with <'a> lifetime of inner data instead of ownership?
pub enum TagUseEntity {
MediaFile(MediaFileToken),
ModelWeight(ModelWeightToken),
}
// TODO(bt, 2024-01-16): Codegen? Make traits for these? Maybe overkill.
impl TagUseEntity {
pub fn from_entity_type_and_token(entity_type: TagUseEntityType, token: &str) -> Self {
match entity_type {
TagUseEntityType::ModelWeight => Self::ModelWeight(ModelWeightToken::new_from_str(token)),
TagUseEntityType::MediaFile => Self::MediaFile(MediaFileToken::new_from_str(token)),
}
}
pub fn get_entity_type(&self) -> TagUseEntityType {
match self {
Self::MediaFile(_) => TagUseEntityType::MediaFile,
Self::ModelWeight(_) => TagUseEntityType::ModelWeight,
}
}
pub fn get_entity_token_str(&self) -> &str {
match self {
Self::MediaFile(token) => token.as_str(),
Self::ModelWeight(token) => token.as_str(),
}
}
pub fn get_composite_keys(&self) -> (TagUseEntityType, &str) {
match self {
Self::MediaFile(token) => (TagUseEntityType::MediaFile, token.as_str()),
Self::ModelWeight(token) => (TagUseEntityType::ModelWeight, token.as_str()),
}
}
}
@@ -57,6 +57,9 @@ pub enum InferenceCategory {
#[serde(rename = "live_portrait")]
LivePortrait,
#[serde(rename="seed_vc")]
SeedVc,
/// DEPRECATED. Do not use.
/// This was for ReRenderAVideo, which we never productionized.
#[deprecated(note = "This was for ReRenderAVideo, which we never productionized.")]
@@ -67,6 +70,9 @@ pub enum InferenceCategory {
#[deprecated(note = "This was for Bevy engine's server side rendering.")]
#[serde(rename = "convert_bvh_to_workflow")]
ConvertBvhToWorkflow,
#[serde(rename = "f5_tts")]
F5TTS,
}
// TODO(bt, 2022-12-21): This desperately needs MySQL integration tests!
@@ -85,8 +91,10 @@ impl InferenceCategory {
Self::ImageGeneration => "image_generation",
Self::Mocap => "mocap",
Self::Workflow => "workflow",
Self::F5TTS => "f5_tts",
Self::FormatConversion => "format_conversion",
Self::LivePortrait => "live_portrait",
Self::SeedVc => "seed_vc",
Self::VideoFilter => "video_filter",
Self::ConvertBvhToWorkflow => "convert_bvh_to_workflow",
}
@@ -99,10 +107,12 @@ impl InferenceCategory {
"text_to_speech" => Ok(Self::TextToSpeech),
"voice_conversion" => Ok(Self::VoiceConversion),
"image_generation" => Ok(Self::ImageGeneration),
"f5_tts" => Ok(Self::F5TTS),
"mocap" => Ok(Self::Mocap),
"workflow" => Ok(Self::Workflow),
"format_conversion" => Ok(Self::FormatConversion),
"live_portrait" => Ok(Self::LivePortrait),
"seed_vc" => Ok(Self::SeedVc),
"video_filter" => Ok(Self::VideoFilter),
"convert_bvh_to_workflow" => Ok(Self::ConvertBvhToWorkflow),
_ => Err(format!("invalid value: {:?}", value)),
@@ -119,6 +129,8 @@ impl InferenceCategory {
Self::VoiceConversion,
Self::ImageGeneration,
Self::Mocap,
Self::F5TTS,
Self::SeedVc,
Self::Workflow,
Self::FormatConversion,
Self::LivePortrait,
@@ -144,6 +156,8 @@ mod tests {
assert_serialization(InferenceCategory::VoiceConversion, "voice_conversion");
assert_serialization(InferenceCategory::ImageGeneration, "image_generation");
assert_serialization(InferenceCategory::Mocap, "mocap");
assert_serialization(InferenceCategory::F5TTS, "f5_tts");
assert_serialization(InferenceCategory::SeedVc, "seed_vc");
assert_serialization(InferenceCategory::Workflow, "workflow");
assert_serialization(InferenceCategory::FormatConversion, "format_conversion");
assert_serialization(InferenceCategory::LivePortrait, "live_portrait");
@@ -158,6 +172,8 @@ mod tests {
assert_eq!(InferenceCategory::TextToSpeech.to_str(), "text_to_speech");
assert_eq!(InferenceCategory::VoiceConversion.to_str(), "voice_conversion");
assert_eq!(InferenceCategory::ImageGeneration.to_str(), "image_generation");
assert_eq!(InferenceCategory::F5TTS.to_str(), "f5_tts");
assert_eq!(InferenceCategory::SeedVc.to_str(), "seed_vc");
assert_eq!(InferenceCategory::Mocap.to_str(), "mocap");
assert_eq!(InferenceCategory::Workflow.to_str(), "workflow");
assert_eq!(InferenceCategory::FormatConversion.to_str(), "format_conversion");
@@ -173,6 +189,8 @@ mod tests {
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("image_generation").unwrap(), InferenceCategory::ImageGeneration);
assert_eq!(InferenceCategory::from_str("f5_tts").unwrap(), InferenceCategory::F5TTS);
assert_eq!(InferenceCategory::from_str("seed_vc").unwrap(), InferenceCategory::SeedVc);
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);
@@ -185,13 +203,15 @@ mod tests {
fn all_variants() {
// Static check
let mut variants = InferenceCategory::all_variants();
assert_eq!(variants.len(), 11);
assert_eq!(variants.len(), 12);
assert_eq!(variants.pop_first(), Some(InferenceCategory::DeprecatedField));
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::ImageGeneration));
assert_eq!(variants.pop_first(), Some(InferenceCategory::F5TTS));
assert_eq!(variants.pop_first(), Some(InferenceCategory::Mocap));
assert_eq!(variants.pop_first(), Some(InferenceCategory::SeedVc));
assert_eq!(variants.pop_first(), Some(InferenceCategory::Workflow));
assert_eq!(variants.pop_first(), Some(InferenceCategory::FormatConversion));
assert_eq!(variants.pop_first(), Some(InferenceCategory::LivePortrait));
@@ -27,6 +27,9 @@ pub enum InferenceJobProductCategory {
/// TTS: GptSoVits
TtsGptSoVits,
/// TTS: F5Tts (Zero Shot)
TtsF5,
/// TTS: StyleTts2 (Zero Shot)
TtsStyleTts2,
@@ -41,6 +44,8 @@ pub enum InferenceJobProductCategory {
/// Voice Conversion: SoVitsSvc
VcSvc,
VcSeedVc, // Ugh
// =============== VIDEO ===============
/// Video: Face Fusion (Lipsync)
@@ -58,6 +63,9 @@ pub enum InferenceJobProductCategory {
/// Video: Studio
VidStudio,
/// Video: Studio Gen 2
VidStudioGen2,
/// Video: Style Transfer
VidStyleTransfer,
@@ -108,13 +116,16 @@ impl InferenceJobProductCategory {
Self::TtsGptSoVits => "tts_gpt_so_vits",
Self::TtsStyleTts2 => "tts_style_tts2",
Self::TtsTacotron2 => "tts_tacotron2",
Self::TtsF5 => "tts_f5",
Self::VcSvc => "vc_svc",
Self::VcRvc2 => "vc_rvc2",
Self::VcSeedVc => "vc_seed_vc",
Self::VidLipsyncFaceFusion => "vid_lipsync_face_fusion",
Self::VidLipsyncSadTalker => "vid_lipsync_sad_talker",
Self::VidLivePortrait => "vid_live_portrait",
Self::VidLivePortraitWebcam => "vid_live_portrait_webcam",
Self::VidStudio => "vid_studio",
Self::VidStudioGen2 => "vid_studio_gen2",
Self::VidStyleTransfer => "vid_style_transfer",
Self::LipsyncFaceFusion => "lipsync_face_fusion",
Self::LipsyncSadTalker => "lipsync_sad_talker",
@@ -133,13 +144,16 @@ impl InferenceJobProductCategory {
"tts_gpt_so_vits" => Ok(Self::TtsGptSoVits),
"tts_style_tts2" => Ok(Self::TtsStyleTts2),
"tts_tacotron2" => Ok(Self::TtsTacotron2),
"tts_f5" => Ok(Self::TtsF5),
"vc_svc" => Ok(Self::VcSvc),
"vc_rvc2" => Ok(Self::VcRvc2),
"vc_seed_vc" => Ok(Self::VcSeedVc),
"vid_lipsync_face_fusion" => Ok(Self::VidLipsyncFaceFusion),
"vid_lipsync_sad_talker" => Ok(Self::VidLipsyncSadTalker),
"vid_live_portrait" => Ok(Self::VidLivePortrait),
"vid_live_portrait_webcam" => Ok(Self::VidLivePortraitWebcam),
"vid_studio" => Ok(Self::VidStudio),
"vid_studio_gen2" => Ok(Self::VidStudioGen2),
"vid_style_transfer" => Ok(Self::VidStyleTransfer),
"lipsync_face_fusion" => Ok(Self::LipsyncFaceFusion),
"lipsync_sad_talker" => Ok(Self::LipsyncSadTalker),
@@ -161,6 +175,8 @@ impl InferenceJobProductCategory {
Self::TtsGptSoVits,
Self::TtsStyleTts2,
Self::TtsTacotron2,
Self::TtsF5,
Self::VcSeedVc,
Self::VcSvc,
Self::VcRvc2,
Self::VidLipsyncFaceFusion,
@@ -168,6 +184,7 @@ impl InferenceJobProductCategory {
Self::VidLivePortrait,
Self::VidLivePortraitWebcam,
Self::VidStudio,
Self::VidStudioGen2,
Self::VidStyleTransfer,
Self::LipsyncFaceFusion,
Self::LipsyncSadTalker,
@@ -195,13 +212,16 @@ mod tests {
assert_serialization(InferenceJobProductCategory::TtsGptSoVits, "tts_gpt_so_vits");
assert_serialization(InferenceJobProductCategory::TtsStyleTts2, "tts_style_tts2");
assert_serialization(InferenceJobProductCategory::TtsTacotron2, "tts_tacotron2");
assert_serialization(InferenceJobProductCategory::TtsF5, "tts_f5");
assert_serialization(InferenceJobProductCategory::VcRvc2, "vc_rvc2");
assert_serialization(InferenceJobProductCategory::VcSvc, "vc_svc");
assert_serialization(InferenceJobProductCategory::VcSvc, "vc_svc");
assert_serialization(InferenceJobProductCategory::VidLipsyncFaceFusion, "vid_lipsync_face_fusion");
assert_serialization(InferenceJobProductCategory::VidLipsyncSadTalker, "vid_lipsync_sad_talker");
assert_serialization(InferenceJobProductCategory::VidLivePortrait, "vid_live_portrait");
assert_serialization(InferenceJobProductCategory::VidLivePortraitWebcam, "vid_live_portrait_webcam");
assert_serialization(InferenceJobProductCategory::VidStudio, "vid_studio");
assert_serialization(InferenceJobProductCategory::VidStudioGen2, "vid_studio_gen2");
assert_serialization(InferenceJobProductCategory::VidStyleTransfer, "vid_style_transfer");
assert_serialization(InferenceJobProductCategory::LipsyncFaceFusion, "lipsync_face_fusion");
assert_serialization(InferenceJobProductCategory::LipsyncSadTalker, "lipsync_sad_talker");
@@ -221,11 +241,13 @@ mod tests {
assert_eq!(InferenceJobProductCategory::TtsTacotron2.to_str(), "tts_tacotron2");
assert_eq!(InferenceJobProductCategory::VcRvc2.to_str(), "vc_rvc2");
assert_eq!(InferenceJobProductCategory::VcSvc.to_str(), "vc_svc");
assert_eq!(InferenceJobProductCategory::VcSvc.to_str(), "vc_svc");
assert_eq!(InferenceJobProductCategory::VidLipsyncFaceFusion.to_str(), "vid_lipsync_face_fusion");
assert_eq!(InferenceJobProductCategory::VidLipsyncSadTalker.to_str(), "vid_lipsync_sad_talker");
assert_eq!(InferenceJobProductCategory::VidLivePortrait.to_str(), "vid_live_portrait");
assert_eq!(InferenceJobProductCategory::VidLivePortraitWebcam.to_str(), "vid_live_portrait_webcam");
assert_eq!(InferenceJobProductCategory::VidStudio.to_str(), "vid_studio");
assert_eq!(InferenceJobProductCategory::VidStudioGen2.to_str(), "vid_studio_gen2");
assert_eq!(InferenceJobProductCategory::VidStyleTransfer.to_str(), "vid_style_transfer");
assert_eq!(InferenceJobProductCategory::LipsyncFaceFusion.to_str(), "lipsync_face_fusion");
assert_eq!(InferenceJobProductCategory::LipsyncSadTalker.to_str(), "lipsync_sad_talker");
@@ -245,11 +267,13 @@ mod tests {
assert_eq!(InferenceJobProductCategory::from_str("tts_tacotron2").unwrap(), InferenceJobProductCategory::TtsTacotron2);
assert_eq!(InferenceJobProductCategory::from_str("vc_rvc2").unwrap(), InferenceJobProductCategory::VcRvc2);
assert_eq!(InferenceJobProductCategory::from_str("vc_svc").unwrap(), InferenceJobProductCategory::VcSvc);
assert_eq!(InferenceJobProductCategory::from_str("vc_svc").unwrap(), InferenceJobProductCategory::VcSvc);
assert_eq!(InferenceJobProductCategory::from_str("vid_lipsync_face_fusion").unwrap(), InferenceJobProductCategory::VidLipsyncFaceFusion);
assert_eq!(InferenceJobProductCategory::from_str("vid_lipsync_sad_talker").unwrap(), InferenceJobProductCategory::VidLipsyncSadTalker);
assert_eq!(InferenceJobProductCategory::from_str("vid_live_portrait").unwrap(), InferenceJobProductCategory::VidLivePortrait);
assert_eq!(InferenceJobProductCategory::from_str("vid_live_portrait_webcam").unwrap(), InferenceJobProductCategory::VidLivePortraitWebcam);
assert_eq!(InferenceJobProductCategory::from_str("vid_studio").unwrap(), InferenceJobProductCategory::VidStudio);
assert_eq!(InferenceJobProductCategory::from_str("vid_studio_gen2").unwrap(), InferenceJobProductCategory::VidStudioGen2);
assert_eq!(InferenceJobProductCategory::from_str("vid_style_transfer").unwrap(), InferenceJobProductCategory::VidStyleTransfer);
assert_eq!(InferenceJobProductCategory::from_str("lipsync_face_fusion").unwrap(), InferenceJobProductCategory::LipsyncFaceFusion);
assert_eq!(InferenceJobProductCategory::from_str("lipsync_sad_talker").unwrap(), InferenceJobProductCategory::LipsyncSadTalker);
@@ -265,18 +289,20 @@ mod tests {
fn all_variants() {
// Static check
let mut variants = InferenceJobProductCategory::all_variants();
assert_eq!(variants.len(), 20);
assert_eq!(variants.len(), 21);
assert_eq!(variants.pop_first(), Some(InferenceJobProductCategory::DownloadGptSoVits));
assert_eq!(variants.pop_first(), Some(InferenceJobProductCategory::TtsGptSoVits));
assert_eq!(variants.pop_first(), Some(InferenceJobProductCategory::TtsStyleTts2));
assert_eq!(variants.pop_first(), Some(InferenceJobProductCategory::TtsTacotron2));
assert_eq!(variants.pop_first(), Some(InferenceJobProductCategory::VcRvc2));
assert_eq!(variants.pop_first(), Some(InferenceJobProductCategory::VcSeedVc));
assert_eq!(variants.pop_first(), Some(InferenceJobProductCategory::VcSvc));
assert_eq!(variants.pop_first(), Some(InferenceJobProductCategory::VidLipsyncFaceFusion));
assert_eq!(variants.pop_first(), Some(InferenceJobProductCategory::VidLipsyncSadTalker));
assert_eq!(variants.pop_first(), Some(InferenceJobProductCategory::VidLivePortrait));
assert_eq!(variants.pop_first(), Some(InferenceJobProductCategory::VidLivePortraitWebcam));
assert_eq!(variants.pop_first(), Some(InferenceJobProductCategory::VidStudio));
assert_eq!(variants.pop_first(), Some(InferenceJobProductCategory::VidStudioGen2));
assert_eq!(variants.pop_first(), Some(InferenceJobProductCategory::VidStyleTransfer));
assert_eq!(variants.pop_first(), Some(InferenceJobProductCategory::LipsyncFaceFusion));
assert_eq!(variants.pop_first(), Some(InferenceJobProductCategory::LipsyncSadTalker));
@@ -36,6 +36,10 @@ pub enum InferenceJobType {
#[deprecated(note = "Use VideoRender instead.")]
ComfyUi,
/// Second gen studio
#[serde(rename = "studio_gen2")]
StudioGen2,
/// A job that turns "FBX" game engine files into "GLTF" files (Bevy-compatible).
#[serde(rename = "convert_fbx_gltf")]
ConvertFbxToGltf,
@@ -43,6 +47,10 @@ pub enum InferenceJobType {
/// Process a video into BVH mocap animation data for game engines
MocapNet,
/// Jobs that run F5TTS
#[serde(rename = "f5_tts")]
F5TTS,
/// RVC is a voice conversion model. RVCv2 is the most popular such model currently.
#[serde(rename = "rvc_v2")]
RvcV2,
@@ -53,6 +61,9 @@ pub enum InferenceJobType {
/// so-vits-svc voice conversion. This predates RVCv2.
SoVitsSvc,
#[serde(rename = "seed_vc")]
SeedVc,
/// Stable diffusion image generation
StableDiffusion,
@@ -93,12 +104,15 @@ impl InferenceJobType {
Self::VideoRender => "video_render",
Self::LivePortrait => "live_portrait",
Self::FaceFusion => "face_fusion",
Self::F5TTS => "f5_tts",
Self::GptSovits => "gpt_sovits",
Self::ComfyUi => "comfy_ui",
Self::StudioGen2 => "studio_gen2",
Self::ConvertFbxToGltf => "convert_fbx_gltf",
Self::MocapNet => "mocap_net",
Self::RvcV2 => "rvc_v2",
Self::SadTalker => "sad_talker",
Self::SeedVc => "seed_vc",
Self::SoVitsSvc => "so_vits_svc",
Self::StableDiffusion => "stable_diffusion",
Self::StyleTTS2 => "styletts2",
@@ -114,12 +128,15 @@ impl InferenceJobType {
"video_render" => Ok(Self::VideoRender),
"live_portrait" => Ok(Self::LivePortrait),
"face_fusion" => Ok(Self::FaceFusion),
"f5_tts" => Ok(Self::F5TTS),
"gpt_sovits" => Ok(Self::GptSovits),
"comfy_ui" => Ok(Self::ComfyUi),
"studio_gen2" => Ok(Self::StudioGen2),
"convert_fbx_gltf" => Ok(Self::ConvertFbxToGltf),
"mocap_net" => Ok(Self::MocapNet),
"rvc_v2" => Ok(Self::RvcV2),
"sad_talker" => Ok(Self::SadTalker),
"seed_vc" => Ok(Self::SeedVc),
"so_vits_svc" => Ok(Self::SoVitsSvc),
"stable_diffusion" => Ok(Self::StableDiffusion),
"styletts2" => Ok(Self::StyleTTS2),
@@ -138,12 +155,15 @@ impl InferenceJobType {
Self::VideoRender,
Self::LivePortrait,
Self::FaceFusion,
Self::F5TTS,
Self::GptSovits,
Self::ComfyUi,
Self::StudioGen2,
Self::ConvertFbxToGltf,
Self::MocapNet,
Self::RvcV2,
Self::SadTalker,
Self::SeedVc,
Self::SoVitsSvc,
Self::StableDiffusion,
Self::StyleTTS2,
@@ -173,12 +193,15 @@ mod tests {
assert_serialization(InferenceJobType::VideoRender, "video_render");
assert_serialization(InferenceJobType::LivePortrait, "live_portrait");
assert_serialization(InferenceJobType::FaceFusion, "face_fusion");
assert_serialization(InferenceJobType::F5TTS, "f5_tts");
assert_serialization(InferenceJobType::GptSovits, "gpt_sovits");
assert_serialization(InferenceJobType::ComfyUi, "comfy_ui");
assert_serialization(InferenceJobType::StudioGen2, "studio_gen2");
assert_serialization(InferenceJobType::ConvertFbxToGltf, "convert_fbx_gltf");
assert_serialization(InferenceJobType::MocapNet, "mocap_net");
assert_serialization(InferenceJobType::RvcV2, "rvc_v2");
assert_serialization(InferenceJobType::SadTalker, "sad_talker");
assert_serialization(InferenceJobType::SeedVc, "seed_vc");
assert_serialization(InferenceJobType::SoVitsSvc, "so_vits_svc");
assert_serialization(InferenceJobType::StableDiffusion, "stable_diffusion");
assert_serialization(InferenceJobType::StyleTTS2, "styletts2");
@@ -193,12 +216,15 @@ mod tests {
assert_eq!(InferenceJobType::VideoRender.to_str(), "video_render");
assert_eq!(InferenceJobType::LivePortrait.to_str(), "live_portrait");
assert_eq!(InferenceJobType::FaceFusion.to_str(), "face_fusion");
assert_eq!(InferenceJobType::F5TTS.to_str(), "f5_tts");
assert_eq!(InferenceJobType::GptSovits.to_str(), "gpt_sovits");
assert_eq!(InferenceJobType::ComfyUi.to_str(), "comfy_ui");
assert_eq!(InferenceJobType::StudioGen2.to_str(), "studio_gen2");
assert_eq!(InferenceJobType::ConvertFbxToGltf.to_str(), "convert_fbx_gltf");
assert_eq!(InferenceJobType::MocapNet.to_str(), "mocap_net");
assert_eq!(InferenceJobType::RvcV2.to_str(), "rvc_v2");
assert_eq!(InferenceJobType::SadTalker.to_str(), "sad_talker");
assert_eq!(InferenceJobType::SeedVc.to_str(), "seed_vc");
assert_eq!(InferenceJobType::SoVitsSvc.to_str(), "so_vits_svc");
assert_eq!(InferenceJobType::StableDiffusion.to_str(), "stable_diffusion");
assert_eq!(InferenceJobType::StyleTTS2.to_str(), "styletts2");
@@ -213,12 +239,15 @@ mod tests {
assert_eq!(InferenceJobType::from_str("video_render").unwrap(), InferenceJobType::VideoRender);
assert_eq!(InferenceJobType::from_str("live_portrait").unwrap(), InferenceJobType::LivePortrait);
assert_eq!(InferenceJobType::from_str("face_fusion").unwrap(), InferenceJobType::FaceFusion);
assert_eq!(InferenceJobType::from_str("f5_tts").unwrap(), InferenceJobType::F5TTS);
assert_eq!(InferenceJobType::from_str("gpt_sovits").unwrap(), InferenceJobType::GptSovits);
assert_eq!(InferenceJobType::from_str("comfy_ui").unwrap(), InferenceJobType::ComfyUi);
assert_eq!(InferenceJobType::from_str("studio_gen2").unwrap(), InferenceJobType::StudioGen2);
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("rvc_v2").unwrap(), InferenceJobType::RvcV2);
assert_eq!(InferenceJobType::from_str("sad_talker").unwrap(), InferenceJobType::SadTalker);
assert_eq!(InferenceJobType::from_str("seed_vc").unwrap(), InferenceJobType::SeedVc);
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);
@@ -236,12 +265,15 @@ mod tests {
assert_eq!(variants.pop_first(), Some(InferenceJobType::VideoRender));
assert_eq!(variants.pop_first(), Some(InferenceJobType::LivePortrait));
assert_eq!(variants.pop_first(), Some(InferenceJobType::FaceFusion));
assert_eq!(variants.pop_first(), Some(InferenceJobType::F5TTS));
assert_eq!(variants.pop_first(), Some(InferenceJobType::GptSovits));
assert_eq!(variants.pop_first(), Some(InferenceJobType::ComfyUi));
assert_eq!(variants.pop_first(), Some(InferenceJobType::StudioGen2));
assert_eq!(variants.pop_first(), Some(InferenceJobType::ConvertFbxToGltf));
assert_eq!(variants.pop_first(), Some(InferenceJobType::MocapNet));
assert_eq!(variants.pop_first(), Some(InferenceJobType::RvcV2));
assert_eq!(variants.pop_first(), Some(InferenceJobType::SadTalker));
assert_eq!(variants.pop_first(), Some(InferenceJobType::SeedVc));
assert_eq!(variants.pop_first(), Some(InferenceJobType::SoVitsSvc));
assert_eq!(variants.pop_first(), Some(InferenceJobType::StableDiffusion));
assert_eq!(variants.pop_first(), Some(InferenceJobType::StyleTTS2));
@@ -29,6 +29,10 @@ pub enum InferenceModelType {
#[serde(rename = "so_vits_svc")]
SoVitsSvc,
// TODO: Does this need to be "legacy_tacotron2" ?
#[serde(rename = "seed_vc")]
SeedVc,
#[serde(rename = "tacotron2")]
Tacotron2,
#[serde(rename = "vits")]
@@ -66,6 +70,7 @@ impl InferenceModelType {
Self::VallEX => "vall_e_x",
Self::RerenderAVideo => "rerender_a_video",
Self::StableDiffusion => "stable_diffusion",
Self::SeedVc => "seed_vc",
Self::MocapNet => "mocap_net",
Self::StyleTTS2 => "styletts2",
Self::ComfyUi => "comfy_ui",
@@ -79,6 +84,7 @@ impl InferenceModelType {
"rvc_v2" => Ok(Self::RvcV2),
"sad_talker" => Ok(Self::SadTalker),
"so_vits_svc" => Ok(Self::SoVitsSvc),
"seed_vc" => Ok(Self::SeedVc),
"tacotron2" => Ok(Self::Tacotron2),
"vits" => Ok(Self::Vits),
"vall_e_x" => Ok(Self::VallEX),
@@ -101,6 +107,7 @@ impl InferenceModelType {
Self::RvcV2,
Self::SadTalker,
Self::SoVitsSvc,
Self::SeedVc,
Self::Tacotron2,
Self::Vits,
Self::VallEX,
@@ -126,6 +133,7 @@ mod tests {
fn test_serialization() {
assert_serialization(InferenceModelType::RvcV2, "rvc_v2");
assert_serialization(InferenceModelType::SadTalker, "sad_talker");
assert_serialization(InferenceModelType::SeedVc, "seed_vc");
assert_serialization(InferenceModelType::SoVitsSvc, "so_vits_svc");
assert_serialization(InferenceModelType::Tacotron2, "tacotron2");
assert_serialization(InferenceModelType::Vits, "vits");
@@ -145,6 +153,7 @@ mod tests {
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::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");
@@ -160,6 +169,7 @@ mod tests {
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("seed_vc").unwrap(), InferenceModelType::SeedVc);
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);
@@ -181,6 +191,7 @@ mod tests {
assert_eq!(variants.pop_first(), Some(InferenceModelType::ComfyUi));
assert_eq!(variants.pop_first(), Some(InferenceModelType::RvcV2));
assert_eq!(variants.pop_first(), Some(InferenceModelType::SadTalker));
assert_eq!(variants.pop_first(), Some(InferenceModelType::SeedVc));
assert_eq!(variants.pop_first(), Some(InferenceModelType::SoVitsSvc));
assert_eq!(variants.pop_first(), Some(InferenceModelType::Tacotron2));
assert_eq!(variants.pop_first(), Some(InferenceModelType::Vits));
@@ -18,6 +18,9 @@ pub enum MediaFileOriginModelType {
#[serde(rename = "face_fusion")]
FaceFusion,
#[serde(rename = "f5_tts")]
F5TTS,
#[serde(rename = "live_portrait")]
LivePortrait,
@@ -39,6 +42,9 @@ pub enum MediaFileOriginModelType {
#[serde(rename = "mocap_net")]
MocapNet,
#[serde(rename = "seed_vc")]
SeedVc,
#[serde(rename = "styletts2")]
StyleTTS2,
@@ -77,9 +83,11 @@ impl MediaFileOriginModelType {
pub fn to_str(&self) -> &'static str {
match self {
Self::FaceFusion => "face_fusion",
Self::F5TTS => "f5_tts",
Self::LivePortrait => "live_portrait",
Self::RvcV2 => "rvc_v2",
Self::SadTalker => "sad_talker",
Self::SeedVc => "seed_vc",
Self::SoVitsSvc => "so_vits_svc",
Self::Tacotron2 => "tacotron2",
Self::MocapNet => "mocap_net",
@@ -97,9 +105,11 @@ impl MediaFileOriginModelType {
pub fn from_str(value: &str) -> Result<Self, String> {
match value {
"face_fusion" => Ok(Self::FaceFusion),
"f5_tts" => Ok(Self::F5TTS),
"live_portrait" => Ok(Self::LivePortrait),
"rvc_v2" => Ok(Self::RvcV2),
"sad_talker" => Ok(Self::SadTalker),
"seed_vc" => Ok(Self::SeedVc),
"so_vits_svc" => Ok(Self::SoVitsSvc),
"tacotron2" => Ok(Self::Tacotron2),
"mocap_net" => Ok(Self::MocapNet),
@@ -120,9 +130,11 @@ impl MediaFileOriginModelType {
// NB: BTreeSet::from() isn't const, but not worth using LazyStatic, etc.
BTreeSet::from([
Self::FaceFusion,
Self::F5TTS,
Self::LivePortrait,
Self::RvcV2,
Self::SadTalker,
Self::SeedVc,
Self::SoVitsSvc,
Self::Tacotron2,
Self::MocapNet,
@@ -155,9 +167,11 @@ mod tests {
#[test]
fn test_to_str() {
assert_eq!(MediaFileOriginModelType::FaceFusion.to_str(), "face_fusion");
assert_eq!(MediaFileOriginModelType::F5TTS.to_str(), "f5_tts");
assert_eq!(MediaFileOriginModelType::LivePortrait.to_str(), "live_portrait");
assert_eq!(MediaFileOriginModelType::RvcV2.to_str(), "rvc_v2");
assert_eq!(MediaFileOriginModelType::SadTalker.to_str(), "sad_talker");
assert_eq!(MediaFileOriginModelType::SeedVc.to_str(), "seed_vc");
assert_eq!(MediaFileOriginModelType::SoVitsSvc.to_str(), "so_vits_svc");
assert_eq!(MediaFileOriginModelType::Tacotron2.to_str(), "tacotron2");
assert_eq!(MediaFileOriginModelType::MocapNet.to_str(), "mocap_net");
@@ -174,9 +188,11 @@ mod tests {
#[test]
fn test_from_str() {
assert_eq!(MediaFileOriginModelType::from_str("face_fusion").unwrap(), MediaFileOriginModelType::FaceFusion);
assert_eq!(MediaFileOriginModelType::from_str("f5_tts").unwrap(), MediaFileOriginModelType::F5TTS);
assert_eq!(MediaFileOriginModelType::from_str("live_portrait").unwrap(), MediaFileOriginModelType::LivePortrait);
assert_eq!(MediaFileOriginModelType::from_str("rvc_v2").unwrap(), MediaFileOriginModelType::RvcV2);
assert_eq!(MediaFileOriginModelType::from_str("sad_talker").unwrap(), MediaFileOriginModelType::SadTalker);
assert_eq!(MediaFileOriginModelType::from_str("seed_vc").unwrap(), MediaFileOriginModelType::SeedVc);
assert_eq!(MediaFileOriginModelType::from_str("so_vits_svc").unwrap(), MediaFileOriginModelType::SoVitsSvc);
assert_eq!(MediaFileOriginModelType::from_str("tacotron2").unwrap(), MediaFileOriginModelType::Tacotron2);
assert_eq!(MediaFileOriginModelType::from_str("mocap_net").unwrap(), MediaFileOriginModelType::MocapNet);
@@ -196,9 +212,11 @@ mod tests {
let mut variants = MediaFileOriginModelType::all_variants();
assert_eq!(variants.len(), 15);
assert_eq!(variants.pop_first(), Some(MediaFileOriginModelType::FaceFusion));
assert_eq!(variants.pop_first(), Some(MediaFileOriginModelType::F5TTS));
assert_eq!(variants.pop_first(), Some(MediaFileOriginModelType::LivePortrait));
assert_eq!(variants.pop_first(), Some(MediaFileOriginModelType::RvcV2));
assert_eq!(variants.pop_first(), Some(MediaFileOriginModelType::SadTalker));
assert_eq!(variants.pop_first(), Some(MediaFileOriginModelType::SeedVc));
assert_eq!(variants.pop_first(), Some(MediaFileOriginModelType::SoVitsSvc));
assert_eq!(variants.pop_first(), Some(MediaFileOriginModelType::Tacotron2));
assert_eq!(variants.pop_first(), Some(MediaFileOriginModelType::MocapNet));
+1
View File
@@ -14,6 +14,7 @@ pub mod media_uploads;
pub mod model_categories;
pub mod model_weights;
pub mod prompts;
pub mod tag_uses;
pub mod trending_model_analytics;
pub mod tts_models;
pub mod usages;
@@ -0,0 +1 @@
pub mod tag_use_entity_type;
@@ -0,0 +1,127 @@
use std::collections::BTreeSet;
use serde::Deserialize;
use serde::Serialize;
#[cfg(test)]
use strum::EnumCount;
#[cfg(test)]
use strum::EnumIter;
use utoipa::ToSchema;
/// Used in the `tag_uses` table in a `VARCHAR(32)` field named `entity_type`.
#[cfg_attr(test, derive(EnumIter, EnumCount))]
#[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash, Deserialize, Serialize, ToSchema)]
pub enum TagUseEntityType {
/// Media files
#[serde(rename = "media_file")]
MediaFile,
/// Model weights
#[serde(rename = "model_weight")]
ModelWeight,
}
// TODO(bt, 2023-01-17): This desperately needs MySQL integration tests!
impl_enum_display_and_debug_using_to_str!(TagUseEntityType);
impl_mysql_enum_coders!(TagUseEntityType);
/// NB: Legacy API for older code.
impl TagUseEntityType {
pub fn to_str(&self) -> &'static str {
match self {
Self::MediaFile => "media_file",
Self::ModelWeight => "model_weight",
}
}
pub fn from_str(value: &str) -> Result<Self, String> {
match value {
"media_file" => Ok(Self::MediaFile),
"model_weight" => Ok(Self::ModelWeight),
_ => 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::MediaFile,
Self::ModelWeight,
])
}
}
#[cfg(test)]
mod tests {
use crate::test_helpers::assert_serialization;
use crate::by_table::tag_uses::tag_use_entity_type::TagUseEntityType;
mod serde {
use super::*;
#[test]
fn test_serialization() {
assert_serialization(TagUseEntityType::MediaFile, "media_file");
assert_serialization(TagUseEntityType::ModelWeight, "model_weight");
}
}
mod impl_methods {
use super::*;
#[test]
fn test_to_str() {
assert_eq!(TagUseEntityType::MediaFile.to_str(), "media_file");
assert_eq!(TagUseEntityType::ModelWeight.to_str(), "model_weight");
}
#[test]
fn test_from_str() {
assert_eq!(TagUseEntityType::from_str("media_file").unwrap(), TagUseEntityType::MediaFile);
assert_eq!(TagUseEntityType::from_str("model_weight").unwrap(), TagUseEntityType::ModelWeight);
assert!(TagUseEntityType::from_str("foo").is_err());
}
}
mod manual_variant_checks {
use super::*;
#[test]
fn all_variants() {
let mut variants = TagUseEntityType::all_variants();
assert_eq!(variants.len(), 2);
assert_eq!(variants.pop_first(), Some(TagUseEntityType::MediaFile));
assert_eq!(variants.pop_first(), Some(TagUseEntityType::ModelWeight));
assert_eq!(variants.pop_first(), None);
}
}
mod mechanical_checks {
use super::*;
#[test]
fn variant_length() {
use strum::IntoEnumIterator;
assert_eq!(TagUseEntityType::all_variants().len(), TagUseEntityType::iter().len());
}
#[test]
fn round_trip() {
for variant in TagUseEntityType::all_variants() {
assert_eq!(variant, TagUseEntityType::from_str(variant.to_str()).unwrap());
assert_eq!(variant, TagUseEntityType::from_str(&format!("{}", variant)).unwrap());
assert_eq!(variant, TagUseEntityType::from_str(&format!("{:?}", variant)).unwrap());
}
}
#[test]
fn serialized_length_ok_for_database() {
const MAX_LENGTH : usize = 32;
for variant in TagUseEntityType::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);
}
}
}
}
@@ -30,6 +30,8 @@ pub enum PublicInferenceJobType {
GptSovits,
#[serde(rename = "f5_tts")]
F5TTS,
// ======= Everything else is the same =======
/// Jobs that run ComfyUI workflows
@@ -37,6 +39,10 @@ pub enum PublicInferenceJobType {
#[deprecated(note = "Use VideoRender instead.")]
ComfyUi,
/// Second gen studio
#[serde(rename = "studio_gen2")]
StudioGen2,
/// A job that turns "FBX" game engine files into "GLTF" files (Bevy-compatible).
#[serde(rename = "convert_fbx_gltf")]
ConvertFbxToGltf,
@@ -51,6 +57,10 @@ pub enum PublicInferenceJobType {
/// SadTalker does image-to-video lip-syncing when given an audio file and image.
SadTalker,
/// Seed-VC
#[serde(rename = "seed_vc")]
SeedVc,
/// so-vits-svc voice conversion. This predates RVCv2.
SoVitsSvc,
@@ -94,11 +104,14 @@ impl PublicInferenceJobType {
// Conserved variants
InferenceJobType::VideoRender => Self::VideoRender,
InferenceJobType::GptSovits => Self::GptSovits,
InferenceJobType::F5TTS => Self::F5TTS,
InferenceJobType::ComfyUi => Self::ComfyUi,
InferenceJobType::StudioGen2 => Self::StudioGen2,
InferenceJobType::ConvertFbxToGltf => Self::ConvertFbxToGltf,
InferenceJobType::MocapNet => Self::MocapNet,
InferenceJobType::RvcV2 => Self::RvcV2,
InferenceJobType::SadTalker => Self::SadTalker,
InferenceJobType::SeedVc => Self::SeedVc,
InferenceJobType::SoVitsSvc => Self::SoVitsSvc,
InferenceJobType::StableDiffusion => Self::StableDiffusion,
InferenceJobType::StyleTTS2 => Self::StyleTTS2,
@@ -117,11 +130,14 @@ impl PublicInferenceJobType {
// Conserved variants
Self::VideoRender => InferenceJobType::VideoRender,
Self::GptSovits => InferenceJobType::GptSovits,
Self::F5TTS => InferenceJobType::F5TTS,
Self::ComfyUi => InferenceJobType::ComfyUi,
Self::StudioGen2 => InferenceJobType::StudioGen2,
Self::ConvertFbxToGltf => InferenceJobType::ConvertFbxToGltf,
Self::MocapNet => InferenceJobType::MocapNet,
Self::RvcV2 => InferenceJobType::RvcV2,
Self::SadTalker => InferenceJobType::SadTalker,
Self::SeedVc => InferenceJobType::SeedVc,
Self::SoVitsSvc => InferenceJobType::SoVitsSvc,
Self::StableDiffusion => InferenceJobType::StableDiffusion,
Self::StyleTTS2 => InferenceJobType::StyleTTS2,
@@ -55,6 +55,12 @@ pub enum PublicMediaFileModelType {
#[serde(rename = "gpt_sovits")]
GptSovits,
#[serde(rename = "f5_tts")]
F5TTS,
#[serde(rename = "seed_vc")]
SeedVc,
#[serde(rename = "studio")]
StorytellerStudio,
@@ -86,10 +92,12 @@ impl PublicMediaFileModelType {
// Conserved variants
MediaFileOriginModelType::RvcV2 => Self::RvcV2,
MediaFileOriginModelType::SoVitsSvc => Self::SoVitsSvc,
MediaFileOriginModelType::SeedVc => Self::SeedVc,
MediaFileOriginModelType::Tacotron2 => Self::Tacotron2,
MediaFileOriginModelType::MocapNet => Self::MocapNet,
MediaFileOriginModelType::StableDiffusion15 => Self::StableDiffusion15,
MediaFileOriginModelType::GptSovits => Self::GptSovits,
MediaFileOriginModelType::F5TTS => Self::F5TTS,
MediaFileOriginModelType::StorytellerStudio => Self::StorytellerStudio,
MediaFileOriginModelType::VideoStyleTransfer => Self::VideoStyleTransfer,
MediaFileOriginModelType::ComfyUi => Self::ComfyUi,
@@ -108,10 +116,12 @@ impl PublicMediaFileModelType {
// Conserved variants
Self::RvcV2 => MediaFileOriginModelType::RvcV2,
Self::SoVitsSvc => MediaFileOriginModelType::SoVitsSvc,
Self::SeedVc => MediaFileOriginModelType::SeedVc,
Self::Tacotron2 => MediaFileOriginModelType::Tacotron2,
Self::MocapNet => MediaFileOriginModelType::MocapNet,
Self::StableDiffusion15 => MediaFileOriginModelType::StableDiffusion15,
Self::GptSovits => MediaFileOriginModelType::GptSovits,
Self::F5TTS => MediaFileOriginModelType::F5TTS,
Self::StorytellerStudio => MediaFileOriginModelType::StorytellerStudio,
Self::VideoStyleTransfer => MediaFileOriginModelType::VideoStyleTransfer,
Self::ComfyUi => MediaFileOriginModelType::ComfyUi,
@@ -30,6 +30,9 @@ pub enum Action {
/// Search the model weights
SearchModelWeights,
/// Evaluate the search algorithm
EvaluateModelWeights,
/// Reindex media_files into ElasticSearch
/// For now, this is just media files that are relevant to the engine.
ReindexMediaFiles,
@@ -6,25 +6,26 @@
//! The intent is to be able to quickly populate documents and indices.
//!
use elasticsearch::http::transport::Transport;
use elasticsearch::Elasticsearch;
use log::info;
use sqlx::mysql::MySqlPoolOptions;
use sqlx::{MySql, Pool};
use std::collections::HashSet;
use std::iter::FromIterator;
use elasticsearch::Elasticsearch;
use elasticsearch::http::transport::Transport;
use log::info;
use sqlx::{MySql, Pool};
use sqlx::mysql::MySqlPoolOptions;
use config::shared_constants::DEFAULT_RUST_LOG;
use elasticsearch_schema::searches::search_model_weights::{search_model_weights, SearchArgs};
use elasticsearch_schema::searches::search_tts_models::search_tts_models;
use enums::by_table::model_weights::weights_types::WeightsType;
use errors::AnyhowResult;
use crate::cli_args::{Action, Environment, parse_cli_args};
use crate::plans::create_all_model_weight_documents::create_all_model_weight_documents;
use crate::cli_args::{parse_cli_args, Action, Environment};
use crate::plans::create_all_tts_documents::create_all_tts_documents;
use crate::plans::media_files::create_dimensional_media_file_documents::create_dimensional_media_file_documents;
use crate::plans::media_files::test_search_media_files_documents::test_search_media_files;
use crate::plans::model_weights::create_all_model_weight_documents::create_all_model_weight_documents;
use crate::plans::model_weights::evaluate::evaluate_model_weights_search::evaluate_model_weights_search;
use crate::plans::model_weights::test_search_model_weights_documents::test_search_model_weights_documents;
pub mod cli_args;
pub mod plans;
@@ -33,7 +34,7 @@ pub mod plans;
pub async fn main() -> AnyhowResult<()> {
println!("elasticsearch-cli: operational tooling for Elasticsearch");
easyenv::init_all_with_default_logging(Some(DEFAULT_RUST_LOG));
easyenv::init_all_with_default_logging(Some("info"));
// NB: This secrets file differs from the rest because we might actually want to cross
// development/production boundaries for seeding, or scope to production for rebuilding indices.
@@ -61,20 +62,16 @@ pub async fn main() -> AnyhowResult<()> {
}
Action::SearchModelWeights => {
info!("Searching model weights...");
let results = search_model_weights(SearchArgs {
search_term: "zel",
maybe_creator_user_token: None,
maybe_ietf_primary_language_subtag: None,
maybe_weights_categories: None,
maybe_weights_types: Some(HashSet::from_iter(vec![WeightsType::RvcV2])),
client: &elasticsearch,
}).await?;
let results = test_search_model_weights_documents(&elasticsearch).await?;
for result in results {
println!("Result: {:#?}", result);
}
}
Action::EvaluateModelWeights => {
info!("Evaluate model weights search...");
let _ = evaluate_model_weights_search(&elasticsearch).await?;
}
Action::ReindexMediaFiles => {
info!("Reindexing media files...");
create_dimensional_media_file_documents(&mysql, &elasticsearch).await?;
@@ -1,3 +1,3 @@
pub mod create_all_model_weight_documents;
pub mod create_all_tts_documents;
pub mod media_files;
pub mod media_files;
pub mod model_weights;
@@ -60,6 +60,24 @@ async fn create_document_from_record(elasticsearch: &Elasticsearch, record: Mode
let is_deleted = record.user_deleted_at.is_some() || record.mod_deleted_at.is_some();
let maybe_ietf_language_tag = record.maybe_ietf_language_tag
.as_deref()
.or_else(|| match record.weights_category {
WeightsCategory::TextToSpeech => record.maybe_tts_ietf_language_tag.as_deref(),
WeightsCategory::VoiceConversion => record.maybe_voice_conversion_ietf_language_tag.as_deref(),
_ => None,
})
.map(|t| t.to_string());
let maybe_ietf_primary_language_subtag = record.maybe_ietf_primary_language_subtag
.as_deref()
.or_else(|| match &record.weights_category {
WeightsCategory::TextToSpeech => record.maybe_tts_ietf_primary_language_subtag.as_deref(),
WeightsCategory::VoiceConversion => record.maybe_voice_conversion_ietf_primary_language_subtag.as_deref(),
_ => None,
})
.map(|t| t.to_string());
let document = ModelWeightDocument {
token: record.token,
@@ -89,17 +107,8 @@ async fn create_document_from_record(elasticsearch: &Elasticsearch, record: Mode
cached_usage_count: Some(u64_to_i32_saturating(record.cached_usage_count)),
maybe_ietf_language_tag: match record.weights_category {
WeightsCategory::TextToSpeech => record.maybe_tts_ietf_language_tag,
WeightsCategory::VoiceConversion => record.maybe_voice_conversion_ietf_language_tag,
_ => None,
},
maybe_ietf_primary_language_subtag: match record.weights_category {
WeightsCategory::TextToSpeech => record.maybe_tts_ietf_primary_language_subtag,
WeightsCategory::VoiceConversion => record.maybe_voice_conversion_ietf_primary_language_subtag,
_ => None,
},
maybe_ietf_language_tag,
maybe_ietf_primary_language_subtag,
created_at: record.created_at,
updated_at: record.updated_at,
@@ -0,0 +1,83 @@
use crate::plans::model_weights::evaluate::print_debugging::{print_titles, to_title_set};
use crate::plans::model_weights::evaluate::search::search_term_only;
use elasticsearch::Elasticsearch;
use elasticsearch_schema::documents::model_weight_document::ModelWeightDocument;
use errors::{anyhow, AnyhowResult};
use log::error;
use std::collections::HashSet;
use tokens::tokens::model_weights::ModelWeightToken;
pub async fn assert_search_term_contains_titles(
client: &Elasticsearch,
search_term: &str,
expected_titles: &Vec<&str>,
) -> AnyhowResult<()> {
let results = search_term_only(search_term, client).await?;
let titles = to_title_set(&results);
println!("Titles:");
print_titles(&results);
if let Err(err) = assert_contains_all(&titles, expected_titles, search_term) {
error!("Title not found for search: {}", search_term);
print_titles(&results);
return Err(err);
}
Ok(())
}
pub fn assert_contains(titles: &HashSet<String>, expected: &str, search_term: &str) -> AnyhowResult<()> {
if !titles.contains(expected) {
error!("Expected title not found: {} for search {} ({} results)",
expected, search_term, titles.len());
return Err(anyhow!("Expected title not found: {} for search {} ({} results)",
expected, search_term, titles.len()));
}
Ok(())
}
pub fn assert_contains_all(titles: &HashSet<String>, expected: &Vec<&str>, search_term: &str) -> AnyhowResult<()> {
for title in expected.iter() {
assert_contains(titles, title, search_term)?;
}
Ok(())
}
pub fn assert_results_contain_tokens(
results: &Vec<ModelWeightDocument>,
expected_tokens: &[&str],
) -> AnyhowResult<()> {
let tokens = results.iter()
.map(|result| result.token.clone())
.collect::<HashSet<_>>();
for token in expected_tokens.iter() {
let token = ModelWeightToken::new_from_str(token);
if !tokens.contains(&token) {
error!("Expected token not found: {}", token);
return Err(anyhow!("Expected token not found: {}", token));
}
}
Ok(())
}
pub fn assert_results_do_not_contain_tokens(
results: &Vec<ModelWeightDocument>,
expected_tokens: &[&str],
) -> AnyhowResult<()> {
let tokens = results.iter()
.map(|result| result.token.clone())
.collect::<HashSet<_>>();
for token in expected_tokens.iter() {
let token = ModelWeightToken::new_from_str(token);
if tokens.contains(&token) {
error!("Unexpected token found: {}", token);
return Err(anyhow!("Unexpected token found: {}", token));
}
}
Ok(())
}
@@ -0,0 +1,19 @@
use crate::plans::model_weights::evaluate::test_cases::test_search_term_zelda::test_search_term_zelda;
use crate::plans::model_weights::evaluate::test_cases::test_upvoted_models::test_upvoted_models;
use crate::plans::model_weights::evaluate::test_cases::test_use_count::test_use_count;
use elasticsearch::Elasticsearch;
use errors::AnyhowResult;
pub async fn evaluate_model_weights_search(client: &Elasticsearch) -> AnyhowResult<()> {
// Test sorting filters
test_upvoted_models(client).await?;
test_use_count(client).await?;
// Test search terms and boosting algorithm
test_search_term_zelda(client).await?;
// New tests
Ok(())
}
@@ -0,0 +1,5 @@
pub mod asserts;
pub mod evaluate_model_weights_search;
pub mod search;
pub mod test_cases;
pub mod print_debugging;
@@ -0,0 +1,31 @@
use std::collections::HashSet;
use elasticsearch_schema::documents::model_weight_document::ModelWeightDocument;
pub fn print_titles(results: &Vec<ModelWeightDocument>) {
for result in results {
println!(" - result: {:#?} ({:?})", result.title, result.token);
}
}
pub fn print_titles_and_usage_counts(results: &Vec<ModelWeightDocument>) {
for result in results {
println!(" - result: {:#?} {} ({:?})",
result.title,
result.cached_usage_count.unwrap_or(0),
result.token);
}
}
pub fn print_titles_and_rating_count(results: &Vec<ModelWeightDocument>) {
for result in results {
println!(" - result: {:#?} {} ({:?})",
result.title,
result.ratings_positive_count,
result.token);
}
}
pub fn to_title_set(results: &Vec<ModelWeightDocument>) -> HashSet<String> {
results.iter().map(|result| result.title.clone()).collect()
}
@@ -0,0 +1,19 @@
use elasticsearch::Elasticsearch;
use elasticsearch_schema::documents::model_weight_document::ModelWeightDocument;
use elasticsearch_schema::searches::search_model_weights::search_model_weights::{search_model_weights, SearchArgs};
use errors::AnyhowResult;
pub async fn search_term_only(search_term: &str, client: &Elasticsearch) -> AnyhowResult<Vec<ModelWeightDocument>> {
search_model_weights(SearchArgs {
search_term,
maybe_creator_user_token: None,
maybe_ietf_primary_language_subtag: None,
maybe_weights_categories: None,
maybe_weights_types: None,
sort_field: None,
sort_direction: None,
minimum_score: None,
client: &client,
}).await
}
@@ -0,0 +1,3 @@
pub mod test_search_term_zelda;
pub mod test_upvoted_models;
pub mod test_use_count;
@@ -0,0 +1,31 @@
use crate::plans::model_weights::evaluate::asserts::assert_search_term_contains_titles;
use elasticsearch::Elasticsearch;
use errors::AnyhowResult;
pub async fn test_search_term_zelda(
client: &Elasticsearch
) -> AnyhowResult<()> {
assert_search_term_contains_titles(client, "zeld", &vec![
"Link (Zelda CDI) (Français)",
"Zelda (BOTW Español Latino)",
"Zelda (BOTW) (Ita)",
"Zelda (BOTW)",
"Zelda (Breath of the Wild)",
"Zelda Rubinstein",
"Zelda. (The Legend of Zelda: Breath of the Wild, Castillian Spanish.) Versión MEJORADA.",
]).await?;
assert_search_term_contains_titles(client, "zelad", &vec![
"Link (Zelda CDI) (Français)",
"Zelda (BOTW Español Latino)",
"Zelda (BOTW) (Ita)",
"Zelda (BOTW)",
"Zelda (Breath of the Wild)",
"Zelda Rubinstein",
"Zelda. (The Legend of Zelda: Breath of the Wild, Castillian Spanish.) Versión MEJORADA.",
]).await?;
Ok(())
}
@@ -0,0 +1,50 @@
use crate::plans::model_weights::evaluate::asserts::{assert_results_contain_tokens, assert_results_do_not_contain_tokens};
use crate::plans::model_weights::evaluate::print_debugging::print_titles_and_rating_count;
use elasticsearch::Elasticsearch;
use elasticsearch_schema::searches::search_model_weights::search_model_weights::{search_model_weights, ModelWeightsSortDirection, ModelWeightsSortField, SearchArgs};
use errors::AnyhowResult;
const UPVOTED_MODELS: [&str; 3] = [
"weight_ppqs5038bvkm6wc29w0xfebzy", // Donald Trump : 132
"weight_ahxbf2104ngsgyegncaefyy6j", // Plankton v2 (Doug Lawrence) : 128
"weight_7jk8mgwkzsycqrxmfw5q4245y", // Homer Simpson: 115
];
pub async fn test_upvoted_models(
client: &Elasticsearch
) -> AnyhowResult<()> {
let results = search_model_weights(SearchArgs {
search_term: "", // No search term
maybe_creator_user_token: None,
maybe_ietf_primary_language_subtag: None,
maybe_weights_categories: None,
maybe_weights_types: None,
sort_field: Some(ModelWeightsSortField::PositiveRatingCount),
sort_direction: None,
minimum_score: None,
client: &client,
}).await?;
print_titles_and_rating_count(&results);
assert_results_contain_tokens(&results, &UPVOTED_MODELS)?;
// Now invert, and we shouldn't expect popular tokens to be found
let results = search_model_weights(SearchArgs {
search_term: "", // No search term
maybe_creator_user_token: None,
maybe_ietf_primary_language_subtag: None,
maybe_weights_categories: None,
maybe_weights_types: None,
sort_field: Some(ModelWeightsSortField::PositiveRatingCount),
sort_direction: Some(ModelWeightsSortDirection::Ascending), // Least popular first
minimum_score: None,
client: &client,
}).await?;
assert_results_do_not_contain_tokens(&results, &UPVOTED_MODELS)?;
Ok(())
}
@@ -0,0 +1,47 @@
use crate::plans::model_weights::evaluate::asserts::{assert_results_contain_tokens, assert_results_do_not_contain_tokens};
use elasticsearch::Elasticsearch;
use elasticsearch_schema::searches::search_model_weights::search_model_weights::{search_model_weights, ModelWeightsSortDirection, ModelWeightsSortField, SearchArgs};
use errors::AnyhowResult;
const POPULAR_VOICES : [&str; 3] = [
"weight_hz7g8f1j4psrsw2sv67e4y61q", // Mariano Closs (Relator de fútbol Argentino)
"weight_rhfg4chgrp42bnha8kqfrtmcq", // Mariano Closs (full version)
"weight_mbcr352wfb1eq76tpy3ef3kx1", // Lionel Messi
];
pub async fn test_use_count(
client: &Elasticsearch
) -> AnyhowResult<()> {
let results = search_model_weights(SearchArgs {
search_term: "", // No search term
maybe_creator_user_token: None,
maybe_ietf_primary_language_subtag: None,
maybe_weights_categories: None,
maybe_weights_types: None,
sort_field: Some(ModelWeightsSortField::UsageCount),
sort_direction: None,
minimum_score: None,
client: &client,
}).await?;
assert_results_contain_tokens(&results, &POPULAR_VOICES)?;
// Now invert, and we shouldn't expect popular tokens to be found
let results = search_model_weights(SearchArgs {
search_term: "", // No search term
maybe_creator_user_token: None,
maybe_ietf_primary_language_subtag: None,
maybe_weights_categories: None,
maybe_weights_types: None,
sort_field: Some(ModelWeightsSortField::UsageCount),
sort_direction: Some(ModelWeightsSortDirection::Ascending), // Least popular first
minimum_score: None,
client: &client,
}).await?;
assert_results_do_not_contain_tokens(&results, &POPULAR_VOICES)?;
Ok(())
}
@@ -0,0 +1,3 @@
pub mod create_all_model_weight_documents;
pub mod evaluate;
pub mod test_search_model_weights_documents;
@@ -0,0 +1,25 @@
use elasticsearch::Elasticsearch;
use elasticsearch_schema::documents::model_weight_document::ModelWeightDocument;
use elasticsearch_schema::searches::search_model_weights::search_model_weights::{search_model_weights, ModelWeightsSortDirection, ModelWeightsSortField, SearchArgs};
use enums::by_table::model_weights::weights_types::WeightsType;
use errors::AnyhowResult;
use std::collections::HashSet;
use std::iter::FromIterator;
pub async fn test_search_model_weights_documents(client: &Elasticsearch) -> AnyhowResult<Vec<ModelWeightDocument>> {
let results = search_model_weights(SearchArgs {
//search_term: "zel",
search_term: "mariano",
maybe_creator_user_token: None,
maybe_ietf_primary_language_subtag: None,
maybe_weights_categories: None,
maybe_weights_types: Some(HashSet::from_iter(vec![WeightsType::Tacotron2])),
sort_field: Some(ModelWeightsSortField::UsageCount),
sort_direction: Some(ModelWeightsSortDirection::Ascending),
minimum_score: None,
client: &client,
}).await?;
Ok(results)
}
@@ -2,5 +2,6 @@ pub (crate) mod development_only;
pub (crate) mod fakeyou;
pub (crate) mod legacy;
pub (crate) mod storyteller;
pub (crate) mod storyteller_studio;
pub (crate) mod video_compositor;
pub (crate) mod storyteller_board;
pub (crate) mod storyteller_render;
pub (crate) mod storyteller_studio;
@@ -2,11 +2,12 @@ use actix_cors::Cors;
use crate::util::netlify_branch_domain_matches::netlify_branch_domain_matches;
pub fn add_video_compositor(cors: Cors, _is_production: bool) -> Cors {
pub fn add_storyteller_board(cors: Cors, _is_production: bool) -> Cors {
cors
// Hypothetical domains
.allowed_origin("https://memeboard.ai")
.allowed_origin("https://dingboard.ai")
.allowed_origin("https://board.storyteller.ai")
// Netlify project
.allowed_origin_fn(|origin, _req_head| {
netlify_branch_domain_matches(origin, "storyteller-board.netlify.app")
@@ -37,6 +38,12 @@ mod tests {
let production_cors = build_cors_config(ServerEnvironment::Production);
assert_origin_ok(&production_cors, "https://dingboard.ai").await;
}
#[actix_rt::test]
async fn board_dot_storyteller() {
let production_cors = build_cors_config(ServerEnvironment::Production);
assert_origin_ok(&production_cors, "https://board.storyteller.ai").await;
}
}
mod netlify {
@@ -0,0 +1,61 @@
use actix_cors::Cors;
use crate::util::netlify_branch_domain_matches::netlify_branch_domain_matches;
pub fn add_storyteller_render(cors: Cors, _is_production: bool) -> Cors {
cors
// Hypothetical domains
.allowed_origin("https://render.storyteller.ai")
// Netlify project
.allowed_origin_fn(|origin, _req_head| {
netlify_branch_domain_matches(origin, "storyteller-render.netlify.app")
})
}
#[cfg(test)]
mod tests {
use reusable_types::server_environment::ServerEnvironment;
use crate::cors::build_cors_config;
use crate::testing::assert_origin_invalid;
use crate::testing::assert_origin_ok;
mod domains {
use super::*;
#[actix_rt::test]
async fn board_dot_storyteller() {
let production_cors = build_cors_config(ServerEnvironment::Production);
assert_origin_ok(&production_cors, "https://render.storyteller.ai").await;
}
}
mod netlify {
use super::*;
#[actix_rt::test]
async fn netlify_main() {
let production_cors = build_cors_config(ServerEnvironment::Production);
assert_origin_ok(&production_cors, "https://storyteller-render.netlify.app").await;
}
#[actix_rt::test]
async fn netlify_branch_deploy() {
let production_cors = build_cors_config(ServerEnvironment::Production);
assert_origin_ok(&production_cors, "https://test--storyteller-render.netlify.app").await;
}
#[actix_rt::test]
async fn netlify_deploy_preview() {
let production_cors = build_cors_config(ServerEnvironment::Production);
assert_origin_ok(&production_cors, "https://deploy-preview-123--storyteller-render.netlify.app").await;
}
#[actix_rt::test]
async fn invalid_netlify_preview_deploy() {
let production_cors = build_cors_config(ServerEnvironment::Production);
assert_origin_invalid(&production_cors, "https://storyteller-render--unrelated.netlify.app").await;
assert_origin_invalid(&production_cors, "https://deploy-preview-123--unrelated.netlify.app").await;
}
}
}
@@ -13,6 +13,7 @@ pub fn add_storyteller_studio(cors: Cors, _is_production: bool) -> Cors {
netlify_branch_domain_matches(origin, "pipeline-gottagofast.netlify.app")
})
.allowed_origin("http://localhost:5173")
.allowed_origin("https://animate.storyteller.ai") // NB: Gen2 Studio
}
#[cfg(test)]
+5 -3
View File
@@ -5,10 +5,11 @@ use reusable_types::server_environment::ServerEnvironment;
use crate::configs::development_only::add_development_only;
use crate::configs::fakeyou::{add_fakeyou, add_fakeyou_dev_proxy};
use crate::configs::storyteller_studio::add_storyteller_studio;
use crate::configs::legacy::{add_legacy_storyteller_stream, add_legacy_trumped, add_legacy_vocodes, add_power_stream};
use crate::configs::storyteller::{add_storyteller, add_storyteller_dev_proxy};
use crate::configs::video_compositor::add_video_compositor;
use crate::configs::storyteller_board::add_storyteller_board;
use crate::configs::storyteller_render::add_storyteller_render;
use crate::configs::storyteller_studio::add_storyteller_studio;
/// Return cors config for FakeYou / Vocodes / OBS / local development
pub fn build_cors_config(server_environment: ServerEnvironment) -> Cors {
@@ -36,7 +37,8 @@ fn do_build_cors_config(is_production: bool) -> Cors {
cors = add_storyteller(cors, is_production);
cors = add_storyteller_dev_proxy(cors, is_production);
cors = add_storyteller_studio(cors, is_production);
cors = add_video_compositor(cors, is_production);
cors = add_storyteller_board(cors, is_production);
cors = add_storyteller_render(cors, is_production);
// Legacy
cors = add_legacy_trumped(cors, is_production);
@@ -4,11 +4,13 @@ use std::time::Duration;
use anyhow::anyhow;
use anyhow::bail;
use log::info;
use log::warn;
use log::{error, warn};
use log::{debug, info};
use s3::bucket::Bucket;
use s3::creds::Credentials;
use s3::error::S3Error;
use s3::region::Region;
use s3::request::ResponseData;
use tokio::fs::File;
use tokio::io::AsyncReadExt;
@@ -70,8 +72,15 @@ impl BucketClient {
let mut bucket = Bucket::new(&bucket_name, region, credentials)?;
bucket.set_request_timeout(bucket_request_timeout);
bucket.set_path_style();
bucket.set_subdomain_style();
match s3_endpoint {
"https://storage.googleapis.com" => {
bucket.set_subdomain_style();
},
_ => {
bucket.set_path_style();
}
}
let optional_bucket_root = optional_bucket_root.map(|s| s.to_string());
@@ -89,23 +98,25 @@ impl BucketClient {
}
pub async fn upload_file(&self, object_name: &str, bytes: &[u8]) -> anyhow::Result<()> {
info!("Filename for bucket: {}", object_name);
debug!("Filename for bucket: {}", object_name);
let object_name = self.get_rooted_object_name(object_name);
info!("Rooted filename for bucket: {}", object_name);
debug!("Rooted filename for bucket: {}", object_name);
let response = self.bucket.put_object(&object_name, bytes).await?;
let body_bytes = response.bytes();
let code = response.status_code();
info!("upload code: {}", code);
debug!("upload code for {}: {}", object_name, code);
if code != 200 {
let body = String::from_utf8_lossy(body_bytes);
warn!("upload body: {}", body);
warn!("failed upload body: {}", body);
}
info!("Successfully uploaded file to bucket: {}", object_name);
Ok(())
}
@@ -134,7 +145,13 @@ impl BucketClient {
let object_name = self.get_rooted_object_name(object_name);
info!("Rooted filename for bucket: {}", object_name);
let response = self.bucket.put_object_with_content_type(&object_name, bytes, content_type).await?;
let response = self.bucket.put_object_with_content_type(&object_name, bytes, content_type).await;
if let Err(err) = &response {
error!("S3 Upload Error for bucket name {}: {:?}", &self.bucket.name, err);
}
let response = response?;
let body_bytes = response.bytes();
let code = response.status_code();
+10 -1
View File
@@ -15,6 +15,11 @@ pub struct BootstrapArgs<'a, P: AsRef<Path>> {
/// Where to look for env conf files.
pub config_search_directories: &'a [P],
/// If true, ignore the root '.env' file.
/// TODO(bt,2025-01-23): Make this the default behavior, then remove reading from '.env' entirely,
/// There are too many apps that share this file.
pub ignore_legacy_dot_env_file: bool,
}
/// Information about how the application is deployed.
@@ -45,7 +50,11 @@ pub struct ContainerEnvironment {
const SERVER_ENVIRONMENT : &str = "SERVER_ENVIRONMENT";
pub fn bootstrap<P: AsRef<Path>>(args: BootstrapArgs<'_, P>) -> AnyhowResult<ContainerEnvironment> {
easyenv::init_all_with_default_logging(args.default_logging_override);
if args.ignore_legacy_dot_env_file {
easyenv::init_env_logger(args.default_logging_override);
} else {
easyenv::init_all_with_default_logging(args.default_logging_override);
}
info!("Bootstrapping application {}", &args.app_name);
@@ -10,7 +10,7 @@ const MEDIA_FILE_DIRECTORY: &str = "/media";
/// Directory for user media uploads.
/// Each uploaded file gets its own directory so that we can store the original
/// file alongside re-processed derivatives.
#[derive(Clone)]
#[derive(Clone, Debug)]
pub struct MediaFileBucketDirectory {
object_hash: String,
directory: String,
@@ -0,0 +1,40 @@
use serde_json::{Number, Value};
pub fn add_min_score(
mut query: Value,
search_term: &str,
maybe_user_defined_minimum_score: Option<u64>,
) -> Value {
if let Some(mimimum_score) = maybe_user_defined_minimum_score {
if let Some(mut object) = query.as_object_mut() {
object.insert(
"min_score".to_string(),
Value::Number(Number::from(mimimum_score))
);
}
return query;
}
if search_term.is_empty() {
return query;
}
let minimum_score = match search_term.len() {
1..=3 => 0,
4 => 1,
5 => 3,
6 => 5,
7 => 7,
_ => 10,
};
if let Some(mut object) = query.as_object_mut() {
object.insert(
"min_score".to_string(),
Value::Number(Number::from(minimum_score))
);
}
query
}
@@ -0,0 +1,4 @@
pub mod search_model_weights;
pub (crate) mod predicates;
pub (crate) mod sort;
pub (crate) mod min_score;
@@ -0,0 +1,72 @@
use std::collections::HashSet;
use serde_json::{json, Value};
use enums::by_table::model_weights::weights_category::WeightsCategory;
use enums::by_table::model_weights::weights_types::WeightsType;
use tokens::tokens::users::UserToken;
pub fn must_be_not_deleted() -> Value {
json!({
"term": {
"is_deleted": false,
}
})
}
// pub fn featured_predicate(is_featured: bool) -> Value {
// json!({
// "term": {
// "is_featured": is_featured,
// }
// })
// }
pub fn creator_user_token_predicate(creator_user_token: &UserToken) -> Value {
json!({
"term": {
"maybe_creator_user_token": creator_user_token.as_str(),
}
})
}
pub fn language_subtag_predicate(language_subtag: &str) -> Value {
json!({
"term": {
"maybe_ietf_primary_language_subtag": language_subtag,
}
})
}
pub fn weights_categories_predicates(weights_categories: &HashSet<WeightsCategory>) -> Value {
should_predicates(weights_categories.iter()
.map(|weight_category| {
json!({
"term": {
"weights_category": weight_category.to_str(),
}
})
})
.collect())
}
pub fn weights_types_predicates(weights_types: &HashSet<WeightsType>) -> Value {
should_predicates(weights_types.iter()
.map(|weights_type| {
json!({
"term": {
"weights_type": weights_type.to_str(),
}
})
})
.collect())
}
// NB: "Should" is a logical OR.
pub fn should_predicates(predicates: Vec<Value>) -> Value {
json!({
"bool": {
"should": predicates,
}
})
}
@@ -2,17 +2,20 @@ use std::collections::HashSet;
use elasticsearch::{Elasticsearch, SearchParts};
use once_cell::sync::Lazy;
use serde_json::{json, Value};
use serde_json::{json, Number, Value};
use enums::by_table::model_weights::weights_category::WeightsCategory;
use enums::by_table::model_weights::weights_types::WeightsType;
use errors::{anyhow, AnyhowResult};
use errors::AnyhowResult;
use tokens::tokens::users::UserToken;
use crate::documents::model_weight_document::{MODEL_WEIGHT_INDEX, ModelWeightDocument};
use crate::documents::model_weight_document::{ModelWeightDocument, MODEL_WEIGHT_INDEX};
use crate::searches::search_model_weights::min_score::add_min_score;
use crate::searches::search_model_weights::predicates::{creator_user_token_predicate, language_subtag_predicate, must_be_not_deleted, weights_categories_predicates, weights_types_predicates};
use crate::searches::search_model_weights::sort::add_sort;
static JSON_QUERY : Lazy<Value> = Lazy::new(|| {
const QUERY_TEMPLATE : &str = include_str!("../../../../../_elasticsearch/searches/model_weights/search.json");
const QUERY_TEMPLATE : &str = include_str!("../../../../../../_elasticsearch/searches/model_weights/search.json");
let json : Value = serde_json::from_str(QUERY_TEMPLATE)
.expect("json should parse");
@@ -20,99 +23,34 @@ static JSON_QUERY : Lazy<Value> = Lazy::new(|| {
json
});
pub struct SearchModelWeightsQuery<'a> {
pub search_term: &'a str,
pub maybe_language_subtag: Option<&'a str>,
pub maybe_weights_type: Option<WeightsType>,
pub maybe_weights_category: Option<WeightsCategory>,
#[derive(Default, Copy, Clone, Eq, PartialEq)]
pub enum ModelWeightsSortField {
/// Sort based on the match score of the search term alone.
#[default]
MatchScore,
/// Sort based on the creation date
CreatedAt,
/// Sort based on the model usage count
UsageCount,
/// Sort based on the model bookmark count
BookmarkCount,
/// Sort based on the model positive ratings count
PositiveRatingCount,
}
impl <'a>SearchModelWeightsQuery<'a> {
#[derive(Default, Copy, Clone, Eq, PartialEq)]
pub enum ModelWeightsSortDirection {
Ascending,
#[default]
Descending,
}
fn base_query(&self) -> Value {
json!({
"query": {
"bool": {
"must": [
{
"bool": {
"should": [
{
"fuzzy": {
"title": {
"value": self.search_term,
"fuzziness": 2
}
}
},
{
"match": {
"title": {
"query": self.search_term,
"boost": 1
}
}
},
{
"multi_match": {
"query": self.search_term,
"type": "bool_prefix",
"fields": [
"title",
"title._2gram",
"title._3gram"
],
"boost": 50
}
}
]
}
}
]
}
}
})
}
pub fn query(&self) -> AnyhowResult<Value> {
let mut search_json = self.base_query();
let must_clause = search_json.pointer_mut("/query/bool/must")
.map(|pointer| pointer.as_array_mut())
.flatten()
.ok_or_else(|| anyhow!("could not get pointer to must clause"))?;
if let Some(maybe_language_subtag) = &self.maybe_language_subtag {
must_clause.push(json!(
{
"match": {
"maybe_ietf_primary_language_subtag": maybe_language_subtag
}
}
));
impl ModelWeightsSortDirection {
pub fn to_str(&self) -> &str {
match self {
Self::Ascending => "asc",
Self::Descending => "desc",
}
if let Some(weights_type) = &self.maybe_weights_type {
must_clause.push(json!(
{
"match": {
"weights_type": weights_type.to_string(),
}
}
));
}
if let Some(weights_category) = &self.maybe_weights_category {
must_clause.push(json!(
{
"match": {
"weights_category": weights_category.to_string(),
}
}
));
}
Ok(search_json)
}
}
@@ -124,12 +62,21 @@ pub struct SearchArgs<'a> {
pub maybe_weights_categories: Option<HashSet<WeightsCategory>>,
pub maybe_weights_types: Option<HashSet<WeightsType>>,
pub sort_field: Option<ModelWeightsSortField>,
pub sort_direction: Option<ModelWeightsSortDirection>,
pub minimum_score: Option<u64>,
pub client: &'a Elasticsearch,
}
pub async fn search_model_weights(args: SearchArgs<'_>) -> AnyhowResult<Vec<ModelWeightDocument>> {
let query = build_query(&args)?;
let json_query = serde_json::to_string(&query)?;
println!("Query: {:#?}", json_query);
let search_response = args.client
.search(SearchParts::Index(&[MODEL_WEIGHT_INDEX]))
.body(query)
@@ -213,75 +160,12 @@ fn build_query(args: &SearchArgs) -> AnyhowResult<Value> {
Some(json!(args.search_term))
})?;
let query = add_sort(query, args.sort_field, args.sort_direction);
let query = add_min_score(query, args.search_term, args.minimum_score);
Ok(query)
}
fn must_be_not_deleted() -> Value {
json!({
"term": {
"is_deleted": false,
}
})
}
// fn featured_predicate(is_featured: bool) -> Value {
// json!({
// "term": {
// "is_featured": is_featured,
// }
// })
// }
fn creator_user_token_predicate(creator_user_token: &UserToken) -> Value {
json!({
"term": {
"maybe_creator_user_token": creator_user_token.as_str(),
}
})
}
fn language_subtag_predicate(language_subtag: &str) -> Value {
json!({
"term": {
"maybe_ietf_primary_language_subtag": language_subtag,
}
})
}
fn weights_categories_predicates(weights_categories: &HashSet<WeightsCategory>) -> Value {
should_predicates(weights_categories.iter()
.map(|weight_category| {
json!({
"term": {
"weights_category": weight_category.to_str(),
}
})
})
.collect())
}
fn weights_types_predicates(weights_types: &HashSet<WeightsType>) -> Value {
should_predicates(weights_types.iter()
.map(|weights_type| {
json!({
"term": {
"weights_type": weights_type.to_str(),
}
})
})
.collect())
}
// NB: "Should" is a logical OR.
fn should_predicates(predicates: Vec<Value>) -> Value {
json!({
"bool": {
"should": predicates,
}
})
}
#[cfg(test)]
mod tests {
@@ -294,8 +178,8 @@ mod tests {
use enums::by_table::model_weights::weights_types::WeightsType;
use tokens::tokens::users::UserToken;
use crate::searches::search_model_weights::build_query;
use crate::searches::search_model_weights::SearchArgs;
use crate::searches::search_model_weights::search_model_weights::build_query;
use crate::searches::search_model_weights::search_model_weights::SearchArgs;
#[test]
fn test_default_search() {
@@ -305,6 +189,9 @@ mod tests {
maybe_ietf_primary_language_subtag: None,
maybe_weights_categories: None,
maybe_weights_types: None,
sort_field: Default::default(),
sort_direction: Default::default(),
minimum_score: None,
client: &elasticsearch::Elasticsearch::default(),
}).unwrap();
@@ -337,6 +224,9 @@ mod tests {
maybe_ietf_primary_language_subtag: None,
maybe_weights_categories: None,
maybe_weights_types: None,
sort_field: Default::default(),
sort_direction: Default::default(),
minimum_score: None,
client: &elasticsearch::Elasticsearch::default(),
}).unwrap();
@@ -375,6 +265,9 @@ mod tests {
maybe_weights_categories: None,
maybe_weights_types: None,
client: &elasticsearch::Elasticsearch::default(),
minimum_score: None,
sort_field: Default::default(),
sort_direction: Default::default(),
}).unwrap();
let value = jsonpath_lib::select(
@@ -414,6 +307,9 @@ mod tests {
WeightsType::Tacotron2,
WeightsType::GptSoVits,
])),
sort_field: Default::default(),
sort_direction: Default::default(),
minimum_score: None,
client: &elasticsearch::Elasticsearch::default(),
}).unwrap();
@@ -441,6 +337,9 @@ mod tests {
WeightsCategory::VoiceConversion,
])),
client: &elasticsearch::Elasticsearch::default(),
minimum_score: None,
sort_field: Default::default(),
sort_direction: Default::default(),
}).unwrap();
let value = select(&search, "$.query.bool.must[0].bool.must[0].term.is_deleted");
@@ -469,165 +368,4 @@ mod tests {
})
.collect()
}
mod old_tests {
use regex::Regex;
use enums::by_table::model_weights::weights_category::WeightsCategory;
use enums::by_table::model_weights::weights_types::WeightsType;
use errors::AnyhowResult;
use crate::searches::search_model_weights::SearchModelWeightsQuery;
fn query_to_json_string(query: SearchModelWeightsQuery<'_>) -> AnyhowResult<String> {
let json = query.query()?;
let json = serde_json::to_string(&json)?;
Ok(json)
}
fn compact_json(json: &str) -> String {
let regex = Regex::new("\\s+").expect("regex should parse");
let json = regex.replace_all(&json, "");
json.to_string()
}
#[test]
fn default_search_only_keyword() {
let query = SearchModelWeightsQuery {
search_term: "FOO_BAR_BAZ",
maybe_language_subtag: None,
maybe_weights_type: None,
maybe_weights_category: None,
};
let json = query_to_json_string(query).unwrap();
// NB: Keys in emitted JSON are sorted.
let expected_json = r#"
{
"query": {
"bool": {
"must": [
{
"bool": {
"should": [
{
"fuzzy": {
"title": {
"value": "FOO_BAR_BAZ",
"fuzziness": 2
}
}
},
{
"match": {
"title": {
"query": "FOO_BAR_BAZ",
"boost": 1
}
}
},
{
"multi_match": {
"query": "FOO_BAR_BAZ",
"type": "bool_prefix",
"fields": [
"title",
"title._2gram",
"title._3gram"
],
"boost": 50
}
}
]
}
}
]
}
}
}
"#;
let expected_json = compact_json(&expected_json);
assert_eq!(&json, &expected_json);
}
#[test]
fn search_with_language_and_weights() {
let query = SearchModelWeightsQuery {
search_term: "FOO_BAR_BAZ",
maybe_language_subtag: Some("en"),
maybe_weights_type: Some(WeightsType::SoVitsSvc),
maybe_weights_category: Some(WeightsCategory::Vocoder),
};
let json = query_to_json_string(query).unwrap();
// NB: Keys in emitted JSON are sorted.
let expected_json = r#"
{
"query": {
"bool": {
"must": [
{
"bool": {
"should": [
{
"fuzzy": {
"title": {
"value": "FOO_BAR_BAZ",
"fuzziness": 2
}
}
},
{
"match": {
"title": {
"query": "FOO_BAR_BAZ",
"boost": 1
}
}
},
{
"multi_match": {
"query": "FOO_BAR_BAZ",
"type": "bool_prefix",
"fields": [
"title",
"title._2gram",
"title._3gram"
],
"boost": 50
}
}
]
}
},
{
"match": {
"maybe_ietf_primary_language_subtag": "en"
}
},
{
"match": {
"weights_type": "so_vits_svc"
}
},
{
"match": {
"weights_category": "vocoder"
}
}
]
}
}
}
"#;
let expected_json = compact_json(&expected_json);
assert_eq!(&json, &expected_json);
}
}
}
@@ -0,0 +1,56 @@
use crate::searches::search_model_weights::search_model_weights::{ModelWeightsSortDirection, ModelWeightsSortField};
use serde_json::{json, Value};
pub fn add_sort(
mut query: Value,
sort_field: Option<ModelWeightsSortField>,
sort_direction: Option<ModelWeightsSortDirection>,
) -> Value {
let sort_field = sort_field.unwrap_or_default();
let sort_direction = sort_direction.unwrap_or_default();
if sort_field == ModelWeightsSortField::MatchScore
&& sort_direction == ModelWeightsSortDirection::Descending {
return query;
}
let sort = match sort_field {
ModelWeightsSortField::CreatedAt => json!([
{
"created_at": {
"order": sort_direction.to_str(),
}
},
"_score"
]),
ModelWeightsSortField::UsageCount => json!([
{
"cached_usage_count": {
"order": sort_direction.to_str(),
}
},
"_score"
]),
ModelWeightsSortField::BookmarkCount => json!([
{
"bookmark_count": {
"order": sort_direction.to_str(),
}
},
"_score"
]),
ModelWeightsSortField::PositiveRatingCount => json!({
"ratings_positive_count": {
"order": sort_direction.to_str(),
}
}),
_ => return query,
};
if let Some(mut object) = query.as_object_mut() {
object.insert("sort".to_string(), sort);
}
query
}
@@ -1,9 +1,10 @@
use std::path::Path;
use errors::AnyhowResult;
use std::path::Path;
pub struct VideoDimensions {
pub width: u64,
pub height: u64,
pub length: u64,
}
pub fn ffprobe_get_dimensions(
@@ -22,11 +23,20 @@ pub fn ffprobe_get_dimensions(
}
});
match maybe_dimensions {
None => Ok(None),
Some((width, height)) => Ok(Some(VideoDimensions {
let maybe_duration = result.streams.iter()
.filter(|stream| stream.codec_type.as_deref() == Some("video"))
.find_map(|stream| {
stream.duration_ts
});
match (maybe_dimensions, maybe_duration) {
(None, None) => Ok(None),
(None, Some(_)) => Ok(None),
(Some(_), None) => Ok(None),
(Some((width, height)), Some(length)) => Ok(Some(VideoDimensions {
width: width.unsigned_abs(),
height: height.unsigned_abs(),
length: length.unsigned_abs(),
})),
}
}
@@ -0,0 +1,63 @@
use std::error::Error;
use std::fmt::{Display, Formatter};
#[derive(Debug)]
pub enum DatabaseInsertError {
/// A duplicate key error occurred.
/// This should be surfaced as a 400 to the user.
DuplicateKeyError,
/// An uncategorized error occurred.
/// This will likely result in a 500 for the user.
SqlxError(sqlx::Error),
/// An uncategorized non-database error occurred.
/// This will likely result in a 500 for the user.
AnyhowError(anyhow::Error),
}
impl Error for DatabaseInsertError {}
impl Display for DatabaseInsertError {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
DatabaseInsertError::DuplicateKeyError => write!(f, "Duplicate key error"),
DatabaseInsertError::SqlxError(e) => write!(f, "SQLx error: {:?}", e),
DatabaseInsertError::AnyhowError(e) => write!(f, "Query error: {:?}", e),
}
}
}
impl From<anyhow::Error> for DatabaseInsertError {
fn from(err: anyhow::Error) -> Self {
DatabaseInsertError::AnyhowError(err)
}
}
impl From<sqlx::Error> for DatabaseInsertError {
fn from(err: sqlx::Error) -> Self {
if let Some(db_err) = err.as_database_error() {
// NB: SQLSTATE[23000]: Integrity constraint violation
// NB: MySQL Error Code 1062: Duplicate key insertion (this is harder to access)
let is_integrity_violation = db_err.code().as_deref() == Some("23000");
let is_duplicate_key = db_err.message().contains("Duplicate entry");
if is_integrity_violation && is_duplicate_key {
return Self::DuplicateKeyError;
}
}
Self::SqlxError(err)
}
}
impl DatabaseInsertError {
/// Whether we should surface this failure as a 400 to the user.
/// This could be any field (for now we only have the idempotency token).
pub fn is_400_error(&self) -> bool {
match self {
DatabaseInsertError::DuplicateKeyError => true,
_ => false,
}
}
}
@@ -1,4 +1,5 @@
// TODO(bt,2024-10-01): This is only used for inserts into the idempotency table. We should move or rename it.
#[derive(Debug)]
pub enum DatabaseQueryError {
/// A duplicate idempotency token error occurred.
@@ -1 +1,2 @@
pub mod database_insert_error;
pub mod database_query_error;
@@ -1,5 +1,4 @@
use errors::AnyhowResult;
use crate::payloads::generic_inference_args::inner_payloads::f5_tts_payload::F5TTSPayload;
use crate::payloads::generic_inference_args::inner_payloads::face_fusion_payload::FaceFusionPayload;
use crate::payloads::generic_inference_args::inner_payloads::gptsovits_payload::GptSovitsPayload;
use crate::payloads::generic_inference_args::inner_payloads::image_generation_payload::StableDiffusionArgs;
@@ -7,9 +6,12 @@ use crate::payloads::generic_inference_args::inner_payloads::lipsync_payload::Li
use crate::payloads::generic_inference_args::inner_payloads::live_portrait_payload::LivePortraitPayload;
use crate::payloads::generic_inference_args::inner_payloads::mocap_payload::MocapArgs;
use crate::payloads::generic_inference_args::inner_payloads::render_engine_scene_to_video_payload::RenderEngineSceneToVideoArgs;
use crate::payloads::generic_inference_args::inner_payloads::seed_vc_payload::SeedVcPayload;
use crate::payloads::generic_inference_args::inner_payloads::studio_gen2_payload::StudioGen2Payload;
use crate::payloads::generic_inference_args::inner_payloads::tts_payload::TTSArgs;
use crate::payloads::generic_inference_args::inner_payloads::videofilter_payload::RerenderArgs;
use crate::payloads::generic_inference_args::inner_payloads::workflow_payload::WorkflowArgs;
use errors::AnyhowResult;
/// Used to encode extra state for the `generic_inference_jobs` table.
/// This should act somewhat like a serialized protobuf stored inside a record.
@@ -76,7 +78,15 @@ pub enum InferenceCategoryAbbreviated {
#[serde(rename = "gs")] // NB: DO NOT CHANGE. It could break live jobs. Renamed to be fewer bytes.
#[serde(alias = "gpt_sovits")]
GptSovits
GptSovits,
#[serde(rename = "ft")] // NB: DO NOT CHANGE. It could break live jobs. Renamed to be fewer bytes.
#[serde(alias = "f5_tts")]
F5TTS,
#[serde(rename = "sv")] // NB: DO NOT CHANGE. It could break live jobs. Renamed to be fewer bytes.
#[serde(alias = "seed_vc")]
SeedVc
}
#[derive(Serialize, Deserialize, Clone, Copy, Debug)]
@@ -139,6 +149,9 @@ pub enum PolymorphicInferenceArgs {
/// ComfyUI (Short name to save space when serializing.)
Cu(WorkflowArgs),
/// Studio Gen2 (Short name to save space when serializing.)
S2(StudioGen2Payload),
/// ComfyUI (Short name to save space when serializing.)
Lp(LivePortraitPayload),
@@ -150,6 +163,10 @@ pub enum PolymorphicInferenceArgs {
/// GPT Sovits
Gs(GptSovitsPayload),
Ft(F5TTSPayload),
Sv(SeedVcPayload),
}
impl GenericInferenceArgs {
@@ -0,0 +1,2 @@
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct F5TTSPayload {}
@@ -5,6 +5,9 @@ pub mod lipsync_payload;
pub mod live_portrait_payload;
pub mod mocap_payload;
pub mod render_engine_scene_to_video_payload;
pub mod studio_gen2_payload;
pub mod tts_payload;
pub mod videofilter_payload;
pub mod workflow_payload;
pub mod f5_tts_payload;
pub mod seed_vc_payload;
@@ -0,0 +1,8 @@
use tokens::tokens::media_files::MediaFileToken;
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct SeedVcPayload {
#[serde(rename = "r")]
#[serde(skip_serializing_if = "Option::is_none")]
pub reference_media_file_token: Option<MediaFileToken>,
}
@@ -0,0 +1,65 @@
use crate::payloads::generic_inference_args::common::watermark_type::WatermarkType;
use enums::common::visibility::Visibility;
use std::time::Duration;
use tokens::tokens::media_files::MediaFileToken;
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
pub struct StudioGen2Payload {
/// The input image media file (required)
#[serde(rename = "i")]
#[serde(skip_serializing_if = "Option::is_none")]
pub image_file: Option<MediaFileToken>,
/// The input video media file (required)
#[serde(rename = "v")]
#[serde(skip_serializing_if = "Option::is_none")]
pub video_file: Option<MediaFileToken>,
#[serde(rename = "wt")]
#[serde(skip_serializing_if = "Option::is_none")]
pub watermark_type: Option<WatermarkType>,
#[serde(rename = "cv")]
#[serde(skip_serializing_if = "Option::is_none")]
pub creator_visibility: Option<Visibility>,
#[serde(rename = "sm")]
#[serde(skip_serializing_if = "Option::is_none")]
pub after_job_debug_sleep_millis: Option<u64>,
#[serde(rename = "ow")]
#[serde(skip_serializing_if = "Option::is_none")]
pub output_width: Option<u64>,
#[serde(rename = "oh")]
#[serde(skip_serializing_if = "Option::is_none")]
pub output_height: Option<u64>,
#[serde(rename = "fp")]
#[serde(skip_serializing_if = "Option::is_none")]
pub fps: Option<u64>,
#[serde(rename = "mf")]
#[serde(skip_serializing_if = "Option::is_none")]
pub max_frames: Option<u64>,
#[serde(rename = "rnd")]
#[serde(skip_serializing_if = "Option::is_none")]
pub rounds: Option<u64>,
#[serde(rename = "td")]
#[serde(skip_serializing_if = "Option::is_none")]
pub trim_duration_millis: Option<u64>,
#[serde(rename = "ski")]
#[serde(skip_serializing_if = "Option::is_none")]
pub skip_image_resize: Option<bool>,
#[serde(rename = "tw")]
#[serde(skip_serializing_if = "Option::is_none")]
pub tensor_image_width: Option<u64>,
#[serde(rename = "th")]
#[serde(skip_serializing_if = "Option::is_none")]
pub tensor_image_height: Option<u64>,
}
@@ -211,6 +211,10 @@ pub struct WorkflowArgs {
#[serde(skip_serializing_if = "Option::is_none")]
pub use_cinematic: Option<bool>,
#[serde(rename = "uv")]
#[serde(skip_serializing_if = "Option::is_none")]
pub use_cogvideo: Option<bool>,
#[serde(rename = "s")]
#[serde(skip_serializing_if = "Option::is_none")]
pub strength: Option<f32>,
@@ -0,0 +1,228 @@
use std::collections::BTreeSet;
use anyhow::anyhow;
use chrono::Utc;
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::job_status_plus::JobStatusPlus;
use enums::common::visibility::Visibility;
use errors::AnyhowResult;
use tokens::tokens::anonymous_visitor_tracking::AnonymousVisitorTrackingToken;
use tokens::tokens::generic_inference_jobs::InferenceJobToken;
use tokens::tokens::media_files::MediaFileToken;
use tokens::tokens::users::UserToken;
use crate::helpers::boolean_converters::i8_to_bool;
use crate::payloads::generic_inference_args::generic_inference_args::GenericInferenceArgs;
use crate::queries::generic_inference::job::_keys::GenericInferenceJobId;
pub struct UntriedJobCount {
pub job_count: usize,
}
pub struct CountUntriedJobsOfTypeArgs<'a> {
pub maybe_scope_by_job_type: Option<&'a BTreeSet<InferenceJobType>>,
pub maybe_scope_by_model_type: Option<&'a BTreeSet<InferenceModelType>>,
pub maybe_scope_by_job_category: Option<&'a BTreeSet<InferenceCategory>>,
pub mysql_pool: &'a MySqlPool,
}
pub async fn count_untried_jobs_of_type(
args: CountUntriedJobsOfTypeArgs<'_>,
)
-> AnyhowResult<UntriedJobCount>
{
let query = perform_query(args).await;
let result = query?;
Ok(UntriedJobCount {
job_count: result.job_count as usize,
})
}
async fn perform_query(args: CountUntriedJobsOfTypeArgs<'_>) -> Result<UntriedJobCountRawInternal, sqlx::Error> {
// NB: Can't be type checked because of WHERE IN clause with dynamic contents
let mut query = core_query(&args);
let query = sqlx::query_as::<_, UntriedJobCountRawInternal>(&query);
query.fetch_one(args.mysql_pool)
.await
}
// TODO(bt,2024-01-25): Make QueryBuilder
fn core_query(args: &CountUntriedJobsOfTypeArgs<'_>) -> String {
// NB: Can't be type checked because of WHERE IN clause with dynamic contents
let mut query = r#"
SELECT
count(*) as job_count
FROM generic_inference_jobs
WHERE
(
status IN ("pending")
)
AND
(
retry_at IS NULL
)
AND
(
attempt_count = 0
)
"#.to_string();
if let Some(job_types) = args.maybe_scope_by_job_type {
if !job_types.is_empty() {
query.push_str(&format!(r#"
AND
(
job_type IN ({})
)
"#, job_type_predicate(job_types)));
}
}
if let Some(model_types) = args.maybe_scope_by_model_type {
if !model_types.is_empty() {
query.push_str(&format!(r#"
AND
(
maybe_model_type IN ({})
)
"#, model_type_predicate(model_types)));
}
}
if let Some(inference_categories) = args.maybe_scope_by_job_category {
if !inference_categories.is_empty() {
query.push_str(&format!(r#"
AND
(
inference_category IN ({})
)
"#, inference_category_predicate(&inference_categories)));
}
}
query
}
#[derive(Debug)]
#[derive(sqlx::FromRow)]
struct UntriedJobCountRawInternal {
pub job_count: u64,
}
/// Return a comma-separated predicate, since SQLx does not yet support WHERE IN(?) for Vec<T>, etc.
/// Issue: https://github.com/launchbadge/sqlx/issues/875
fn job_type_predicate(types: &BTreeSet<InferenceJobType>) -> String {
let mut vec = types.iter()
.map(|ty| ty.to_str())
.map(|ty| format!("\"{}\"", ty))
.collect::<Vec<String>>();
vec.sort(); // NB: For the benefit of tests.
vec.join(", ")
}
/// Return a comma-separated predicate, since SQLx does not yet support WHERE IN(?) for Vec<T>, etc.
/// Issue: https://github.com/launchbadge/sqlx/issues/875
fn model_type_predicate(types: &BTreeSet<InferenceModelType>) -> String {
let mut vec = types.iter()
.map(|ty| ty.to_str())
.map(|ty| format!("\"{}\"", ty))
.collect::<Vec<String>>();
vec.sort(); // NB: For the benefit of tests.
vec.join(", ")
}
/// Return a comma-separated predicate, since SQLx does not yet support WHERE IN(?) for Vec<T>, etc.
/// Issue: https://github.com/launchbadge/sqlx/issues/875
fn inference_category_predicate(categories: &BTreeSet<InferenceCategory>) -> String {
let mut vec = categories.iter()
.map(|ty| ty.to_str())
.map(|ty| format!("\"{}\"", ty))
.collect::<Vec<String>>();
vec.sort(); // NB: For the benefit of tests.
vec.join(", ")
}
#[cfg(test)]
mod tests {
use std::collections::BTreeSet;
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 crate::queries::generic_inference::job::count_untried_jobs_of_type::{inference_category_predicate, job_type_predicate, model_type_predicate};
#[test]
fn test_job_type_predicate() {
// None
let types = BTreeSet::from([]);
assert_eq!(job_type_predicate(&types), "".to_string());
// One
let types = BTreeSet::from([
InferenceJobType::RvcV2,
]);
assert_eq!(job_type_predicate(&types), "\"rvc_v2\"".to_string());
// Multiple
let types = BTreeSet::from([
InferenceJobType::MocapNet,
InferenceJobType::RvcV2,
]);
assert_eq!(job_type_predicate(&types), "\"mocap_net\", \"rvc_v2\"".to_string());
}
#[test]
fn test_model_type_predicate() {
// None
let types = BTreeSet::from([]);
assert_eq!(model_type_predicate(&types), "".to_string());
// One
let types = BTreeSet::from([
InferenceModelType::RvcV2,
]);
assert_eq!(model_type_predicate(&types), "\"rvc_v2\"".to_string());
// Multiple
let types = BTreeSet::from([
InferenceModelType::RvcV2,
InferenceModelType::SoVitsSvc,
]);
assert_eq!(model_type_predicate(&types), "\"rvc_v2\", \"so_vits_svc\"".to_string());
}
#[test]
fn test_inference_category_predicate() {
// None
let types = BTreeSet::from([]);
assert_eq!(inference_category_predicate(&types), "".to_string());
// Some
let types = BTreeSet::from([
InferenceCategory::VoiceConversion,
]);
assert_eq!(inference_category_predicate(&types), "\"voice_conversion\"".to_string());
// All
let types = BTreeSet::from([
InferenceCategory::TextToSpeech,
InferenceCategory::VoiceConversion,
]);
assert_eq!(inference_category_predicate(&types), "\"text_to_speech\", \"voice_conversion\"".to_string());
}
}
@@ -1,4 +1,5 @@
pub mod _keys;
pub mod count_untried_jobs_of_type;
pub mod list_available_generic_inference_jobs;
pub mod mark_generic_inference_job_completely_failed;
pub mod mark_generic_inference_job_failure;
@@ -1,8 +1,8 @@
use anyhow::anyhow;
use chrono::{DateTime, Utc};
use sqlx::{FromRow, MySql, MySqlPool, QueryBuilder, Row};
use sqlx::mysql::MySqlRow;
use sqlx::pool::PoolConnection;
use sqlx::{FromRow, MySql, MySqlPool, QueryBuilder, Row};
use enums::by_table::generic_inference_jobs::frontend_failure_category::FrontendFailureCategory;
use enums::by_table::generic_inference_jobs::inference_category::InferenceCategory;
@@ -178,6 +178,8 @@ fn raw_records_to_public_result(records: Vec<RawGenericInferenceJobStatus>) -> V
InferenceCategory::Workflow => Some("Workflow"),
InferenceCategory::FormatConversion => Some("format conversion"),
InferenceCategory::LivePortrait => Some("Live Portrait"),
InferenceCategory::F5TTS => Some("F5 TTS"),
InferenceCategory::SeedVc => Some("Seed VC"),
InferenceCategory::ConvertBvhToWorkflow => Some("BVH to Workflow"),
InferenceCategory::DeprecatedField => Some("Job"), // TODO(bt,2024-07-16): Fix
};
@@ -188,12 +190,14 @@ fn raw_records_to_public_result(records: Vec<RawGenericInferenceJobStatus>) -> V
let (mut bucket_path_is_hash, mut maybe_public_bucket_hash) = match record.inference_category {
InferenceCategory::LipsyncAnimation => (true, record.maybe_media_file_public_bucket_directory_hash.as_deref()),
InferenceCategory::TextToSpeech => (false, record.maybe_tts_public_bucket_path.as_deref()),
InferenceCategory::F5TTS => (true, record.maybe_media_file_public_bucket_directory_hash.as_deref()),
InferenceCategory::VoiceConversion => (true, record.maybe_voice_conversion_public_bucket_hash.as_deref()),
InferenceCategory::VideoFilter => (true, record.maybe_media_file_public_bucket_directory_hash.as_deref()),
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()),
InferenceCategory::SeedVc => (true, record.maybe_media_file_public_bucket_directory_hash.as_deref()),
InferenceCategory::LivePortrait => (true, record.maybe_media_file_public_bucket_directory_hash.as_deref()),
InferenceCategory::ConvertBvhToWorkflow => (true, record.maybe_media_file_public_bucket_directory_hash.as_deref()),
InferenceCategory::DeprecatedField => (true, None), // TODO(bt,2024-07-16): We'll need to read another field!
@@ -1,8 +1,8 @@
use anyhow::anyhow;
use chrono::{DateTime, NaiveDateTime, TimeZone, Utc};
use log::warn;
use sqlx::{MySql, MySqlPool};
use sqlx::pool::PoolConnection;
use sqlx::{MySql, MySqlPool};
use enums::by_table::generic_inference_jobs::frontend_failure_category::FrontendFailureCategory;
use enums::by_table::generic_inference_jobs::inference_category::InferenceCategory;
@@ -154,6 +154,8 @@ fn raw_record_to_public_result(record: RawGenericInferenceJobStatus) -> GenericI
InferenceCategory::Workflow => Some("Workflow"),
InferenceCategory::FormatConversion => Some("format conversion"),
InferenceCategory::LivePortrait => Some("Live Portrait"),
InferenceCategory::F5TTS => Some("F5 TTS"),
InferenceCategory::SeedVc => Some("Seed VC"),
InferenceCategory::ConvertBvhToWorkflow => Some("BVH to Workflow"),
InferenceCategory::DeprecatedField => Some("Job"), // TODO(bt,2024-07-16): Fix
};
@@ -164,6 +166,7 @@ fn raw_record_to_public_result(record: RawGenericInferenceJobStatus) -> GenericI
let (mut bucket_path_is_hash, mut maybe_public_bucket_hash) = match record.inference_category {
InferenceCategory::LipsyncAnimation => (true, record.maybe_media_file_public_bucket_directory_hash.as_deref()),
InferenceCategory::TextToSpeech => (false, record.maybe_tts_public_bucket_path.as_deref()),
InferenceCategory::F5TTS => (true, record.maybe_media_file_public_bucket_directory_hash.as_deref()),
InferenceCategory::VoiceConversion => (true, record.maybe_voice_conversion_public_bucket_hash.as_deref()),
InferenceCategory::VideoFilter => (true, record.maybe_media_file_public_bucket_directory_hash.as_deref()),
InferenceCategory::ImageGeneration => (true, record.maybe_media_file_public_bucket_directory_hash.as_deref()),
@@ -171,6 +174,7 @@ fn raw_record_to_public_result(record: RawGenericInferenceJobStatus) -> GenericI
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()),
InferenceCategory::LivePortrait => (true, record.maybe_media_file_public_bucket_directory_hash.as_deref()),
InferenceCategory::SeedVc => (true, record.maybe_media_file_public_bucket_directory_hash.as_deref()),
InferenceCategory::ConvertBvhToWorkflow => (true, record.maybe_media_file_public_bucket_directory_hash.as_deref()),
InferenceCategory::DeprecatedField => (true, None), // TODO(bt,2024-07-16): We'll need to read another field!
};
@@ -0,0 +1,48 @@
use anyhow::anyhow;
use sqlx;
use sqlx::pool::PoolConnection;
use sqlx::{MySql, MySqlPool};
use errors::AnyhowResult;
/// Kill a handful of jobs when called.
/// This should only be used in development to keep the job queue empty.
pub async fn kill_jobs_in_development(mysql_pool: &MySqlPool) -> AnyhowResult<()>
{
let mut connection = mysql_pool.acquire().await?;
kill_jobs_in_development_using_connection(&mut connection).await
}
/// Kill a handful of jobs when called.
/// This should only be used in development to keep the job queue empty.
pub async fn kill_jobs_in_development_using_connection(
mysql_connection: &mut PoolConnection<MySql>,
) -> AnyhowResult<()> {
// We cancel any non-complete / non-terminal jobs
// We also mark "started" jobs in the event that they're left in a bad state.
// Any started jobs that are actually running should still run to completion, and
// the user should still see the final result even if the job system UI doesn't
// indicate it.
let query_result = sqlx::query!(
r#"
UPDATE generic_inference_jobs
SET
status = 'cancelled_by_user',
retry_at = NULL
WHERE status IN (
'pending',
'started',
'attempt_failed'
)
LIMIT 100
"#
)
.execute(&mut **mysql_connection)
.await;
if let Err(err) = query_result {
return Err(anyhow!("error with terminate job query (1): {:?}", err));
}
Ok(())
}
@@ -2,9 +2,9 @@ use std::collections::HashSet;
use anyhow::anyhow;
use chrono::{DateTime, Utc};
use sqlx::{FromRow, MySql, MySqlPool, QueryBuilder, Row};
use sqlx::mysql::MySqlRow;
use sqlx::pool::PoolConnection;
use sqlx::{FromRow, MySql, MySqlPool, QueryBuilder, Row};
use enums::by_table::generic_inference_jobs::frontend_failure_category::FrontendFailureCategory;
use enums::by_table::generic_inference_jobs::inference_category::InferenceCategory;
@@ -217,6 +217,7 @@ fn raw_records_to_public_result(records: Vec<RawGenericInferenceJobStatus>) -> V
maybe_model_title = match record.inference_category {
InferenceCategory::LipsyncAnimation => Some("lipsync animation"),
InferenceCategory::TextToSpeech => record.maybe_tts_model_title.as_deref(),
InferenceCategory::F5TTS => Some("F5 TTS"),
InferenceCategory::VoiceConversion => record.maybe_voice_conversion_model_title.as_deref(),
InferenceCategory::VideoFilter => Some("Video Filter"),
InferenceCategory::ImageGeneration => Some("Image Generation"),
@@ -225,6 +226,7 @@ fn raw_records_to_public_result(records: Vec<RawGenericInferenceJobStatus>) -> V
InferenceCategory::FormatConversion => Some("format conversion"),
InferenceCategory::ConvertBvhToWorkflow => Some("BVH to Workflow"),
InferenceCategory::LivePortrait => Some("Live Portrait"),
InferenceCategory::SeedVc => Some("Seed VC"),
InferenceCategory::DeprecatedField => Some("Job"), // TODO(bt,2024-07-16): Fix
};
}
@@ -234,6 +236,7 @@ fn raw_records_to_public_result(records: Vec<RawGenericInferenceJobStatus>) -> V
let (mut bucket_path_is_hash, mut maybe_public_bucket_hash) = match record.inference_category {
InferenceCategory::LipsyncAnimation => (true, record.maybe_media_file_public_bucket_directory_hash.as_deref()),
InferenceCategory::TextToSpeech => (false, record.maybe_tts_public_bucket_path.as_deref()),
InferenceCategory::F5TTS => (true, record.maybe_media_file_public_bucket_directory_hash.as_deref()),
InferenceCategory::VoiceConversion => (true, record.maybe_voice_conversion_public_bucket_hash.as_deref()),
InferenceCategory::VideoFilter => (true, record.maybe_media_file_public_bucket_directory_hash.as_deref()),
InferenceCategory::ImageGeneration => (true, record.maybe_media_file_public_bucket_directory_hash.as_deref()),
@@ -241,6 +244,7 @@ fn raw_records_to_public_result(records: Vec<RawGenericInferenceJobStatus>) -> V
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()),
InferenceCategory::LivePortrait => (true, record.maybe_media_file_public_bucket_directory_hash.as_deref()),
InferenceCategory::SeedVc => (true, record.maybe_media_file_public_bucket_directory_hash.as_deref()),
InferenceCategory::ConvertBvhToWorkflow => (true, record.maybe_media_file_public_bucket_directory_hash.as_deref()),
InferenceCategory::DeprecatedField => (true, None), // TODO(bt,2024-07-16): We'll need to read another field!
};
@@ -5,5 +5,6 @@ pub mod get_pending_inference_job_count;
pub mod insert_generic_inference_job;
pub mod job_status;
pub mod kill_generic_inference_jobs;
pub mod kill_jobs_in_development;
pub mod list_session_jobs;
pub mod mark_generic_inference_job_cancelled_by_user;
@@ -0,0 +1,174 @@
use anyhow::anyhow;
use sqlx;
use sqlx::MySqlPool;
use enums::by_table::generic_synthetic_ids::id_category::IdCategory;
use enums::by_table::media_files::media_file_class::MediaFileClass;
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::media_files::MediaFileToken;
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;
pub struct InsertF5TTSMediaFileArgs<'a> {
pub pool: &'a MySqlPool,
pub job: &'a AvailableInferenceJob,
pub maybe_mime_type: Option<&'a str>,
pub file_size_bytes: u64,
pub sha256_checksum: &'a str,
pub maybe_duration_millis: Option<u64>,
pub maybe_audio_codec_name: Option<&'a str>,
pub public_bucket_directory_hash: &'a str,
pub maybe_public_bucket_prefix: Option<&'a str>,
pub maybe_public_bucket_extension: Option<&'a str>,
pub is_on_prem: bool,
pub worker_hostname: &'a str,
pub worker_cluster: &'a str,
}
pub async fn insert_media_file_from_f5_tts(
args: InsertF5TTSMediaFileArgs<'_>
) -> AnyhowResult<(MediaFileToken, u64)>
{
let result_token = MediaFileToken::generate();
let origin_model_type = MediaFileOriginModelType::F5TTS;
let mut maybe_creator_file_synthetic_id : Option<u64> = None;
let mut maybe_creator_category_synthetic_id : Option<u64> = None;
let mut transaction = args.pool.begin().await?;
if let Some(creator_user_token) = args.job.maybe_creator_user_token.as_deref() {
let user_token = UserToken::new_from_str(creator_user_token);
let next_media_file_id = transactional_increment_generic_synthetic_id(
&user_token,
IdCategory::MediaFile,
&mut transaction
).await?;
let next_voice_conversion_id = transactional_increment_generic_synthetic_id(
&user_token,
IdCategory::TtsResult,
&mut transaction
).await?;
maybe_creator_file_synthetic_id = Some(next_media_file_id);
maybe_creator_category_synthetic_id = Some(next_voice_conversion_id);
}
const ORIGIN_CATEGORY : MediaFileOriginCategory = MediaFileOriginCategory::Inference;
const ORIGIN_PRODUCT_CATEGORY : MediaFileOriginProductCategory = MediaFileOriginProductCategory::TextToSpeech;
let record_id = {
let query_result = sqlx::query!(
r#"
INSERT INTO media_files
SET
token = ?,
media_class = ?,
media_type = ?,
origin_category = ?,
origin_product_category = ?,
maybe_origin_model_type = ?,
maybe_origin_model_token = ?,
maybe_text_transcript = ?,
maybe_mime_type = ?,
file_size_bytes = ?,
maybe_duration_millis = ?,
maybe_audio_encoding = ?,
checksum_sha2 = ?,
public_bucket_directory_hash = ?,
maybe_public_bucket_prefix = ?,
maybe_public_bucket_extension = ?,
maybe_creator_user_token = ?,
maybe_creator_anonymous_visitor_token = ?,
creator_ip_address = ?,
creator_set_visibility = ?,
maybe_creator_file_synthetic_id = ?,
maybe_creator_category_synthetic_id = ?,
is_generated_on_prem = ?,
generated_by_worker = ?,
generated_by_cluster = ?
"#,
result_token.as_str(),
MediaFileClass::Audio.to_str(),
MediaFileType::Audio.to_str(), // TODO(bt,2024-04-30): This needs to become "wav" after a frontend migration
ORIGIN_CATEGORY.to_str(),
ORIGIN_PRODUCT_CATEGORY.to_str(),
origin_model_type.to_str(),
args.job.maybe_model_token,
args.job.maybe_raw_inference_text,
args.maybe_mime_type,
args.file_size_bytes,
args.maybe_duration_millis,
args.maybe_audio_codec_name,
args.sha256_checksum,
args.public_bucket_directory_hash,
args.maybe_public_bucket_prefix,
args.maybe_public_bucket_extension,
args.job.maybe_creator_user_token,
args.job.maybe_creator_anonymous_visitor_token,
args.job.creator_ip_address,
args.job.creator_set_visibility.to_str(),
maybe_creator_file_synthetic_id,
maybe_creator_category_synthetic_id,
args.is_on_prem,
args.worker_hostname,
args.worker_cluster
)
.execute(&mut *transaction)
.await;
let record_id = match query_result {
Ok(res) => {
res.last_insert_id()
},
Err(err) => {
// TODO: handle better
//transaction.rollback().await?;
return Err(anyhow!("Mysql error: {:?}", err));
}
};
record_id
};
transaction.commit().await?;
Ok((result_token, record_id))
}
@@ -0,0 +1,174 @@
use anyhow::anyhow;
use sqlx;
use sqlx::MySqlPool;
use enums::by_table::generic_synthetic_ids::id_category::IdCategory;
use enums::by_table::media_files::media_file_class::MediaFileClass;
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::media_files::MediaFileToken;
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;
pub struct InsertSeedVcMediaFileArgs<'a> {
pub pool: &'a MySqlPool,
pub job: &'a AvailableInferenceJob,
pub maybe_mime_type: Option<&'a str>,
pub file_size_bytes: u64,
pub sha256_checksum: &'a str,
pub maybe_duration_millis: Option<u64>,
pub maybe_audio_codec_name: Option<&'a str>,
pub public_bucket_directory_hash: &'a str,
pub maybe_public_bucket_prefix: Option<&'a str>,
pub maybe_public_bucket_extension: Option<&'a str>,
pub is_on_prem: bool,
pub worker_hostname: &'a str,
pub worker_cluster: &'a str,
}
pub async fn insert_media_file_from_seed_vc(
args: InsertSeedVcMediaFileArgs<'_>
) -> AnyhowResult<(MediaFileToken, u64)>
{
let result_token = MediaFileToken::generate();
let origin_model_type = MediaFileOriginModelType::SeedVc;
let mut maybe_creator_file_synthetic_id : Option<u64> = None;
let mut maybe_creator_category_synthetic_id : Option<u64> = None;
let mut transaction = args.pool.begin().await?;
if let Some(creator_user_token) = args.job.maybe_creator_user_token.as_deref() {
let user_token = UserToken::new_from_str(creator_user_token);
let next_media_file_id = transactional_increment_generic_synthetic_id(
&user_token,
IdCategory::MediaFile,
&mut transaction
).await?;
let next_voice_conversion_id = transactional_increment_generic_synthetic_id(
&user_token,
IdCategory::VoiceConversionResult,
&mut transaction
).await?;
maybe_creator_file_synthetic_id = Some(next_media_file_id);
maybe_creator_category_synthetic_id = Some(next_voice_conversion_id);
}
const ORIGIN_CATEGORY : MediaFileOriginCategory = MediaFileOriginCategory::Inference;
const ORIGIN_PRODUCT_CATEGORY : MediaFileOriginProductCategory = MediaFileOriginProductCategory::TextToSpeech;
let record_id = {
let query_result = sqlx::query!(
r#"
INSERT INTO media_files
SET
token = ?,
media_class = ?,
media_type = ?,
origin_category = ?,
origin_product_category = ?,
maybe_origin_model_type = ?,
maybe_origin_model_token = ?,
maybe_text_transcript = ?,
maybe_mime_type = ?,
file_size_bytes = ?,
maybe_duration_millis = ?,
maybe_audio_encoding = ?,
checksum_sha2 = ?,
public_bucket_directory_hash = ?,
maybe_public_bucket_prefix = ?,
maybe_public_bucket_extension = ?,
maybe_creator_user_token = ?,
maybe_creator_anonymous_visitor_token = ?,
creator_ip_address = ?,
creator_set_visibility = ?,
maybe_creator_file_synthetic_id = ?,
maybe_creator_category_synthetic_id = ?,
is_generated_on_prem = ?,
generated_by_worker = ?,
generated_by_cluster = ?
"#,
result_token.as_str(),
MediaFileClass::Audio.to_str(),
MediaFileType::Audio.to_str(), // TODO(bt,2024-04-30): This needs to become "wav" after a frontend migration
ORIGIN_CATEGORY.to_str(),
ORIGIN_PRODUCT_CATEGORY.to_str(),
origin_model_type.to_str(),
args.job.maybe_model_token,
args.job.maybe_raw_inference_text,
args.maybe_mime_type,
args.file_size_bytes,
args.maybe_duration_millis,
args.maybe_audio_codec_name,
args.sha256_checksum,
args.public_bucket_directory_hash,
args.maybe_public_bucket_prefix,
args.maybe_public_bucket_extension,
args.job.maybe_creator_user_token,
args.job.maybe_creator_anonymous_visitor_token,
args.job.creator_ip_address,
args.job.creator_set_visibility.to_str(),
maybe_creator_file_synthetic_id,
maybe_creator_category_synthetic_id,
args.is_on_prem,
args.worker_hostname,
args.worker_cluster
)
.execute(&mut *transaction)
.await;
let record_id = match query_result {
Ok(res) => {
res.last_insert_id()
},
Err(err) => {
// TODO: handle better
//transaction.rollback().await?;
return Err(anyhow!("Mysql error: {:?}", err));
}
};
record_id
};
transaction.commit().await?;
Ok((result_token, record_id))
}
@@ -0,0 +1,178 @@
use anyhow::anyhow;
use sqlx;
use sqlx::MySqlPool;
use enums::by_table::generic_synthetic_ids::id_category::IdCategory;
use enums::by_table::media_files::media_file_class::MediaFileClass;
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::media_files::MediaFileToken;
use tokens::tokens::prompts::PromptToken;
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;
pub struct InsertArgs<'a> {
pub pool: &'a MySqlPool,
pub job: &'a AvailableInferenceJob,
pub maybe_mime_type: Option<&'a str>,
pub file_size_bytes: u64,
pub sha256_checksum: &'a str,
// TODO: Media duration.
//pub duration_millis: u64,
pub maybe_title: Option<&'a str>,
pub maybe_style_transfer_source_media_file_token: Option<&'a MediaFileToken>,
pub maybe_scene_source_media_file_token: Option<&'a MediaFileToken>,
pub maybe_prompt_token: Option<&'a PromptToken>,
pub public_bucket_directory_hash: &'a str,
pub maybe_public_bucket_prefix: Option<&'a str>,
pub maybe_public_bucket_extension: Option<&'a str>,
pub is_on_prem: bool,
pub worker_hostname: &'a str,
pub worker_cluster: &'a str,
}
pub async fn insert_media_file_from_studio_gen2(args: InsertArgs<'_>) -> AnyhowResult<(MediaFileToken, u64)>
{
let result_token = MediaFileToken::generate();
let mut maybe_creator_file_synthetic_id : Option<u64> = None;
let mut maybe_creator_category_synthetic_id : Option<u64> = None;
let mut transaction = args.pool.begin().await?;
if let Some(creator_user_token) = args.job.maybe_creator_user_token.as_deref() {
let user_token = UserToken::new_from_str(creator_user_token);
let next_media_file_id = transactional_increment_generic_synthetic_id(
&user_token,
IdCategory::MediaFile,
&mut transaction
).await?;
let next_comfy_ui_id = transactional_increment_generic_synthetic_id(
&user_token,
IdCategory::WorkflowResult,
&mut transaction
).await?;
maybe_creator_file_synthetic_id = Some(next_media_file_id);
maybe_creator_category_synthetic_id = Some(next_comfy_ui_id);
}
const ORIGIN_CATEGORY : MediaFileOriginCategory = MediaFileOriginCategory::Inference;
const PRODUCT : MediaFileOriginProductCategory = MediaFileOriginProductCategory::StorytellerStudio;
const MODEL_TYPE : MediaFileOriginModelType = MediaFileOriginModelType::StorytellerStudio;
let record_id = {
let query_result = sqlx::query!(
r#"
INSERT INTO media_files
SET
token = ?,
media_class = ?,
media_type = ?,
origin_category = ?,
origin_product_category = ?,
maybe_origin_model_type = ?,
maybe_mime_type = ?,
file_size_bytes = ?,
checksum_sha2 = ?,
maybe_style_transfer_source_media_file_token = ?,
maybe_scene_source_media_file_token = ?,
maybe_title = ?,
maybe_prompt_token = ?,
public_bucket_directory_hash = ?,
maybe_public_bucket_prefix = ?,
maybe_public_bucket_extension = ?,
maybe_creator_user_token = ?,
maybe_creator_anonymous_visitor_token = ?,
creator_ip_address = ?,
creator_set_visibility = ?,
maybe_creator_file_synthetic_id = ?,
maybe_creator_category_synthetic_id = ?,
is_generated_on_prem = ?,
generated_by_worker = ?,
generated_by_cluster = ?
"#,
result_token.as_str(),
MediaFileClass::Video.to_str(),
MediaFileType::Video.to_str(), // TODO(bt,2024-04-30): This needs to become "mp4" after a frontend migration
ORIGIN_CATEGORY.to_str(),
PRODUCT.to_str(),
MODEL_TYPE.to_str(),
args.maybe_mime_type,
args.file_size_bytes,
args.sha256_checksum,
args.maybe_style_transfer_source_media_file_token.map(|t| t.as_str()),
args.maybe_scene_source_media_file_token.map(|t| t.as_str()),
args.maybe_title,
args.maybe_prompt_token.map(|e| e.as_str()),
args.public_bucket_directory_hash,
args.maybe_public_bucket_prefix,
args.maybe_public_bucket_extension,
args.job.maybe_creator_user_token,
args.job.maybe_creator_anonymous_visitor_token,
args.job.creator_ip_address,
args.job.creator_set_visibility.to_str(),
maybe_creator_file_synthetic_id,
maybe_creator_category_synthetic_id,
args.is_on_prem,
args.worker_hostname,
args.worker_cluster
)
.execute(&mut *transaction)
.await;
let record_id = match query_result {
Ok(res) => {
res.last_insert_id()
},
Err(err) => {
// TODO: handle better
//transaction.rollback().await?;
return Err(anyhow!("Mysql error: {:?}", err));
}
};
record_id
};
transaction.commit().await?;
Ok((result_token, record_id))
}
@@ -1,9 +1,12 @@
pub mod insert_media_file_from_cli_tool;
pub mod insert_media_file_from_comfy_ui;
pub mod insert_media_file_from_f5_tts;
pub mod insert_media_file_from_face_animation;
pub mod insert_media_file_from_file_upload;
pub mod insert_media_file_from_mocapnet;
pub mod insert_media_file_from_rerender;
pub mod insert_media_file_from_seed_vc;
pub mod insert_media_file_from_studio_gen2;
pub mod insert_media_file_from_tacotron2;
pub mod insert_media_file_from_voice_conversion;
pub mod insert_media_file_from_zero_shot_tts;
pub mod insert_media_file_from_zero_shot_tts;
@@ -24,6 +24,7 @@ pub mod model_weights;
pub mod prompts;
pub mod public_event_feed;
pub mod stats;
pub mod tags;
pub mod trending_model_analytics;
pub mod tts;
pub mod twitch;
@@ -27,6 +27,10 @@ pub struct ModelWeightForElasticsearchRecord {
pub title: String,
// NB: These language tags are built into the `model_weights` table.
pub maybe_ietf_language_tag: Option<String>,
pub maybe_ietf_primary_language_subtag: Option<String>,
// Cover images
pub maybe_cover_image_media_file_token: Option<MediaFileToken>,
pub maybe_cover_image_public_bucket_hash: Option<String>,
@@ -49,11 +53,15 @@ pub struct ModelWeightForElasticsearchRecord {
pub cached_usage_count: u64,
// TTS extensions
#[deprecated(note="use the fields built into the model_weights table rather than in the join table")]
pub maybe_tts_ietf_language_tag: Option<String>,
#[deprecated(note="use the fields built into the model_weights table rather than in the join table")]
pub maybe_tts_ietf_primary_language_subtag: Option<String>,
// VC extensions
#[deprecated(note="use the fields built into the model_weights table rather than in the join table")]
pub maybe_voice_conversion_ietf_language_tag: Option<String>,
#[deprecated(note="use the fields built into the model_weights table rather than in the join table")]
pub maybe_voice_conversion_ietf_primary_language_subtag: Option<String>,
pub created_at: DateTime<Utc>,
@@ -98,6 +106,8 @@ pub async fn batch_get_model_weights_for_elastic_search_backfill<'e, 'c, E>(
title: model.title,
weights_type: model.weights_type,
weights_category: model.weights_category,
maybe_ietf_language_tag: model.maybe_ietf_language_tag,
maybe_ietf_primary_language_subtag: model.maybe_ietf_primary_language_subtag,
maybe_cover_image_media_file_token: model.maybe_cover_image_media_file_token,
maybe_cover_image_public_bucket_hash: model.maybe_cover_image_public_bucket_hash,
maybe_cover_image_public_bucket_prefix: model.maybe_cover_image_public_bucket_prefix,
@@ -149,6 +159,9 @@ SELECT
w.title,
w.maybe_ietf_language_tag,
w.maybe_ietf_primary_language_subtag,
cover_image.token as maybe_cover_image_media_file_token,
cover_image.public_bucket_directory_hash as maybe_cover_image_public_bucket_hash,
cover_image.maybe_public_bucket_prefix as maybe_cover_image_public_bucket_prefix,
@@ -232,6 +245,10 @@ struct RawRecord {
pub title: String,
// NB: These language tags are built into the `model_weights` table.
pub maybe_ietf_language_tag: Option<String>,
pub maybe_ietf_primary_language_subtag: Option<String>,
// Cover images
pub maybe_cover_image_media_file_token: Option<MediaFileToken>,
pub maybe_cover_image_public_bucket_hash: Option<String>,
@@ -254,11 +271,15 @@ struct RawRecord {
pub cached_usage_count: u64,
// TTS extensions
#[deprecated(note="use the fields built into the model_weights table rather than in the join table")]
pub maybe_tts_ietf_language_tag: Option<String>,
#[deprecated(note="use the fields built into the model_weights table rather than in the join table")]
pub maybe_tts_ietf_primary_language_subtag: Option<String>,
// Voice conversion extensions
#[deprecated(note="use the fields built into the model_weights table rather than in the join table")]
pub maybe_vc_ietf_language_tag: Option<String>,
#[deprecated(note="use the fields built into the model_weights table rather than in the join table")]
pub maybe_vc_ietf_primary_language_subtag: Option<String>,
pub created_at: DateTime<Utc>,
@@ -298,6 +319,9 @@ impl FromRow<'_, MySqlRow> for RawRecord {
title: row.try_get("title")?,
maybe_ietf_language_tag: row.try_get("maybe_ietf_language_tag")?,
maybe_ietf_primary_language_subtag: row.try_get("maybe_ietf_primary_language_subtag")?,
maybe_cover_image_media_file_token: maybe_cover_image_media_file_token.map(|token| MediaFileToken::new(token)),
maybe_cover_image_public_bucket_hash: row.try_get("maybe_cover_image_public_bucket_hash")?,
maybe_cover_image_public_bucket_prefix: row.try_get("maybe_cover_image_public_bucket_prefix")?,
@@ -1,19 +1,22 @@
use anyhow::anyhow;
use sqlx::{MySql, MySqlPool, QueryBuilder};
use sqlx::{MySql, QueryBuilder};
use crate::utils::transactor::Transactor;
use enums::common::visibility::Visibility;
use errors::AnyhowResult;
use tokens::tokens::media_files::MediaFileToken;
use tokens::tokens::model_weights::ModelWeightToken;
pub struct UpdateWeightArgs<'a> {
pub struct UpdateWeightArgs<'a, 't> {
pub weight_token: &'a ModelWeightToken,
pub title: Option<&'a str>,
pub cover_image: Option<CoverImageOption<'a>>,
pub maybe_description_markdown: Option<&'a str>,
pub maybe_description_rendered_html: Option<&'a str>,
pub creator_set_visibility: Option<&'a Visibility>,
pub mysql_pool: &'a MySqlPool,
pub ietf_language_tag: Option<&'a str>,
pub ietf_primary_language_subtag: Option<&'a str>,
pub transactor: Transactor<'a, 't>,
}
pub enum CoverImageOption<'a> {
@@ -21,7 +24,7 @@ pub enum CoverImageOption<'a> {
SetCoverImage(&'a MediaFileToken),
}
pub async fn update_weights(args: UpdateWeightArgs<'_>) -> AnyhowResult<()> {
pub async fn update_weights(args: UpdateWeightArgs<'_, '_>) -> AnyhowResult<()> {
if args.title.is_none()
&& args.maybe_description_markdown.is_none()
&& args.maybe_description_rendered_html.is_none()
@@ -41,19 +44,19 @@ SET
let mut separated = query_builder.separated(", ");
if let Some(title) = args.title {
separated.push("title = ");
separated.push(" title = ");
separated.push_bind_unseparated(title);
}
if let Some(maybe_description_markdown) = args.maybe_description_markdown {
separated.push("maybe_description_markdown = ");
separated.push(" maybe_description_markdown = ");
separated.push_bind_unseparated(maybe_description_markdown);
}
if let Some(maybe_description_rendered_html) = args.maybe_description_rendered_html {
separated.push("maybe_description_rendered_html = ");
separated.push(" maybe_description_rendered_html = ");
separated.push_bind_unseparated(maybe_description_rendered_html);
}
if let Some(creator_set_visibility) = args.creator_set_visibility {
separated.push("creator_set_visibility = ");
separated.push(" creator_set_visibility = ");
separated.push_bind_unseparated(creator_set_visibility.to_str());
}
if let Some(cover_image_option) = args.cover_image {
@@ -62,11 +65,20 @@ SET
separated.push(" maybe_cover_image_media_file_token = NULL ");
}
CoverImageOption::SetCoverImage(media_file_token) => {
separated.push("maybe_cover_image_media_file_token = ");
separated.push(" maybe_cover_image_media_file_token = ");
separated.push_bind_unseparated(media_file_token.as_str());
}
}
}
if let Some(ietf_language_tag) = args.ietf_language_tag {
if let Some(ietf_primary_language_subtag) = args.ietf_primary_language_subtag {
separated.push(" maybe_ietf_language_tag = ");
separated.push_bind_unseparated(ietf_language_tag);
separated.push(" maybe_ietf_primary_language_subtag = ");
separated.push_bind_unseparated(ietf_primary_language_subtag);
}
}
separated.push("version = version + 1");
@@ -74,16 +86,10 @@ SET
separated.push_bind_unseparated(args.weight_token.as_str());
separated.push_unseparated(" LIMIT 1");
let transaction = args.mysql_pool.begin().await?;
let query_result = query_builder.build().execute(args.mysql_pool).await;
transaction.commit().await?;
let query = query_builder.build();
let _r = args.transactor.execute(query).await?;
match query_result {
Ok(_) => Ok(()),
Err(err) => {
Err(anyhow!("weights update error: {:?}", err))
}
}
Ok(())
}
#[cfg(test)]
@@ -27,6 +27,9 @@ pub struct RetrievedModelWeight {
pub maybe_description_markdown: Option<String>,
pub maybe_description_rendered_html: Option<String>,
pub maybe_ietf_language_tag: Option<String>,
pub maybe_ietf_primary_language_subtag: Option<String>,
pub creator_user_token: UserToken,
pub creator_username: String,
pub creator_display_name: String,
@@ -40,6 +43,7 @@ pub struct RetrievedModelWeight {
pub original_filename: Option<String>,
pub file_size_bytes: i64,
pub file_checksum_sha2: String,
pub public_bucket_hash: String,
pub maybe_public_bucket_prefix: Option<String>,
pub maybe_public_bucket_extension: Option<String>,
@@ -114,6 +118,8 @@ pub async fn get_weight_by_token_with_transactor(
weights_category: record.weights_category,
maybe_description_markdown: record.maybe_description_markdown,
maybe_description_rendered_html: record.maybe_description_rendered_html,
maybe_ietf_language_tag: record.maybe_ietf_language_tag,
maybe_ietf_primary_language_subtag: record.maybe_ietf_primary_language_subtag,
creator_user_token: record.creator_user_token,
creator_username: record.creator_username,
creator_display_name: record.creator_display_name,
@@ -161,6 +167,9 @@ async fn select_include_deleted(
wt.maybe_description_markdown,
wt.maybe_description_rendered_html,
wt.maybe_ietf_language_tag,
wt.maybe_ietf_primary_language_subtag,
wt.creator_user_token as `creator_user_token: tokens::tokens::users::UserToken`,
users.username as creator_username,
users.display_name as creator_display_name,
@@ -245,6 +254,10 @@ async fn select_without_deleted(
wt.weights_category as `weights_category: enums::by_table::model_weights::weights_category::WeightsCategory`,
wt.maybe_description_markdown,
wt.maybe_description_rendered_html,
wt.maybe_ietf_language_tag,
wt.maybe_ietf_primary_language_subtag,
wt.creator_ip_address,
wt.creator_set_visibility as `creator_set_visibility: enums::common::visibility::Visibility`,
@@ -318,44 +331,47 @@ async fn select_without_deleted(
// RawWeight is the struct that is returned from the database in raw form.
#[derive(Serialize)]
pub struct RawWeight {
pub token: ModelWeightToken,
pub title: String,
pub weights_type: WeightsType,
pub weights_category: WeightsCategory,
pub maybe_description_markdown: Option<String>,
pub maybe_description_rendered_html: Option<String>,
struct RawWeight {
token: ModelWeightToken,
title: String,
weights_type: WeightsType,
weights_category: WeightsCategory,
maybe_description_markdown: Option<String>,
maybe_description_rendered_html: Option<String>,
pub creator_user_token: UserToken,
pub creator_username: String,
pub creator_display_name: String,
pub creator_gravatar_hash: String,
maybe_ietf_language_tag: Option<String>,
maybe_ietf_primary_language_subtag: Option<String>,
pub creator_ip_address: String,
pub creator_set_visibility: Visibility,
pub maybe_last_update_user_token: Option<UserToken>,
pub original_download_url: Option<String>,
pub original_filename: Option<String>,
pub file_size_bytes: i64,
pub file_checksum_sha2: String,
pub public_bucket_hash: String,
pub maybe_public_bucket_prefix: Option<String>,
pub maybe_public_bucket_extension: Option<String>,
creator_user_token: UserToken,
creator_username: String,
creator_display_name: String,
creator_gravatar_hash: String,
pub maybe_cover_image_public_bucket_hash: Option<String>,
pub maybe_cover_image_public_bucket_prefix: Option<String>,
pub maybe_cover_image_public_bucket_extension: Option<String>,
creator_ip_address: String,
creator_set_visibility: Visibility,
maybe_last_update_user_token: Option<UserToken>,
original_download_url: Option<String>,
original_filename: Option<String>,
file_size_bytes: i64,
file_checksum_sha2: String,
public_bucket_hash: String,
maybe_public_bucket_prefix: Option<String>,
maybe_public_bucket_extension: Option<String>,
pub maybe_ratings_positive_count: Option<u32>,
pub maybe_ratings_negative_count: Option<u32>,
pub maybe_bookmark_count: Option<u32>,
pub cached_usage_count: u64,
maybe_cover_image_public_bucket_hash: Option<String>,
maybe_cover_image_public_bucket_prefix: Option<String>,
maybe_cover_image_public_bucket_extension: Option<String>,
pub is_featured: i64,
maybe_ratings_positive_count: Option<u32>,
maybe_ratings_negative_count: Option<u32>,
maybe_bookmark_count: Option<u32>,
cached_usage_count: u64,
pub version: i32,
pub created_at: DateTime<Utc>,
pub updated_at: DateTime<Utc>,
pub user_deleted_at: Option<DateTime<Utc>>,
pub mod_deleted_at: Option<DateTime<Utc>>,
is_featured: i64,
version: i32,
created_at: DateTime<Utc>,
updated_at: DateTime<Utc>,
user_deleted_at: Option<DateTime<Utc>>,
mod_deleted_at: Option<DateTime<Utc>>,
}
@@ -40,6 +40,9 @@ pub struct FeaturedWeight {
//pub maybe_description_markdown: String,
//pub maybe_description_rendered_html: String,
pub maybe_ietf_language_tag: Option<String>,
pub maybe_ietf_primary_language_subtag: Option<String>,
pub maybe_creator_user_token: Option<UserToken>,
pub maybe_creator_username: Option<String>,
pub maybe_creator_display_name: Option<String>,
@@ -155,6 +158,8 @@ fn select_result_fields() -> String {
mw.title,
mw.weights_type,
mw.weights_category,
mw.maybe_ietf_language_tag,
mw.maybe_ietf_primary_language_subtag,
u.token as maybe_creator_user_token,
u.username as maybe_creator_username,
u.display_name as maybe_creator_display_name,
@@ -329,6 +334,9 @@ async fn map_to_weights(dataset:Vec<RawWeightJoinUser>) -> Vec<FeaturedWeight> {
weights_type: weight.weights_type,
weights_category: weight.weights_category,
maybe_ietf_language_tag: weight.maybe_ietf_language_tag,
maybe_ietf_primary_language_subtag: weight.maybe_ietf_primary_language_subtag,
maybe_creator_user_token: weight.maybe_creator_user_token,
maybe_creator_username: weight.maybe_creator_username,
maybe_creator_display_name: weight.maybe_creator_display_name,
@@ -375,6 +383,9 @@ struct RawWeightJoinUser {
title: String,
maybe_ietf_language_tag: Option<String>,
maybe_ietf_primary_language_subtag: Option<String>,
// TODO(bt,2023-12-24): These aren't really appropriate for a list endpoint.
// Hopefully we don't break the frontend by omitting these.
//pub description_markdown: String,
@@ -426,6 +437,8 @@ impl FromRow<'_, MySqlRow> for RawWeightJoinUser {
weights_type: row.try_get("weights_type")?,
weights_category: row.try_get("weights_category")?,
title: row.try_get("title")?,
maybe_ietf_language_tag: row.try_get("maybe_ietf_language_tag")?,
maybe_ietf_primary_language_subtag: row.try_get("maybe_ietf_primary_language_subtag")?,
// TODO(bt,2023-12-24): These aren't really appropriate for a list endpoint.
// Hopefully we don't break the frontend by omitting these.
//maybe_description_markdown: row.try_get("maybe_description_markdown")?,
@@ -26,6 +26,10 @@ pub struct ModelWeightForElasticsearchRecord {
pub title: String,
// NB: These language tags are built into the `model_weights` table.
pub maybe_ietf_language_tag: Option<String>,
pub maybe_ietf_primary_language_subtag: Option<String>,
// Cover images
pub maybe_cover_image_media_file_token: Option<MediaFileToken>,
pub maybe_cover_image_public_bucket_hash: Option<String>,
@@ -48,11 +52,15 @@ pub struct ModelWeightForElasticsearchRecord {
pub cached_usage_count: u64,
// TTS extensions
#[deprecated(note="use the fields built into the model_weights table rather than in the join table")]
pub maybe_tts_ietf_language_tag: Option<String>,
#[deprecated(note="use the fields built into the model_weights table rather than in the join table")]
pub maybe_tts_ietf_primary_language_subtag: Option<String>,
// VC extensions
#[deprecated(note="use the fields built into the model_weights table rather than in the join table")]
pub maybe_voice_conversion_ietf_language_tag: Option<String>,
#[deprecated(note="use the fields built into the model_weights table rather than in the join table")]
pub maybe_voice_conversion_ietf_primary_language_subtag: Option<String>,
pub created_at: DateTime<Utc>,
@@ -92,6 +100,8 @@ pub async fn list_model_weights_for_elastic_search_backfill_using_cursor(
title: model.title,
weights_type: model.weights_type,
weights_category: model.weights_category,
maybe_ietf_language_tag: model.maybe_ietf_language_tag,
maybe_ietf_primary_language_subtag: model.maybe_ietf_primary_language_subtag,
maybe_cover_image_media_file_token: model.maybe_cover_image_media_file_token,
maybe_cover_image_public_bucket_hash: model.maybe_cover_image_public_bucket_hash,
maybe_cover_image_public_bucket_prefix: model.maybe_cover_image_public_bucket_prefix,
@@ -139,6 +149,9 @@ SELECT
w.title,
w.maybe_ietf_language_tag,
w.maybe_ietf_primary_language_subtag,
cover_image.token as `maybe_cover_image_media_file_token: tokens::tokens::media_files::MediaFileToken`,
cover_image.public_bucket_directory_hash as maybe_cover_image_public_bucket_hash,
cover_image.maybe_public_bucket_prefix as maybe_cover_image_public_bucket_prefix,
@@ -212,6 +225,10 @@ struct RawRecord {
pub title: String,
// NB: These language tags are built into the `model_weights` table.
pub maybe_ietf_language_tag: Option<String>,
pub maybe_ietf_primary_language_subtag: Option<String>,
// Cover images
pub maybe_cover_image_media_file_token: Option<MediaFileToken>,
pub maybe_cover_image_public_bucket_hash: Option<String>,
@@ -234,11 +251,15 @@ struct RawRecord {
pub cached_usage_count: u64,
// TTS extensions
#[deprecated(note="use the fields built into the model_weights table rather than in the join table")]
pub maybe_tts_ietf_language_tag: Option<String>,
#[deprecated(note="use the fields built into the model_weights table rather than in the join table")]
pub maybe_tts_ietf_primary_language_subtag: Option<String>,
// Voice conversion extensions
#[deprecated(note="use the fields built into the model_weights table rather than in the join table")]
pub maybe_vc_ietf_language_tag: Option<String>,
#[deprecated(note="use the fields built into the model_weights table rather than in the join table")]
pub maybe_vc_ietf_primary_language_subtag: Option<String>,
pub created_at: DateTime<Utc>,
@@ -20,6 +20,9 @@ pub struct WeightsByTokensRecord {
pub title: String,
pub maybe_ietf_language_tag: Option<String>,
pub maybe_ietf_primary_language_subtag: Option<String>,
pub creator_user_token: UserToken,
pub creator_username: String,
pub creator_display_name: String,
@@ -72,6 +75,8 @@ async fn get_raw_weights_by_tokens(
mw.title,
mw.weights_type,
mw.weights_category,
mw.maybe_ietf_language_tag,
mw.maybe_ietf_primary_language_subtag,
users.token as creator_user_token,
users.username as creator_username,
users.display_name as creator_display_name,
@@ -111,6 +116,8 @@ async fn get_raw_weights_by_tokens(
mw.title,
mw.weights_type,
mw.weights_category,
mw.maybe_ietf_language_tag,
mw.maybe_ietf_primary_language_subtag,
users.token as creator_user_token,
users.username as creator_username,
users.display_name as creator_display_name,
@@ -177,6 +184,9 @@ fn map_to_weights(dataset:Vec<RawWeightJoinUser>) -> Vec<WeightsByTokensRecord>
weights_type: weight.weights_type,
weights_category: weight.weights_category,
maybe_ietf_language_tag: weight.maybe_ietf_language_tag,
maybe_ietf_primary_language_subtag: weight.maybe_ietf_primary_language_subtag,
creator_user_token: weight.creator_user_token,
creator_username: weight.creator_username,
creator_display_name: weight.creator_display_name,
@@ -205,37 +215,40 @@ fn map_to_weights(dataset:Vec<RawWeightJoinUser>) -> Vec<WeightsByTokensRecord>
weights
}
#[derive(Serialize)]
pub struct RawWeightJoinUser {
pub token: ModelWeightToken,
#[derive(Serialize)]
struct RawWeightJoinUser {
token: ModelWeightToken,
pub weights_type: WeightsType,
pub weights_category: WeightsCategory,
pub title: String,
pub creator_user_token: UserToken,
pub creator_username: String,
pub creator_display_name: String,
pub creator_email_gravatar_hash: String,
weights_type: WeightsType,
weights_category: WeightsCategory,
pub public_bucket_hash: String,
pub maybe_public_bucket_prefix: Option<String>,
pub maybe_public_bucket_extension: Option<String>,
title: String,
pub maybe_cover_image_public_bucket_hash: Option<String>,
pub maybe_cover_image_public_bucket_prefix: Option<String>,
pub maybe_cover_image_public_bucket_extension: Option<String>,
maybe_ietf_language_tag: Option<String>,
maybe_ietf_primary_language_subtag: Option<String>,
pub maybe_ratings_positive_count: Option<u32>,
pub maybe_ratings_negative_count: Option<u32>,
pub maybe_bookmark_count: Option<u32>,
pub cached_usage_count: u64,
creator_user_token: UserToken,
creator_username: String,
creator_display_name: String,
creator_email_gravatar_hash: String,
pub created_at: DateTime<Utc>,
pub updated_at: DateTime<Utc>,
pub user_deleted_at: Option<DateTime<Utc>>,
pub mod_deleted_at: Option<DateTime<Utc>>,
public_bucket_hash: String,
maybe_public_bucket_prefix: Option<String>,
maybe_public_bucket_extension: Option<String>,
maybe_cover_image_public_bucket_hash: Option<String>,
maybe_cover_image_public_bucket_prefix: Option<String>,
maybe_cover_image_public_bucket_extension: Option<String>,
maybe_ratings_positive_count: Option<u32>,
maybe_ratings_negative_count: Option<u32>,
maybe_bookmark_count: Option<u32>,
cached_usage_count: u64,
created_at: DateTime<Utc>,
updated_at: DateTime<Utc>,
user_deleted_at: Option<DateTime<Utc>>,
mod_deleted_at: Option<DateTime<Utc>>,
}
// NB(bt,2023-12-05): There's an issue with type hinting in the `as` clauses with QueryBuilder (or
@@ -257,6 +270,8 @@ impl FromRow<'_, MySqlRow> for RawWeightJoinUser {
weights_type: WeightsType::try_from_mysql_row(row, "weights_type")?,
weights_category: WeightsCategory::try_from_mysql_row(row, "weights_category")?,
title: row.try_get("title")?,
maybe_ietf_language_tag: row.try_get("maybe_ietf_language_tag")?,
maybe_ietf_primary_language_subtag: row.try_get("maybe_ietf_primary_language_subtag")?,
creator_user_token: UserToken::new_from_str(row.try_get("creator_user_token")?),
creator_username: row.try_get("creator_username")?,
creator_display_name: row.try_get("creator_display_name")?,
@@ -31,6 +31,9 @@ pub struct WeightsJoinUserRecord {
pub title: String,
pub maybe_ietf_language_tag: Option<String>,
pub maybe_ietf_primary_language_subtag: Option<String>,
// TODO(bt,2023-12-24): These aren't really appropriate for a list endpoint.
// Hopefully we don't break the frontend by omitting these.
//pub maybe_description_markdown: String,
@@ -144,6 +147,8 @@ fn select_result_fields() -> String {
mw.title,
mw.weights_type,
mw.weights_category,
mw.maybe_ietf_language_tag,
mw.maybe_ietf_primary_language_subtag,
u.token as creator_user_token,
u.username as creator_username,
u.display_name as creator_display_name,
@@ -255,6 +260,10 @@ async fn map_to_weights(dataset:Vec<RawWeightJoinUser>) -> Vec<WeightsJoinUserRe
title: weight.title,
weights_type: weight.weights_type,
weights_category: weight.weights_category,
maybe_ietf_language_tag: weight.maybe_ietf_language_tag,
maybe_ietf_primary_language_subtag: weight.maybe_ietf_primary_language_subtag,
//maybe_description_markdown: weight.maybe_description_markdown,
//maybe_description_rendered_html: weight.maybe_description_rendered_html,
@@ -296,55 +305,58 @@ async fn map_to_weights(dataset:Vec<RawWeightJoinUser>) -> Vec<WeightsJoinUserRe
}
pub struct RawWeightJoinUser {
pub token: ModelWeightToken,
struct RawWeightJoinUser {
token: ModelWeightToken,
pub weights_type: WeightsType,
pub weights_category: WeightsCategory,
pub title: String,
weights_type: WeightsType,
weights_category: WeightsCategory,
// TODO(bt,2023-12-24): These aren't really appropriate for a list endpoint.
// Hopefully we don't break the frontend by omitting these.
//pub description_markdown: String,
//pub description_rendered_html: String,
pub creator_user_token: UserToken,
pub creator_ip_address: String,
pub creator_set_visibility: Visibility,
pub maybe_last_update_user_token: Option<UserToken>,
pub original_download_url: Option<String>,
pub original_filename: Option<String>,
pub file_size_bytes: i32,
pub file_checksum_sha2: String,
pub public_bucket_hash: String,
pub maybe_public_bucket_prefix: Option<String>,
pub maybe_public_bucket_extension: Option<String>,
title: String,
pub maybe_cover_image_public_bucket_hash: Option<String>,
pub maybe_cover_image_public_bucket_prefix: Option<String>,
pub maybe_cover_image_public_bucket_extension: Option<String>,
maybe_ietf_language_tag: Option<String>,
maybe_ietf_primary_language_subtag: Option<String>,
pub maybe_ratings_positive_count: Option<u32>,
pub maybe_ratings_negative_count: Option<u32>,
pub maybe_bookmark_count: Option<u32>,
pub cached_usage_count: u64,
// TODO(bt,2023-12-24): These aren't really appropriate for a list endpoint.
// Hopefully we don't break the frontend by omitting these.
//pub description_markdown: String,
//pub description_rendered_html: String,
pub version: i32,
pub created_at: DateTime<Utc>,
pub updated_at: DateTime<Utc>,
pub user_deleted_at: Option<DateTime<Utc>>,
pub mod_deleted_at: Option<DateTime<Utc>>,
pub creator_username: String,
pub creator_display_name: String,
pub creator_email_gravatar_hash: String,
creator_user_token: UserToken,
creator_ip_address: String,
creator_set_visibility: Visibility,
maybe_last_update_user_token: Option<UserToken>,
original_download_url: Option<String>,
original_filename: Option<String>,
file_size_bytes: i32,
file_checksum_sha2: String,
public_bucket_hash: String,
maybe_public_bucket_prefix: Option<String>,
maybe_public_bucket_extension: Option<String>,
maybe_cover_image_public_bucket_hash: Option<String>,
maybe_cover_image_public_bucket_prefix: Option<String>,
maybe_cover_image_public_bucket_extension: Option<String>,
maybe_ratings_positive_count: Option<u32>,
maybe_ratings_negative_count: Option<u32>,
maybe_bookmark_count: Option<u32>,
cached_usage_count: u64,
version: i32,
created_at: DateTime<Utc>,
updated_at: DateTime<Utc>,
user_deleted_at: Option<DateTime<Utc>>,
mod_deleted_at: Option<DateTime<Utc>>,
creator_username: String,
creator_display_name: String,
creator_email_gravatar_hash: String,
}
impl FromRow<'_, MySqlRow> for RawWeightJoinUser {
@@ -354,6 +366,8 @@ impl FromRow<'_, MySqlRow> for RawWeightJoinUser {
weights_type: row.try_get("weights_type")?,
weights_category: row.try_get("weights_category")?,
title: row.try_get("title")?,
maybe_ietf_language_tag: row.try_get("maybe_ietf_language_tag")?,
maybe_ietf_primary_language_subtag: row.try_get("maybe_ietf_primary_language_subtag")?,
// TODO(bt,2023-12-24): These aren't really appropriate for a list endpoint.
// Hopefully we don't break the frontend by omitting these.
//maybe_description_markdown: row.try_get("maybe_description_markdown")?,
@@ -37,6 +37,9 @@ pub struct WeightJoinUser {
pub title: String,
pub maybe_ietf_language_tag: Option<String>,
pub maybe_ietf_primary_language_subtag: Option<String>,
pub maybe_description_markdown: Option<String>,
pub maybe_description_rendered_html: Option<String>,
@@ -216,6 +219,8 @@ impl ListWeightsQueryBuilder {
record.weights_category.as_str()
).unwrap(),
title: record.title,
maybe_ietf_language_tag: record.maybe_ietf_language_tag,
maybe_ietf_primary_language_subtag: record.maybe_ietf_primary_language_subtag,
maybe_description_markdown: record.maybe_description_markdown,
maybe_description_rendered_html: record.maybe_description_rendered_html,
creator_user_token: UserToken::new_from_str(&record.creator_user_token),
@@ -271,6 +276,8 @@ impl ListWeightsQueryBuilder {
model_weights.title,
model_weights.maybe_description_markdown,
model_weights.maybe_description_rendered_html,
model_weights.maybe_ietf_language_tag,
model_weights.maybe_ietf_primary_language_subtag,
model_weights.creator_user_token,
model_weights.creator_ip_address,
model_weights.creator_set_visibility,
@@ -417,53 +424,56 @@ impl ListWeightsQueryBuilder {
#[derive(Serialize)]
#[derive(sqlx::FromRow)]
struct RawWeightJoinUser {
pub weight_id: i64,
pub token: ModelWeightToken,
weight_id: i64,
token: ModelWeightToken,
pub weights_type: String,
pub weights_category: String,
weights_type: String,
weights_category: String,
pub title: String,
title: String,
pub maybe_description_markdown: Option<String>,
pub maybe_description_rendered_html: Option<String>,
maybe_description_markdown: Option<String>,
maybe_description_rendered_html: Option<String>,
pub creator_user_token: String,
pub creator_ip_address: String,
pub creator_set_visibility: String,
maybe_ietf_language_tag: Option<String>,
maybe_ietf_primary_language_subtag: Option<String>,
pub maybe_last_update_user_token: Option<String>,
creator_user_token: String,
creator_ip_address: String,
creator_set_visibility: String,
pub original_download_url: Option<String>,
pub original_filename: Option<String>,
maybe_last_update_user_token: Option<String>,
pub file_size_bytes: i32,
pub file_checksum_sha2: String,
original_download_url: Option<String>,
original_filename: Option<String>,
pub public_bucket_hash: String,
pub maybe_public_bucket_prefix: Option<String>,
pub maybe_public_bucket_extension: Option<String>,
file_size_bytes: i32,
file_checksum_sha2: String,
pub maybe_cover_image_public_bucket_hash: Option<String>,
pub maybe_cover_image_public_bucket_prefix: Option<String>,
pub maybe_cover_image_public_bucket_extension: Option<String>,
public_bucket_hash: String,
maybe_public_bucket_prefix: Option<String>,
maybe_public_bucket_extension: Option<String>,
pub creator_username: String,
pub creator_display_name: String,
pub creator_email_gravatar_hash: String,
maybe_cover_image_public_bucket_hash: Option<String>,
maybe_cover_image_public_bucket_prefix: Option<String>,
maybe_cover_image_public_bucket_extension: Option<String>,
pub maybe_ratings_positive_count: Option<u32>,
pub maybe_ratings_negative_count: Option<u32>,
pub maybe_bookmark_count: Option<u32>,
pub cached_usage_count: u64,
creator_username: String,
creator_display_name: String,
creator_email_gravatar_hash: String,
pub version: i32,
maybe_ratings_positive_count: Option<u32>,
maybe_ratings_negative_count: Option<u32>,
maybe_bookmark_count: Option<u32>,
cached_usage_count: u64,
pub created_at: DateTime<Utc>,
pub updated_at: DateTime<Utc>,
version: i32,
pub user_deleted_at: Option<DateTime<Utc>>,
pub mod_deleted_at: Option<DateTime<Utc>>,
created_at: DateTime<Utc>,
updated_at: DateTime<Utc>,
user_deleted_at: Option<DateTime<Utc>>,
mod_deleted_at: Option<DateTime<Utc>>,
}
#[cfg(test)]
@@ -0,0 +1,27 @@
use crate::errors::database_insert_error::DatabaseInsertError;
use crate::utils::transactor::Transactor;
use tokens::tokens::tags::TagToken;
pub async fn create_tag(
tag: &str,
transactor: Transactor<'_, '_>,
) -> Result<TagToken, DatabaseInsertError>
{
let tag = tag.trim().to_lowercase();
let token = TagToken::generate();
let query = sqlx::query!(
r#"
INSERT INTO tags
SET
token = ?,
tag_value = ?
"#,
token,
tag,
);
let _r = transactor.execute(query).await?;
Ok(token)
}
@@ -0,0 +1,64 @@
use composite_identifiers::by_table::tag_uses::tag_use_entity::TagUseEntity;
use errors::AnyhowResult;
use tokens::tokens::tags::TagToken;
use crate::utils::transactor::Transactor;
pub struct EntityTag {
pub token: TagToken,
pub tag_value: String,
}
pub async fn list_tags_for_entity(
tag_entity: TagUseEntity,
transactor: Transactor<'_, '_>,
) -> AnyhowResult<Vec<EntityTag>> {
let (entity_type, entity_token) = tag_entity.get_composite_keys();
let query = sqlx::query_as!(
RawTag,
r#"
SELECT
t.token as `token: tokens::tokens::tags::TagToken`,
t.tag_value
FROM
tags AS t
JOIN tag_uses AS u
ON t.token = u.tag_token
WHERE
u.entity_type = ?
AND u.entity_token = ?
AND u.maybe_deleted_at IS NULL
ORDER BY t.id DESC
LIMIT 500
"#,
entity_type,
entity_token
);
let results = match transactor {
Transactor::Pool { pool } => {
query.fetch_all(pool).await?
},
Transactor::Connection { connection } => {
query.fetch_all(connection).await?
},
Transactor::Transaction { transaction } => {
query.fetch_all(&mut **transaction).await?
},
};
Ok(results.into_iter()
.map(|record| EntityTag {
token: record.token,
tag_value: record.tag_value,
})
.collect())
}
struct RawTag {
token: TagToken,
tag_value: String,
}
@@ -0,0 +1,4 @@
pub mod create_tag;
pub mod list_tags_for_entity;
pub mod select_matching_tags;
pub mod update_tags_for_entity;
@@ -0,0 +1,78 @@
use crate::utils::transactor::Transactor;
use errors::AnyhowResult;
use sqlx::mysql::MySqlRow;
use sqlx::{FromRow, MySql, QueryBuilder, Row};
use tokens::tokens::tags::TagToken;
pub struct MatchingTag {
pub token: TagToken,
pub tag_value: String,
}
pub async fn select_matching_tags(
tag_values: &[String],
transactor: Transactor<'_, '_>,
) -> AnyhowResult<Vec<MatchingTag>> {
if tag_values.is_empty() {
return Ok(Vec::new()) // This prevents a SQL error when the IN clause is empty.
}
let tag_values = tag_values.iter()
.map(|tag| tag.trim().to_lowercase())
.collect::<Vec<_>>();
let mut query_builder: QueryBuilder<MySql> = QueryBuilder::new(
r#"
SELECT
token,
tag_value
FROM
tags
WHERE
tag_value IN (
"#,
);
let mut separated = query_builder.separated(", ");
for tag_value in tag_values.iter() {
separated.push_bind(tag_value);
}
separated.push_unseparated(") ");
let query = query_builder.build_query_as::<RawTag>();
let results = match transactor {
Transactor::Pool { pool } => {
query.fetch_all(pool).await?
},
Transactor::Connection { connection } => {
query.fetch_all(connection).await?
},
Transactor::Transaction { transaction } => {
query.fetch_all(&mut **transaction).await?
},
};
Ok(results.into_iter()
.map(|record| MatchingTag {
token: record.token,
tag_value: record.tag_value,
})
.collect())
}
struct RawTag {
token: TagToken,
tag_value: String,
}
impl FromRow<'_, MySqlRow> for RawTag {
fn from_row(row: &MySqlRow) -> Result<Self, sqlx::Error> {
Ok(Self {
token: TagToken::new(row.try_get("token")?),
tag_value: row.try_get("tag_value")?,
})
}
}

Some files were not shown because too many files have changed in this diff Show More