mirror of
https://github.com/storytold/artcraft.git
synced 2026-11-04 13:28:06 -05:00
Compare commits
20
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7489986632 | ||
|
|
8ba36676df | ||
|
|
c74ca625ff | ||
|
|
87f35f3e6f | ||
|
|
df0a82cbdb | ||
|
|
c5071d4f2e | ||
|
|
57c77cbbc2 | ||
|
|
98c4fad729 | ||
|
|
f50bfa9ee7 | ||
|
|
d90d446643 | ||
|
|
c4d188a600 | ||
|
|
1d5d63fda1 | ||
|
|
6af5cc7d0b | ||
|
|
a7cd9f3b2f | ||
|
|
d538bc2a68 | ||
|
|
bec8c7ede4 | ||
|
|
0866e63dd4 | ||
|
|
78a5c4ff87 | ||
|
|
d53fad0072 | ||
|
|
2e5e9443a3 |
+223
@@ -0,0 +1,223 @@
|
||||
{
|
||||
"db_name": "MySQL",
|
||||
"query": "\nSELECT\n m.id as `id: u64`,\n m.token as `token: tokens::tokens::media_files::MediaFileToken`,\n\n m.media_class as `media_class: enums::by_table::media_files::media_file_class::MediaFileClass`,\n m.maybe_project_type as `project_type!: enums::by_table::media_files::media_file_project_type::MediaFileProjectType`,\n m.media_type as `media_type: enums::by_table::media_files::media_file_type::MediaFileType`,\n\n m.public_bucket_directory_hash,\n m.maybe_public_bucket_prefix,\n m.maybe_public_bucket_extension,\n\n users.token as `maybe_creator_user_token: tokens::tokens::users::UserToken`,\n users.username as maybe_creator_username,\n users.display_name as maybe_creator_display_name,\n users.email_gravatar_hash as maybe_creator_gravatar_hash,\n\n media_file_cover_image.public_bucket_directory_hash as maybe_file_cover_image_public_bucket_hash,\n media_file_cover_image.maybe_public_bucket_prefix as maybe_file_cover_image_public_bucket_prefix,\n media_file_cover_image.maybe_public_bucket_extension as maybe_file_cover_image_public_bucket_extension,\n\n m.creator_set_visibility as `creator_set_visibility: enums::common::visibility::Visibility`,\n\n m.maybe_title,\n\n m.created_at,\n m.updated_at\n\nFROM media_files AS m FORCE INDEX (fk_maybe_creator_user_token)\nLEFT OUTER JOIN users\n ON m.maybe_creator_user_token = users.token\nLEFT OUTER JOIN media_files as media_file_cover_image\n ON media_file_cover_image.token = m.maybe_cover_image_media_file_token\nWHERE\n m.maybe_creator_user_token = ?\n AND m.media_class = 'project'\n AND m.maybe_project_type IS NOT NULL\n AND m.maybe_project_type = ?\n AND m.user_deleted_at IS NULL\n AND m.mod_deleted_at IS NULL\n AND NOT m.is_intermediate_system_file\n AND m.id > ?\nORDER BY m.id ASC\nLIMIT ?\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "id: u64",
|
||||
"type_info": {
|
||||
"type": "LongLong",
|
||||
"flags": "NOT_NULL | PRIMARY_KEY | AUTO_INCREMENT",
|
||||
"char_set": 63,
|
||||
"max_size": 20
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "token: tokens::tokens::media_files::MediaFileToken",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "NOT_NULL | UNIQUE_KEY | BINARY | NO_DEFAULT_VALUE",
|
||||
"char_set": 224,
|
||||
"max_size": 128
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"name": "media_class: enums::by_table::media_files::media_file_class::MediaFileClass",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "NOT_NULL | MULTIPLE_KEY | BINARY",
|
||||
"char_set": 224,
|
||||
"max_size": 64
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 3,
|
||||
"name": "project_type!: enums::by_table::media_files::media_file_project_type::MediaFileProjectType",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "MULTIPLE_KEY | BINARY",
|
||||
"char_set": 224,
|
||||
"max_size": 128
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 4,
|
||||
"name": "media_type: enums::by_table::media_files::media_file_type::MediaFileType",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "NOT_NULL | MULTIPLE_KEY | BINARY | NO_DEFAULT_VALUE",
|
||||
"char_set": 224,
|
||||
"max_size": 64
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 5,
|
||||
"name": "public_bucket_directory_hash",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "NOT_NULL | BINARY | NO_DEFAULT_VALUE",
|
||||
"char_set": 224,
|
||||
"max_size": 128
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 6,
|
||||
"name": "maybe_public_bucket_prefix",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "BINARY",
|
||||
"char_set": 224,
|
||||
"max_size": 64
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 7,
|
||||
"name": "maybe_public_bucket_extension",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "BINARY",
|
||||
"char_set": 224,
|
||||
"max_size": 64
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 8,
|
||||
"name": "maybe_creator_user_token: tokens::tokens::users::UserToken",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "UNIQUE_KEY | BINARY | NO_DEFAULT_VALUE",
|
||||
"char_set": 224,
|
||||
"max_size": 128
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 9,
|
||||
"name": "maybe_creator_username",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "UNIQUE_KEY | BINARY | NO_DEFAULT_VALUE",
|
||||
"char_set": 224,
|
||||
"max_size": 80
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 10,
|
||||
"name": "maybe_creator_display_name",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "BINARY | NO_DEFAULT_VALUE",
|
||||
"char_set": 224,
|
||||
"max_size": 80
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 11,
|
||||
"name": "maybe_creator_gravatar_hash",
|
||||
"type_info": {
|
||||
"type": "String",
|
||||
"flags": "BINARY | NO_DEFAULT_VALUE",
|
||||
"char_set": 224,
|
||||
"max_size": 128
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 12,
|
||||
"name": "maybe_file_cover_image_public_bucket_hash",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "BINARY | NO_DEFAULT_VALUE",
|
||||
"char_set": 224,
|
||||
"max_size": 128
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 13,
|
||||
"name": "maybe_file_cover_image_public_bucket_prefix",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "BINARY",
|
||||
"char_set": 224,
|
||||
"max_size": 64
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 14,
|
||||
"name": "maybe_file_cover_image_public_bucket_extension",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "BINARY",
|
||||
"char_set": 224,
|
||||
"max_size": 64
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 15,
|
||||
"name": "creator_set_visibility: enums::common::visibility::Visibility",
|
||||
"type_info": {
|
||||
"type": "String",
|
||||
"flags": "NOT_NULL | BINARY | ENUM",
|
||||
"char_set": 224,
|
||||
"max_size": 28
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 16,
|
||||
"name": "maybe_title",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "BINARY",
|
||||
"char_set": 224,
|
||||
"max_size": 1020
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 17,
|
||||
"name": "created_at",
|
||||
"type_info": {
|
||||
"type": "Timestamp",
|
||||
"flags": "NOT_NULL | MULTIPLE_KEY | BINARY | TIMESTAMP",
|
||||
"char_set": 63,
|
||||
"max_size": 19
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 18,
|
||||
"name": "updated_at",
|
||||
"type_info": {
|
||||
"type": "Timestamp",
|
||||
"flags": "NOT_NULL | MULTIPLE_KEY | BINARY | TIMESTAMP | ON_UPDATE_NOW",
|
||||
"char_set": 63,
|
||||
"max_size": 19
|
||||
}
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Right": 4
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "11cc7b91730784e9e8c8f073aeaf51a1f1660584e379b9a5be3aea370d84f692"
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"db_name": "MySQL",
|
||||
"query": "\n INSERT INTO media_files\n SET\n token = ?,\n\n media_class = ?,\n media_type = ?,\n\n is_user_upload = ?,\n is_intermediate_system_file = ?,\n\n origin_category = ?, \n origin_product_category = ?, \n maybe_origin_model_type = ?, \n maybe_origin_model_token = ?, \n maybe_origin_filename = ?,\n\n maybe_batch_token = ?,\n\n maybe_mime_type = ?,\n file_size_bytes = ?,\n maybe_duration_millis = ?,\n maybe_audio_encoding = ?, \n maybe_video_encoding = ?, \n maybe_frame_width = ?, \n maybe_frame_height = ?,\n maybe_prompt_token = ?,\n checksum_sha2 = ?,\n \n maybe_engine_category = ?,\n\n maybe_title = ?,\n maybe_text_transcript = ?,\n\n maybe_scene_source_media_file_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\n creator_ip_address = ?, \n creator_set_visibility = ?, \n\n maybe_creator_file_synthetic_id = ?, \n maybe_creator_category_synthetic_id = ?,\n\n extra_file_modification_info = ?,\n\n maybe_generation_provider = ?,\n\n platform_type = ?,\n\n maybe_cover_image_media_file_token = ?,\n\n maybe_mod_user_token = ?,\n is_generated_on_prem = ?,\n generated_by_worker = ?,\n generated_by_cluster = ?\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Right": 41
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "1c74e9c2dcc9205e2c9fe60a6de0828208e497e68560246e588092bafd03eb59"
|
||||
}
|
||||
-12
@@ -1,12 +0,0 @@
|
||||
{
|
||||
"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\n maybe_engine_category = ?,\n maybe_animation_type = ?,\n\n maybe_media_subtype = ?,\n maybe_mime_type = ?,\n file_size_bytes = ?,\n\n checksum_sha2 = ?,\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_user_upload = TRUE,\n\n maybe_origin_model_type = NULL,\n maybe_origin_model_token = NULL\n\nON DUPLICATE KEY UPDATE\n\n media_class = ?,\n media_type = ?,\n\n origin_category = ?,\n origin_product_category = ?,\n\n maybe_engine_category = ?,\n maybe_animation_type = ?,\n\n maybe_media_subtype = ?,\n maybe_mime_type = ?,\n file_size_bytes = ?,\n\n checksum_sha2 = ?,\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_user_upload = TRUE,\n\n maybe_origin_model_type = NULL,\n maybe_origin_model_token = NULL\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Right": 39
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "55706e6ac35be221b9d9c6e8b247812944c61941964bb53815d31dc30d506be8"
|
||||
}
|
||||
+223
@@ -0,0 +1,223 @@
|
||||
{
|
||||
"db_name": "MySQL",
|
||||
"query": "\nSELECT\n m.id as `id: u64`,\n m.token as `token: tokens::tokens::media_files::MediaFileToken`,\n\n m.media_class as `media_class: enums::by_table::media_files::media_file_class::MediaFileClass`,\n m.maybe_project_type as `project_type!: enums::by_table::media_files::media_file_project_type::MediaFileProjectType`,\n m.media_type as `media_type: enums::by_table::media_files::media_file_type::MediaFileType`,\n\n m.public_bucket_directory_hash,\n m.maybe_public_bucket_prefix,\n m.maybe_public_bucket_extension,\n\n users.token as `maybe_creator_user_token: tokens::tokens::users::UserToken`,\n users.username as maybe_creator_username,\n users.display_name as maybe_creator_display_name,\n users.email_gravatar_hash as maybe_creator_gravatar_hash,\n\n media_file_cover_image.public_bucket_directory_hash as maybe_file_cover_image_public_bucket_hash,\n media_file_cover_image.maybe_public_bucket_prefix as maybe_file_cover_image_public_bucket_prefix,\n media_file_cover_image.maybe_public_bucket_extension as maybe_file_cover_image_public_bucket_extension,\n\n m.creator_set_visibility as `creator_set_visibility: enums::common::visibility::Visibility`,\n\n m.maybe_title,\n\n m.created_at,\n m.updated_at\n\nFROM media_files AS m FORCE INDEX (fk_maybe_creator_user_token)\nLEFT OUTER JOIN users\n ON m.maybe_creator_user_token = users.token\nLEFT OUTER JOIN media_files as media_file_cover_image\n ON media_file_cover_image.token = m.maybe_cover_image_media_file_token\nWHERE\n m.maybe_creator_user_token = ?\n AND m.media_class = 'project'\n AND m.maybe_project_type IS NOT NULL\n AND m.maybe_project_type = ?\n AND m.user_deleted_at IS NULL\n AND m.mod_deleted_at IS NULL\n AND NOT m.is_intermediate_system_file\n AND m.id < ?\nORDER BY m.id DESC\nLIMIT ?\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "id: u64",
|
||||
"type_info": {
|
||||
"type": "LongLong",
|
||||
"flags": "NOT_NULL | PRIMARY_KEY | AUTO_INCREMENT",
|
||||
"char_set": 63,
|
||||
"max_size": 20
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "token: tokens::tokens::media_files::MediaFileToken",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "NOT_NULL | UNIQUE_KEY | BINARY | NO_DEFAULT_VALUE",
|
||||
"char_set": 224,
|
||||
"max_size": 128
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"name": "media_class: enums::by_table::media_files::media_file_class::MediaFileClass",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "NOT_NULL | MULTIPLE_KEY | BINARY",
|
||||
"char_set": 224,
|
||||
"max_size": 64
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 3,
|
||||
"name": "project_type!: enums::by_table::media_files::media_file_project_type::MediaFileProjectType",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "MULTIPLE_KEY | BINARY",
|
||||
"char_set": 224,
|
||||
"max_size": 128
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 4,
|
||||
"name": "media_type: enums::by_table::media_files::media_file_type::MediaFileType",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "NOT_NULL | MULTIPLE_KEY | BINARY | NO_DEFAULT_VALUE",
|
||||
"char_set": 224,
|
||||
"max_size": 64
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 5,
|
||||
"name": "public_bucket_directory_hash",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "NOT_NULL | BINARY | NO_DEFAULT_VALUE",
|
||||
"char_set": 224,
|
||||
"max_size": 128
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 6,
|
||||
"name": "maybe_public_bucket_prefix",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "BINARY",
|
||||
"char_set": 224,
|
||||
"max_size": 64
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 7,
|
||||
"name": "maybe_public_bucket_extension",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "BINARY",
|
||||
"char_set": 224,
|
||||
"max_size": 64
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 8,
|
||||
"name": "maybe_creator_user_token: tokens::tokens::users::UserToken",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "UNIQUE_KEY | BINARY | NO_DEFAULT_VALUE",
|
||||
"char_set": 224,
|
||||
"max_size": 128
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 9,
|
||||
"name": "maybe_creator_username",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "UNIQUE_KEY | BINARY | NO_DEFAULT_VALUE",
|
||||
"char_set": 224,
|
||||
"max_size": 80
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 10,
|
||||
"name": "maybe_creator_display_name",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "BINARY | NO_DEFAULT_VALUE",
|
||||
"char_set": 224,
|
||||
"max_size": 80
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 11,
|
||||
"name": "maybe_creator_gravatar_hash",
|
||||
"type_info": {
|
||||
"type": "String",
|
||||
"flags": "BINARY | NO_DEFAULT_VALUE",
|
||||
"char_set": 224,
|
||||
"max_size": 128
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 12,
|
||||
"name": "maybe_file_cover_image_public_bucket_hash",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "BINARY | NO_DEFAULT_VALUE",
|
||||
"char_set": 224,
|
||||
"max_size": 128
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 13,
|
||||
"name": "maybe_file_cover_image_public_bucket_prefix",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "BINARY",
|
||||
"char_set": 224,
|
||||
"max_size": 64
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 14,
|
||||
"name": "maybe_file_cover_image_public_bucket_extension",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "BINARY",
|
||||
"char_set": 224,
|
||||
"max_size": 64
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 15,
|
||||
"name": "creator_set_visibility: enums::common::visibility::Visibility",
|
||||
"type_info": {
|
||||
"type": "String",
|
||||
"flags": "NOT_NULL | BINARY | ENUM",
|
||||
"char_set": 224,
|
||||
"max_size": 28
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 16,
|
||||
"name": "maybe_title",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "BINARY",
|
||||
"char_set": 224,
|
||||
"max_size": 1020
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 17,
|
||||
"name": "created_at",
|
||||
"type_info": {
|
||||
"type": "Timestamp",
|
||||
"flags": "NOT_NULL | MULTIPLE_KEY | BINARY | TIMESTAMP",
|
||||
"char_set": 63,
|
||||
"max_size": 19
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 18,
|
||||
"name": "updated_at",
|
||||
"type_info": {
|
||||
"type": "Timestamp",
|
||||
"flags": "NOT_NULL | MULTIPLE_KEY | BINARY | TIMESTAMP | ON_UPDATE_NOW",
|
||||
"char_set": 63,
|
||||
"max_size": 19
|
||||
}
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Right": 4
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "6c7adbc8171bc3fdfb6109193ffb0a55413fa0942746d65037c2b1d39b361bd0"
|
||||
}
|
||||
-12
@@ -1,12 +0,0 @@
|
||||
{
|
||||
"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\n maybe_engine_category = ?,\n maybe_animation_type = ?,\n\n maybe_mime_type = ?,\n file_size_bytes = ?,\n\n checksum_sha2 = ?,\n\n maybe_prompt_token = ?,\n maybe_batch_token = ?,\n\n maybe_title = ?,\n maybe_duration_millis = ?,\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_scene_source_media_file_token = ?,\n\n is_intermediate_system_file = ?,\n is_user_upload = ?,\n\n maybe_generation_provider = ?,\n\n maybe_creator_file_synthetic_id = ?,\n maybe_creator_category_synthetic_id = ?,\n\n maybe_origin_model_type = NULL,\n maybe_origin_model_token = NULL\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Right": 27
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "7369b2f3fd9e7c1e968dc5501ef9bea7e1610fd7a044a0f9ad8b908dfada2b6a"
|
||||
}
|
||||
+43
-54
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "MySQL",
|
||||
"query": "\nSELECT\n m.token as `token: tokens::tokens::media_files::MediaFileToken`,\n\n m.media_class as `media_class: enums::by_table::media_files::media_file_class::MediaFileClass`,\n m.media_type as `media_type: enums::by_table::media_files::media_file_type::MediaFileType`,\n\n m.maybe_engine_category as `maybe_engine_category: enums::by_table::media_files::media_file_engine_category::MediaFileEngineCategory`,\n m.maybe_animation_type as `maybe_animation_type: enums::by_table::media_files::media_file_animation_type::MediaFileAnimationType`,\n\n m.maybe_media_subtype as `maybe_media_subtype: enums::by_table::media_files::media_file_subtype::MediaFileSubtype`,\n\n m.maybe_mime_type,\n\n users.token as `maybe_creator_user_token: tokens::tokens::users::UserToken`,\n users.username as maybe_creator_username,\n users.display_name as maybe_creator_display_name,\n users.email_gravatar_hash as maybe_creator_gravatar_hash,\n\n m.maybe_creator_anonymous_visitor_token as `maybe_creator_anonymous_visitor_token: tokens::tokens::anonymous_visitor_tracking::AnonymousVisitorTrackingToken`,\n\n m.maybe_batch_token as `maybe_batch_token: tokens::tokens::batch_generations::BatchGenerationToken`,\n\n m.maybe_style_transfer_source_media_file_token as `maybe_style_transfer_source_media_file_token: tokens::tokens::media_files::MediaFileToken`,\n m.maybe_scene_source_media_file_token as `maybe_scene_source_media_file_token: tokens::tokens::media_files::MediaFileToken`,\n\n m.maybe_title,\n m.maybe_text_transcript,\n\n m.maybe_origin_filename,\n\n m.maybe_duration_millis,\n\n m.maybe_prompt_token as `maybe_prompt_token: tokens::tokens::prompts::PromptToken`,\n prompts.maybe_other_args as maybe_other_prompt_args,\n\n media_file_cover_image.public_bucket_directory_hash as maybe_file_cover_image_public_bucket_hash,\n media_file_cover_image.maybe_public_bucket_prefix as maybe_file_cover_image_public_bucket_prefix,\n media_file_cover_image.maybe_public_bucket_extension as maybe_file_cover_image_public_bucket_extension,\n\n m.creator_set_visibility as `creator_set_visibility: enums::common::visibility::Visibility`,\n\n m.is_user_upload,\n m.is_intermediate_system_file,\n\n model_weights.token as `maybe_model_weights_token: tokens::tokens::model_weights::ModelWeightToken`,\n model_weights.title as maybe_model_weights_title,\n model_weights.weights_type as `maybe_model_weights_type: enums::by_table::model_weights::weights_types::WeightsType`,\n model_weights.weights_category as `maybe_model_weights_category: enums::by_table::model_weights::weights_category::WeightsCategory`,\n\n model_weight_cover_image.public_bucket_directory_hash as maybe_model_cover_image_public_bucket_hash,\n model_weight_cover_image.maybe_public_bucket_prefix as maybe_model_cover_image_public_bucket_prefix,\n model_weight_cover_image.maybe_public_bucket_extension as maybe_model_cover_image_public_bucket_extension,\n\n model_weight_creator.token as `maybe_model_weight_creator_user_token: tokens::tokens::users::UserToken`,\n model_weight_creator.username as maybe_model_weight_creator_username,\n model_weight_creator.display_name as maybe_model_weight_creator_display_name,\n model_weight_creator.email_gravatar_hash as maybe_model_weight_creator_gravatar_hash,\n\n m.extra_file_modification_info,\n\n m.public_bucket_directory_hash,\n m.maybe_public_bucket_prefix,\n m.maybe_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\n featured_items.entity_token IS NOT NULL AS is_featured,\n\n m.created_at,\n m.updated_at\n\nFROM media_files AS m\nLEFT OUTER JOIN users\n ON m.maybe_creator_user_token = users.token\nLEFT OUTER JOIN model_weights\n ON m.maybe_origin_model_token = model_weights.token\nLEFT OUTER JOIN media_files as media_file_cover_image\n ON media_file_cover_image.token = m.maybe_cover_image_media_file_token\nLEFT OUTER JOIN media_files as model_weight_cover_image\n ON model_weight_cover_image.token = model_weights.maybe_cover_image_media_file_token\nLEFT OUTER JOIN users as model_weight_creator\n ON model_weight_creator.token = model_weights.creator_user_token\nLEFT OUTER JOIN entity_stats\n ON entity_stats.entity_type = \"media_file\"\n AND entity_stats.entity_token = m.token\nLEFT OUTER JOIN prompts\n ON prompts.token = m.maybe_prompt_token\nLEFT OUTER JOIN featured_items\n ON featured_items.entity_type = 'media_file'\n AND featured_items.entity_token = m.token\n AND featured_items.deleted_at IS NULL\nWHERE\n m.token = ?\n ",
|
||||
"query": "\nSELECT\n m.token as `token: tokens::tokens::media_files::MediaFileToken`,\n\n m.media_class as `media_class: enums::by_table::media_files::media_file_class::MediaFileClass`,\n m.media_type as `media_type: enums::by_table::media_files::media_file_type::MediaFileType`,\n\n m.maybe_engine_category as `maybe_engine_category: enums::by_table::media_files::media_file_engine_category::MediaFileEngineCategory`,\n m.maybe_animation_type as `maybe_animation_type: enums::by_table::media_files::media_file_animation_type::MediaFileAnimationType`,\n\n m.maybe_mime_type,\n\n users.token as `maybe_creator_user_token: tokens::tokens::users::UserToken`,\n users.username as maybe_creator_username,\n users.display_name as maybe_creator_display_name,\n users.email_gravatar_hash as maybe_creator_gravatar_hash,\n\n m.maybe_creator_anonymous_visitor_token as `maybe_creator_anonymous_visitor_token: tokens::tokens::anonymous_visitor_tracking::AnonymousVisitorTrackingToken`,\n\n m.maybe_batch_token as `maybe_batch_token: tokens::tokens::batch_generations::BatchGenerationToken`,\n\n m.maybe_style_transfer_source_media_file_token as `maybe_style_transfer_source_media_file_token: tokens::tokens::media_files::MediaFileToken`,\n m.maybe_scene_source_media_file_token as `maybe_scene_source_media_file_token: tokens::tokens::media_files::MediaFileToken`,\n\n m.maybe_title,\n m.maybe_text_transcript,\n\n m.maybe_origin_filename,\n\n m.maybe_duration_millis,\n\n m.maybe_prompt_token as `maybe_prompt_token: tokens::tokens::prompts::PromptToken`,\n prompts.maybe_other_args as maybe_other_prompt_args,\n\n media_file_cover_image.public_bucket_directory_hash as maybe_file_cover_image_public_bucket_hash,\n media_file_cover_image.maybe_public_bucket_prefix as maybe_file_cover_image_public_bucket_prefix,\n media_file_cover_image.maybe_public_bucket_extension as maybe_file_cover_image_public_bucket_extension,\n\n m.creator_set_visibility as `creator_set_visibility: enums::common::visibility::Visibility`,\n\n m.is_user_upload,\n m.is_intermediate_system_file,\n\n model_weights.token as `maybe_model_weights_token: tokens::tokens::model_weights::ModelWeightToken`,\n model_weights.title as maybe_model_weights_title,\n model_weights.weights_type as `maybe_model_weights_type: enums::by_table::model_weights::weights_types::WeightsType`,\n model_weights.weights_category as `maybe_model_weights_category: enums::by_table::model_weights::weights_category::WeightsCategory`,\n\n model_weight_cover_image.public_bucket_directory_hash as maybe_model_cover_image_public_bucket_hash,\n model_weight_cover_image.maybe_public_bucket_prefix as maybe_model_cover_image_public_bucket_prefix,\n model_weight_cover_image.maybe_public_bucket_extension as maybe_model_cover_image_public_bucket_extension,\n\n model_weight_creator.token as `maybe_model_weight_creator_user_token: tokens::tokens::users::UserToken`,\n model_weight_creator.username as maybe_model_weight_creator_username,\n model_weight_creator.display_name as maybe_model_weight_creator_display_name,\n model_weight_creator.email_gravatar_hash as maybe_model_weight_creator_gravatar_hash,\n\n m.extra_file_modification_info,\n\n m.public_bucket_directory_hash,\n m.maybe_public_bucket_prefix,\n m.maybe_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\n featured_items.entity_token IS NOT NULL AS is_featured,\n\n m.created_at,\n m.updated_at\n\nFROM media_files AS m\nLEFT OUTER JOIN users\n ON m.maybe_creator_user_token = users.token\nLEFT OUTER JOIN model_weights\n ON m.maybe_origin_model_token = model_weights.token\nLEFT OUTER JOIN media_files as media_file_cover_image\n ON media_file_cover_image.token = m.maybe_cover_image_media_file_token\nLEFT OUTER JOIN media_files as model_weight_cover_image\n ON model_weight_cover_image.token = model_weights.maybe_cover_image_media_file_token\nLEFT OUTER JOIN users as model_weight_creator\n ON model_weight_creator.token = model_weights.creator_user_token\nLEFT OUTER JOIN entity_stats\n ON entity_stats.entity_type = \"media_file\"\n AND entity_stats.entity_token = m.token\nLEFT OUTER JOIN prompts\n ON prompts.token = m.maybe_prompt_token\nLEFT OUTER JOIN featured_items\n ON featured_items.entity_type = 'media_file'\n AND featured_items.entity_token = m.token\n AND featured_items.deleted_at IS NULL\nWHERE\n m.token = ?\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
@@ -55,16 +55,6 @@
|
||||
},
|
||||
{
|
||||
"ordinal": 5,
|
||||
"name": "maybe_media_subtype: enums::by_table::media_files::media_file_subtype::MediaFileSubtype",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "BINARY",
|
||||
"char_set": 224,
|
||||
"max_size": 128
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 6,
|
||||
"name": "maybe_mime_type",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
@@ -74,7 +64,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 7,
|
||||
"ordinal": 6,
|
||||
"name": "maybe_creator_user_token: tokens::tokens::users::UserToken",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
@@ -84,7 +74,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 8,
|
||||
"ordinal": 7,
|
||||
"name": "maybe_creator_username",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
@@ -94,7 +84,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 9,
|
||||
"ordinal": 8,
|
||||
"name": "maybe_creator_display_name",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
@@ -104,7 +94,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 10,
|
||||
"ordinal": 9,
|
||||
"name": "maybe_creator_gravatar_hash",
|
||||
"type_info": {
|
||||
"type": "String",
|
||||
@@ -114,7 +104,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 11,
|
||||
"ordinal": 10,
|
||||
"name": "maybe_creator_anonymous_visitor_token: tokens::tokens::anonymous_visitor_tracking::AnonymousVisitorTrackingToken",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
@@ -124,7 +114,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 12,
|
||||
"ordinal": 11,
|
||||
"name": "maybe_batch_token: tokens::tokens::batch_generations::BatchGenerationToken",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
@@ -134,7 +124,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 13,
|
||||
"ordinal": 12,
|
||||
"name": "maybe_style_transfer_source_media_file_token: tokens::tokens::media_files::MediaFileToken",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
@@ -144,7 +134,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 14,
|
||||
"ordinal": 13,
|
||||
"name": "maybe_scene_source_media_file_token: tokens::tokens::media_files::MediaFileToken",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
@@ -154,7 +144,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 15,
|
||||
"ordinal": 14,
|
||||
"name": "maybe_title",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
@@ -164,7 +154,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 16,
|
||||
"ordinal": 15,
|
||||
"name": "maybe_text_transcript",
|
||||
"type_info": {
|
||||
"type": "Blob",
|
||||
@@ -174,7 +164,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 17,
|
||||
"ordinal": 16,
|
||||
"name": "maybe_origin_filename",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
@@ -184,7 +174,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 18,
|
||||
"ordinal": 17,
|
||||
"name": "maybe_duration_millis",
|
||||
"type_info": {
|
||||
"type": "Long",
|
||||
@@ -194,7 +184,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 19,
|
||||
"ordinal": 18,
|
||||
"name": "maybe_prompt_token: tokens::tokens::prompts::PromptToken",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
@@ -204,7 +194,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 20,
|
||||
"ordinal": 19,
|
||||
"name": "maybe_other_prompt_args",
|
||||
"type_info": {
|
||||
"type": "Blob",
|
||||
@@ -214,7 +204,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 21,
|
||||
"ordinal": 20,
|
||||
"name": "maybe_file_cover_image_public_bucket_hash",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
@@ -224,7 +214,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 22,
|
||||
"ordinal": 21,
|
||||
"name": "maybe_file_cover_image_public_bucket_prefix",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
@@ -234,7 +224,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 23,
|
||||
"ordinal": 22,
|
||||
"name": "maybe_file_cover_image_public_bucket_extension",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
@@ -244,7 +234,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 24,
|
||||
"ordinal": 23,
|
||||
"name": "creator_set_visibility: enums::common::visibility::Visibility",
|
||||
"type_info": {
|
||||
"type": "String",
|
||||
@@ -254,7 +244,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 25,
|
||||
"ordinal": 24,
|
||||
"name": "is_user_upload",
|
||||
"type_info": {
|
||||
"type": "Tiny",
|
||||
@@ -264,7 +254,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 26,
|
||||
"ordinal": 25,
|
||||
"name": "is_intermediate_system_file",
|
||||
"type_info": {
|
||||
"type": "Tiny",
|
||||
@@ -274,7 +264,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 27,
|
||||
"ordinal": 26,
|
||||
"name": "maybe_model_weights_token: tokens::tokens::model_weights::ModelWeightToken",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
@@ -284,7 +274,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 28,
|
||||
"ordinal": 27,
|
||||
"name": "maybe_model_weights_title",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
@@ -294,7 +284,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 29,
|
||||
"ordinal": 28,
|
||||
"name": "maybe_model_weights_type: enums::by_table::model_weights::weights_types::WeightsType",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
@@ -304,7 +294,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 30,
|
||||
"ordinal": 29,
|
||||
"name": "maybe_model_weights_category: enums::by_table::model_weights::weights_category::WeightsCategory",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
@@ -314,7 +304,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 31,
|
||||
"ordinal": 30,
|
||||
"name": "maybe_model_cover_image_public_bucket_hash",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
@@ -324,7 +314,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 32,
|
||||
"ordinal": 31,
|
||||
"name": "maybe_model_cover_image_public_bucket_prefix",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
@@ -334,7 +324,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 33,
|
||||
"ordinal": 32,
|
||||
"name": "maybe_model_cover_image_public_bucket_extension",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
@@ -344,7 +334,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 34,
|
||||
"ordinal": 33,
|
||||
"name": "maybe_model_weight_creator_user_token: tokens::tokens::users::UserToken",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
@@ -354,7 +344,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 35,
|
||||
"ordinal": 34,
|
||||
"name": "maybe_model_weight_creator_username",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
@@ -364,7 +354,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 36,
|
||||
"ordinal": 35,
|
||||
"name": "maybe_model_weight_creator_display_name",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
@@ -374,7 +364,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 37,
|
||||
"ordinal": 36,
|
||||
"name": "maybe_model_weight_creator_gravatar_hash",
|
||||
"type_info": {
|
||||
"type": "String",
|
||||
@@ -384,7 +374,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 38,
|
||||
"ordinal": 37,
|
||||
"name": "extra_file_modification_info",
|
||||
"type_info": {
|
||||
"type": "Blob",
|
||||
@@ -394,7 +384,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 39,
|
||||
"ordinal": 38,
|
||||
"name": "public_bucket_directory_hash",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
@@ -404,7 +394,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 40,
|
||||
"ordinal": 39,
|
||||
"name": "maybe_public_bucket_prefix",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
@@ -414,7 +404,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 41,
|
||||
"ordinal": 40,
|
||||
"name": "maybe_public_bucket_extension",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
@@ -424,7 +414,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 42,
|
||||
"ordinal": 41,
|
||||
"name": "maybe_ratings_positive_count",
|
||||
"type_info": {
|
||||
"type": "Long",
|
||||
@@ -434,7 +424,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 43,
|
||||
"ordinal": 42,
|
||||
"name": "maybe_ratings_negative_count",
|
||||
"type_info": {
|
||||
"type": "Long",
|
||||
@@ -444,7 +434,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 44,
|
||||
"ordinal": 43,
|
||||
"name": "maybe_bookmark_count",
|
||||
"type_info": {
|
||||
"type": "Long",
|
||||
@@ -454,7 +444,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 45,
|
||||
"ordinal": 44,
|
||||
"name": "is_featured",
|
||||
"type_info": {
|
||||
"type": "LongLong",
|
||||
@@ -464,7 +454,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 46,
|
||||
"ordinal": 45,
|
||||
"name": "created_at",
|
||||
"type_info": {
|
||||
"type": "Timestamp",
|
||||
@@ -474,7 +464,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 47,
|
||||
"ordinal": 46,
|
||||
"name": "updated_at",
|
||||
"type_info": {
|
||||
"type": "Timestamp",
|
||||
@@ -511,7 +501,6 @@
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
@@ -538,5 +527,5 @@
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "f6c37066ab3e508050e81cf3bea38cc708b05c5c59ba1c322dcfb775e4b9e7a6"
|
||||
"hash": "784ca68c6cb61d481b605bfe6ee3248a5b44499038c71b57fc9ea6c4722d170f"
|
||||
}
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
{
|
||||
"db_name": "MySQL",
|
||||
"query": "\nSELECT\n maybe_creator_user_token as `maybe_creator_user_token: UserToken`,\n maybe_creator_anonymous_visitor_token as `maybe_creator_anonymous_visitor_token: AnonymousVisitorTrackingToken`,\n media_type as `media_type: MediaFileType`,\n maybe_project_type as `maybe_project_type: MediaFileProjectType`\nFROM media_files\nWHERE token = ?\n AND user_deleted_at IS NULL\n AND mod_deleted_at IS NULL\nLIMIT 1\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "maybe_creator_user_token: UserToken",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "MULTIPLE_KEY | BINARY",
|
||||
"char_set": 224,
|
||||
"max_size": 128
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "maybe_creator_anonymous_visitor_token: AnonymousVisitorTrackingToken",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "BINARY",
|
||||
"char_set": 224,
|
||||
"max_size": 128
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"name": "media_type: MediaFileType",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "NOT_NULL | MULTIPLE_KEY | BINARY | NO_DEFAULT_VALUE",
|
||||
"char_set": 224,
|
||||
"max_size": 64
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 3,
|
||||
"name": "maybe_project_type: MediaFileProjectType",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "MULTIPLE_KEY | BINARY",
|
||||
"char_set": 224,
|
||||
"max_size": 128
|
||||
}
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Right": 1
|
||||
},
|
||||
"nullable": [
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
true
|
||||
]
|
||||
},
|
||||
"hash": "8dcd4bb064881f245beb39dd246e74ec1a2c47b69d7ce92a0d175f58dc4b9e63"
|
||||
}
|
||||
+223
@@ -0,0 +1,223 @@
|
||||
{
|
||||
"db_name": "MySQL",
|
||||
"query": "\nSELECT\n m.id as `id: u64`,\n m.token as `token: tokens::tokens::media_files::MediaFileToken`,\n\n m.media_class as `media_class: enums::by_table::media_files::media_file_class::MediaFileClass`,\n m.maybe_project_type as `project_type!: enums::by_table::media_files::media_file_project_type::MediaFileProjectType`,\n m.media_type as `media_type: enums::by_table::media_files::media_file_type::MediaFileType`,\n\n m.public_bucket_directory_hash,\n m.maybe_public_bucket_prefix,\n m.maybe_public_bucket_extension,\n\n users.token as `maybe_creator_user_token: tokens::tokens::users::UserToken`,\n users.username as maybe_creator_username,\n users.display_name as maybe_creator_display_name,\n users.email_gravatar_hash as maybe_creator_gravatar_hash,\n\n media_file_cover_image.public_bucket_directory_hash as maybe_file_cover_image_public_bucket_hash,\n media_file_cover_image.maybe_public_bucket_prefix as maybe_file_cover_image_public_bucket_prefix,\n media_file_cover_image.maybe_public_bucket_extension as maybe_file_cover_image_public_bucket_extension,\n\n m.creator_set_visibility as `creator_set_visibility: enums::common::visibility::Visibility`,\n\n m.maybe_title,\n\n m.created_at,\n m.updated_at\n\nFROM media_files AS m FORCE INDEX (fk_maybe_creator_user_token)\nLEFT OUTER JOIN users\n ON m.maybe_creator_user_token = users.token\nLEFT OUTER JOIN media_files as media_file_cover_image\n ON media_file_cover_image.token = m.maybe_cover_image_media_file_token\nWHERE\n m.maybe_creator_user_token = ?\n AND m.media_class = 'project'\n AND m.maybe_project_type IS NOT NULL\n AND m.user_deleted_at IS NULL\n AND m.mod_deleted_at IS NULL\n AND NOT m.is_intermediate_system_file\n AND m.id < ?\nORDER BY m.id DESC\nLIMIT ?\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "id: u64",
|
||||
"type_info": {
|
||||
"type": "LongLong",
|
||||
"flags": "NOT_NULL | PRIMARY_KEY | AUTO_INCREMENT",
|
||||
"char_set": 63,
|
||||
"max_size": 20
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "token: tokens::tokens::media_files::MediaFileToken",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "NOT_NULL | UNIQUE_KEY | BINARY | NO_DEFAULT_VALUE",
|
||||
"char_set": 224,
|
||||
"max_size": 128
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"name": "media_class: enums::by_table::media_files::media_file_class::MediaFileClass",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "NOT_NULL | MULTIPLE_KEY | BINARY",
|
||||
"char_set": 224,
|
||||
"max_size": 64
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 3,
|
||||
"name": "project_type!: enums::by_table::media_files::media_file_project_type::MediaFileProjectType",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "MULTIPLE_KEY | BINARY",
|
||||
"char_set": 224,
|
||||
"max_size": 128
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 4,
|
||||
"name": "media_type: enums::by_table::media_files::media_file_type::MediaFileType",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "NOT_NULL | MULTIPLE_KEY | BINARY | NO_DEFAULT_VALUE",
|
||||
"char_set": 224,
|
||||
"max_size": 64
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 5,
|
||||
"name": "public_bucket_directory_hash",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "NOT_NULL | BINARY | NO_DEFAULT_VALUE",
|
||||
"char_set": 224,
|
||||
"max_size": 128
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 6,
|
||||
"name": "maybe_public_bucket_prefix",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "BINARY",
|
||||
"char_set": 224,
|
||||
"max_size": 64
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 7,
|
||||
"name": "maybe_public_bucket_extension",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "BINARY",
|
||||
"char_set": 224,
|
||||
"max_size": 64
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 8,
|
||||
"name": "maybe_creator_user_token: tokens::tokens::users::UserToken",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "UNIQUE_KEY | BINARY | NO_DEFAULT_VALUE",
|
||||
"char_set": 224,
|
||||
"max_size": 128
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 9,
|
||||
"name": "maybe_creator_username",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "UNIQUE_KEY | BINARY | NO_DEFAULT_VALUE",
|
||||
"char_set": 224,
|
||||
"max_size": 80
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 10,
|
||||
"name": "maybe_creator_display_name",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "BINARY | NO_DEFAULT_VALUE",
|
||||
"char_set": 224,
|
||||
"max_size": 80
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 11,
|
||||
"name": "maybe_creator_gravatar_hash",
|
||||
"type_info": {
|
||||
"type": "String",
|
||||
"flags": "BINARY | NO_DEFAULT_VALUE",
|
||||
"char_set": 224,
|
||||
"max_size": 128
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 12,
|
||||
"name": "maybe_file_cover_image_public_bucket_hash",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "BINARY | NO_DEFAULT_VALUE",
|
||||
"char_set": 224,
|
||||
"max_size": 128
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 13,
|
||||
"name": "maybe_file_cover_image_public_bucket_prefix",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "BINARY",
|
||||
"char_set": 224,
|
||||
"max_size": 64
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 14,
|
||||
"name": "maybe_file_cover_image_public_bucket_extension",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "BINARY",
|
||||
"char_set": 224,
|
||||
"max_size": 64
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 15,
|
||||
"name": "creator_set_visibility: enums::common::visibility::Visibility",
|
||||
"type_info": {
|
||||
"type": "String",
|
||||
"flags": "NOT_NULL | BINARY | ENUM",
|
||||
"char_set": 224,
|
||||
"max_size": 28
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 16,
|
||||
"name": "maybe_title",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "BINARY",
|
||||
"char_set": 224,
|
||||
"max_size": 1020
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 17,
|
||||
"name": "created_at",
|
||||
"type_info": {
|
||||
"type": "Timestamp",
|
||||
"flags": "NOT_NULL | MULTIPLE_KEY | BINARY | TIMESTAMP",
|
||||
"char_set": 63,
|
||||
"max_size": 19
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 18,
|
||||
"name": "updated_at",
|
||||
"type_info": {
|
||||
"type": "Timestamp",
|
||||
"flags": "NOT_NULL | MULTIPLE_KEY | BINARY | TIMESTAMP | ON_UPDATE_NOW",
|
||||
"char_set": 63,
|
||||
"max_size": 19
|
||||
}
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Right": 3
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "97e4bc3ab037126e3f4e213cdb7535c2ad233809fbeb6607c34fb02ec45cfe47"
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"db_name": "MySQL",
|
||||
"query": "\nINSERT INTO media_files\nSET\n token = ?,\n\n media_class = ?,\n media_type = ?,\n maybe_project_type = ?,\n\n origin_category = ?,\n origin_product_category = ?,\n\n maybe_engine_category = ?,\n maybe_animation_type = ?,\n\n maybe_mime_type = ?,\n file_size_bytes = ?,\n\n checksum_sha2 = ?,\n\n maybe_prompt_token = ?,\n maybe_batch_token = ?,\n\n maybe_title = ?,\n maybe_duration_millis = ?,\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_scene_source_media_file_token = ?,\n\n is_intermediate_system_file = ?,\n is_user_upload = ?,\n\n maybe_generation_provider = ?,\n\n maybe_creator_file_synthetic_id = ?,\n maybe_creator_category_synthetic_id = ?,\n\n maybe_origin_model_type = NULL,\n maybe_origin_model_token = NULL\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Right": 28
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "a25fe977f51329ed078e2441446ef7ff16683dcad8b81c8d0a0a4540b5e985bc"
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"db_name": "MySQL",
|
||||
"query": "\nUPDATE media_files\nSET\n media_class = ?,\n media_type = ?,\n maybe_project_type = ?,\n\n public_bucket_directory_hash = ?,\n maybe_public_bucket_prefix = ?,\n maybe_public_bucket_extension = ?,\n\n maybe_mime_type = ?,\n file_size_bytes = ?,\n checksum_sha2 = ?,\n\n creator_ip_address = ?\nWHERE token = ?\nLIMIT 1\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Right": 11
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "a3a86db1b77af3973bb37751d797a7dc5ed34b0f0fa156845e3b4b805e397f9d"
|
||||
}
|
||||
+43
-54
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "MySQL",
|
||||
"query": "\nSELECT\n m.token as `token: tokens::tokens::media_files::MediaFileToken`,\n\n m.media_class as `media_class: enums::by_table::media_files::media_file_class::MediaFileClass`,\n m.media_type as `media_type: enums::by_table::media_files::media_file_type::MediaFileType`,\n\n m.maybe_engine_category as `maybe_engine_category: enums::by_table::media_files::media_file_engine_category::MediaFileEngineCategory`,\n m.maybe_animation_type as `maybe_animation_type: enums::by_table::media_files::media_file_animation_type::MediaFileAnimationType`,\n\n m.maybe_media_subtype as `maybe_media_subtype: enums::by_table::media_files::media_file_subtype::MediaFileSubtype`,\n\n m.maybe_mime_type,\n\n users.token as `maybe_creator_user_token: tokens::tokens::users::UserToken`,\n users.username as maybe_creator_username,\n users.display_name as maybe_creator_display_name,\n users.email_gravatar_hash as maybe_creator_gravatar_hash,\n\n m.maybe_creator_anonymous_visitor_token as `maybe_creator_anonymous_visitor_token: tokens::tokens::anonymous_visitor_tracking::AnonymousVisitorTrackingToken`,\n\n m.maybe_batch_token as `maybe_batch_token: tokens::tokens::batch_generations::BatchGenerationToken`,\n\n m.maybe_style_transfer_source_media_file_token as `maybe_style_transfer_source_media_file_token: tokens::tokens::media_files::MediaFileToken`,\n m.maybe_scene_source_media_file_token as `maybe_scene_source_media_file_token: tokens::tokens::media_files::MediaFileToken`,\n\n\n m.maybe_title,\n m.maybe_text_transcript,\n\n m.maybe_origin_filename,\n\n m.maybe_duration_millis,\n\n m.maybe_prompt_token as `maybe_prompt_token: tokens::tokens::prompts::PromptToken`,\n prompts.maybe_other_args as maybe_other_prompt_args,\n\n media_file_cover_image.public_bucket_directory_hash as maybe_file_cover_image_public_bucket_hash,\n media_file_cover_image.maybe_public_bucket_prefix as maybe_file_cover_image_public_bucket_prefix,\n media_file_cover_image.maybe_public_bucket_extension as maybe_file_cover_image_public_bucket_extension,\n\n m.creator_set_visibility as `creator_set_visibility: enums::common::visibility::Visibility`,\n\n m.is_user_upload,\n m.is_intermediate_system_file,\n\n model_weights.token as `maybe_model_weights_token: tokens::tokens::model_weights::ModelWeightToken`,\n model_weights.title as maybe_model_weights_title,\n model_weights.weights_type as `maybe_model_weights_type: enums::by_table::model_weights::weights_types::WeightsType`,\n model_weights.weights_category as `maybe_model_weights_category: enums::by_table::model_weights::weights_category::WeightsCategory`,\n\n model_weight_cover_image.public_bucket_directory_hash as maybe_model_cover_image_public_bucket_hash,\n model_weight_cover_image.maybe_public_bucket_prefix as maybe_model_cover_image_public_bucket_prefix,\n model_weight_cover_image.maybe_public_bucket_extension as maybe_model_cover_image_public_bucket_extension,\n\n model_weight_creator.token as `maybe_model_weight_creator_user_token: tokens::tokens::users::UserToken`,\n model_weight_creator.username as maybe_model_weight_creator_username,\n model_weight_creator.display_name as maybe_model_weight_creator_display_name,\n model_weight_creator.email_gravatar_hash as maybe_model_weight_creator_gravatar_hash,\n\n m.extra_file_modification_info,\n\n m.public_bucket_directory_hash,\n m.maybe_public_bucket_prefix,\n m.maybe_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\n featured_items.entity_token IS NOT NULL AS is_featured,\n\n m.created_at,\n m.updated_at\n\nFROM media_files AS m\nLEFT OUTER JOIN users\n ON m.maybe_creator_user_token = users.token\nLEFT OUTER JOIN model_weights\n ON m.maybe_origin_model_token = model_weights.token\nLEFT OUTER JOIN media_files as media_file_cover_image\n ON media_file_cover_image.token = m.maybe_cover_image_media_file_token\nLEFT OUTER JOIN media_files as model_weight_cover_image\n ON model_weight_cover_image.token = model_weights.maybe_cover_image_media_file_token\nLEFT OUTER JOIN users as model_weight_creator\n ON model_weight_creator.token = model_weights.creator_user_token\nLEFT OUTER JOIN entity_stats\n ON entity_stats.entity_type = \"media_file\"\n AND entity_stats.entity_token = m.token\nLEFT OUTER JOIN prompts\n ON prompts.token = m.maybe_prompt_token\nLEFT OUTER JOIN featured_items\n ON featured_items.entity_type = 'media_file'\n AND featured_items.entity_token = m.token\n AND featured_items.deleted_at IS NULL\nWHERE\n m.token = ?\n AND m.user_deleted_at IS NULL\n AND m.mod_deleted_at IS NULL\n ",
|
||||
"query": "\nSELECT\n m.token as `token: tokens::tokens::media_files::MediaFileToken`,\n\n m.media_class as `media_class: enums::by_table::media_files::media_file_class::MediaFileClass`,\n m.media_type as `media_type: enums::by_table::media_files::media_file_type::MediaFileType`,\n\n m.maybe_engine_category as `maybe_engine_category: enums::by_table::media_files::media_file_engine_category::MediaFileEngineCategory`,\n m.maybe_animation_type as `maybe_animation_type: enums::by_table::media_files::media_file_animation_type::MediaFileAnimationType`,\n\n m.maybe_mime_type,\n\n users.token as `maybe_creator_user_token: tokens::tokens::users::UserToken`,\n users.username as maybe_creator_username,\n users.display_name as maybe_creator_display_name,\n users.email_gravatar_hash as maybe_creator_gravatar_hash,\n\n m.maybe_creator_anonymous_visitor_token as `maybe_creator_anonymous_visitor_token: tokens::tokens::anonymous_visitor_tracking::AnonymousVisitorTrackingToken`,\n\n m.maybe_batch_token as `maybe_batch_token: tokens::tokens::batch_generations::BatchGenerationToken`,\n\n m.maybe_style_transfer_source_media_file_token as `maybe_style_transfer_source_media_file_token: tokens::tokens::media_files::MediaFileToken`,\n m.maybe_scene_source_media_file_token as `maybe_scene_source_media_file_token: tokens::tokens::media_files::MediaFileToken`,\n\n\n m.maybe_title,\n m.maybe_text_transcript,\n\n m.maybe_origin_filename,\n\n m.maybe_duration_millis,\n\n m.maybe_prompt_token as `maybe_prompt_token: tokens::tokens::prompts::PromptToken`,\n prompts.maybe_other_args as maybe_other_prompt_args,\n\n media_file_cover_image.public_bucket_directory_hash as maybe_file_cover_image_public_bucket_hash,\n media_file_cover_image.maybe_public_bucket_prefix as maybe_file_cover_image_public_bucket_prefix,\n media_file_cover_image.maybe_public_bucket_extension as maybe_file_cover_image_public_bucket_extension,\n\n m.creator_set_visibility as `creator_set_visibility: enums::common::visibility::Visibility`,\n\n m.is_user_upload,\n m.is_intermediate_system_file,\n\n model_weights.token as `maybe_model_weights_token: tokens::tokens::model_weights::ModelWeightToken`,\n model_weights.title as maybe_model_weights_title,\n model_weights.weights_type as `maybe_model_weights_type: enums::by_table::model_weights::weights_types::WeightsType`,\n model_weights.weights_category as `maybe_model_weights_category: enums::by_table::model_weights::weights_category::WeightsCategory`,\n\n model_weight_cover_image.public_bucket_directory_hash as maybe_model_cover_image_public_bucket_hash,\n model_weight_cover_image.maybe_public_bucket_prefix as maybe_model_cover_image_public_bucket_prefix,\n model_weight_cover_image.maybe_public_bucket_extension as maybe_model_cover_image_public_bucket_extension,\n\n model_weight_creator.token as `maybe_model_weight_creator_user_token: tokens::tokens::users::UserToken`,\n model_weight_creator.username as maybe_model_weight_creator_username,\n model_weight_creator.display_name as maybe_model_weight_creator_display_name,\n model_weight_creator.email_gravatar_hash as maybe_model_weight_creator_gravatar_hash,\n\n m.extra_file_modification_info,\n\n m.public_bucket_directory_hash,\n m.maybe_public_bucket_prefix,\n m.maybe_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\n featured_items.entity_token IS NOT NULL AS is_featured,\n\n m.created_at,\n m.updated_at\n\nFROM media_files AS m\nLEFT OUTER JOIN users\n ON m.maybe_creator_user_token = users.token\nLEFT OUTER JOIN model_weights\n ON m.maybe_origin_model_token = model_weights.token\nLEFT OUTER JOIN media_files as media_file_cover_image\n ON media_file_cover_image.token = m.maybe_cover_image_media_file_token\nLEFT OUTER JOIN media_files as model_weight_cover_image\n ON model_weight_cover_image.token = model_weights.maybe_cover_image_media_file_token\nLEFT OUTER JOIN users as model_weight_creator\n ON model_weight_creator.token = model_weights.creator_user_token\nLEFT OUTER JOIN entity_stats\n ON entity_stats.entity_type = \"media_file\"\n AND entity_stats.entity_token = m.token\nLEFT OUTER JOIN prompts\n ON prompts.token = m.maybe_prompt_token\nLEFT OUTER JOIN featured_items\n ON featured_items.entity_type = 'media_file'\n AND featured_items.entity_token = m.token\n AND featured_items.deleted_at IS NULL\nWHERE\n m.token = ?\n AND m.user_deleted_at IS NULL\n AND m.mod_deleted_at IS NULL\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
@@ -55,16 +55,6 @@
|
||||
},
|
||||
{
|
||||
"ordinal": 5,
|
||||
"name": "maybe_media_subtype: enums::by_table::media_files::media_file_subtype::MediaFileSubtype",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "BINARY",
|
||||
"char_set": 224,
|
||||
"max_size": 128
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 6,
|
||||
"name": "maybe_mime_type",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
@@ -74,7 +64,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 7,
|
||||
"ordinal": 6,
|
||||
"name": "maybe_creator_user_token: tokens::tokens::users::UserToken",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
@@ -84,7 +74,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 8,
|
||||
"ordinal": 7,
|
||||
"name": "maybe_creator_username",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
@@ -94,7 +84,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 9,
|
||||
"ordinal": 8,
|
||||
"name": "maybe_creator_display_name",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
@@ -104,7 +94,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 10,
|
||||
"ordinal": 9,
|
||||
"name": "maybe_creator_gravatar_hash",
|
||||
"type_info": {
|
||||
"type": "String",
|
||||
@@ -114,7 +104,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 11,
|
||||
"ordinal": 10,
|
||||
"name": "maybe_creator_anonymous_visitor_token: tokens::tokens::anonymous_visitor_tracking::AnonymousVisitorTrackingToken",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
@@ -124,7 +114,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 12,
|
||||
"ordinal": 11,
|
||||
"name": "maybe_batch_token: tokens::tokens::batch_generations::BatchGenerationToken",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
@@ -134,7 +124,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 13,
|
||||
"ordinal": 12,
|
||||
"name": "maybe_style_transfer_source_media_file_token: tokens::tokens::media_files::MediaFileToken",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
@@ -144,7 +134,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 14,
|
||||
"ordinal": 13,
|
||||
"name": "maybe_scene_source_media_file_token: tokens::tokens::media_files::MediaFileToken",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
@@ -154,7 +144,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 15,
|
||||
"ordinal": 14,
|
||||
"name": "maybe_title",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
@@ -164,7 +154,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 16,
|
||||
"ordinal": 15,
|
||||
"name": "maybe_text_transcript",
|
||||
"type_info": {
|
||||
"type": "Blob",
|
||||
@@ -174,7 +164,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 17,
|
||||
"ordinal": 16,
|
||||
"name": "maybe_origin_filename",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
@@ -184,7 +174,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 18,
|
||||
"ordinal": 17,
|
||||
"name": "maybe_duration_millis",
|
||||
"type_info": {
|
||||
"type": "Long",
|
||||
@@ -194,7 +184,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 19,
|
||||
"ordinal": 18,
|
||||
"name": "maybe_prompt_token: tokens::tokens::prompts::PromptToken",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
@@ -204,7 +194,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 20,
|
||||
"ordinal": 19,
|
||||
"name": "maybe_other_prompt_args",
|
||||
"type_info": {
|
||||
"type": "Blob",
|
||||
@@ -214,7 +204,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 21,
|
||||
"ordinal": 20,
|
||||
"name": "maybe_file_cover_image_public_bucket_hash",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
@@ -224,7 +214,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 22,
|
||||
"ordinal": 21,
|
||||
"name": "maybe_file_cover_image_public_bucket_prefix",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
@@ -234,7 +224,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 23,
|
||||
"ordinal": 22,
|
||||
"name": "maybe_file_cover_image_public_bucket_extension",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
@@ -244,7 +234,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 24,
|
||||
"ordinal": 23,
|
||||
"name": "creator_set_visibility: enums::common::visibility::Visibility",
|
||||
"type_info": {
|
||||
"type": "String",
|
||||
@@ -254,7 +244,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 25,
|
||||
"ordinal": 24,
|
||||
"name": "is_user_upload",
|
||||
"type_info": {
|
||||
"type": "Tiny",
|
||||
@@ -264,7 +254,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 26,
|
||||
"ordinal": 25,
|
||||
"name": "is_intermediate_system_file",
|
||||
"type_info": {
|
||||
"type": "Tiny",
|
||||
@@ -274,7 +264,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 27,
|
||||
"ordinal": 26,
|
||||
"name": "maybe_model_weights_token: tokens::tokens::model_weights::ModelWeightToken",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
@@ -284,7 +274,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 28,
|
||||
"ordinal": 27,
|
||||
"name": "maybe_model_weights_title",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
@@ -294,7 +284,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 29,
|
||||
"ordinal": 28,
|
||||
"name": "maybe_model_weights_type: enums::by_table::model_weights::weights_types::WeightsType",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
@@ -304,7 +294,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 30,
|
||||
"ordinal": 29,
|
||||
"name": "maybe_model_weights_category: enums::by_table::model_weights::weights_category::WeightsCategory",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
@@ -314,7 +304,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 31,
|
||||
"ordinal": 30,
|
||||
"name": "maybe_model_cover_image_public_bucket_hash",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
@@ -324,7 +314,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 32,
|
||||
"ordinal": 31,
|
||||
"name": "maybe_model_cover_image_public_bucket_prefix",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
@@ -334,7 +324,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 33,
|
||||
"ordinal": 32,
|
||||
"name": "maybe_model_cover_image_public_bucket_extension",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
@@ -344,7 +334,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 34,
|
||||
"ordinal": 33,
|
||||
"name": "maybe_model_weight_creator_user_token: tokens::tokens::users::UserToken",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
@@ -354,7 +344,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 35,
|
||||
"ordinal": 34,
|
||||
"name": "maybe_model_weight_creator_username",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
@@ -364,7 +354,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 36,
|
||||
"ordinal": 35,
|
||||
"name": "maybe_model_weight_creator_display_name",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
@@ -374,7 +364,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 37,
|
||||
"ordinal": 36,
|
||||
"name": "maybe_model_weight_creator_gravatar_hash",
|
||||
"type_info": {
|
||||
"type": "String",
|
||||
@@ -384,7 +374,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 38,
|
||||
"ordinal": 37,
|
||||
"name": "extra_file_modification_info",
|
||||
"type_info": {
|
||||
"type": "Blob",
|
||||
@@ -394,7 +384,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 39,
|
||||
"ordinal": 38,
|
||||
"name": "public_bucket_directory_hash",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
@@ -404,7 +394,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 40,
|
||||
"ordinal": 39,
|
||||
"name": "maybe_public_bucket_prefix",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
@@ -414,7 +404,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 41,
|
||||
"ordinal": 40,
|
||||
"name": "maybe_public_bucket_extension",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
@@ -424,7 +414,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 42,
|
||||
"ordinal": 41,
|
||||
"name": "maybe_ratings_positive_count",
|
||||
"type_info": {
|
||||
"type": "Long",
|
||||
@@ -434,7 +424,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 43,
|
||||
"ordinal": 42,
|
||||
"name": "maybe_ratings_negative_count",
|
||||
"type_info": {
|
||||
"type": "Long",
|
||||
@@ -444,7 +434,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 44,
|
||||
"ordinal": 43,
|
||||
"name": "maybe_bookmark_count",
|
||||
"type_info": {
|
||||
"type": "Long",
|
||||
@@ -454,7 +444,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 45,
|
||||
"ordinal": 44,
|
||||
"name": "is_featured",
|
||||
"type_info": {
|
||||
"type": "LongLong",
|
||||
@@ -464,7 +454,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 46,
|
||||
"ordinal": 45,
|
||||
"name": "created_at",
|
||||
"type_info": {
|
||||
"type": "Timestamp",
|
||||
@@ -474,7 +464,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 47,
|
||||
"ordinal": 46,
|
||||
"name": "updated_at",
|
||||
"type_info": {
|
||||
"type": "Timestamp",
|
||||
@@ -511,7 +501,6 @@
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
@@ -538,5 +527,5 @@
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "57fa22e6cd4e82b801605c96eac21babd78df3df361b3be566993b78a46efef2"
|
||||
"hash": "ad217c26087b2ebed6f2c9f79b16ead63727107dfce3366f3c1d12bc9d565c2c"
|
||||
}
|
||||
-12
@@ -1,12 +0,0 @@
|
||||
{
|
||||
"db_name": "MySQL",
|
||||
"query": "\n INSERT INTO media_files\n SET\n token = ?,\n\n media_class = ?,\n media_type = ?,\n\n is_user_upload = ?,\n is_intermediate_system_file = ?,\n\n origin_category = ?, \n origin_product_category = ?, \n maybe_origin_model_type = ?, \n maybe_origin_model_token = ?, \n maybe_origin_filename = ?,\n\n is_batch_generated = ?,\n maybe_batch_token = ?,\n\n maybe_mime_type = ?,\n file_size_bytes = ?,\n maybe_duration_millis = ?,\n maybe_audio_encoding = ?, \n maybe_video_encoding = ?, \n maybe_frame_width = ?, \n maybe_frame_height = ?,\n maybe_prompt_token = ?,\n checksum_sha2 = ?,\n \n maybe_engine_category = ?,\n\n maybe_title = ?,\n maybe_text_transcript = ?,\n\n maybe_scene_source_media_file_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\n creator_ip_address = ?, \n creator_set_visibility = ?, \n\n maybe_creator_file_synthetic_id = ?, \n maybe_creator_category_synthetic_id = ?,\n\n extra_file_modification_info = ?,\n\n maybe_generation_provider = ?,\n\n platform_type = ?,\n\n maybe_cover_image_media_file_token = ?,\n\n maybe_mod_user_token = ?,\n is_generated_on_prem = ?,\n generated_by_worker = ?,\n generated_by_cluster = ?\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Right": 42
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "ad2ffbd9f48e52678a5c61817bec043bf1df4033463e5c5a18468c04af391e6a"
|
||||
}
|
||||
-12
@@ -1,12 +0,0 @@
|
||||
{
|
||||
"db_name": "MySQL",
|
||||
"query": "\n UPDATE media_files\n SET\n public_bucket_directory_hash = ?,\n maybe_public_bucket_prefix = ?,\n maybe_public_bucket_extension = ?,\n\n maybe_mime_type = ?,\n file_size_bytes = ?,\n\n creator_ip_address = ?\n WHERE token = ?\n LIMIT 1\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Right": 7
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "ad311b4aa95a71b1dc37aa09c27850a0e963380c6e7780f47a9fc5a95ab70583"
|
||||
}
|
||||
+223
@@ -0,0 +1,223 @@
|
||||
{
|
||||
"db_name": "MySQL",
|
||||
"query": "\nSELECT\n m.id as `id: u64`,\n m.token as `token: tokens::tokens::media_files::MediaFileToken`,\n\n m.media_class as `media_class: enums::by_table::media_files::media_file_class::MediaFileClass`,\n m.maybe_project_type as `project_type!: enums::by_table::media_files::media_file_project_type::MediaFileProjectType`,\n m.media_type as `media_type: enums::by_table::media_files::media_file_type::MediaFileType`,\n\n m.public_bucket_directory_hash,\n m.maybe_public_bucket_prefix,\n m.maybe_public_bucket_extension,\n\n users.token as `maybe_creator_user_token: tokens::tokens::users::UserToken`,\n users.username as maybe_creator_username,\n users.display_name as maybe_creator_display_name,\n users.email_gravatar_hash as maybe_creator_gravatar_hash,\n\n media_file_cover_image.public_bucket_directory_hash as maybe_file_cover_image_public_bucket_hash,\n media_file_cover_image.maybe_public_bucket_prefix as maybe_file_cover_image_public_bucket_prefix,\n media_file_cover_image.maybe_public_bucket_extension as maybe_file_cover_image_public_bucket_extension,\n\n m.creator_set_visibility as `creator_set_visibility: enums::common::visibility::Visibility`,\n\n m.maybe_title,\n\n m.created_at,\n m.updated_at\n\nFROM media_files AS m FORCE INDEX (fk_maybe_creator_user_token)\nLEFT OUTER JOIN users\n ON m.maybe_creator_user_token = users.token\nLEFT OUTER JOIN media_files as media_file_cover_image\n ON media_file_cover_image.token = m.maybe_cover_image_media_file_token\nWHERE\n m.maybe_creator_user_token = ?\n AND m.media_class = 'project'\n AND m.maybe_project_type IS NOT NULL\n AND m.user_deleted_at IS NULL\n AND m.mod_deleted_at IS NULL\n AND NOT m.is_intermediate_system_file\n AND m.id > ?\nORDER BY m.id ASC\nLIMIT ?\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "id: u64",
|
||||
"type_info": {
|
||||
"type": "LongLong",
|
||||
"flags": "NOT_NULL | PRIMARY_KEY | AUTO_INCREMENT",
|
||||
"char_set": 63,
|
||||
"max_size": 20
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "token: tokens::tokens::media_files::MediaFileToken",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "NOT_NULL | UNIQUE_KEY | BINARY | NO_DEFAULT_VALUE",
|
||||
"char_set": 224,
|
||||
"max_size": 128
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"name": "media_class: enums::by_table::media_files::media_file_class::MediaFileClass",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "NOT_NULL | MULTIPLE_KEY | BINARY",
|
||||
"char_set": 224,
|
||||
"max_size": 64
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 3,
|
||||
"name": "project_type!: enums::by_table::media_files::media_file_project_type::MediaFileProjectType",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "MULTIPLE_KEY | BINARY",
|
||||
"char_set": 224,
|
||||
"max_size": 128
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 4,
|
||||
"name": "media_type: enums::by_table::media_files::media_file_type::MediaFileType",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "NOT_NULL | MULTIPLE_KEY | BINARY | NO_DEFAULT_VALUE",
|
||||
"char_set": 224,
|
||||
"max_size": 64
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 5,
|
||||
"name": "public_bucket_directory_hash",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "NOT_NULL | BINARY | NO_DEFAULT_VALUE",
|
||||
"char_set": 224,
|
||||
"max_size": 128
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 6,
|
||||
"name": "maybe_public_bucket_prefix",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "BINARY",
|
||||
"char_set": 224,
|
||||
"max_size": 64
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 7,
|
||||
"name": "maybe_public_bucket_extension",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "BINARY",
|
||||
"char_set": 224,
|
||||
"max_size": 64
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 8,
|
||||
"name": "maybe_creator_user_token: tokens::tokens::users::UserToken",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "UNIQUE_KEY | BINARY | NO_DEFAULT_VALUE",
|
||||
"char_set": 224,
|
||||
"max_size": 128
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 9,
|
||||
"name": "maybe_creator_username",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "UNIQUE_KEY | BINARY | NO_DEFAULT_VALUE",
|
||||
"char_set": 224,
|
||||
"max_size": 80
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 10,
|
||||
"name": "maybe_creator_display_name",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "BINARY | NO_DEFAULT_VALUE",
|
||||
"char_set": 224,
|
||||
"max_size": 80
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 11,
|
||||
"name": "maybe_creator_gravatar_hash",
|
||||
"type_info": {
|
||||
"type": "String",
|
||||
"flags": "BINARY | NO_DEFAULT_VALUE",
|
||||
"char_set": 224,
|
||||
"max_size": 128
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 12,
|
||||
"name": "maybe_file_cover_image_public_bucket_hash",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "BINARY | NO_DEFAULT_VALUE",
|
||||
"char_set": 224,
|
||||
"max_size": 128
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 13,
|
||||
"name": "maybe_file_cover_image_public_bucket_prefix",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "BINARY",
|
||||
"char_set": 224,
|
||||
"max_size": 64
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 14,
|
||||
"name": "maybe_file_cover_image_public_bucket_extension",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "BINARY",
|
||||
"char_set": 224,
|
||||
"max_size": 64
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 15,
|
||||
"name": "creator_set_visibility: enums::common::visibility::Visibility",
|
||||
"type_info": {
|
||||
"type": "String",
|
||||
"flags": "NOT_NULL | BINARY | ENUM",
|
||||
"char_set": 224,
|
||||
"max_size": 28
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 16,
|
||||
"name": "maybe_title",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "BINARY",
|
||||
"char_set": 224,
|
||||
"max_size": 1020
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 17,
|
||||
"name": "created_at",
|
||||
"type_info": {
|
||||
"type": "Timestamp",
|
||||
"flags": "NOT_NULL | MULTIPLE_KEY | BINARY | TIMESTAMP",
|
||||
"char_set": 63,
|
||||
"max_size": 19
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 18,
|
||||
"name": "updated_at",
|
||||
"type_info": {
|
||||
"type": "Timestamp",
|
||||
"flags": "NOT_NULL | MULTIPLE_KEY | BINARY | TIMESTAMP | ON_UPDATE_NOW",
|
||||
"char_set": 63,
|
||||
"max_size": 19
|
||||
}
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Right": 3
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "d8f4ce4e1026c71a7be8fd87f00f6f8858c401701da1ca3316eed1ed971d4bb2"
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"db_name": "MySQL",
|
||||
"query": "\n UPDATE media_files\n SET\n media_class = ?,\n maybe_project_type = ?,\n\n public_bucket_directory_hash = ?,\n maybe_public_bucket_prefix = ?,\n maybe_public_bucket_extension = ?,\n\n maybe_mime_type = ?,\n file_size_bytes = ?,\n\n creator_ip_address = ?\n WHERE token = ?\n LIMIT 1\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Right": 9
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "e33fa6280a39fff22b2e8c2731b7b6058b4705b5b557f34dba52a6440c22529b"
|
||||
}
|
||||
@@ -77,3 +77,20 @@ Organize for top-to-bottom reading. Important things first, details later.
|
||||
- Private helpers go *below* the methods that call them
|
||||
- Among helpers: meatier logic above leaf-level formatters
|
||||
- **In test modules**: constants first, then test cases (grouped into sub-modules when 2+), then helper functions last
|
||||
|
||||
## Markdown
|
||||
|
||||
- **Tables must be space-padded so columns align in plain text.** Markdown
|
||||
tables are read raw (terminals, diffs, editors) at least as often as
|
||||
rendered, and condensed tables are unreadable there. Pad every cell to its
|
||||
column width:
|
||||
|
||||
```markdown
|
||||
| Model | Configuration | Credits | Speed | Score |
|
||||
|--------------|---------------|--------------------|---------|
|
||||
| Meshy 6 | text or image | 104 | 80.0 | +24 |
|
||||
| Rodin 2.5 | text or image | 13 | 10.0 | +3 |
|
||||
```
|
||||
|
||||
Not: `| Model | Configuration | Credits | Speed | Score |` packed tight
|
||||
with varying widths per row.
|
||||
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
-- noinspection SqlDialectInspectionForFile
|
||||
-- noinspection SqlNoDataSourceInspectionForFile
|
||||
-- noinspection SqlResolveForFile
|
||||
|
||||
ALTER TABLE media_files
|
||||
ADD COLUMN has_watermark BOOLEAN NOT NULL DEFAULT FALSE
|
||||
AFTER checksum_sha2;
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
-- noinspection SqlDialectInspectionForFile
|
||||
-- noinspection SqlNoDataSourceInspectionForFile
|
||||
-- noinspection SqlResolveForFile
|
||||
|
||||
-- NB: MySQL 8.0.29+ performs DROP COLUMN with ALGORITHM=INSTANT (metadata-only).
|
||||
-- On older versions this is an INPLACE table rebuild that still permits
|
||||
-- concurrent reads and writes. No ALGORITHM clause so the server picks the
|
||||
-- best available.
|
||||
ALTER TABLE media_files
|
||||
DROP COLUMN has_watermark;
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
-- noinspection SqlDialectInspectionForFile
|
||||
-- noinspection SqlNoDataSourceInspectionForFile
|
||||
-- noinspection SqlResolveForFile
|
||||
|
||||
ALTER TABLE media_files
|
||||
ADD COLUMN is_in_aws BOOLEAN NOT NULL DEFAULT FALSE
|
||||
AFTER maybe_public_bucket_extension;
|
||||
|
||||
ALTER TABLE media_files
|
||||
ADD INDEX index_is_in_aws (is_in_aws);
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
-- noinspection SqlDialectInspectionForFile
|
||||
-- noinspection SqlNoDataSourceInspectionForFile
|
||||
-- noinspection SqlResolveForFile
|
||||
|
||||
ALTER TABLE media_files
|
||||
DROP INDEX index_is_in_aws,
|
||||
DROP COLUMN is_in_aws;
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
-- noinspection SqlDialectInspectionForFile
|
||||
-- noinspection SqlNoDataSourceInspectionForFile
|
||||
-- noinspection SqlResolveForFile
|
||||
|
||||
-- NB: anchored to maybe_public_bucket_extension (not is_in_aws) so this works
|
||||
-- whether or not the is_in_aws drop migration has been reverted yet.
|
||||
ALTER TABLE media_files
|
||||
ADD COLUMN is_not_in_gcp BOOLEAN NOT NULL DEFAULT FALSE
|
||||
AFTER maybe_public_bucket_extension;
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
-- noinspection SqlDialectInspectionForFile
|
||||
-- noinspection SqlNoDataSourceInspectionForFile
|
||||
-- noinspection SqlResolveForFile
|
||||
|
||||
ALTER TABLE media_files
|
||||
DROP COLUMN is_not_in_gcp;
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
-- noinspection SqlDialectInspectionForFile
|
||||
-- noinspection SqlNoDataSourceInspectionForFile
|
||||
-- noinspection SqlResolveForFile
|
||||
|
||||
ALTER TABLE media_files
|
||||
ADD COLUMN nsfw_status VARCHAR(32) NOT NULL DEFAULT 'unknown'
|
||||
AFTER maybe_scene_source_media_file_token;
|
||||
|
||||
ALTER TABLE media_files
|
||||
ADD INDEX index_nsfw_status (nsfw_status);
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
-- noinspection SqlDialectInspectionForFile
|
||||
-- noinspection SqlNoDataSourceInspectionForFile
|
||||
-- noinspection SqlResolveForFile
|
||||
|
||||
ALTER TABLE media_files
|
||||
DROP INDEX index_nsfw_status,
|
||||
DROP COLUMN nsfw_status;
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
-- noinspection SqlDialectInspectionForFile
|
||||
-- noinspection SqlNoDataSourceInspectionForFile
|
||||
-- noinspection SqlResolveForFile
|
||||
|
||||
ALTER TABLE media_files
|
||||
ADD COLUMN is_batch_generated BOOLEAN NOT NULL DEFAULT FALSE
|
||||
AFTER maybe_origin_filename;
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
-- noinspection SqlDialectInspectionForFile
|
||||
-- noinspection SqlNoDataSourceInspectionForFile
|
||||
-- noinspection SqlResolveForFile
|
||||
|
||||
ALTER TABLE media_files
|
||||
DROP COLUMN is_batch_generated;
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
-- noinspection SqlDialectInspectionForFile
|
||||
-- noinspection SqlNoDataSourceInspectionForFile
|
||||
-- noinspection SqlResolveForFile
|
||||
|
||||
ALTER TABLE media_files
|
||||
ADD COLUMN maybe_media_subtype VARCHAR(32) DEFAULT NULL
|
||||
AFTER media_class;
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
-- noinspection SqlDialectInspectionForFile
|
||||
-- noinspection SqlNoDataSourceInspectionForFile
|
||||
-- noinspection SqlResolveForFile
|
||||
|
||||
ALTER TABLE media_files
|
||||
DROP COLUMN maybe_media_subtype;
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
-- noinspection SqlDialectInspectionForFile
|
||||
-- noinspection SqlNoDataSourceInspectionForFile
|
||||
-- noinspection SqlResolveForFile
|
||||
|
||||
ALTER TABLE media_files
|
||||
DROP INDEX index_maybe_project_type,
|
||||
DROP COLUMN maybe_project_type;
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
-- noinspection SqlDialectInspectionForFile
|
||||
-- noinspection SqlNoDataSourceInspectionForFile
|
||||
-- noinspection SqlResolveForFile
|
||||
|
||||
-- Which kind of internal Artcraft project document this media file is
|
||||
-- ('scene_3d', 'mood_board', 'workflow', 'video_timeline', ...).
|
||||
-- Only set when media_class = 'project'. The file format lives in media_type;
|
||||
-- the JSON payload carries its own schema version.
|
||||
ALTER TABLE media_files
|
||||
ADD COLUMN maybe_project_type VARCHAR(32) DEFAULT NULL
|
||||
AFTER media_class;
|
||||
|
||||
-- For "list my mood boards"-style filtered queries.
|
||||
-- The column is all-NULL at creation time, so the index build is cheap.
|
||||
ALTER TABLE media_files
|
||||
ADD INDEX index_maybe_project_type (maybe_project_type);
|
||||
@@ -99,12 +99,6 @@ CREATE TABLE media_files (
|
||||
-- The original filename of the media (if uploaded by a user)
|
||||
maybe_origin_filename VARCHAR(255) DEFAULT NULL,
|
||||
|
||||
-- TODO: Remove after `maybe_batch_token` gains use.
|
||||
-- Whether this media file was generated as part of a batch
|
||||
-- If so, we can look up the batch in a separate query/call to the `batch_generations` table.
|
||||
-- We won't hold the batch token in this table since it'll be very sparsely populated.
|
||||
is_batch_generated BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
|
||||
-- If the media file is generated as part of a batch, this designates the batch.
|
||||
maybe_batch_token VARCHAR(32) DEFAULT NULL,
|
||||
|
||||
@@ -138,9 +132,6 @@ CREATE TABLE media_files (
|
||||
-- The token is a media file token.
|
||||
maybe_scene_source_media_file_token VARCHAR(32) DEFAULT NULL,
|
||||
|
||||
-- A flag for NSFW status.
|
||||
nsfw_status VARCHAR(32) NOT NULL DEFAULT "unknown",
|
||||
|
||||
-- ========== MEDIA DETAILS ==========
|
||||
|
||||
-- Type of media file:
|
||||
@@ -166,20 +157,19 @@ CREATE TABLE media_files (
|
||||
-- This is especially helpful with engine types that map to specific semantics.
|
||||
-- * 'unknown' (TODO: This is the default until all records are backfilled.)
|
||||
-- * 'audio' for wav, mp3, etc.
|
||||
-- * 'image' for a variety of video types.
|
||||
-- * 'image' for a variety of image types.
|
||||
-- * 'video' for a variety of video types.
|
||||
-- * 'dimensional' for a variety of 3d types
|
||||
-- * 'dimensional' (DEPRECATED) split into 'mesh', 'splat', and 'project'; read-only until backfilled
|
||||
-- * 'mesh' for 3d mesh assets (glb, gltf, fbx, obj, pmx, pmd, etc.)
|
||||
-- * 'splat' for Gaussian splats (spz, etc.)
|
||||
-- * 'project' for internal Artcraft project documents (see maybe_project_type)
|
||||
media_class VARCHAR(16) NOT NULL DEFAULT "unknown",
|
||||
|
||||
-- DEPRECATED / DO NOT USE. THIS FIELD IS NO LONGER RELEVANT.
|
||||
-- A media file's possible subtype. Typically used for Storyteller Studio.
|
||||
-- * 'mixamo' for mixamo animations (eg. for BVH, GLB, FBX, etc. files)
|
||||
-- * 'mocap_net' for mocapnet animations (eg. for BVH files)
|
||||
-- * 'scene'
|
||||
-- * 'character'
|
||||
-- * 'animation'
|
||||
-- DO NOT USE THE `maybe_media_subtype` FIELD!
|
||||
maybe_media_subtype VARCHAR(32) DEFAULT NULL,
|
||||
-- Which kind of internal Artcraft project document this media file is
|
||||
-- ('scene_3d', 'mood_board', 'workflow', 'video_timeline', ...).
|
||||
-- Only set when media_class = 'project'. The file format lives in media_type;
|
||||
-- the JSON payload carries its own schema version.
|
||||
maybe_project_type VARCHAR(32) DEFAULT NULL,
|
||||
|
||||
-- The file's mime type.
|
||||
maybe_mime_type VARCHAR(32) DEFAULT NULL,
|
||||
@@ -227,10 +217,6 @@ CREATE TABLE media_files (
|
||||
-- and produces different lengths output depending on the choice of algorithm.
|
||||
checksum_sha2 CHAR(64) NOT NULL,
|
||||
|
||||
-- Whether the image or video has a visible watermark.
|
||||
-- If so, we may not need to add a watermark downstream in further processing steps.
|
||||
has_watermark BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
|
||||
-- Tracks the thumbnail algorithm/type used for this media file.
|
||||
-- NULL = no thumbnail generated yet.
|
||||
-- 1 = video thumbnail.
|
||||
@@ -259,13 +245,6 @@ CREATE TABLE media_files (
|
||||
-- `[{maybe_public_bucket_prefix}]{public_bucket_directory_hash}[{maybe_public_bucket_extension}]`
|
||||
maybe_public_bucket_extension VARCHAR(16) DEFAULT NULL,
|
||||
|
||||
-- This is a migration flag that denotes whether this media file is stored in AWS.
|
||||
-- If not, it's stored in GCP.
|
||||
is_in_aws BOOLEAN NOT NULL DEFAULT false,
|
||||
|
||||
-- This is a migration flag that denotes that weight is not stored in GCP.
|
||||
is_not_in_gcp BOOLEAN NOT NULL DEFAULT false,
|
||||
|
||||
-- NB: Removed, since this can be derived.
|
||||
-- The directory this media is uploaded to will be exclusive for this file.
|
||||
-- Only this given record will live in this bucket, but the directory may include
|
||||
@@ -351,13 +330,12 @@ CREATE TABLE media_files (
|
||||
KEY index_is_user_upload (is_user_upload),
|
||||
KEY index_is_intermediate_system_file (is_intermediate_system_file),
|
||||
KEY index_maybe_scene_source_media_file_token (maybe_scene_source_media_file_token),
|
||||
KEY index_nsfw_status (nsfw_status),
|
||||
KEY index_media_type (media_type),
|
||||
KEY index_media_class (media_class),
|
||||
KEY index_maybe_project_type (maybe_project_type),
|
||||
KEY index_maybe_engine_category (maybe_engine_category),
|
||||
KEY fk_maybe_prompt_token (maybe_prompt_token),
|
||||
KEY index_checksum_sha2 (checksum_sha2),
|
||||
KEY index_is_in_aws (is_in_aws),
|
||||
KEY fk_maybe_creator_user_token (maybe_creator_user_token),
|
||||
KEY fk_maybe_mod_user_token (maybe_mod_user_token),
|
||||
KEY index_creator_ip_address (creator_ip_address),
|
||||
|
||||
@@ -1,267 +0,0 @@
|
||||
{
|
||||
"title": "[GET] /v1/engine/scene/{token}",
|
||||
"description": "Auto-generated per-endpoint dashboard for `GET /v1/engine/scene/{token}`.\n\nFilters all metrics on `service:storyteller-web,route:/v1/engine/scene/_token,method:get`.\n\nSource: `script/metrics/generate_endpoint_dashboards.py`. Do not hand-edit; changes will be overwritten on the next regeneration.",
|
||||
"layout_type": "ordered",
|
||||
"notify_list": [],
|
||||
"reflow_type": "fixed",
|
||||
"widgets": [
|
||||
{
|
||||
"definition": {
|
||||
"type": "group",
|
||||
"layout_type": "ordered",
|
||||
"title": "Top-line health",
|
||||
"show_title": true,
|
||||
"widgets": [
|
||||
{
|
||||
"definition": {
|
||||
"type": "query_value",
|
||||
"title": "Requests/sec",
|
||||
"title_size": "16",
|
||||
"title_align": "left",
|
||||
"precision": 2,
|
||||
"autoscale": true,
|
||||
"requests": [
|
||||
{
|
||||
"q": "sum:http.server.request.count{service:storyteller-web,route:/v1/engine/scene/_token,method:get}.as_rate()",
|
||||
"aggregator": "avg"
|
||||
}
|
||||
]
|
||||
},
|
||||
"layout": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"width": 3,
|
||||
"height": 2
|
||||
}
|
||||
},
|
||||
{
|
||||
"definition": {
|
||||
"type": "query_value",
|
||||
"title": "5xx error rate (%)",
|
||||
"title_size": "16",
|
||||
"title_align": "left",
|
||||
"precision": 2,
|
||||
"autoscale": false,
|
||||
"custom_unit": "%",
|
||||
"requests": [
|
||||
{
|
||||
"q": "(sum:http.server.request.count{service:storyteller-web,route:/v1/engine/scene/_token,method:get,status_class:5xx}.as_count() / sum:http.server.request.count{service:storyteller-web,route:/v1/engine/scene/_token,method:get}.as_count()) * 100",
|
||||
"aggregator": "avg",
|
||||
"conditional_formats": [
|
||||
{
|
||||
"comparator": ">",
|
||||
"value": 5.0,
|
||||
"palette": "white_on_red"
|
||||
},
|
||||
{
|
||||
"comparator": ">",
|
||||
"value": 1.0,
|
||||
"palette": "white_on_yellow"
|
||||
},
|
||||
{
|
||||
"comparator": "<=",
|
||||
"value": 1.0,
|
||||
"palette": "white_on_green"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"layout": {
|
||||
"x": 3,
|
||||
"y": 0,
|
||||
"width": 3,
|
||||
"height": 2
|
||||
}
|
||||
},
|
||||
{
|
||||
"definition": {
|
||||
"type": "query_value",
|
||||
"title": "Latency p95 (ms)",
|
||||
"title_size": "16",
|
||||
"title_align": "left",
|
||||
"precision": 0,
|
||||
"autoscale": true,
|
||||
"custom_unit": "ms",
|
||||
"requests": [
|
||||
{
|
||||
"q": "p95:http.server.request.duration_ms{service:storyteller-web,route:/v1/engine/scene/_token,method:get}",
|
||||
"aggregator": "avg",
|
||||
"conditional_formats": [
|
||||
{
|
||||
"comparator": ">",
|
||||
"value": 1000,
|
||||
"palette": "white_on_red"
|
||||
},
|
||||
{
|
||||
"comparator": ">",
|
||||
"value": 250,
|
||||
"palette": "white_on_yellow"
|
||||
},
|
||||
{
|
||||
"comparator": "<=",
|
||||
"value": 250,
|
||||
"palette": "white_on_green"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"layout": {
|
||||
"x": 6,
|
||||
"y": 0,
|
||||
"width": 3,
|
||||
"height": 2
|
||||
}
|
||||
},
|
||||
{
|
||||
"definition": {
|
||||
"type": "query_value",
|
||||
"title": "Latency p99 (ms)",
|
||||
"title_size": "16",
|
||||
"title_align": "left",
|
||||
"precision": 0,
|
||||
"autoscale": true,
|
||||
"custom_unit": "ms",
|
||||
"requests": [
|
||||
{
|
||||
"q": "p99:http.server.request.duration_ms{service:storyteller-web,route:/v1/engine/scene/_token,method:get}",
|
||||
"aggregator": "avg"
|
||||
}
|
||||
]
|
||||
},
|
||||
"layout": {
|
||||
"x": 9,
|
||||
"y": 0,
|
||||
"width": 3,
|
||||
"height": 2
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"layout": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"width": 12,
|
||||
"height": 3
|
||||
}
|
||||
},
|
||||
{
|
||||
"definition": {
|
||||
"type": "group",
|
||||
"layout_type": "ordered",
|
||||
"title": "Trends",
|
||||
"show_title": true,
|
||||
"widgets": [
|
||||
{
|
||||
"definition": {
|
||||
"type": "timeseries",
|
||||
"title": "Requests/sec by status class",
|
||||
"title_size": "16",
|
||||
"title_align": "left",
|
||||
"show_legend": true,
|
||||
"legend_layout": "horizontal",
|
||||
"requests": [
|
||||
{
|
||||
"q": "sum:http.server.request.count{service:storyteller-web,route:/v1/engine/scene/_token,method:get} by {status_class}.as_rate()",
|
||||
"display_type": "bars",
|
||||
"style": {
|
||||
"palette": "semantic",
|
||||
"line_type": "solid",
|
||||
"line_width": "normal"
|
||||
}
|
||||
}
|
||||
],
|
||||
"yaxis": {
|
||||
"scale": "linear",
|
||||
"include_zero": true
|
||||
}
|
||||
},
|
||||
"layout": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"width": 6,
|
||||
"height": 4
|
||||
}
|
||||
},
|
||||
{
|
||||
"definition": {
|
||||
"type": "timeseries",
|
||||
"title": "Latency percentiles",
|
||||
"title_size": "16",
|
||||
"title_align": "left",
|
||||
"show_legend": true,
|
||||
"legend_layout": "horizontal",
|
||||
"requests": [
|
||||
{
|
||||
"q": "p50:http.server.request.duration_ms{service:storyteller-web,route:/v1/engine/scene/_token,method:get}",
|
||||
"display_type": "line",
|
||||
"style": {
|
||||
"palette": "cool",
|
||||
"line_type": "solid",
|
||||
"line_width": "normal"
|
||||
}
|
||||
},
|
||||
{
|
||||
"q": "p95:http.server.request.duration_ms{service:storyteller-web,route:/v1/engine/scene/_token,method:get}",
|
||||
"display_type": "line",
|
||||
"style": {
|
||||
"palette": "warm",
|
||||
"line_type": "solid",
|
||||
"line_width": "normal"
|
||||
}
|
||||
},
|
||||
{
|
||||
"q": "p99:http.server.request.duration_ms{service:storyteller-web,route:/v1/engine/scene/_token,method:get}",
|
||||
"display_type": "line",
|
||||
"style": {
|
||||
"palette": "warm",
|
||||
"line_type": "dashed",
|
||||
"line_width": "normal"
|
||||
}
|
||||
}
|
||||
],
|
||||
"yaxis": {
|
||||
"scale": "linear",
|
||||
"include_zero": true,
|
||||
"label": "ms"
|
||||
}
|
||||
},
|
||||
"layout": {
|
||||
"x": 6,
|
||||
"y": 0,
|
||||
"width": 6,
|
||||
"height": 4
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"layout": {
|
||||
"x": 0,
|
||||
"y": 3,
|
||||
"width": 12,
|
||||
"height": 5
|
||||
}
|
||||
},
|
||||
{
|
||||
"definition": {
|
||||
"type": "toplist",
|
||||
"title": "Status code distribution",
|
||||
"title_size": "16",
|
||||
"title_align": "left",
|
||||
"requests": [
|
||||
{
|
||||
"q": "top(sum:http.server.request.count{service:storyteller-web,route:/v1/engine/scene/_token,method:get} by {status_code}.as_count(), 25, 'sum', 'desc')"
|
||||
}
|
||||
]
|
||||
},
|
||||
"layout": {
|
||||
"x": 0,
|
||||
"y": 8,
|
||||
"width": 12,
|
||||
"height": 4
|
||||
}
|
||||
}
|
||||
],
|
||||
"id": "24m-c4g-btm"
|
||||
}
|
||||
@@ -1,267 +0,0 @@
|
||||
{
|
||||
"title": "[POST] /v1/engine/create_scene",
|
||||
"description": "Auto-generated per-endpoint dashboard for `POST /v1/engine/create_scene`.\n\nFilters all metrics on `service:storyteller-web,route:/v1/engine/create_scene,method:post`.\n\nSource: `script/metrics/generate_endpoint_dashboards.py`. Do not hand-edit; changes will be overwritten on the next regeneration.",
|
||||
"layout_type": "ordered",
|
||||
"notify_list": [],
|
||||
"reflow_type": "fixed",
|
||||
"widgets": [
|
||||
{
|
||||
"definition": {
|
||||
"type": "group",
|
||||
"layout_type": "ordered",
|
||||
"title": "Top-line health",
|
||||
"show_title": true,
|
||||
"widgets": [
|
||||
{
|
||||
"definition": {
|
||||
"type": "query_value",
|
||||
"title": "Requests/sec",
|
||||
"title_size": "16",
|
||||
"title_align": "left",
|
||||
"precision": 2,
|
||||
"autoscale": true,
|
||||
"requests": [
|
||||
{
|
||||
"q": "sum:http.server.request.count{service:storyteller-web,route:/v1/engine/create_scene,method:post}.as_rate()",
|
||||
"aggregator": "avg"
|
||||
}
|
||||
]
|
||||
},
|
||||
"layout": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"width": 3,
|
||||
"height": 2
|
||||
}
|
||||
},
|
||||
{
|
||||
"definition": {
|
||||
"type": "query_value",
|
||||
"title": "5xx error rate (%)",
|
||||
"title_size": "16",
|
||||
"title_align": "left",
|
||||
"precision": 2,
|
||||
"autoscale": false,
|
||||
"custom_unit": "%",
|
||||
"requests": [
|
||||
{
|
||||
"q": "(sum:http.server.request.count{service:storyteller-web,route:/v1/engine/create_scene,method:post,status_class:5xx}.as_count() / sum:http.server.request.count{service:storyteller-web,route:/v1/engine/create_scene,method:post}.as_count()) * 100",
|
||||
"aggregator": "avg",
|
||||
"conditional_formats": [
|
||||
{
|
||||
"comparator": ">",
|
||||
"value": 5.0,
|
||||
"palette": "white_on_red"
|
||||
},
|
||||
{
|
||||
"comparator": ">",
|
||||
"value": 1.0,
|
||||
"palette": "white_on_yellow"
|
||||
},
|
||||
{
|
||||
"comparator": "<=",
|
||||
"value": 1.0,
|
||||
"palette": "white_on_green"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"layout": {
|
||||
"x": 3,
|
||||
"y": 0,
|
||||
"width": 3,
|
||||
"height": 2
|
||||
}
|
||||
},
|
||||
{
|
||||
"definition": {
|
||||
"type": "query_value",
|
||||
"title": "Latency p95 (ms)",
|
||||
"title_size": "16",
|
||||
"title_align": "left",
|
||||
"precision": 0,
|
||||
"autoscale": true,
|
||||
"custom_unit": "ms",
|
||||
"requests": [
|
||||
{
|
||||
"q": "p95:http.server.request.duration_ms{service:storyteller-web,route:/v1/engine/create_scene,method:post}",
|
||||
"aggregator": "avg",
|
||||
"conditional_formats": [
|
||||
{
|
||||
"comparator": ">",
|
||||
"value": 1000,
|
||||
"palette": "white_on_red"
|
||||
},
|
||||
{
|
||||
"comparator": ">",
|
||||
"value": 250,
|
||||
"palette": "white_on_yellow"
|
||||
},
|
||||
{
|
||||
"comparator": "<=",
|
||||
"value": 250,
|
||||
"palette": "white_on_green"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"layout": {
|
||||
"x": 6,
|
||||
"y": 0,
|
||||
"width": 3,
|
||||
"height": 2
|
||||
}
|
||||
},
|
||||
{
|
||||
"definition": {
|
||||
"type": "query_value",
|
||||
"title": "Latency p99 (ms)",
|
||||
"title_size": "16",
|
||||
"title_align": "left",
|
||||
"precision": 0,
|
||||
"autoscale": true,
|
||||
"custom_unit": "ms",
|
||||
"requests": [
|
||||
{
|
||||
"q": "p99:http.server.request.duration_ms{service:storyteller-web,route:/v1/engine/create_scene,method:post}",
|
||||
"aggregator": "avg"
|
||||
}
|
||||
]
|
||||
},
|
||||
"layout": {
|
||||
"x": 9,
|
||||
"y": 0,
|
||||
"width": 3,
|
||||
"height": 2
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"layout": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"width": 12,
|
||||
"height": 3
|
||||
}
|
||||
},
|
||||
{
|
||||
"definition": {
|
||||
"type": "group",
|
||||
"layout_type": "ordered",
|
||||
"title": "Trends",
|
||||
"show_title": true,
|
||||
"widgets": [
|
||||
{
|
||||
"definition": {
|
||||
"type": "timeseries",
|
||||
"title": "Requests/sec by status class",
|
||||
"title_size": "16",
|
||||
"title_align": "left",
|
||||
"show_legend": true,
|
||||
"legend_layout": "horizontal",
|
||||
"requests": [
|
||||
{
|
||||
"q": "sum:http.server.request.count{service:storyteller-web,route:/v1/engine/create_scene,method:post} by {status_class}.as_rate()",
|
||||
"display_type": "bars",
|
||||
"style": {
|
||||
"palette": "semantic",
|
||||
"line_type": "solid",
|
||||
"line_width": "normal"
|
||||
}
|
||||
}
|
||||
],
|
||||
"yaxis": {
|
||||
"scale": "linear",
|
||||
"include_zero": true
|
||||
}
|
||||
},
|
||||
"layout": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"width": 6,
|
||||
"height": 4
|
||||
}
|
||||
},
|
||||
{
|
||||
"definition": {
|
||||
"type": "timeseries",
|
||||
"title": "Latency percentiles",
|
||||
"title_size": "16",
|
||||
"title_align": "left",
|
||||
"show_legend": true,
|
||||
"legend_layout": "horizontal",
|
||||
"requests": [
|
||||
{
|
||||
"q": "p50:http.server.request.duration_ms{service:storyteller-web,route:/v1/engine/create_scene,method:post}",
|
||||
"display_type": "line",
|
||||
"style": {
|
||||
"palette": "cool",
|
||||
"line_type": "solid",
|
||||
"line_width": "normal"
|
||||
}
|
||||
},
|
||||
{
|
||||
"q": "p95:http.server.request.duration_ms{service:storyteller-web,route:/v1/engine/create_scene,method:post}",
|
||||
"display_type": "line",
|
||||
"style": {
|
||||
"palette": "warm",
|
||||
"line_type": "solid",
|
||||
"line_width": "normal"
|
||||
}
|
||||
},
|
||||
{
|
||||
"q": "p99:http.server.request.duration_ms{service:storyteller-web,route:/v1/engine/create_scene,method:post}",
|
||||
"display_type": "line",
|
||||
"style": {
|
||||
"palette": "warm",
|
||||
"line_type": "dashed",
|
||||
"line_width": "normal"
|
||||
}
|
||||
}
|
||||
],
|
||||
"yaxis": {
|
||||
"scale": "linear",
|
||||
"include_zero": true,
|
||||
"label": "ms"
|
||||
}
|
||||
},
|
||||
"layout": {
|
||||
"x": 6,
|
||||
"y": 0,
|
||||
"width": 6,
|
||||
"height": 4
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"layout": {
|
||||
"x": 0,
|
||||
"y": 3,
|
||||
"width": 12,
|
||||
"height": 5
|
||||
}
|
||||
},
|
||||
{
|
||||
"definition": {
|
||||
"type": "toplist",
|
||||
"title": "Status code distribution",
|
||||
"title_size": "16",
|
||||
"title_align": "left",
|
||||
"requests": [
|
||||
{
|
||||
"q": "top(sum:http.server.request.count{service:storyteller-web,route:/v1/engine/create_scene,method:post} by {status_code}.as_count(), 25, 'sum', 'desc')"
|
||||
}
|
||||
]
|
||||
},
|
||||
"layout": {
|
||||
"x": 0,
|
||||
"y": 8,
|
||||
"width": 12,
|
||||
"height": 4
|
||||
}
|
||||
}
|
||||
],
|
||||
"id": "z9c-89r-p57"
|
||||
}
|
||||
@@ -1,267 +0,0 @@
|
||||
{
|
||||
"title": "[POST] /v1/engine/scene/{token}",
|
||||
"description": "Auto-generated per-endpoint dashboard for `POST /v1/engine/scene/{token}`.\n\nFilters all metrics on `service:storyteller-web,route:/v1/engine/scene/_token,method:post`.\n\nSource: `script/metrics/generate_endpoint_dashboards.py`. Do not hand-edit; changes will be overwritten on the next regeneration.",
|
||||
"layout_type": "ordered",
|
||||
"notify_list": [],
|
||||
"reflow_type": "fixed",
|
||||
"widgets": [
|
||||
{
|
||||
"definition": {
|
||||
"type": "group",
|
||||
"layout_type": "ordered",
|
||||
"title": "Top-line health",
|
||||
"show_title": true,
|
||||
"widgets": [
|
||||
{
|
||||
"definition": {
|
||||
"type": "query_value",
|
||||
"title": "Requests/sec",
|
||||
"title_size": "16",
|
||||
"title_align": "left",
|
||||
"precision": 2,
|
||||
"autoscale": true,
|
||||
"requests": [
|
||||
{
|
||||
"q": "sum:http.server.request.count{service:storyteller-web,route:/v1/engine/scene/_token,method:post}.as_rate()",
|
||||
"aggregator": "avg"
|
||||
}
|
||||
]
|
||||
},
|
||||
"layout": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"width": 3,
|
||||
"height": 2
|
||||
}
|
||||
},
|
||||
{
|
||||
"definition": {
|
||||
"type": "query_value",
|
||||
"title": "5xx error rate (%)",
|
||||
"title_size": "16",
|
||||
"title_align": "left",
|
||||
"precision": 2,
|
||||
"autoscale": false,
|
||||
"custom_unit": "%",
|
||||
"requests": [
|
||||
{
|
||||
"q": "(sum:http.server.request.count{service:storyteller-web,route:/v1/engine/scene/_token,method:post,status_class:5xx}.as_count() / sum:http.server.request.count{service:storyteller-web,route:/v1/engine/scene/_token,method:post}.as_count()) * 100",
|
||||
"aggregator": "avg",
|
||||
"conditional_formats": [
|
||||
{
|
||||
"comparator": ">",
|
||||
"value": 5.0,
|
||||
"palette": "white_on_red"
|
||||
},
|
||||
{
|
||||
"comparator": ">",
|
||||
"value": 1.0,
|
||||
"palette": "white_on_yellow"
|
||||
},
|
||||
{
|
||||
"comparator": "<=",
|
||||
"value": 1.0,
|
||||
"palette": "white_on_green"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"layout": {
|
||||
"x": 3,
|
||||
"y": 0,
|
||||
"width": 3,
|
||||
"height": 2
|
||||
}
|
||||
},
|
||||
{
|
||||
"definition": {
|
||||
"type": "query_value",
|
||||
"title": "Latency p95 (ms)",
|
||||
"title_size": "16",
|
||||
"title_align": "left",
|
||||
"precision": 0,
|
||||
"autoscale": true,
|
||||
"custom_unit": "ms",
|
||||
"requests": [
|
||||
{
|
||||
"q": "p95:http.server.request.duration_ms{service:storyteller-web,route:/v1/engine/scene/_token,method:post}",
|
||||
"aggregator": "avg",
|
||||
"conditional_formats": [
|
||||
{
|
||||
"comparator": ">",
|
||||
"value": 1000,
|
||||
"palette": "white_on_red"
|
||||
},
|
||||
{
|
||||
"comparator": ">",
|
||||
"value": 250,
|
||||
"palette": "white_on_yellow"
|
||||
},
|
||||
{
|
||||
"comparator": "<=",
|
||||
"value": 250,
|
||||
"palette": "white_on_green"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"layout": {
|
||||
"x": 6,
|
||||
"y": 0,
|
||||
"width": 3,
|
||||
"height": 2
|
||||
}
|
||||
},
|
||||
{
|
||||
"definition": {
|
||||
"type": "query_value",
|
||||
"title": "Latency p99 (ms)",
|
||||
"title_size": "16",
|
||||
"title_align": "left",
|
||||
"precision": 0,
|
||||
"autoscale": true,
|
||||
"custom_unit": "ms",
|
||||
"requests": [
|
||||
{
|
||||
"q": "p99:http.server.request.duration_ms{service:storyteller-web,route:/v1/engine/scene/_token,method:post}",
|
||||
"aggregator": "avg"
|
||||
}
|
||||
]
|
||||
},
|
||||
"layout": {
|
||||
"x": 9,
|
||||
"y": 0,
|
||||
"width": 3,
|
||||
"height": 2
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"layout": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"width": 12,
|
||||
"height": 3
|
||||
}
|
||||
},
|
||||
{
|
||||
"definition": {
|
||||
"type": "group",
|
||||
"layout_type": "ordered",
|
||||
"title": "Trends",
|
||||
"show_title": true,
|
||||
"widgets": [
|
||||
{
|
||||
"definition": {
|
||||
"type": "timeseries",
|
||||
"title": "Requests/sec by status class",
|
||||
"title_size": "16",
|
||||
"title_align": "left",
|
||||
"show_legend": true,
|
||||
"legend_layout": "horizontal",
|
||||
"requests": [
|
||||
{
|
||||
"q": "sum:http.server.request.count{service:storyteller-web,route:/v1/engine/scene/_token,method:post} by {status_class}.as_rate()",
|
||||
"display_type": "bars",
|
||||
"style": {
|
||||
"palette": "semantic",
|
||||
"line_type": "solid",
|
||||
"line_width": "normal"
|
||||
}
|
||||
}
|
||||
],
|
||||
"yaxis": {
|
||||
"scale": "linear",
|
||||
"include_zero": true
|
||||
}
|
||||
},
|
||||
"layout": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"width": 6,
|
||||
"height": 4
|
||||
}
|
||||
},
|
||||
{
|
||||
"definition": {
|
||||
"type": "timeseries",
|
||||
"title": "Latency percentiles",
|
||||
"title_size": "16",
|
||||
"title_align": "left",
|
||||
"show_legend": true,
|
||||
"legend_layout": "horizontal",
|
||||
"requests": [
|
||||
{
|
||||
"q": "p50:http.server.request.duration_ms{service:storyteller-web,route:/v1/engine/scene/_token,method:post}",
|
||||
"display_type": "line",
|
||||
"style": {
|
||||
"palette": "cool",
|
||||
"line_type": "solid",
|
||||
"line_width": "normal"
|
||||
}
|
||||
},
|
||||
{
|
||||
"q": "p95:http.server.request.duration_ms{service:storyteller-web,route:/v1/engine/scene/_token,method:post}",
|
||||
"display_type": "line",
|
||||
"style": {
|
||||
"palette": "warm",
|
||||
"line_type": "solid",
|
||||
"line_width": "normal"
|
||||
}
|
||||
},
|
||||
{
|
||||
"q": "p99:http.server.request.duration_ms{service:storyteller-web,route:/v1/engine/scene/_token,method:post}",
|
||||
"display_type": "line",
|
||||
"style": {
|
||||
"palette": "warm",
|
||||
"line_type": "dashed",
|
||||
"line_width": "normal"
|
||||
}
|
||||
}
|
||||
],
|
||||
"yaxis": {
|
||||
"scale": "linear",
|
||||
"include_zero": true,
|
||||
"label": "ms"
|
||||
}
|
||||
},
|
||||
"layout": {
|
||||
"x": 6,
|
||||
"y": 0,
|
||||
"width": 6,
|
||||
"height": 4
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"layout": {
|
||||
"x": 0,
|
||||
"y": 3,
|
||||
"width": 12,
|
||||
"height": 5
|
||||
}
|
||||
},
|
||||
{
|
||||
"definition": {
|
||||
"type": "toplist",
|
||||
"title": "Status code distribution",
|
||||
"title_size": "16",
|
||||
"title_align": "left",
|
||||
"requests": [
|
||||
{
|
||||
"q": "top(sum:http.server.request.count{service:storyteller-web,route:/v1/engine/scene/_token,method:post} by {status_code}.as_count(), 25, 'sum', 'desc')"
|
||||
}
|
||||
]
|
||||
},
|
||||
"layout": {
|
||||
"x": 0,
|
||||
"y": 8,
|
||||
"width": 12,
|
||||
"height": 4
|
||||
}
|
||||
}
|
||||
],
|
||||
"id": "bxf-r4f-xfs"
|
||||
}
|
||||
-267
@@ -1,267 +0,0 @@
|
||||
{
|
||||
"title": "[POST] /v1/media_files/upload/engine_asset",
|
||||
"description": "Auto-generated per-endpoint dashboard for `POST /v1/media_files/upload/engine_asset`.\n\nFilters all metrics on `service:storyteller-web,route:/v1/media_files/upload/engine_asset,method:post`.\n\nSource: `script/metrics/generate_endpoint_dashboards.py`. Do not hand-edit; changes will be overwritten on the next regeneration.",
|
||||
"layout_type": "ordered",
|
||||
"notify_list": [],
|
||||
"reflow_type": "fixed",
|
||||
"widgets": [
|
||||
{
|
||||
"definition": {
|
||||
"type": "group",
|
||||
"layout_type": "ordered",
|
||||
"title": "Top-line health",
|
||||
"show_title": true,
|
||||
"widgets": [
|
||||
{
|
||||
"definition": {
|
||||
"type": "query_value",
|
||||
"title": "Requests/sec",
|
||||
"title_size": "16",
|
||||
"title_align": "left",
|
||||
"precision": 2,
|
||||
"autoscale": true,
|
||||
"requests": [
|
||||
{
|
||||
"q": "sum:http.server.request.count{service:storyteller-web,route:/v1/media_files/upload/engine_asset,method:post}.as_rate()",
|
||||
"aggregator": "avg"
|
||||
}
|
||||
]
|
||||
},
|
||||
"layout": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"width": 3,
|
||||
"height": 2
|
||||
}
|
||||
},
|
||||
{
|
||||
"definition": {
|
||||
"type": "query_value",
|
||||
"title": "5xx error rate (%)",
|
||||
"title_size": "16",
|
||||
"title_align": "left",
|
||||
"precision": 2,
|
||||
"autoscale": false,
|
||||
"custom_unit": "%",
|
||||
"requests": [
|
||||
{
|
||||
"q": "(sum:http.server.request.count{service:storyteller-web,route:/v1/media_files/upload/engine_asset,method:post,status_class:5xx}.as_count() / sum:http.server.request.count{service:storyteller-web,route:/v1/media_files/upload/engine_asset,method:post}.as_count()) * 100",
|
||||
"aggregator": "avg",
|
||||
"conditional_formats": [
|
||||
{
|
||||
"comparator": ">",
|
||||
"value": 5.0,
|
||||
"palette": "white_on_red"
|
||||
},
|
||||
{
|
||||
"comparator": ">",
|
||||
"value": 1.0,
|
||||
"palette": "white_on_yellow"
|
||||
},
|
||||
{
|
||||
"comparator": "<=",
|
||||
"value": 1.0,
|
||||
"palette": "white_on_green"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"layout": {
|
||||
"x": 3,
|
||||
"y": 0,
|
||||
"width": 3,
|
||||
"height": 2
|
||||
}
|
||||
},
|
||||
{
|
||||
"definition": {
|
||||
"type": "query_value",
|
||||
"title": "Latency p95 (ms)",
|
||||
"title_size": "16",
|
||||
"title_align": "left",
|
||||
"precision": 0,
|
||||
"autoscale": true,
|
||||
"custom_unit": "ms",
|
||||
"requests": [
|
||||
{
|
||||
"q": "p95:http.server.request.duration_ms{service:storyteller-web,route:/v1/media_files/upload/engine_asset,method:post}",
|
||||
"aggregator": "avg",
|
||||
"conditional_formats": [
|
||||
{
|
||||
"comparator": ">",
|
||||
"value": 1000,
|
||||
"palette": "white_on_red"
|
||||
},
|
||||
{
|
||||
"comparator": ">",
|
||||
"value": 250,
|
||||
"palette": "white_on_yellow"
|
||||
},
|
||||
{
|
||||
"comparator": "<=",
|
||||
"value": 250,
|
||||
"palette": "white_on_green"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"layout": {
|
||||
"x": 6,
|
||||
"y": 0,
|
||||
"width": 3,
|
||||
"height": 2
|
||||
}
|
||||
},
|
||||
{
|
||||
"definition": {
|
||||
"type": "query_value",
|
||||
"title": "Latency p99 (ms)",
|
||||
"title_size": "16",
|
||||
"title_align": "left",
|
||||
"precision": 0,
|
||||
"autoscale": true,
|
||||
"custom_unit": "ms",
|
||||
"requests": [
|
||||
{
|
||||
"q": "p99:http.server.request.duration_ms{service:storyteller-web,route:/v1/media_files/upload/engine_asset,method:post}",
|
||||
"aggregator": "avg"
|
||||
}
|
||||
]
|
||||
},
|
||||
"layout": {
|
||||
"x": 9,
|
||||
"y": 0,
|
||||
"width": 3,
|
||||
"height": 2
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"layout": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"width": 12,
|
||||
"height": 3
|
||||
}
|
||||
},
|
||||
{
|
||||
"definition": {
|
||||
"type": "group",
|
||||
"layout_type": "ordered",
|
||||
"title": "Trends",
|
||||
"show_title": true,
|
||||
"widgets": [
|
||||
{
|
||||
"definition": {
|
||||
"type": "timeseries",
|
||||
"title": "Requests/sec by status class",
|
||||
"title_size": "16",
|
||||
"title_align": "left",
|
||||
"show_legend": true,
|
||||
"legend_layout": "horizontal",
|
||||
"requests": [
|
||||
{
|
||||
"q": "sum:http.server.request.count{service:storyteller-web,route:/v1/media_files/upload/engine_asset,method:post} by {status_class}.as_rate()",
|
||||
"display_type": "bars",
|
||||
"style": {
|
||||
"palette": "semantic",
|
||||
"line_type": "solid",
|
||||
"line_width": "normal"
|
||||
}
|
||||
}
|
||||
],
|
||||
"yaxis": {
|
||||
"scale": "linear",
|
||||
"include_zero": true
|
||||
}
|
||||
},
|
||||
"layout": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"width": 6,
|
||||
"height": 4
|
||||
}
|
||||
},
|
||||
{
|
||||
"definition": {
|
||||
"type": "timeseries",
|
||||
"title": "Latency percentiles",
|
||||
"title_size": "16",
|
||||
"title_align": "left",
|
||||
"show_legend": true,
|
||||
"legend_layout": "horizontal",
|
||||
"requests": [
|
||||
{
|
||||
"q": "p50:http.server.request.duration_ms{service:storyteller-web,route:/v1/media_files/upload/engine_asset,method:post}",
|
||||
"display_type": "line",
|
||||
"style": {
|
||||
"palette": "cool",
|
||||
"line_type": "solid",
|
||||
"line_width": "normal"
|
||||
}
|
||||
},
|
||||
{
|
||||
"q": "p95:http.server.request.duration_ms{service:storyteller-web,route:/v1/media_files/upload/engine_asset,method:post}",
|
||||
"display_type": "line",
|
||||
"style": {
|
||||
"palette": "warm",
|
||||
"line_type": "solid",
|
||||
"line_width": "normal"
|
||||
}
|
||||
},
|
||||
{
|
||||
"q": "p99:http.server.request.duration_ms{service:storyteller-web,route:/v1/media_files/upload/engine_asset,method:post}",
|
||||
"display_type": "line",
|
||||
"style": {
|
||||
"palette": "warm",
|
||||
"line_type": "dashed",
|
||||
"line_width": "normal"
|
||||
}
|
||||
}
|
||||
],
|
||||
"yaxis": {
|
||||
"scale": "linear",
|
||||
"include_zero": true,
|
||||
"label": "ms"
|
||||
}
|
||||
},
|
||||
"layout": {
|
||||
"x": 6,
|
||||
"y": 0,
|
||||
"width": 6,
|
||||
"height": 4
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"layout": {
|
||||
"x": 0,
|
||||
"y": 3,
|
||||
"width": 12,
|
||||
"height": 5
|
||||
}
|
||||
},
|
||||
{
|
||||
"definition": {
|
||||
"type": "toplist",
|
||||
"title": "Status code distribution",
|
||||
"title_size": "16",
|
||||
"title_align": "left",
|
||||
"requests": [
|
||||
{
|
||||
"q": "top(sum:http.server.request.count{service:storyteller-web,route:/v1/media_files/upload/engine_asset,method:post} by {status_code}.as_count(), 25, 'sum', 'desc')"
|
||||
}
|
||||
]
|
||||
},
|
||||
"layout": {
|
||||
"x": 0,
|
||||
"y": 8,
|
||||
"width": 12,
|
||||
"height": 4
|
||||
}
|
||||
}
|
||||
],
|
||||
"id": "7nm-j6w-9tz"
|
||||
}
|
||||
-267
@@ -1,267 +0,0 @@
|
||||
{
|
||||
"title": "[POST] /v1/media_files/write/engine_asset",
|
||||
"description": "Auto-generated per-endpoint dashboard for `POST /v1/media_files/write/engine_asset`.\n\nFilters all metrics on `service:storyteller-web,route:/v1/media_files/write/engine_asset,method:post`.\n\nSource: `script/metrics/generate_endpoint_dashboards.py`. Do not hand-edit; changes will be overwritten on the next regeneration.",
|
||||
"layout_type": "ordered",
|
||||
"notify_list": [],
|
||||
"reflow_type": "fixed",
|
||||
"widgets": [
|
||||
{
|
||||
"definition": {
|
||||
"type": "group",
|
||||
"layout_type": "ordered",
|
||||
"title": "Top-line health",
|
||||
"show_title": true,
|
||||
"widgets": [
|
||||
{
|
||||
"definition": {
|
||||
"type": "query_value",
|
||||
"title": "Requests/sec",
|
||||
"title_size": "16",
|
||||
"title_align": "left",
|
||||
"precision": 2,
|
||||
"autoscale": true,
|
||||
"requests": [
|
||||
{
|
||||
"q": "sum:http.server.request.count{service:storyteller-web,route:/v1/media_files/write/engine_asset,method:post}.as_rate()",
|
||||
"aggregator": "avg"
|
||||
}
|
||||
]
|
||||
},
|
||||
"layout": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"width": 3,
|
||||
"height": 2
|
||||
}
|
||||
},
|
||||
{
|
||||
"definition": {
|
||||
"type": "query_value",
|
||||
"title": "5xx error rate (%)",
|
||||
"title_size": "16",
|
||||
"title_align": "left",
|
||||
"precision": 2,
|
||||
"autoscale": false,
|
||||
"custom_unit": "%",
|
||||
"requests": [
|
||||
{
|
||||
"q": "(sum:http.server.request.count{service:storyteller-web,route:/v1/media_files/write/engine_asset,method:post,status_class:5xx}.as_count() / sum:http.server.request.count{service:storyteller-web,route:/v1/media_files/write/engine_asset,method:post}.as_count()) * 100",
|
||||
"aggregator": "avg",
|
||||
"conditional_formats": [
|
||||
{
|
||||
"comparator": ">",
|
||||
"value": 5.0,
|
||||
"palette": "white_on_red"
|
||||
},
|
||||
{
|
||||
"comparator": ">",
|
||||
"value": 1.0,
|
||||
"palette": "white_on_yellow"
|
||||
},
|
||||
{
|
||||
"comparator": "<=",
|
||||
"value": 1.0,
|
||||
"palette": "white_on_green"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"layout": {
|
||||
"x": 3,
|
||||
"y": 0,
|
||||
"width": 3,
|
||||
"height": 2
|
||||
}
|
||||
},
|
||||
{
|
||||
"definition": {
|
||||
"type": "query_value",
|
||||
"title": "Latency p95 (ms)",
|
||||
"title_size": "16",
|
||||
"title_align": "left",
|
||||
"precision": 0,
|
||||
"autoscale": true,
|
||||
"custom_unit": "ms",
|
||||
"requests": [
|
||||
{
|
||||
"q": "p95:http.server.request.duration_ms{service:storyteller-web,route:/v1/media_files/write/engine_asset,method:post}",
|
||||
"aggregator": "avg",
|
||||
"conditional_formats": [
|
||||
{
|
||||
"comparator": ">",
|
||||
"value": 1000,
|
||||
"palette": "white_on_red"
|
||||
},
|
||||
{
|
||||
"comparator": ">",
|
||||
"value": 250,
|
||||
"palette": "white_on_yellow"
|
||||
},
|
||||
{
|
||||
"comparator": "<=",
|
||||
"value": 250,
|
||||
"palette": "white_on_green"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"layout": {
|
||||
"x": 6,
|
||||
"y": 0,
|
||||
"width": 3,
|
||||
"height": 2
|
||||
}
|
||||
},
|
||||
{
|
||||
"definition": {
|
||||
"type": "query_value",
|
||||
"title": "Latency p99 (ms)",
|
||||
"title_size": "16",
|
||||
"title_align": "left",
|
||||
"precision": 0,
|
||||
"autoscale": true,
|
||||
"custom_unit": "ms",
|
||||
"requests": [
|
||||
{
|
||||
"q": "p99:http.server.request.duration_ms{service:storyteller-web,route:/v1/media_files/write/engine_asset,method:post}",
|
||||
"aggregator": "avg"
|
||||
}
|
||||
]
|
||||
},
|
||||
"layout": {
|
||||
"x": 9,
|
||||
"y": 0,
|
||||
"width": 3,
|
||||
"height": 2
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"layout": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"width": 12,
|
||||
"height": 3
|
||||
}
|
||||
},
|
||||
{
|
||||
"definition": {
|
||||
"type": "group",
|
||||
"layout_type": "ordered",
|
||||
"title": "Trends",
|
||||
"show_title": true,
|
||||
"widgets": [
|
||||
{
|
||||
"definition": {
|
||||
"type": "timeseries",
|
||||
"title": "Requests/sec by status class",
|
||||
"title_size": "16",
|
||||
"title_align": "left",
|
||||
"show_legend": true,
|
||||
"legend_layout": "horizontal",
|
||||
"requests": [
|
||||
{
|
||||
"q": "sum:http.server.request.count{service:storyteller-web,route:/v1/media_files/write/engine_asset,method:post} by {status_class}.as_rate()",
|
||||
"display_type": "bars",
|
||||
"style": {
|
||||
"palette": "semantic",
|
||||
"line_type": "solid",
|
||||
"line_width": "normal"
|
||||
}
|
||||
}
|
||||
],
|
||||
"yaxis": {
|
||||
"scale": "linear",
|
||||
"include_zero": true
|
||||
}
|
||||
},
|
||||
"layout": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"width": 6,
|
||||
"height": 4
|
||||
}
|
||||
},
|
||||
{
|
||||
"definition": {
|
||||
"type": "timeseries",
|
||||
"title": "Latency percentiles",
|
||||
"title_size": "16",
|
||||
"title_align": "left",
|
||||
"show_legend": true,
|
||||
"legend_layout": "horizontal",
|
||||
"requests": [
|
||||
{
|
||||
"q": "p50:http.server.request.duration_ms{service:storyteller-web,route:/v1/media_files/write/engine_asset,method:post}",
|
||||
"display_type": "line",
|
||||
"style": {
|
||||
"palette": "cool",
|
||||
"line_type": "solid",
|
||||
"line_width": "normal"
|
||||
}
|
||||
},
|
||||
{
|
||||
"q": "p95:http.server.request.duration_ms{service:storyteller-web,route:/v1/media_files/write/engine_asset,method:post}",
|
||||
"display_type": "line",
|
||||
"style": {
|
||||
"palette": "warm",
|
||||
"line_type": "solid",
|
||||
"line_width": "normal"
|
||||
}
|
||||
},
|
||||
{
|
||||
"q": "p99:http.server.request.duration_ms{service:storyteller-web,route:/v1/media_files/write/engine_asset,method:post}",
|
||||
"display_type": "line",
|
||||
"style": {
|
||||
"palette": "warm",
|
||||
"line_type": "dashed",
|
||||
"line_width": "normal"
|
||||
}
|
||||
}
|
||||
],
|
||||
"yaxis": {
|
||||
"scale": "linear",
|
||||
"include_zero": true,
|
||||
"label": "ms"
|
||||
}
|
||||
},
|
||||
"layout": {
|
||||
"x": 6,
|
||||
"y": 0,
|
||||
"width": 6,
|
||||
"height": 4
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"layout": {
|
||||
"x": 0,
|
||||
"y": 3,
|
||||
"width": 12,
|
||||
"height": 5
|
||||
}
|
||||
},
|
||||
{
|
||||
"definition": {
|
||||
"type": "toplist",
|
||||
"title": "Status code distribution",
|
||||
"title_size": "16",
|
||||
"title_align": "left",
|
||||
"requests": [
|
||||
{
|
||||
"q": "top(sum:http.server.request.count{service:storyteller-web,route:/v1/media_files/write/engine_asset,method:post} by {status_code}.as_count(), 25, 'sum', 'desc')"
|
||||
}
|
||||
]
|
||||
},
|
||||
"layout": {
|
||||
"x": 0,
|
||||
"y": 8,
|
||||
"width": 12,
|
||||
"height": 4
|
||||
}
|
||||
}
|
||||
],
|
||||
"id": "xv7-2ca-2c5"
|
||||
}
|
||||
@@ -1,267 +0,0 @@
|
||||
{
|
||||
"title": "[POST] /v1/media_files/write/scene_file",
|
||||
"description": "Auto-generated per-endpoint dashboard for `POST /v1/media_files/write/scene_file`.\n\nFilters all metrics on `service:storyteller-web,route:/v1/media_files/write/scene_file,method:post`.\n\nSource: `script/metrics/generate_endpoint_dashboards.py`. Do not hand-edit; changes will be overwritten on the next regeneration.",
|
||||
"layout_type": "ordered",
|
||||
"notify_list": [],
|
||||
"reflow_type": "fixed",
|
||||
"widgets": [
|
||||
{
|
||||
"definition": {
|
||||
"type": "group",
|
||||
"layout_type": "ordered",
|
||||
"title": "Top-line health",
|
||||
"show_title": true,
|
||||
"widgets": [
|
||||
{
|
||||
"definition": {
|
||||
"type": "query_value",
|
||||
"title": "Requests/sec",
|
||||
"title_size": "16",
|
||||
"title_align": "left",
|
||||
"precision": 2,
|
||||
"autoscale": true,
|
||||
"requests": [
|
||||
{
|
||||
"q": "sum:http.server.request.count{service:storyteller-web,route:/v1/media_files/write/scene_file,method:post}.as_rate()",
|
||||
"aggregator": "avg"
|
||||
}
|
||||
]
|
||||
},
|
||||
"layout": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"width": 3,
|
||||
"height": 2
|
||||
}
|
||||
},
|
||||
{
|
||||
"definition": {
|
||||
"type": "query_value",
|
||||
"title": "5xx error rate (%)",
|
||||
"title_size": "16",
|
||||
"title_align": "left",
|
||||
"precision": 2,
|
||||
"autoscale": false,
|
||||
"custom_unit": "%",
|
||||
"requests": [
|
||||
{
|
||||
"q": "(sum:http.server.request.count{service:storyteller-web,route:/v1/media_files/write/scene_file,method:post,status_class:5xx}.as_count() / sum:http.server.request.count{service:storyteller-web,route:/v1/media_files/write/scene_file,method:post}.as_count()) * 100",
|
||||
"aggregator": "avg",
|
||||
"conditional_formats": [
|
||||
{
|
||||
"comparator": ">",
|
||||
"value": 5.0,
|
||||
"palette": "white_on_red"
|
||||
},
|
||||
{
|
||||
"comparator": ">",
|
||||
"value": 1.0,
|
||||
"palette": "white_on_yellow"
|
||||
},
|
||||
{
|
||||
"comparator": "<=",
|
||||
"value": 1.0,
|
||||
"palette": "white_on_green"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"layout": {
|
||||
"x": 3,
|
||||
"y": 0,
|
||||
"width": 3,
|
||||
"height": 2
|
||||
}
|
||||
},
|
||||
{
|
||||
"definition": {
|
||||
"type": "query_value",
|
||||
"title": "Latency p95 (ms)",
|
||||
"title_size": "16",
|
||||
"title_align": "left",
|
||||
"precision": 0,
|
||||
"autoscale": true,
|
||||
"custom_unit": "ms",
|
||||
"requests": [
|
||||
{
|
||||
"q": "p95:http.server.request.duration_ms{service:storyteller-web,route:/v1/media_files/write/scene_file,method:post}",
|
||||
"aggregator": "avg",
|
||||
"conditional_formats": [
|
||||
{
|
||||
"comparator": ">",
|
||||
"value": 1000,
|
||||
"palette": "white_on_red"
|
||||
},
|
||||
{
|
||||
"comparator": ">",
|
||||
"value": 250,
|
||||
"palette": "white_on_yellow"
|
||||
},
|
||||
{
|
||||
"comparator": "<=",
|
||||
"value": 250,
|
||||
"palette": "white_on_green"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"layout": {
|
||||
"x": 6,
|
||||
"y": 0,
|
||||
"width": 3,
|
||||
"height": 2
|
||||
}
|
||||
},
|
||||
{
|
||||
"definition": {
|
||||
"type": "query_value",
|
||||
"title": "Latency p99 (ms)",
|
||||
"title_size": "16",
|
||||
"title_align": "left",
|
||||
"precision": 0,
|
||||
"autoscale": true,
|
||||
"custom_unit": "ms",
|
||||
"requests": [
|
||||
{
|
||||
"q": "p99:http.server.request.duration_ms{service:storyteller-web,route:/v1/media_files/write/scene_file,method:post}",
|
||||
"aggregator": "avg"
|
||||
}
|
||||
]
|
||||
},
|
||||
"layout": {
|
||||
"x": 9,
|
||||
"y": 0,
|
||||
"width": 3,
|
||||
"height": 2
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"layout": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"width": 12,
|
||||
"height": 3
|
||||
}
|
||||
},
|
||||
{
|
||||
"definition": {
|
||||
"type": "group",
|
||||
"layout_type": "ordered",
|
||||
"title": "Trends",
|
||||
"show_title": true,
|
||||
"widgets": [
|
||||
{
|
||||
"definition": {
|
||||
"type": "timeseries",
|
||||
"title": "Requests/sec by status class",
|
||||
"title_size": "16",
|
||||
"title_align": "left",
|
||||
"show_legend": true,
|
||||
"legend_layout": "horizontal",
|
||||
"requests": [
|
||||
{
|
||||
"q": "sum:http.server.request.count{service:storyteller-web,route:/v1/media_files/write/scene_file,method:post} by {status_class}.as_rate()",
|
||||
"display_type": "bars",
|
||||
"style": {
|
||||
"palette": "semantic",
|
||||
"line_type": "solid",
|
||||
"line_width": "normal"
|
||||
}
|
||||
}
|
||||
],
|
||||
"yaxis": {
|
||||
"scale": "linear",
|
||||
"include_zero": true
|
||||
}
|
||||
},
|
||||
"layout": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"width": 6,
|
||||
"height": 4
|
||||
}
|
||||
},
|
||||
{
|
||||
"definition": {
|
||||
"type": "timeseries",
|
||||
"title": "Latency percentiles",
|
||||
"title_size": "16",
|
||||
"title_align": "left",
|
||||
"show_legend": true,
|
||||
"legend_layout": "horizontal",
|
||||
"requests": [
|
||||
{
|
||||
"q": "p50:http.server.request.duration_ms{service:storyteller-web,route:/v1/media_files/write/scene_file,method:post}",
|
||||
"display_type": "line",
|
||||
"style": {
|
||||
"palette": "cool",
|
||||
"line_type": "solid",
|
||||
"line_width": "normal"
|
||||
}
|
||||
},
|
||||
{
|
||||
"q": "p95:http.server.request.duration_ms{service:storyteller-web,route:/v1/media_files/write/scene_file,method:post}",
|
||||
"display_type": "line",
|
||||
"style": {
|
||||
"palette": "warm",
|
||||
"line_type": "solid",
|
||||
"line_width": "normal"
|
||||
}
|
||||
},
|
||||
{
|
||||
"q": "p99:http.server.request.duration_ms{service:storyteller-web,route:/v1/media_files/write/scene_file,method:post}",
|
||||
"display_type": "line",
|
||||
"style": {
|
||||
"palette": "warm",
|
||||
"line_type": "dashed",
|
||||
"line_width": "normal"
|
||||
}
|
||||
}
|
||||
],
|
||||
"yaxis": {
|
||||
"scale": "linear",
|
||||
"include_zero": true,
|
||||
"label": "ms"
|
||||
}
|
||||
},
|
||||
"layout": {
|
||||
"x": 6,
|
||||
"y": 0,
|
||||
"width": 6,
|
||||
"height": 4
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"layout": {
|
||||
"x": 0,
|
||||
"y": 3,
|
||||
"width": 12,
|
||||
"height": 5
|
||||
}
|
||||
},
|
||||
{
|
||||
"definition": {
|
||||
"type": "toplist",
|
||||
"title": "Status code distribution",
|
||||
"title_size": "16",
|
||||
"title_align": "left",
|
||||
"requests": [
|
||||
{
|
||||
"q": "top(sum:http.server.request.count{service:storyteller-web,route:/v1/media_files/write/scene_file,method:post} by {status_code}.as_count(), 25, 'sum', 'desc')"
|
||||
}
|
||||
]
|
||||
},
|
||||
"layout": {
|
||||
"x": 0,
|
||||
"y": 8,
|
||||
"width": 12,
|
||||
"height": 4
|
||||
}
|
||||
}
|
||||
],
|
||||
"id": "c8z-t2z-nk4"
|
||||
}
|
||||
@@ -2,6 +2,7 @@ pub mod cover_image_links;
|
||||
pub mod job_details;
|
||||
pub mod media_file_cover_image_details;
|
||||
pub mod media_links;
|
||||
pub mod pagination_cursors;
|
||||
pub mod simple_entity_stats;
|
||||
pub mod simple_generic_json_success;
|
||||
pub mod user_details_light;
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use utoipa::ToSchema;
|
||||
|
||||
// TODO(bt,2026-07-09): Replace the storyteller-web version of this.
|
||||
|
||||
/// Pagination by cursors
|
||||
/// These types of pagination are for "infinite scrolling", which do not reveal a number of pages.
|
||||
/// This is good so that investors and competitors cannot reveal how many database records we have.
|
||||
/// This is typically used for "discovery" type pages, not user profiles.
|
||||
#[derive(Serialize, Deserialize, ToSchema, Debug)]
|
||||
pub struct PaginationCursors {
|
||||
/// The "next" cursor.
|
||||
/// This is an opaque (typically even encrypted) handle.
|
||||
pub maybe_next: Option<String>,
|
||||
|
||||
/// The "previous" cursor.
|
||||
/// This is an opaque (typically even encrypted) handle.
|
||||
pub maybe_previous: Option<String>,
|
||||
|
||||
/// Details whether we're walking forward or backward.
|
||||
pub cursor_is_reversed: bool,
|
||||
}
|
||||
+78
@@ -0,0 +1,78 @@
|
||||
use chrono::{DateTime, Utc};
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use utoipa::{IntoParams, ToSchema};
|
||||
|
||||
use enums::by_table::media_files::media_file_class::MediaFileClass;
|
||||
use enums::by_table::media_files::media_file_project_type::MediaFileProjectType;
|
||||
use enums::by_table::media_files::media_file_type::MediaFileType;
|
||||
use enums::common::visibility::Visibility;
|
||||
use tokens::tokens::media_files::MediaFileToken;
|
||||
|
||||
use crate::common::responses::media_file_cover_image_details::MediaFileCoverImageDetails;
|
||||
use crate::common::responses::media_links::MediaLinks;
|
||||
use crate::common::responses::pagination_cursors::PaginationCursors;
|
||||
use crate::common::responses::user_details_light::UserDetailsLight;
|
||||
|
||||
pub const LIST_SESSION_PROJECT_MEDIA_FILES_URL_PATH: &str = "/v1/media_files/project/list";
|
||||
|
||||
/// Query string parameters for listing the session user's project files.
|
||||
#[derive(Deserialize, IntoParams)]
|
||||
pub struct ListSessionProjectMediaFilesQueryParams {
|
||||
pub sort_ascending: Option<bool>,
|
||||
pub page_size: Option<usize>,
|
||||
pub cursor: Option<String>,
|
||||
pub cursor_is_reversed: Option<bool>,
|
||||
|
||||
/// Optional filter on the specific project document type.
|
||||
///
|
||||
/// Usage:
|
||||
/// - `?filter_project_type=scene_3d`
|
||||
/// - `?filter_project_type=mood_board`
|
||||
/// - `?filter_project_type=workflow`
|
||||
/// - `?filter_project_type=video_timeline`
|
||||
pub filter_project_type: Option<MediaFileProjectType>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, ToSchema)]
|
||||
pub struct ListSessionProjectMediaFilesSuccessResponse {
|
||||
pub success: bool,
|
||||
pub results: Vec<ProjectMediaFileInfo>,
|
||||
pub pagination: PaginationCursors,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, ToSchema)]
|
||||
pub struct ProjectMediaFileInfo {
|
||||
/// Primary key identifier
|
||||
pub token: MediaFileToken,
|
||||
|
||||
/// The coarse-grained class of media file.
|
||||
/// Always `project` for this endpoint.
|
||||
pub media_class: MediaFileClass,
|
||||
|
||||
/// The specific kind of project document: 3D scene, mood board, etc.
|
||||
pub project_type: MediaFileProjectType,
|
||||
|
||||
/// Type of media will dictate which fields are populated and what
|
||||
/// the frontend should display (eg. video player vs audio player).
|
||||
/// This is closer in meaning to a "mime type".
|
||||
pub media_type: MediaFileType,
|
||||
|
||||
/// Rich CDN links to the media, including thumbnails, previews, and more.
|
||||
pub media_links: MediaLinks,
|
||||
|
||||
/// Information about the cover image. Many media files do not require a cover image,
|
||||
/// e.g. image files, video files with thumbnails, audio files, etc.
|
||||
/// 3D files require them.
|
||||
pub cover_image: MediaFileCoverImageDetails,
|
||||
|
||||
/// User info
|
||||
pub maybe_creator_user: Option<UserDetailsLight>,
|
||||
|
||||
pub creator_set_visibility: Visibility,
|
||||
|
||||
/// The name or title of the media file (optional)
|
||||
pub maybe_title: Option<String>,
|
||||
|
||||
pub created_at: DateTime<Utc>,
|
||||
pub updated_at: DateTime<Utc>,
|
||||
}
|
||||
@@ -1,2 +1,4 @@
|
||||
pub mod delete_media_file;
|
||||
pub mod list_batch_generated_media_files;
|
||||
pub mod list_batch_generated_media_files;
|
||||
pub mod list_session_project_media_files;
|
||||
pub mod project;
|
||||
@@ -0,0 +1,8 @@
|
||||
pub mod upload_new_editor_2d_project;
|
||||
pub mod upload_new_mood_board_project;
|
||||
pub mod upload_new_scene_3d_project;
|
||||
pub mod upload_new_video_timeline_project;
|
||||
pub mod upload_updated_editor_2d_project;
|
||||
pub mod upload_updated_mood_board_project;
|
||||
pub mod upload_updated_scene_3d_project;
|
||||
pub mod upload_updated_video_timeline_project;
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use utoipa::ToSchema;
|
||||
|
||||
use tokens::tokens::media_files::MediaFileToken;
|
||||
|
||||
pub const UPLOAD_NEW_EDITOR_2D_PROJECT_URL_PATH: &str = "/v1/media_files/upload/project/editor_2d/new";
|
||||
|
||||
/// Response for saving a new 2D editor document project.
|
||||
#[derive(Serialize, Deserialize, ToSchema)]
|
||||
pub struct UploadNewEditor2dProjectSuccessResponse {
|
||||
pub success: bool,
|
||||
|
||||
/// Token for the newly created project media file. Use it to query the
|
||||
/// project and to save subsequent updates via the update endpoint.
|
||||
pub media_file_token: MediaFileToken,
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use utoipa::ToSchema;
|
||||
|
||||
use tokens::tokens::media_files::MediaFileToken;
|
||||
|
||||
pub const UPLOAD_NEW_MOOD_BOARD_PROJECT_URL_PATH: &str = "/v1/media_files/upload/project/mood_board/new";
|
||||
|
||||
/// Response for saving a new mood board project.
|
||||
#[derive(Serialize, Deserialize, ToSchema)]
|
||||
pub struct UploadNewMoodBoardProjectSuccessResponse {
|
||||
pub success: bool,
|
||||
|
||||
/// Token for the newly created project media file. Use it to query the
|
||||
/// project and to save subsequent updates via the update endpoint.
|
||||
pub media_file_token: MediaFileToken,
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use utoipa::ToSchema;
|
||||
|
||||
use tokens::tokens::media_files::MediaFileToken;
|
||||
|
||||
pub const UPLOAD_NEW_SCENE_3D_PROJECT_URL_PATH: &str = "/v1/media_files/upload/project/scene_3d/new";
|
||||
|
||||
/// Response for saving a new 3D scene project.
|
||||
#[derive(Serialize, Deserialize, ToSchema)]
|
||||
pub struct UploadNewScene3dProjectSuccessResponse {
|
||||
pub success: bool,
|
||||
|
||||
/// Token for the newly created project media file. Use it to query the
|
||||
/// project and to save subsequent updates via the update endpoint.
|
||||
pub media_file_token: MediaFileToken,
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use utoipa::ToSchema;
|
||||
|
||||
use tokens::tokens::media_files::MediaFileToken;
|
||||
|
||||
pub const UPLOAD_NEW_VIDEO_TIMELINE_PROJECT_URL_PATH: &str = "/v1/media_files/upload/project/video_timeline/new";
|
||||
|
||||
/// Response for saving a new video editor timeline project.
|
||||
#[derive(Serialize, Deserialize, ToSchema)]
|
||||
pub struct UploadNewVideoTimelineProjectSuccessResponse {
|
||||
pub success: bool,
|
||||
|
||||
/// Token for the newly created project media file. Use it to query the
|
||||
/// project and to save subsequent updates via the update endpoint.
|
||||
pub media_file_token: MediaFileToken,
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use utoipa::ToSchema;
|
||||
|
||||
use tokens::tokens::media_files::MediaFileToken;
|
||||
|
||||
pub const UPLOAD_UPDATED_EDITOR_2D_PROJECT_URL_PATH: &str = "/v1/media_files/upload/project/editor_2d/update/{token}";
|
||||
|
||||
/// For the URL PathInfo
|
||||
#[derive(Serialize, Deserialize, ToSchema)]
|
||||
pub struct UploadUpdatedEditor2dProjectPathInfo {
|
||||
pub token: MediaFileToken,
|
||||
}
|
||||
|
||||
/// Response for overwriting an existing 2D editor project.
|
||||
#[derive(Serialize, Deserialize, ToSchema)]
|
||||
pub struct UploadUpdatedEditor2dProjectSuccessResponse {
|
||||
pub success: bool,
|
||||
|
||||
/// Token of the updated project media file (unchanged by the update).
|
||||
pub media_file_token: MediaFileToken,
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use utoipa::ToSchema;
|
||||
|
||||
use tokens::tokens::media_files::MediaFileToken;
|
||||
|
||||
pub const UPLOAD_UPDATED_MOOD_BOARD_PROJECT_URL_PATH: &str = "/v1/media_files/upload/project/mood_board/update/{token}";
|
||||
|
||||
/// For the URL PathInfo
|
||||
#[derive(Serialize, Deserialize, ToSchema)]
|
||||
pub struct UploadUpdatedMoodBoardProjectPathInfo {
|
||||
pub token: MediaFileToken,
|
||||
}
|
||||
|
||||
/// Response for overwriting an existing mood board project.
|
||||
#[derive(Serialize, Deserialize, ToSchema)]
|
||||
pub struct UploadUpdatedMoodBoardProjectSuccessResponse {
|
||||
pub success: bool,
|
||||
|
||||
/// Token of the updated project media file (unchanged by the update).
|
||||
pub media_file_token: MediaFileToken,
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use utoipa::ToSchema;
|
||||
|
||||
use tokens::tokens::media_files::MediaFileToken;
|
||||
|
||||
pub const UPLOAD_UPDATED_SCENE_3D_PROJECT_URL_PATH: &str = "/v1/media_files/upload/project/scene_3d/update/{token}";
|
||||
|
||||
/// For the URL PathInfo
|
||||
#[derive(Serialize, Deserialize, ToSchema)]
|
||||
pub struct UploadUpdatedScene3dProjectPathInfo {
|
||||
pub token: MediaFileToken,
|
||||
}
|
||||
|
||||
/// Response for overwriting an existing 3D scene project.
|
||||
#[derive(Serialize, Deserialize, ToSchema)]
|
||||
pub struct UploadUpdatedScene3dProjectSuccessResponse {
|
||||
pub success: bool,
|
||||
|
||||
/// Token of the updated project media file (unchanged by the update).
|
||||
pub media_file_token: MediaFileToken,
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use utoipa::ToSchema;
|
||||
|
||||
use tokens::tokens::media_files::MediaFileToken;
|
||||
|
||||
pub const UPLOAD_UPDATED_VIDEO_TIMELINE_PROJECT_URL_PATH: &str = "/v1/media_files/upload/project/video_timeline/update/{token}";
|
||||
|
||||
/// For the URL PathInfo
|
||||
#[derive(Serialize, Deserialize, ToSchema)]
|
||||
pub struct UploadUpdatedVideoTimelineProjectPathInfo {
|
||||
pub token: MediaFileToken,
|
||||
}
|
||||
|
||||
/// Response for overwriting an existing video editor timeline project.
|
||||
#[derive(Serialize, Deserialize, ToSchema)]
|
||||
pub struct UploadUpdatedVideoTimelineProjectSuccessResponse {
|
||||
pub success: bool,
|
||||
|
||||
/// Token of the updated project media file (unchanged by the update).
|
||||
pub media_file_token: MediaFileToken,
|
||||
}
|
||||
+21
@@ -3,6 +3,7 @@ use utoipa::ToSchema;
|
||||
|
||||
use enums::common::generation::common_mesh_model::CommonMeshModel;
|
||||
use enums::common::generation::common_mesh_output_type::CommonMeshOutputType;
|
||||
use enums::common::generation::common_mesh_quality::CommonMeshQuality;
|
||||
use enums::common::generation::common_polygon_type::CommonPolygonType;
|
||||
use tokens::tokens::media_files::MediaFileToken;
|
||||
|
||||
@@ -38,6 +39,12 @@ pub struct OmniGenMeshCostAndGenerateRequest {
|
||||
/// Right view image (optional; multi-view input).
|
||||
pub right_image_media_token: Option<MediaFileToken>,
|
||||
|
||||
/// Input mesh file for mesh-to-mesh models (part splitting, retopology).
|
||||
/// Required by `hunyuan_3d_3p1_part` (FBX) and
|
||||
/// `hunyuan_3d_3p1_topology` (GLB/OBJ).
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub input_mesh_media_token: Option<MediaFileToken>,
|
||||
|
||||
/// The mesh output type to generate (normal, low-poly, or geometry-only).
|
||||
pub mesh_output_type: Option<CommonMeshOutputType>,
|
||||
|
||||
@@ -49,4 +56,18 @@ pub struct OmniGenMeshCostAndGenerateRequest {
|
||||
|
||||
/// Whether to generate PBR (physically based rendering) materials.
|
||||
pub enable_pbr: Option<bool>,
|
||||
|
||||
/// Whether to generate textures. Some models (Tripo3D) can skip texturing
|
||||
/// for a cheaper, faster generation.
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub enable_texture: Option<bool>,
|
||||
|
||||
/// Texture quality. `Detailed` costs extra on some models (Tripo3D's HD
|
||||
/// texture tier).
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub texture_quality: Option<CommonMeshQuality>,
|
||||
|
||||
/// Geometry quality. `Detailed` costs extra on some models (Tripo3D).
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub geometry_quality: Option<CommonMeshQuality>,
|
||||
}
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@ use serde_derive::{Deserialize, Serialize};
|
||||
use utoipa::ToSchema;
|
||||
|
||||
/// Response body for the omni-gen video cost estimate endpoint.
|
||||
#[derive(Serialize, Deserialize, ToSchema)]
|
||||
#[derive(Debug, Serialize, Deserialize, ToSchema)]
|
||||
pub struct OmniGenVideoCostResponse {
|
||||
pub success: bool,
|
||||
|
||||
|
||||
+21
@@ -71,6 +71,11 @@ pub struct OmniGenMeshModelDetails {
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub multi_view_supported: Option<bool>,
|
||||
|
||||
/// Whether the model takes an existing mesh file as input
|
||||
/// (mesh-to-mesh models like part splitting and retopology).
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub mesh_input_supported: Option<bool>,
|
||||
|
||||
/// The mesh output types the model supports.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub mesh_output_types: Option<Vec<CommonMeshOutputType>>,
|
||||
@@ -87,6 +92,18 @@ pub struct OmniGenMeshModelDetails {
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub pbr_supported: Option<bool>,
|
||||
|
||||
/// Whether texture generation can be toggled off (untextured output).
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub texture_toggle_supported: Option<bool>,
|
||||
|
||||
/// Whether a texture quality level (standard/detailed) can be selected.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub texture_quality_supported: Option<bool>,
|
||||
|
||||
/// Whether a geometry quality level (standard/detailed) can be selected.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub geometry_quality_supported: Option<bool>,
|
||||
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub is_disabled: Option<bool>,
|
||||
}
|
||||
@@ -103,10 +120,14 @@ impl Default for OmniGenMeshModelDetails {
|
||||
image_input_supported: None,
|
||||
sketch_input_supported: None,
|
||||
multi_view_supported: None,
|
||||
mesh_input_supported: None,
|
||||
mesh_output_types: None,
|
||||
polygon_types: None,
|
||||
face_count_supported: None,
|
||||
pbr_supported: None,
|
||||
texture_toggle_supported: None,
|
||||
texture_quality_supported: None,
|
||||
geometry_quality_supported: None,
|
||||
is_disabled: None,
|
||||
}
|
||||
}
|
||||
|
||||
-5
@@ -76,11 +76,6 @@ pub struct MediaFileInfo {
|
||||
/// this describes the animation regime used or supported.
|
||||
pub maybe_animation_type: Option<MediaFileAnimationType>,
|
||||
|
||||
// /// If the media file has a subtype, we'll report it.
|
||||
// /// This is mostly used for Bevy engine files.
|
||||
// #[deprecated(note="This was for the Bevy engine. Do not use.")]
|
||||
// pub maybe_media_subtype: Option<MediaFileSubtype>,
|
||||
|
||||
// /// Extension for the engine to load over remote:// URLs.
|
||||
// #[deprecated(note="This was for the Bevy engine. Do not use.")]
|
||||
// pub maybe_engine_extension: Option<String>,
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
use tokens::tokens::media_files::MediaFileToken;
|
||||
|
||||
/// Reference to an input 3D mesh file (e.g. GLB/OBJ/FBX) for mesh-to-mesh
|
||||
/// models like part splitting and retopology.
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum MeshRef {
|
||||
MediaFileToken(MediaFileToken),
|
||||
Url(String),
|
||||
}
|
||||
@@ -2,6 +2,7 @@ pub mod audio_list_ref;
|
||||
pub mod character_list_ref;
|
||||
pub mod image_list_ref;
|
||||
pub mod image_ref;
|
||||
pub mod mesh_ref;
|
||||
pub mod router_aspect_ratio;
|
||||
pub mod router_audio_model;
|
||||
pub mod router_bitrate;
|
||||
|
||||
@@ -18,6 +18,37 @@ pub enum RouterMeshModel {
|
||||
/// [`Self::Hunyuan3d3`], but takes a sketch image plus a prompt as input.
|
||||
#[serde(rename = "hunyuan_3d_3_sketch")]
|
||||
Hunyuan3d3Sketch,
|
||||
|
||||
/// Hunyuan 3D v3.1 Pro: text or (multi-view) image input.
|
||||
#[serde(rename = "hunyuan_3d_3p1_pro")]
|
||||
Hunyuan3d3p1Pro,
|
||||
|
||||
/// Hunyuan 3D v3.1 Rapid: the fast, low-cost tier. Text or single-image
|
||||
/// input.
|
||||
#[serde(rename = "hunyuan_3d_3p1_rapid")]
|
||||
Hunyuan3d3p1Rapid,
|
||||
|
||||
/// Hunyuan 3D v3.1 Part: splits an existing mesh into semantic parts.
|
||||
/// Takes a mesh file as input, not text/images.
|
||||
#[serde(rename = "hunyuan_3d_3p1_part")]
|
||||
Hunyuan3d3p1Part,
|
||||
|
||||
/// Hunyuan 3D v3.1 Smart Topology: retopologizes an existing mesh.
|
||||
/// Takes a mesh file as input, not text/images.
|
||||
#[serde(rename = "hunyuan_3d_3p1_topology")]
|
||||
Hunyuan3d3p1SmartTopology,
|
||||
|
||||
/// Tripo3D H3.1: text, single-image, or multi-view image input.
|
||||
#[serde(rename = "tripo3d_h3p1")]
|
||||
Tripo3dH3p1,
|
||||
|
||||
/// Meshy 6: text or single-image input.
|
||||
#[serde(rename = "meshy_v6")]
|
||||
MeshyV6,
|
||||
|
||||
/// Hyper3D Rodin v2.5 Fast: text or image(s) input.
|
||||
#[serde(rename = "rodin_2p5_fast")]
|
||||
Rodin2p5Fast,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -32,6 +63,13 @@ mod tests {
|
||||
assert_serde_round_trip(RouterMeshModel::Hunyuan3d2p1, "hunyuan_3d_2p1");
|
||||
assert_serde_round_trip(RouterMeshModel::Hunyuan3d3, "hunyuan_3d_3");
|
||||
assert_serde_round_trip(RouterMeshModel::Hunyuan3d3Sketch, "hunyuan_3d_3_sketch");
|
||||
assert_serde_round_trip(RouterMeshModel::Hunyuan3d3p1Pro, "hunyuan_3d_3p1_pro");
|
||||
assert_serde_round_trip(RouterMeshModel::Hunyuan3d3p1Rapid, "hunyuan_3d_3p1_rapid");
|
||||
assert_serde_round_trip(RouterMeshModel::Hunyuan3d3p1Part, "hunyuan_3d_3p1_part");
|
||||
assert_serde_round_trip(RouterMeshModel::Hunyuan3d3p1SmartTopology, "hunyuan_3d_3p1_topology");
|
||||
assert_serde_round_trip(RouterMeshModel::Tripo3dH3p1, "tripo3d_h3p1");
|
||||
assert_serde_round_trip(RouterMeshModel::MeshyV6, "meshy_v6");
|
||||
assert_serde_round_trip(RouterMeshModel::Rodin2p5Fast, "rodin_2p5_fast");
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -43,6 +81,13 @@ mod tests {
|
||||
(RouterMeshModel::Hunyuan3d2p1, CommonMeshModel::Hunyuan3d2p1),
|
||||
(RouterMeshModel::Hunyuan3d3, CommonMeshModel::Hunyuan3d3),
|
||||
(RouterMeshModel::Hunyuan3d3Sketch, CommonMeshModel::Hunyuan3d3Sketch),
|
||||
(RouterMeshModel::Hunyuan3d3p1Pro, CommonMeshModel::Hunyuan3d3p1Pro),
|
||||
(RouterMeshModel::Hunyuan3d3p1Rapid, CommonMeshModel::Hunyuan3d3p1Rapid),
|
||||
(RouterMeshModel::Hunyuan3d3p1Part, CommonMeshModel::Hunyuan3d3p1Part),
|
||||
(RouterMeshModel::Hunyuan3d3p1SmartTopology, CommonMeshModel::Hunyuan3d3p1SmartTopology),
|
||||
(RouterMeshModel::Tripo3dH3p1, CommonMeshModel::Tripo3dH3p1),
|
||||
(RouterMeshModel::MeshyV6, CommonMeshModel::MeshyV6),
|
||||
(RouterMeshModel::Rodin2p5Fast, CommonMeshModel::Rodin2p5Fast),
|
||||
];
|
||||
for (router_model, expected_common) in cases {
|
||||
let json = serde_json::to_string(&router_model).unwrap();
|
||||
|
||||
@@ -24,6 +24,10 @@ pub enum RouterSplatModel {
|
||||
|
||||
#[serde(rename = "marble_1p1_plus")]
|
||||
Marble1p1Plus,
|
||||
|
||||
/// TripoSplat: single-image to 3D Gaussian splat (via Fal).
|
||||
#[serde(rename = "triposplat")]
|
||||
TripoSplat,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -40,6 +44,7 @@ mod tests {
|
||||
assert_serde_round_trip(RouterSplatModel::Marble1p0Draft, "marble_1p0_draft");
|
||||
assert_serde_round_trip(RouterSplatModel::Marble1p1, "marble_1p1");
|
||||
assert_serde_round_trip(RouterSplatModel::Marble1p1Plus, "marble_1p1_plus");
|
||||
assert_serde_round_trip(RouterSplatModel::TripoSplat, "triposplat");
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -53,6 +58,7 @@ mod tests {
|
||||
(RouterSplatModel::Marble1p0Draft, CommonSplatModel::Marble1p0Draft),
|
||||
(RouterSplatModel::Marble1p1, CommonSplatModel::Marble1p1),
|
||||
(RouterSplatModel::Marble1p1Plus, CommonSplatModel::Marble1p1Plus),
|
||||
(RouterSplatModel::TripoSplat, CommonSplatModel::TripoSplat),
|
||||
];
|
||||
for (router_model, expected_common) in cases {
|
||||
let json = serde_json::to_string(&router_model).unwrap();
|
||||
|
||||
+47
@@ -1,8 +1,10 @@
|
||||
use enums::common::generation::common_mesh_output_type::CommonMeshOutputType;
|
||||
use enums::common::generation::common_mesh_quality::CommonMeshQuality;
|
||||
use enums::common::generation::common_polygon_type::CommonPolygonType;
|
||||
|
||||
use crate::api::image_list_ref::ImageListRef;
|
||||
use crate::api::image_ref::ImageRef;
|
||||
use crate::api::mesh_ref::MeshRef;
|
||||
use crate::api::router_mesh_model::RouterMeshModel;
|
||||
use crate::api::router_provider::RouterProvider;
|
||||
use crate::client::request_mismatch_mitigation_strategy::RequestMismatchMitigationStrategy;
|
||||
@@ -12,10 +14,24 @@ use crate::generate::generate_mesh::providers::artcraft::hunyuan3d_3::build::bui
|
||||
use crate::generate::generate_mesh::providers::artcraft::hunyuan3d_3_sketch::build::build_artcraft_hunyuan3d_3_sketch;
|
||||
use crate::generate::generate_mesh::providers::artcraft::hunyuan_3d_2p0::build::build_artcraft_hunyuan_3d_2p0;
|
||||
use crate::generate::generate_mesh::providers::artcraft::hunyuan_3d_2p1::build::build_artcraft_hunyuan_3d_2p1;
|
||||
use crate::generate::generate_mesh::providers::artcraft::hunyuan_3d_3p1_part::build::build_artcraft_hunyuan_3d_3p1_part;
|
||||
use crate::generate::generate_mesh::providers::artcraft::hunyuan_3d_3p1_pro::build::build_artcraft_hunyuan_3d_3p1_pro;
|
||||
use crate::generate::generate_mesh::providers::artcraft::hunyuan_3d_3p1_rapid::build::build_artcraft_hunyuan_3d_3p1_rapid;
|
||||
use crate::generate::generate_mesh::providers::artcraft::hunyuan_3d_3p1_smart_topology::build::build_artcraft_hunyuan_3d_3p1_smart_topology;
|
||||
use crate::generate::generate_mesh::providers::artcraft::meshy_v6::build::build_artcraft_meshy_v6;
|
||||
use crate::generate::generate_mesh::providers::artcraft::rodin_2p5_fast::build::build_artcraft_rodin_2p5_fast;
|
||||
use crate::generate::generate_mesh::providers::artcraft::tripo3d_h3p1::build::build_artcraft_tripo3d_h3p1;
|
||||
use crate::generate::generate_mesh::providers::fal::hunyuan3d_3::build::build_fal_hunyuan3d_3;
|
||||
use crate::generate::generate_mesh::providers::fal::hunyuan3d_3_sketch::build::build_fal_hunyuan3d_3_sketch;
|
||||
use crate::generate::generate_mesh::providers::fal::hunyuan_3d_2p0::build::build_fal_hunyuan_3d_2p0;
|
||||
use crate::generate::generate_mesh::providers::fal::hunyuan_3d_2p1::build::build_fal_hunyuan_3d_2p1;
|
||||
use crate::generate::generate_mesh::providers::fal::hunyuan_3d_3p1_part::build::build_fal_hunyuan_3d_3p1_part;
|
||||
use crate::generate::generate_mesh::providers::fal::hunyuan_3d_3p1_pro::build::build_fal_hunyuan_3d_3p1_pro;
|
||||
use crate::generate::generate_mesh::providers::fal::hunyuan_3d_3p1_rapid::build::build_fal_hunyuan_3d_3p1_rapid;
|
||||
use crate::generate::generate_mesh::providers::fal::hunyuan_3d_3p1_smart_topology::build::build_fal_hunyuan_3d_3p1_smart_topology;
|
||||
use crate::generate::generate_mesh::providers::fal::meshy_v6::build::build_fal_meshy_v6;
|
||||
use crate::generate::generate_mesh::providers::fal::rodin_2p5_fast::build::build_fal_rodin_2p5_fast;
|
||||
use crate::generate::generate_mesh::providers::fal::tripo3d_h3p1::build::build_fal_tripo3d_h3p1;
|
||||
|
||||
/// RouterProvider-agnostic mesh (3D object) generation request. Distilled by
|
||||
/// `build2()` into a `MeshGenerationDraftOrRequest` for the selected
|
||||
@@ -49,6 +65,9 @@ pub struct GenerateMeshRequestBuilder {
|
||||
/// Right view image (optional; multi-view input).
|
||||
pub right_image: Option<ImageRef>,
|
||||
|
||||
/// Input mesh file for mesh-to-mesh models (part splitting, retopology).
|
||||
pub input_mesh: Option<MeshRef>,
|
||||
|
||||
/// The mesh output type to generate (normal, low-poly, or geometry-only).
|
||||
pub mesh_output_type: Option<CommonMeshOutputType>,
|
||||
|
||||
@@ -61,6 +80,16 @@ pub struct GenerateMeshRequestBuilder {
|
||||
/// Whether to generate PBR (physically based rendering) materials.
|
||||
pub enable_pbr: Option<bool>,
|
||||
|
||||
/// Whether to generate textures. Some models (Tripo3D) can skip texturing
|
||||
/// for a cheaper, faster generation.
|
||||
pub enable_texture: Option<bool>,
|
||||
|
||||
/// Texture quality. `Detailed` may cost extra (e.g. Tripo3D's HD tier).
|
||||
pub texture_quality: Option<CommonMeshQuality>,
|
||||
|
||||
/// Geometry quality. `Detailed` may cost extra (e.g. Tripo3D).
|
||||
pub geometry_quality: Option<CommonMeshQuality>,
|
||||
|
||||
/// If the request is a mismatch with the (model/provider), how to mitigate it.
|
||||
pub request_mismatch_mitigation_strategy: RequestMismatchMitigationStrategy,
|
||||
|
||||
@@ -81,10 +110,14 @@ impl Default for GenerateMeshRequestBuilder {
|
||||
back_image: None,
|
||||
left_image: None,
|
||||
right_image: None,
|
||||
input_mesh: None,
|
||||
mesh_output_type: None,
|
||||
polygon_type: None,
|
||||
face_count: None,
|
||||
enable_pbr: None,
|
||||
enable_texture: None,
|
||||
texture_quality: None,
|
||||
geometry_quality: None,
|
||||
idempotency_token: None,
|
||||
}
|
||||
}
|
||||
@@ -99,11 +132,25 @@ impl GenerateMeshRequestBuilder {
|
||||
(RouterProvider::Artcraft, RouterMeshModel::Hunyuan3d2p1) => build_artcraft_hunyuan_3d_2p1(self),
|
||||
(RouterProvider::Artcraft, RouterMeshModel::Hunyuan3d3) => build_artcraft_hunyuan3d_3(self),
|
||||
(RouterProvider::Artcraft, RouterMeshModel::Hunyuan3d3Sketch) => build_artcraft_hunyuan3d_3_sketch(self),
|
||||
(RouterProvider::Artcraft, RouterMeshModel::Hunyuan3d3p1Pro) => build_artcraft_hunyuan_3d_3p1_pro(self),
|
||||
(RouterProvider::Artcraft, RouterMeshModel::Hunyuan3d3p1Rapid) => build_artcraft_hunyuan_3d_3p1_rapid(self),
|
||||
(RouterProvider::Artcraft, RouterMeshModel::Hunyuan3d3p1Part) => build_artcraft_hunyuan_3d_3p1_part(self),
|
||||
(RouterProvider::Artcraft, RouterMeshModel::Hunyuan3d3p1SmartTopology) => build_artcraft_hunyuan_3d_3p1_smart_topology(self),
|
||||
(RouterProvider::Artcraft, RouterMeshModel::Tripo3dH3p1) => build_artcraft_tripo3d_h3p1(self),
|
||||
(RouterProvider::Artcraft, RouterMeshModel::MeshyV6) => build_artcraft_meshy_v6(self),
|
||||
(RouterProvider::Artcraft, RouterMeshModel::Rodin2p5Fast) => build_artcraft_rodin_2p5_fast(self),
|
||||
// Fal
|
||||
(RouterProvider::Fal, RouterMeshModel::Hunyuan3d2p0) => build_fal_hunyuan_3d_2p0(self),
|
||||
(RouterProvider::Fal, RouterMeshModel::Hunyuan3d2p1) => build_fal_hunyuan_3d_2p1(self),
|
||||
(RouterProvider::Fal, RouterMeshModel::Hunyuan3d3) => build_fal_hunyuan3d_3(self),
|
||||
(RouterProvider::Fal, RouterMeshModel::Hunyuan3d3Sketch) => build_fal_hunyuan3d_3_sketch(self),
|
||||
(RouterProvider::Fal, RouterMeshModel::Hunyuan3d3p1Pro) => build_fal_hunyuan_3d_3p1_pro(self),
|
||||
(RouterProvider::Fal, RouterMeshModel::Hunyuan3d3p1Rapid) => build_fal_hunyuan_3d_3p1_rapid(self),
|
||||
(RouterProvider::Fal, RouterMeshModel::Hunyuan3d3p1Part) => build_fal_hunyuan_3d_3p1_part(self),
|
||||
(RouterProvider::Fal, RouterMeshModel::Hunyuan3d3p1SmartTopology) => build_fal_hunyuan_3d_3p1_smart_topology(self),
|
||||
(RouterProvider::Fal, RouterMeshModel::Tripo3dH3p1) => build_fal_tripo3d_h3p1(self),
|
||||
(RouterProvider::Fal, RouterMeshModel::MeshyV6) => build_fal_meshy_v6(self),
|
||||
(RouterProvider::Fal, RouterMeshModel::Rodin2p5Fast) => build_fal_rodin_2p5_fast(self),
|
||||
_ => self.unsupported_provider_and_model(),
|
||||
}
|
||||
}
|
||||
|
||||
+155
-54
@@ -11,6 +11,20 @@ use crate::generate::generate_mesh::providers::artcraft::hunyuan_3d_2p0::cost::A
|
||||
use crate::generate::generate_mesh::providers::artcraft::hunyuan_3d_2p0::request::ArtcraftHunyuan3d2p0RequestState;
|
||||
use crate::generate::generate_mesh::providers::artcraft::hunyuan_3d_2p1::cost::ArtcraftHunyuan3d2p1CostState;
|
||||
use crate::generate::generate_mesh::providers::artcraft::hunyuan_3d_2p1::request::ArtcraftHunyuan3d2p1RequestState;
|
||||
use crate::generate::generate_mesh::providers::artcraft::hunyuan_3d_3p1_part::cost::ArtcraftHunyuan3d3p1PartCostState;
|
||||
use crate::generate::generate_mesh::providers::artcraft::hunyuan_3d_3p1_part::request::ArtcraftHunyuan3d3p1PartRequestState;
|
||||
use crate::generate::generate_mesh::providers::artcraft::hunyuan_3d_3p1_pro::cost::ArtcraftHunyuan3d3p1ProCostState;
|
||||
use crate::generate::generate_mesh::providers::artcraft::hunyuan_3d_3p1_pro::request::ArtcraftHunyuan3d3p1ProRequestState;
|
||||
use crate::generate::generate_mesh::providers::artcraft::hunyuan_3d_3p1_rapid::cost::ArtcraftHunyuan3d3p1RapidCostState;
|
||||
use crate::generate::generate_mesh::providers::artcraft::hunyuan_3d_3p1_rapid::request::ArtcraftHunyuan3d3p1RapidRequestState;
|
||||
use crate::generate::generate_mesh::providers::artcraft::hunyuan_3d_3p1_smart_topology::cost::ArtcraftHunyuan3d3p1SmartTopologyCostState;
|
||||
use crate::generate::generate_mesh::providers::artcraft::hunyuan_3d_3p1_smart_topology::request::ArtcraftHunyuan3d3p1SmartTopologyRequestState;
|
||||
use crate::generate::generate_mesh::providers::artcraft::meshy_v6::cost::ArtcraftMeshyV6CostState;
|
||||
use crate::generate::generate_mesh::providers::artcraft::meshy_v6::request::ArtcraftMeshyV6RequestState;
|
||||
use crate::generate::generate_mesh::providers::artcraft::rodin_2p5_fast::cost::ArtcraftRodin2p5FastCostState;
|
||||
use crate::generate::generate_mesh::providers::artcraft::rodin_2p5_fast::request::ArtcraftRodin2p5FastRequestState;
|
||||
use crate::generate::generate_mesh::providers::artcraft::tripo3d_h3p1::cost::ArtcraftTripo3dH3p1CostState;
|
||||
use crate::generate::generate_mesh::providers::artcraft::tripo3d_h3p1::request::ArtcraftTripo3dH3p1RequestState;
|
||||
use crate::generate::generate_mesh::providers::fal::hunyuan3d_3::cost::{
|
||||
FalHunyuan3d3ImageCostState, FalHunyuan3d3TextCostState,
|
||||
};
|
||||
@@ -23,6 +37,40 @@ use crate::generate::generate_mesh::providers::fal::hunyuan_3d_2p0::cost::FalHun
|
||||
use crate::generate::generate_mesh::providers::fal::hunyuan_3d_2p0::request::FalHunyuan3d2p0RequestState;
|
||||
use crate::generate::generate_mesh::providers::fal::hunyuan_3d_2p1::cost::FalHunyuan3d2p1CostState;
|
||||
use crate::generate::generate_mesh::providers::fal::hunyuan_3d_2p1::request::FalHunyuan3d2p1RequestState;
|
||||
use crate::generate::generate_mesh::providers::fal::hunyuan_3d_3p1_part::cost::FalHunyuan3d3p1PartCostState;
|
||||
use crate::generate::generate_mesh::providers::fal::hunyuan_3d_3p1_part::request::FalHunyuan3d3p1PartRequestState;
|
||||
use crate::generate::generate_mesh::providers::fal::hunyuan_3d_3p1_pro::cost::{
|
||||
FalHunyuan3d3p1ProImageCostState, FalHunyuan3d3p1ProTextCostState,
|
||||
};
|
||||
use crate::generate::generate_mesh::providers::fal::hunyuan_3d_3p1_pro::request::{
|
||||
FalHunyuan3d3p1ProImageRequestState, FalHunyuan3d3p1ProTextRequestState,
|
||||
};
|
||||
use crate::generate::generate_mesh::providers::fal::hunyuan_3d_3p1_rapid::cost::{
|
||||
FalHunyuan3d3p1RapidImageCostState, FalHunyuan3d3p1RapidTextCostState,
|
||||
};
|
||||
use crate::generate::generate_mesh::providers::fal::hunyuan_3d_3p1_rapid::request::{
|
||||
FalHunyuan3d3p1RapidImageRequestState, FalHunyuan3d3p1RapidTextRequestState,
|
||||
};
|
||||
use crate::generate::generate_mesh::providers::fal::hunyuan_3d_3p1_smart_topology::cost::FalHunyuan3d3p1SmartTopologyCostState;
|
||||
use crate::generate::generate_mesh::providers::fal::hunyuan_3d_3p1_smart_topology::request::FalHunyuan3d3p1SmartTopologyRequestState;
|
||||
use crate::generate::generate_mesh::providers::fal::meshy_v6::cost::{
|
||||
FalMeshyV6ImageCostState, FalMeshyV6TextCostState,
|
||||
};
|
||||
use crate::generate::generate_mesh::providers::fal::meshy_v6::request::{
|
||||
FalMeshyV6ImageRequestState, FalMeshyV6TextRequestState,
|
||||
};
|
||||
use crate::generate::generate_mesh::providers::fal::rodin_2p5_fast::cost::{
|
||||
FalRodin2p5FastImageCostState, FalRodin2p5FastTextCostState,
|
||||
};
|
||||
use crate::generate::generate_mesh::providers::fal::rodin_2p5_fast::request::{
|
||||
FalRodin2p5FastImageRequestState, FalRodin2p5FastTextRequestState,
|
||||
};
|
||||
use crate::generate::generate_mesh::providers::fal::tripo3d_h3p1::cost::{
|
||||
FalTripo3dH3p1ImageCostState, FalTripo3dH3p1MultiviewCostState, FalTripo3dH3p1TextCostState,
|
||||
};
|
||||
use crate::generate::generate_mesh::providers::fal::tripo3d_h3p1::request::{
|
||||
FalTripo3dH3p1ImageRequestState, FalTripo3dH3p1MultiviewRequestState, FalTripo3dH3p1TextRequestState,
|
||||
};
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum MeshGenerationRequest {
|
||||
@@ -30,41 +78,101 @@ pub enum MeshGenerationRequest {
|
||||
ArtcraftHunyuan3d2p1(ArtcraftHunyuan3d2p1RequestState),
|
||||
ArtcraftHunyuan3d3(ArtcraftHunyuan3d3RequestState),
|
||||
ArtcraftHunyuan3d3Sketch(ArtcraftHunyuan3d3SketchRequestState),
|
||||
ArtcraftHunyuan3d3p1Pro(ArtcraftHunyuan3d3p1ProRequestState),
|
||||
ArtcraftHunyuan3d3p1Rapid(ArtcraftHunyuan3d3p1RapidRequestState),
|
||||
ArtcraftHunyuan3d3p1Part(ArtcraftHunyuan3d3p1PartRequestState),
|
||||
ArtcraftHunyuan3d3p1SmartTopology(ArtcraftHunyuan3d3p1SmartTopologyRequestState),
|
||||
ArtcraftTripo3dH3p1(ArtcraftTripo3dH3p1RequestState),
|
||||
ArtcraftMeshyV6(ArtcraftMeshyV6RequestState),
|
||||
ArtcraftRodin2p5Fast(ArtcraftRodin2p5FastRequestState),
|
||||
FalHunyuan3d2p0(FalHunyuan3d2p0RequestState),
|
||||
FalHunyuan3d2p1(FalHunyuan3d2p1RequestState),
|
||||
FalHunyuan3d3Image(FalHunyuan3d3ImageRequestState),
|
||||
FalHunyuan3d3Text(FalHunyuan3d3TextRequestState),
|
||||
FalHunyuan3d3Sketch(FalHunyuan3d3SketchRequestState),
|
||||
FalHunyuan3d3p1ProImage(FalHunyuan3d3p1ProImageRequestState),
|
||||
FalHunyuan3d3p1ProText(FalHunyuan3d3p1ProTextRequestState),
|
||||
FalHunyuan3d3p1RapidImage(FalHunyuan3d3p1RapidImageRequestState),
|
||||
FalHunyuan3d3p1RapidText(FalHunyuan3d3p1RapidTextRequestState),
|
||||
FalHunyuan3d3p1Part(FalHunyuan3d3p1PartRequestState),
|
||||
FalHunyuan3d3p1SmartTopology(FalHunyuan3d3p1SmartTopologyRequestState),
|
||||
FalTripo3dH3p1Text(FalTripo3dH3p1TextRequestState),
|
||||
FalTripo3dH3p1Image(FalTripo3dH3p1ImageRequestState),
|
||||
FalTripo3dH3p1Multiview(FalTripo3dH3p1MultiviewRequestState),
|
||||
FalMeshyV6Text(FalMeshyV6TextRequestState),
|
||||
FalMeshyV6Image(FalMeshyV6ImageRequestState),
|
||||
FalRodin2p5FastText(FalRodin2p5FastTextRequestState),
|
||||
FalRodin2p5FastImage(FalRodin2p5FastImageRequestState),
|
||||
}
|
||||
|
||||
impl MeshGenerationRequest {
|
||||
|
||||
pub fn get_provider(&self) -> RouterProvider {
|
||||
match self {
|
||||
Self::ArtcraftHunyuan3d2p0(_) => RouterProvider::Artcraft,
|
||||
Self::ArtcraftHunyuan3d2p1(_) => RouterProvider::Artcraft,
|
||||
Self::ArtcraftHunyuan3d3(_) => RouterProvider::Artcraft,
|
||||
Self::ArtcraftHunyuan3d3Sketch(_) => RouterProvider::Artcraft,
|
||||
Self::FalHunyuan3d2p0(_) => RouterProvider::Fal,
|
||||
Self::FalHunyuan3d2p1(_) => RouterProvider::Fal,
|
||||
Self::FalHunyuan3d3Image(_) => RouterProvider::Fal,
|
||||
Self::FalHunyuan3d3Text(_) => RouterProvider::Fal,
|
||||
Self::FalHunyuan3d3Sketch(_) => RouterProvider::Fal,
|
||||
Self::ArtcraftHunyuan3d2p0(_)
|
||||
| Self::ArtcraftHunyuan3d2p1(_)
|
||||
| Self::ArtcraftHunyuan3d3(_)
|
||||
| Self::ArtcraftHunyuan3d3Sketch(_)
|
||||
| Self::ArtcraftHunyuan3d3p1Pro(_)
|
||||
| Self::ArtcraftHunyuan3d3p1Rapid(_)
|
||||
| Self::ArtcraftHunyuan3d3p1Part(_)
|
||||
| Self::ArtcraftHunyuan3d3p1SmartTopology(_)
|
||||
| Self::ArtcraftTripo3dH3p1(_)
|
||||
| Self::ArtcraftMeshyV6(_)
|
||||
| Self::ArtcraftRodin2p5Fast(_) => RouterProvider::Artcraft,
|
||||
Self::FalHunyuan3d2p0(_)
|
||||
| Self::FalHunyuan3d2p1(_)
|
||||
| Self::FalHunyuan3d3Image(_)
|
||||
| Self::FalHunyuan3d3Text(_)
|
||||
| Self::FalHunyuan3d3Sketch(_)
|
||||
| Self::FalHunyuan3d3p1ProImage(_)
|
||||
| Self::FalHunyuan3d3p1ProText(_)
|
||||
| Self::FalHunyuan3d3p1RapidImage(_)
|
||||
| Self::FalHunyuan3d3p1RapidText(_)
|
||||
| Self::FalHunyuan3d3p1Part(_)
|
||||
| Self::FalHunyuan3d3p1SmartTopology(_)
|
||||
| Self::FalTripo3dH3p1Text(_)
|
||||
| Self::FalTripo3dH3p1Image(_)
|
||||
| Self::FalTripo3dH3p1Multiview(_)
|
||||
| Self::FalMeshyV6Text(_)
|
||||
| Self::FalMeshyV6Image(_)
|
||||
| Self::FalRodin2p5FastText(_)
|
||||
| Self::FalRodin2p5FastImage(_) => RouterProvider::Fal,
|
||||
}
|
||||
}
|
||||
|
||||
/// Return a cost estimate to fulfill the request.
|
||||
pub fn estimate_cost(&self) -> Result<MeshGenerationCostEstimate, ArtcraftRouterError> {
|
||||
match self {
|
||||
MeshGenerationRequest::ArtcraftHunyuan3d2p0(request) => Ok(ArtcraftHunyuan3d2p0CostState::from_request(request).estimate_cost()),
|
||||
MeshGenerationRequest::ArtcraftHunyuan3d2p1(request) => Ok(ArtcraftHunyuan3d2p1CostState::from_request(request).estimate_cost()),
|
||||
MeshGenerationRequest::ArtcraftHunyuan3d3(request) => Ok(ArtcraftHunyuan3d3CostState::from_request(request).estimate_cost()),
|
||||
MeshGenerationRequest::ArtcraftHunyuan3d3Sketch(request) => Ok(ArtcraftHunyuan3d3SketchCostState::from_request(request).estimate_cost()),
|
||||
MeshGenerationRequest::FalHunyuan3d2p0(request) => Ok(FalHunyuan3d2p0CostState::from_request(request).estimate_cost()),
|
||||
MeshGenerationRequest::FalHunyuan3d2p1(request) => Ok(FalHunyuan3d2p1CostState::from_request(request).estimate_cost()),
|
||||
MeshGenerationRequest::FalHunyuan3d3Image(request) => Ok(FalHunyuan3d3ImageCostState::from_request(request).estimate_cost()),
|
||||
MeshGenerationRequest::FalHunyuan3d3Text(request) => Ok(FalHunyuan3d3TextCostState::from_request(request).estimate_cost()),
|
||||
MeshGenerationRequest::FalHunyuan3d3Sketch(request) => Ok(FalHunyuan3d3SketchCostState::from_request(request).estimate_cost()),
|
||||
Self::ArtcraftHunyuan3d2p0(request) => Ok(ArtcraftHunyuan3d2p0CostState::from_request(request).estimate_cost()),
|
||||
Self::ArtcraftHunyuan3d2p1(request) => Ok(ArtcraftHunyuan3d2p1CostState::from_request(request).estimate_cost()),
|
||||
Self::ArtcraftHunyuan3d3(request) => Ok(ArtcraftHunyuan3d3CostState::from_request(request).estimate_cost()),
|
||||
Self::ArtcraftHunyuan3d3Sketch(request) => Ok(ArtcraftHunyuan3d3SketchCostState::from_request(request).estimate_cost()),
|
||||
Self::ArtcraftHunyuan3d3p1Pro(request) => Ok(ArtcraftHunyuan3d3p1ProCostState::from_request(request).estimate_cost()),
|
||||
Self::ArtcraftHunyuan3d3p1Rapid(request) => Ok(ArtcraftHunyuan3d3p1RapidCostState::from_request(request).estimate_cost()),
|
||||
Self::ArtcraftHunyuan3d3p1Part(request) => Ok(ArtcraftHunyuan3d3p1PartCostState::from_request(request).estimate_cost()),
|
||||
Self::ArtcraftHunyuan3d3p1SmartTopology(request) => Ok(ArtcraftHunyuan3d3p1SmartTopologyCostState::from_request(request).estimate_cost()),
|
||||
Self::ArtcraftTripo3dH3p1(request) => Ok(ArtcraftTripo3dH3p1CostState::from_request(request).estimate_cost()),
|
||||
Self::ArtcraftMeshyV6(request) => Ok(ArtcraftMeshyV6CostState::from_request(request).estimate_cost()),
|
||||
Self::ArtcraftRodin2p5Fast(request) => Ok(ArtcraftRodin2p5FastCostState::from_request(request).estimate_cost()),
|
||||
Self::FalHunyuan3d2p0(request) => Ok(FalHunyuan3d2p0CostState::from_request(request).estimate_cost()),
|
||||
Self::FalHunyuan3d2p1(request) => Ok(FalHunyuan3d2p1CostState::from_request(request).estimate_cost()),
|
||||
Self::FalHunyuan3d3Image(request) => Ok(FalHunyuan3d3ImageCostState::from_request(request).estimate_cost()),
|
||||
Self::FalHunyuan3d3Text(request) => Ok(FalHunyuan3d3TextCostState::from_request(request).estimate_cost()),
|
||||
Self::FalHunyuan3d3Sketch(request) => Ok(FalHunyuan3d3SketchCostState::from_request(request).estimate_cost()),
|
||||
Self::FalHunyuan3d3p1ProImage(request) => Ok(FalHunyuan3d3p1ProImageCostState::from_request(request).estimate_cost()),
|
||||
Self::FalHunyuan3d3p1ProText(request) => Ok(FalHunyuan3d3p1ProTextCostState::from_request(request).estimate_cost()),
|
||||
Self::FalHunyuan3d3p1RapidImage(request) => Ok(FalHunyuan3d3p1RapidImageCostState::from_request(request).estimate_cost()),
|
||||
Self::FalHunyuan3d3p1RapidText(request) => Ok(FalHunyuan3d3p1RapidTextCostState::from_request(request).estimate_cost()),
|
||||
Self::FalHunyuan3d3p1Part(request) => Ok(FalHunyuan3d3p1PartCostState::from_request(request).estimate_cost()),
|
||||
Self::FalHunyuan3d3p1SmartTopology(request) => Ok(FalHunyuan3d3p1SmartTopologyCostState::from_request(request).estimate_cost()),
|
||||
Self::FalTripo3dH3p1Text(request) => Ok(FalTripo3dH3p1TextCostState::from_request(request).estimate_cost()),
|
||||
Self::FalTripo3dH3p1Image(request) => Ok(FalTripo3dH3p1ImageCostState::from_request(request).estimate_cost()),
|
||||
Self::FalTripo3dH3p1Multiview(request) => Ok(FalTripo3dH3p1MultiviewCostState::from_request(request).estimate_cost()),
|
||||
Self::FalMeshyV6Text(request) => Ok(FalMeshyV6TextCostState::from_request(request).estimate_cost()),
|
||||
Self::FalMeshyV6Image(request) => Ok(FalMeshyV6ImageCostState::from_request(request).estimate_cost()),
|
||||
Self::FalRodin2p5FastText(request) => Ok(FalRodin2p5FastTextCostState::from_request(request).estimate_cost()),
|
||||
Self::FalRodin2p5FastImage(request) => Ok(FalRodin2p5FastImageCostState::from_request(request).estimate_cost()),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,42 +180,35 @@ impl MeshGenerationRequest {
|
||||
/// If successful, returns the job IDs.
|
||||
pub async fn send_request(&self, client: &RouterClient) -> Result<GenerateMeshResponse, ArtcraftRouterError> {
|
||||
match self {
|
||||
MeshGenerationRequest::ArtcraftHunyuan3d2p0(request) => {
|
||||
let client_ref = client.get_artcraft_client_ref()?;
|
||||
request.send(client_ref).await
|
||||
},
|
||||
MeshGenerationRequest::ArtcraftHunyuan3d2p1(request) => {
|
||||
let client_ref = client.get_artcraft_client_ref()?;
|
||||
request.send(client_ref).await
|
||||
},
|
||||
MeshGenerationRequest::ArtcraftHunyuan3d3(request) => {
|
||||
let client_ref = client.get_artcraft_client_ref()?;
|
||||
request.send(client_ref).await
|
||||
},
|
||||
MeshGenerationRequest::ArtcraftHunyuan3d3Sketch(request) => {
|
||||
let client_ref = client.get_artcraft_client_ref()?;
|
||||
request.send(client_ref).await
|
||||
},
|
||||
MeshGenerationRequest::FalHunyuan3d2p0(request) => {
|
||||
let client_ref = client.get_fal_client_ref()?;
|
||||
request.send(client_ref).await
|
||||
},
|
||||
MeshGenerationRequest::FalHunyuan3d2p1(request) => {
|
||||
let client_ref = client.get_fal_client_ref()?;
|
||||
request.send(client_ref).await
|
||||
},
|
||||
MeshGenerationRequest::FalHunyuan3d3Image(request) => {
|
||||
let client_ref = client.get_fal_client_ref()?;
|
||||
request.send(client_ref).await
|
||||
},
|
||||
MeshGenerationRequest::FalHunyuan3d3Text(request) => {
|
||||
let client_ref = client.get_fal_client_ref()?;
|
||||
request.send(client_ref).await
|
||||
},
|
||||
MeshGenerationRequest::FalHunyuan3d3Sketch(request) => {
|
||||
let client_ref = client.get_fal_client_ref()?;
|
||||
request.send(client_ref).await
|
||||
},
|
||||
Self::ArtcraftHunyuan3d2p0(request) => request.send(client.get_artcraft_client_ref()?).await,
|
||||
Self::ArtcraftHunyuan3d2p1(request) => request.send(client.get_artcraft_client_ref()?).await,
|
||||
Self::ArtcraftHunyuan3d3(request) => request.send(client.get_artcraft_client_ref()?).await,
|
||||
Self::ArtcraftHunyuan3d3Sketch(request) => request.send(client.get_artcraft_client_ref()?).await,
|
||||
Self::ArtcraftHunyuan3d3p1Pro(request) => request.send(client.get_artcraft_client_ref()?).await,
|
||||
Self::ArtcraftHunyuan3d3p1Rapid(request) => request.send(client.get_artcraft_client_ref()?).await,
|
||||
Self::ArtcraftHunyuan3d3p1Part(request) => request.send(client.get_artcraft_client_ref()?).await,
|
||||
Self::ArtcraftHunyuan3d3p1SmartTopology(request) => request.send(client.get_artcraft_client_ref()?).await,
|
||||
Self::ArtcraftTripo3dH3p1(request) => request.send(client.get_artcraft_client_ref()?).await,
|
||||
Self::ArtcraftMeshyV6(request) => request.send(client.get_artcraft_client_ref()?).await,
|
||||
Self::ArtcraftRodin2p5Fast(request) => request.send(client.get_artcraft_client_ref()?).await,
|
||||
Self::FalHunyuan3d2p0(request) => request.send(client.get_fal_client_ref()?).await,
|
||||
Self::FalHunyuan3d2p1(request) => request.send(client.get_fal_client_ref()?).await,
|
||||
Self::FalHunyuan3d3Image(request) => request.send(client.get_fal_client_ref()?).await,
|
||||
Self::FalHunyuan3d3Text(request) => request.send(client.get_fal_client_ref()?).await,
|
||||
Self::FalHunyuan3d3Sketch(request) => request.send(client.get_fal_client_ref()?).await,
|
||||
Self::FalHunyuan3d3p1ProImage(request) => request.send(client.get_fal_client_ref()?).await,
|
||||
Self::FalHunyuan3d3p1ProText(request) => request.send(client.get_fal_client_ref()?).await,
|
||||
Self::FalHunyuan3d3p1RapidImage(request) => request.send(client.get_fal_client_ref()?).await,
|
||||
Self::FalHunyuan3d3p1RapidText(request) => request.send(client.get_fal_client_ref()?).await,
|
||||
Self::FalHunyuan3d3p1Part(request) => request.send(client.get_fal_client_ref()?).await,
|
||||
Self::FalHunyuan3d3p1SmartTopology(request) => request.send(client.get_fal_client_ref()?).await,
|
||||
Self::FalTripo3dH3p1Text(request) => request.send(client.get_fal_client_ref()?).await,
|
||||
Self::FalTripo3dH3p1Image(request) => request.send(client.get_fal_client_ref()?).await,
|
||||
Self::FalTripo3dH3p1Multiview(request) => request.send(client.get_fal_client_ref()?).await,
|
||||
Self::FalMeshyV6Text(request) => request.send(client.get_fal_client_ref()?).await,
|
||||
Self::FalMeshyV6Image(request) => request.send(client.get_fal_client_ref()?).await,
|
||||
Self::FalRodin2p5FastText(request) => request.send(client.get_fal_client_ref()?).await,
|
||||
Self::FalRodin2p5FastImage(request) => request.send(client.get_fal_client_ref()?).await,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+6
-1
@@ -4,7 +4,7 @@ use enums::common::generation::common_mesh_model::CommonMeshModel as CommonMeshM
|
||||
use crate::errors::artcraft_router_error::ArtcraftRouterError;
|
||||
use crate::generate::generate_mesh::generate_mesh_request_builder::GenerateMeshRequestBuilder;
|
||||
use crate::generate::generate_mesh::providers::artcraft::resolve::{
|
||||
resolve_image_list_ref, resolve_image_ref,
|
||||
resolve_image_list_ref, resolve_image_ref, resolve_mesh_ref,
|
||||
};
|
||||
|
||||
/// Build an `OmniGenMeshCostAndGenerateRequest` from the builder. The
|
||||
@@ -20,6 +20,7 @@ pub fn build_artcraft_omni_mesh_request(
|
||||
let back_image_media_token = resolve_image_ref(builder.back_image.take())?;
|
||||
let left_image_media_token = resolve_image_ref(builder.left_image.take())?;
|
||||
let right_image_media_token = resolve_image_ref(builder.right_image.take())?;
|
||||
let input_mesh_media_token = resolve_mesh_ref(builder.input_mesh.take())?;
|
||||
let idempotency_token = builder.get_or_generate_idempotency_token();
|
||||
|
||||
Ok(OmniGenMeshCostAndGenerateRequest {
|
||||
@@ -31,10 +32,14 @@ pub fn build_artcraft_omni_mesh_request(
|
||||
back_image_media_token,
|
||||
left_image_media_token,
|
||||
right_image_media_token,
|
||||
input_mesh_media_token,
|
||||
mesh_output_type: builder.mesh_output_type,
|
||||
polygon_type: builder.polygon_type,
|
||||
face_count: builder.face_count,
|
||||
enable_pbr: builder.enable_pbr,
|
||||
enable_texture: builder.enable_texture,
|
||||
texture_quality: builder.texture_quality,
|
||||
geometry_quality: builder.geometry_quality,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
use enums::common::generation::common_mesh_model::CommonMeshModel as CommonMeshModelEnum;
|
||||
|
||||
use crate::errors::artcraft_router_error::ArtcraftRouterError;
|
||||
use crate::generate::generate_mesh::generate_mesh_request_builder::GenerateMeshRequestBuilder;
|
||||
use crate::generate::generate_mesh::mesh_generation_draft_or_request::MeshGenerationDraftOrRequest;
|
||||
use crate::generate::generate_mesh::mesh_generation_request::MeshGenerationRequest;
|
||||
use crate::generate::generate_mesh::providers::artcraft::build_common::build_artcraft_omni_mesh_request;
|
||||
use crate::generate::generate_mesh::providers::artcraft::hunyuan_3d_3p1_part::request::ArtcraftHunyuan3d3p1PartRequestState;
|
||||
|
||||
pub fn build_artcraft_hunyuan_3d_3p1_part(builder: GenerateMeshRequestBuilder) -> Result<MeshGenerationDraftOrRequest, ArtcraftRouterError> {
|
||||
let request = build_artcraft_omni_mesh_request(builder, CommonMeshModelEnum::Hunyuan3d3p1Part)?;
|
||||
let state = ArtcraftHunyuan3d3p1PartRequestState { request };
|
||||
Ok(MeshGenerationDraftOrRequest::Request(MeshGenerationRequest::ArtcraftHunyuan3d3p1Part(state)))
|
||||
}
|
||||
+78
@@ -0,0 +1,78 @@
|
||||
use crate::generate::generate_mesh::mesh_generation_cost_estimate::MeshGenerationCostEstimate;
|
||||
use crate::generate::generate_mesh::providers::artcraft::hunyuan_3d_3p1_part::request::ArtcraftHunyuan3d3p1PartRequestState;
|
||||
|
||||
const FLAT_COST_IN_USD_CENTS: u64 = 59;
|
||||
|
||||
/// Hunyuan 3D v3.1 Part via Artcraft: flat 59¢ per generation, with no
|
||||
/// add-ons.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct ArtcraftHunyuan3d3p1PartCostState {
|
||||
pub cost_in_usd_cents: u64,
|
||||
}
|
||||
|
||||
impl ArtcraftHunyuan3d3p1PartCostState {
|
||||
pub fn from_request(_request: &ArtcraftHunyuan3d3p1PartRequestState) -> Self {
|
||||
Self { cost_in_usd_cents: FLAT_COST_IN_USD_CENTS }
|
||||
}
|
||||
|
||||
pub fn estimate_cost(&self) -> MeshGenerationCostEstimate {
|
||||
MeshGenerationCostEstimate {
|
||||
cost_in_credits: Some(self.cost_in_usd_cents),
|
||||
cost_in_usd_cents: Some(self.cost_in_usd_cents),
|
||||
is_free: false,
|
||||
is_unlimited: false,
|
||||
is_rate_limited: false,
|
||||
has_watermark: false,
|
||||
failures_are_refunded: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use tokens::tokens::media_files::MediaFileToken;
|
||||
|
||||
use crate::api::mesh_ref::MeshRef;
|
||||
use crate::api::router_mesh_model::RouterMeshModel;
|
||||
use crate::api::router_provider::RouterProvider;
|
||||
use crate::generate::generate_mesh::generate_mesh_request_builder::GenerateMeshRequestBuilder;
|
||||
|
||||
mod cost_table {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn cost_is_a_flat_fifty_nine_cents() {
|
||||
assert_eq!(estimate_usd_cents(base_builder()), 59);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn options_do_not_change_the_price() {
|
||||
let builder = GenerateMeshRequestBuilder {
|
||||
enable_pbr: Some(true),
|
||||
face_count: Some(100_000),
|
||||
..base_builder()
|
||||
};
|
||||
assert_eq!(estimate_usd_cents(builder), 59);
|
||||
}
|
||||
}
|
||||
|
||||
// ── Helpers ──
|
||||
|
||||
fn base_builder() -> GenerateMeshRequestBuilder {
|
||||
GenerateMeshRequestBuilder {
|
||||
model: RouterMeshModel::Hunyuan3d3p1Part,
|
||||
provider: RouterProvider::Artcraft,
|
||||
input_mesh: Some(MeshRef::MediaFileToken(MediaFileToken::new("mf_mesh".to_string()))),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
fn estimate_usd_cents(builder: GenerateMeshRequestBuilder) -> u64 {
|
||||
builder.build2()
|
||||
.expect("build should succeed")
|
||||
.estimate_cost()
|
||||
.expect("estimate should succeed")
|
||||
.cost_in_usd_cents
|
||||
.expect("cost should be present")
|
||||
}
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
pub mod build;
|
||||
pub mod cost;
|
||||
pub mod request;
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
use artcraft_api_defs::omni_gen::cost_and_generate_requests::omni_gen_mesh_cost_and_generate_request::OmniGenMeshCostAndGenerateRequest;
|
||||
|
||||
use crate::client::router_artcraft_client::RouterArtcraftClient;
|
||||
use crate::errors::artcraft_router_error::ArtcraftRouterError;
|
||||
use crate::generate::generate_mesh::generate_mesh_response::GenerateMeshResponse;
|
||||
use crate::generate::generate_mesh::providers::artcraft::request_common::send_artcraft_omni_mesh_request;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct ArtcraftHunyuan3d3p1PartRequestState {
|
||||
pub request: OmniGenMeshCostAndGenerateRequest,
|
||||
}
|
||||
|
||||
impl ArtcraftHunyuan3d3p1PartRequestState {
|
||||
pub async fn send(&self, client: &RouterArtcraftClient) -> Result<GenerateMeshResponse, ArtcraftRouterError> {
|
||||
send_artcraft_omni_mesh_request(&self.request, client).await
|
||||
}
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
use enums::common::generation::common_mesh_model::CommonMeshModel as CommonMeshModelEnum;
|
||||
|
||||
use crate::errors::artcraft_router_error::ArtcraftRouterError;
|
||||
use crate::generate::generate_mesh::generate_mesh_request_builder::GenerateMeshRequestBuilder;
|
||||
use crate::generate::generate_mesh::mesh_generation_draft_or_request::MeshGenerationDraftOrRequest;
|
||||
use crate::generate::generate_mesh::mesh_generation_request::MeshGenerationRequest;
|
||||
use crate::generate::generate_mesh::providers::artcraft::build_common::build_artcraft_omni_mesh_request;
|
||||
use crate::generate::generate_mesh::providers::artcraft::hunyuan_3d_3p1_pro::request::ArtcraftHunyuan3d3p1ProRequestState;
|
||||
|
||||
pub fn build_artcraft_hunyuan_3d_3p1_pro(builder: GenerateMeshRequestBuilder) -> Result<MeshGenerationDraftOrRequest, ArtcraftRouterError> {
|
||||
let request = build_artcraft_omni_mesh_request(builder, CommonMeshModelEnum::Hunyuan3d3p1Pro)?;
|
||||
let state = ArtcraftHunyuan3d3p1ProRequestState { request };
|
||||
Ok(MeshGenerationDraftOrRequest::Request(MeshGenerationRequest::ArtcraftHunyuan3d3p1Pro(state)))
|
||||
}
|
||||
+183
@@ -0,0 +1,183 @@
|
||||
use crate::generate::generate_mesh::mesh_generation_cost_estimate::MeshGenerationCostEstimate;
|
||||
use crate::generate::generate_mesh::providers::artcraft::hunyuan_3d_3p1_pro::request::ArtcraftHunyuan3d3p1ProRequestState;
|
||||
|
||||
const BASE_COST_IN_USD_CENTS: u64 = 49;
|
||||
const ADD_ON_COST_IN_USD_CENTS: u64 = 20;
|
||||
|
||||
/// Hunyuan 3D v3.1 Pro via Artcraft: flat 49¢ base price (any output type),
|
||||
/// plus a flat 20¢ add-on for each extra option (PBR materials, custom face
|
||||
/// count, and multi-view side images).
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct ArtcraftHunyuan3d3p1ProCostState {
|
||||
pub cost_in_usd_cents: u64,
|
||||
}
|
||||
|
||||
impl ArtcraftHunyuan3d3p1ProCostState {
|
||||
pub fn from_request(request: &ArtcraftHunyuan3d3p1ProRequestState) -> Self {
|
||||
let request = &request.request;
|
||||
|
||||
let mut cost = BASE_COST_IN_USD_CENTS;
|
||||
if request.enable_pbr.unwrap_or(false) {
|
||||
cost += ADD_ON_COST_IN_USD_CENTS;
|
||||
}
|
||||
if request.face_count.is_some() {
|
||||
cost += ADD_ON_COST_IN_USD_CENTS;
|
||||
}
|
||||
let uses_multi_view = request.back_image_media_token.is_some()
|
||||
|| request.left_image_media_token.is_some()
|
||||
|| request.right_image_media_token.is_some();
|
||||
if uses_multi_view {
|
||||
cost += ADD_ON_COST_IN_USD_CENTS;
|
||||
}
|
||||
|
||||
Self { cost_in_usd_cents: cost }
|
||||
}
|
||||
|
||||
pub fn estimate_cost(&self) -> MeshGenerationCostEstimate {
|
||||
MeshGenerationCostEstimate {
|
||||
cost_in_credits: Some(self.cost_in_usd_cents),
|
||||
cost_in_usd_cents: Some(self.cost_in_usd_cents),
|
||||
is_free: false,
|
||||
is_unlimited: false,
|
||||
is_rate_limited: false,
|
||||
has_watermark: false,
|
||||
failures_are_refunded: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use enums::common::generation::common_mesh_output_type::CommonMeshOutputType;
|
||||
use tokens::tokens::media_files::MediaFileToken;
|
||||
|
||||
use crate::api::image_list_ref::ImageListRef;
|
||||
use crate::api::image_ref::ImageRef;
|
||||
use crate::api::router_mesh_model::RouterMeshModel;
|
||||
use crate::api::router_provider::RouterProvider;
|
||||
use crate::generate::generate_mesh::generate_mesh_request_builder::GenerateMeshRequestBuilder;
|
||||
|
||||
use super::*;
|
||||
|
||||
mod base_costs {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn default_output_type_is_forty_nine_cents() {
|
||||
assert_eq!(estimate_usd_cents(base_builder()), 49);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn every_output_type_prices_the_same() {
|
||||
let cases = [
|
||||
Some(CommonMeshOutputType::Normal),
|
||||
Some(CommonMeshOutputType::Geometry),
|
||||
];
|
||||
for output_type in cases {
|
||||
let builder = GenerateMeshRequestBuilder {
|
||||
mesh_output_type: output_type,
|
||||
..base_builder()
|
||||
};
|
||||
assert_eq!(estimate_usd_cents(builder), 49, "for {output_type:?}");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn text_mode_prices_the_same_as_image_mode() {
|
||||
// Text-only request (no image references).
|
||||
let text_builder = GenerateMeshRequestBuilder {
|
||||
model: RouterMeshModel::Hunyuan3d3p1Pro,
|
||||
provider: RouterProvider::Artcraft,
|
||||
prompt: Some("a red ceramic teapot".to_string()),
|
||||
..Default::default()
|
||||
};
|
||||
assert_eq!(estimate_usd_cents(text_builder), 49);
|
||||
}
|
||||
}
|
||||
|
||||
mod add_ons {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn pbr_adds_twenty_cents() {
|
||||
let builder = GenerateMeshRequestBuilder {
|
||||
enable_pbr: Some(true),
|
||||
..base_builder()
|
||||
};
|
||||
assert_eq!(estimate_usd_cents(builder), 49 + 20);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pbr_false_adds_nothing() {
|
||||
let builder = GenerateMeshRequestBuilder {
|
||||
enable_pbr: Some(false),
|
||||
..base_builder()
|
||||
};
|
||||
assert_eq!(estimate_usd_cents(builder), 49);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn face_count_adds_twenty_cents() {
|
||||
let builder = GenerateMeshRequestBuilder {
|
||||
face_count: Some(100_000),
|
||||
..base_builder()
|
||||
};
|
||||
assert_eq!(estimate_usd_cents(builder), 49 + 20);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn each_side_image_adds_twenty_cents_at_most_once() {
|
||||
for side in ["back", "left", "right"] {
|
||||
let mut builder = base_builder();
|
||||
let image = Some(ImageRef::MediaFileToken(MediaFileToken::new(format!("mf_{side}"))));
|
||||
match side {
|
||||
"back" => builder.back_image = image,
|
||||
"left" => builder.left_image = image,
|
||||
_ => builder.right_image = image,
|
||||
}
|
||||
assert_eq!(estimate_usd_cents(builder), 49 + 20, "for {side} image");
|
||||
}
|
||||
|
||||
let builder = GenerateMeshRequestBuilder {
|
||||
back_image: Some(ImageRef::MediaFileToken(MediaFileToken::new("mf_back".to_string()))),
|
||||
left_image: Some(ImageRef::MediaFileToken(MediaFileToken::new("mf_left".to_string()))),
|
||||
right_image: Some(ImageRef::MediaFileToken(MediaFileToken::new("mf_right".to_string()))),
|
||||
..base_builder()
|
||||
};
|
||||
assert_eq!(estimate_usd_cents(builder), 49 + 20);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn all_add_ons_stack() {
|
||||
let builder = GenerateMeshRequestBuilder {
|
||||
enable_pbr: Some(true),
|
||||
face_count: Some(100_000),
|
||||
back_image: Some(ImageRef::MediaFileToken(MediaFileToken::new("mf_back".to_string()))),
|
||||
..base_builder()
|
||||
};
|
||||
assert_eq!(estimate_usd_cents(builder), 49 + 20 + 20 + 20);
|
||||
}
|
||||
}
|
||||
|
||||
// ── Helpers ──
|
||||
|
||||
fn base_builder() -> GenerateMeshRequestBuilder {
|
||||
GenerateMeshRequestBuilder {
|
||||
model: RouterMeshModel::Hunyuan3d3p1Pro,
|
||||
provider: RouterProvider::Artcraft,
|
||||
reference_images: Some(ImageListRef::MediaFileTokens(vec![
|
||||
MediaFileToken::new("mf_front".to_string()),
|
||||
])),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
fn estimate_usd_cents(builder: GenerateMeshRequestBuilder) -> u64 {
|
||||
builder.build2()
|
||||
.expect("build should succeed")
|
||||
.estimate_cost()
|
||||
.expect("estimate should succeed")
|
||||
.cost_in_usd_cents
|
||||
.expect("cost should be present")
|
||||
}
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
pub mod build;
|
||||
pub mod cost;
|
||||
pub mod request;
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
use artcraft_api_defs::omni_gen::cost_and_generate_requests::omni_gen_mesh_cost_and_generate_request::OmniGenMeshCostAndGenerateRequest;
|
||||
|
||||
use crate::client::router_artcraft_client::RouterArtcraftClient;
|
||||
use crate::errors::artcraft_router_error::ArtcraftRouterError;
|
||||
use crate::generate::generate_mesh::generate_mesh_response::GenerateMeshResponse;
|
||||
use crate::generate::generate_mesh::providers::artcraft::request_common::send_artcraft_omni_mesh_request;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct ArtcraftHunyuan3d3p1ProRequestState {
|
||||
pub request: OmniGenMeshCostAndGenerateRequest,
|
||||
}
|
||||
|
||||
impl ArtcraftHunyuan3d3p1ProRequestState {
|
||||
pub async fn send(&self, client: &RouterArtcraftClient) -> Result<GenerateMeshResponse, ArtcraftRouterError> {
|
||||
send_artcraft_omni_mesh_request(&self.request, client).await
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::api::router_mesh_model::RouterMeshModel;
|
||||
use crate::api::router_provider::RouterProvider;
|
||||
use crate::generate::generate_mesh::generate_mesh_request_builder::GenerateMeshRequestBuilder;
|
||||
use crate::generate::generate_mesh::generate_mesh_response::GenerateMeshResponse;
|
||||
use crate::generate::generate_mesh::mesh_generation_draft_or_request::MeshGenerationDraftOrRequest;
|
||||
use crate::test_helpers::get_artcraft_client;
|
||||
|
||||
#[tokio::test]
|
||||
#[ignore] // sends a real generation to the Artcraft backend, incurs cost
|
||||
async fn text_to_mesh_teapot() {
|
||||
let response = run_pipeline(GenerateMeshRequestBuilder {
|
||||
model: RouterMeshModel::Hunyuan3d3p1Pro,
|
||||
provider: RouterProvider::Artcraft,
|
||||
prompt: Some("A red ceramic teapot with a curved spout.".to_string()),
|
||||
..Default::default()
|
||||
}).await;
|
||||
assert!(matches!(response, GenerateMeshResponse::Artcraft(_)));
|
||||
assert_eq!(1, 2, "Inspect output above");
|
||||
}
|
||||
|
||||
// ── Helpers ──
|
||||
|
||||
async fn run_pipeline(builder: GenerateMeshRequestBuilder) -> GenerateMeshResponse {
|
||||
let client = get_artcraft_client();
|
||||
|
||||
let draft_or_request = builder.build2().expect("build2 should succeed");
|
||||
let request = match draft_or_request {
|
||||
MeshGenerationDraftOrRequest::Request(r) => r,
|
||||
_ => panic!("expected Request variant (Artcraft skips draft)"),
|
||||
};
|
||||
|
||||
let response = request.send_request(&client).await.expect("send_request should succeed");
|
||||
|
||||
match &response {
|
||||
GenerateMeshResponse::Artcraft(p) => {
|
||||
println!("inference_job_token={:?}", p.inference_job_token);
|
||||
println!("all_inference_job_tokens={:?}", p.all_inference_job_tokens);
|
||||
}
|
||||
other => println!("response: {:?}", other),
|
||||
}
|
||||
|
||||
response
|
||||
}
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
use enums::common::generation::common_mesh_model::CommonMeshModel as CommonMeshModelEnum;
|
||||
|
||||
use crate::errors::artcraft_router_error::ArtcraftRouterError;
|
||||
use crate::generate::generate_mesh::generate_mesh_request_builder::GenerateMeshRequestBuilder;
|
||||
use crate::generate::generate_mesh::mesh_generation_draft_or_request::MeshGenerationDraftOrRequest;
|
||||
use crate::generate::generate_mesh::mesh_generation_request::MeshGenerationRequest;
|
||||
use crate::generate::generate_mesh::providers::artcraft::build_common::build_artcraft_omni_mesh_request;
|
||||
use crate::generate::generate_mesh::providers::artcraft::hunyuan_3d_3p1_rapid::request::ArtcraftHunyuan3d3p1RapidRequestState;
|
||||
|
||||
pub fn build_artcraft_hunyuan_3d_3p1_rapid(builder: GenerateMeshRequestBuilder) -> Result<MeshGenerationDraftOrRequest, ArtcraftRouterError> {
|
||||
let request = build_artcraft_omni_mesh_request(builder, CommonMeshModelEnum::Hunyuan3d3p1Rapid)?;
|
||||
let state = ArtcraftHunyuan3d3p1RapidRequestState { request };
|
||||
Ok(MeshGenerationDraftOrRequest::Request(MeshGenerationRequest::ArtcraftHunyuan3d3p1Rapid(state)))
|
||||
}
|
||||
+124
@@ -0,0 +1,124 @@
|
||||
use crate::generate::generate_mesh::mesh_generation_cost_estimate::MeshGenerationCostEstimate;
|
||||
use crate::generate::generate_mesh::providers::artcraft::hunyuan_3d_3p1_rapid::request::ArtcraftHunyuan3d3p1RapidRequestState;
|
||||
|
||||
const BASE_COST_IN_USD_CENTS: u64 = 30;
|
||||
const PBR_ADD_ON_COST_IN_USD_CENTS: u64 = 20;
|
||||
|
||||
/// Hunyuan 3D v3.1 Rapid via Artcraft: flat 30¢ base price, plus a flat 20¢
|
||||
/// add-on for PBR materials.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct ArtcraftHunyuan3d3p1RapidCostState {
|
||||
pub cost_in_usd_cents: u64,
|
||||
}
|
||||
|
||||
impl ArtcraftHunyuan3d3p1RapidCostState {
|
||||
pub fn from_request(request: &ArtcraftHunyuan3d3p1RapidRequestState) -> Self {
|
||||
let request = &request.request;
|
||||
|
||||
let mut cost = BASE_COST_IN_USD_CENTS;
|
||||
if request.enable_pbr.unwrap_or(false) {
|
||||
cost += PBR_ADD_ON_COST_IN_USD_CENTS;
|
||||
}
|
||||
|
||||
Self { cost_in_usd_cents: cost }
|
||||
}
|
||||
|
||||
pub fn estimate_cost(&self) -> MeshGenerationCostEstimate {
|
||||
MeshGenerationCostEstimate {
|
||||
cost_in_credits: Some(self.cost_in_usd_cents),
|
||||
cost_in_usd_cents: Some(self.cost_in_usd_cents),
|
||||
is_free: false,
|
||||
is_unlimited: false,
|
||||
is_rate_limited: false,
|
||||
has_watermark: false,
|
||||
failures_are_refunded: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use enums::common::generation::common_mesh_output_type::CommonMeshOutputType;
|
||||
use tokens::tokens::media_files::MediaFileToken;
|
||||
|
||||
use crate::api::image_list_ref::ImageListRef;
|
||||
use crate::api::router_mesh_model::RouterMeshModel;
|
||||
use crate::api::router_provider::RouterProvider;
|
||||
use crate::generate::generate_mesh::generate_mesh_request_builder::GenerateMeshRequestBuilder;
|
||||
|
||||
use super::*;
|
||||
|
||||
mod base_costs {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn base_cost_is_thirty_cents() {
|
||||
assert_eq!(estimate_usd_cents(base_builder()), 30);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn text_mode_prices_the_same_as_image_mode() {
|
||||
// Text-only request (no image references).
|
||||
let text_builder = GenerateMeshRequestBuilder {
|
||||
model: RouterMeshModel::Hunyuan3d3p1Rapid,
|
||||
provider: RouterProvider::Artcraft,
|
||||
prompt: Some("a red ceramic teapot".to_string()),
|
||||
..Default::default()
|
||||
};
|
||||
assert_eq!(estimate_usd_cents(text_builder), 30);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn output_type_does_not_change_the_price() {
|
||||
let builder = GenerateMeshRequestBuilder {
|
||||
mesh_output_type: Some(CommonMeshOutputType::Geometry),
|
||||
..base_builder()
|
||||
};
|
||||
assert_eq!(estimate_usd_cents(builder), 30);
|
||||
}
|
||||
}
|
||||
|
||||
mod add_ons {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn pbr_adds_twenty_cents() {
|
||||
let builder = GenerateMeshRequestBuilder {
|
||||
enable_pbr: Some(true),
|
||||
..base_builder()
|
||||
};
|
||||
assert_eq!(estimate_usd_cents(builder), 30 + 20);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pbr_false_adds_nothing() {
|
||||
let builder = GenerateMeshRequestBuilder {
|
||||
enable_pbr: Some(false),
|
||||
..base_builder()
|
||||
};
|
||||
assert_eq!(estimate_usd_cents(builder), 30);
|
||||
}
|
||||
}
|
||||
|
||||
// ── Helpers ──
|
||||
|
||||
fn base_builder() -> GenerateMeshRequestBuilder {
|
||||
GenerateMeshRequestBuilder {
|
||||
model: RouterMeshModel::Hunyuan3d3p1Rapid,
|
||||
provider: RouterProvider::Artcraft,
|
||||
reference_images: Some(ImageListRef::MediaFileTokens(vec![
|
||||
MediaFileToken::new("mf_front".to_string()),
|
||||
])),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
fn estimate_usd_cents(builder: GenerateMeshRequestBuilder) -> u64 {
|
||||
builder.build2()
|
||||
.expect("build should succeed")
|
||||
.estimate_cost()
|
||||
.expect("estimate should succeed")
|
||||
.cost_in_usd_cents
|
||||
.expect("cost should be present")
|
||||
}
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
pub mod build;
|
||||
pub mod cost;
|
||||
pub mod request;
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
use artcraft_api_defs::omni_gen::cost_and_generate_requests::omni_gen_mesh_cost_and_generate_request::OmniGenMeshCostAndGenerateRequest;
|
||||
|
||||
use crate::client::router_artcraft_client::RouterArtcraftClient;
|
||||
use crate::errors::artcraft_router_error::ArtcraftRouterError;
|
||||
use crate::generate::generate_mesh::generate_mesh_response::GenerateMeshResponse;
|
||||
use crate::generate::generate_mesh::providers::artcraft::request_common::send_artcraft_omni_mesh_request;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct ArtcraftHunyuan3d3p1RapidRequestState {
|
||||
pub request: OmniGenMeshCostAndGenerateRequest,
|
||||
}
|
||||
|
||||
impl ArtcraftHunyuan3d3p1RapidRequestState {
|
||||
pub async fn send(&self, client: &RouterArtcraftClient) -> Result<GenerateMeshResponse, ArtcraftRouterError> {
|
||||
send_artcraft_omni_mesh_request(&self.request, client).await
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::api::router_mesh_model::RouterMeshModel;
|
||||
use crate::api::router_provider::RouterProvider;
|
||||
use crate::generate::generate_mesh::generate_mesh_request_builder::GenerateMeshRequestBuilder;
|
||||
use crate::generate::generate_mesh::generate_mesh_response::GenerateMeshResponse;
|
||||
use crate::generate::generate_mesh::mesh_generation_draft_or_request::MeshGenerationDraftOrRequest;
|
||||
use crate::test_helpers::get_artcraft_client;
|
||||
|
||||
#[tokio::test]
|
||||
#[ignore] // sends a real generation to the Artcraft backend, incurs cost
|
||||
async fn text_to_mesh_teapot() {
|
||||
let response = run_pipeline(GenerateMeshRequestBuilder {
|
||||
model: RouterMeshModel::Hunyuan3d3p1Rapid,
|
||||
provider: RouterProvider::Artcraft,
|
||||
prompt: Some("A red ceramic teapot with a curved spout.".to_string()),
|
||||
..Default::default()
|
||||
}).await;
|
||||
assert!(matches!(response, GenerateMeshResponse::Artcraft(_)));
|
||||
assert_eq!(1, 2, "Inspect output above");
|
||||
}
|
||||
|
||||
// ── Helpers ──
|
||||
|
||||
async fn run_pipeline(builder: GenerateMeshRequestBuilder) -> GenerateMeshResponse {
|
||||
let client = get_artcraft_client();
|
||||
|
||||
let draft_or_request = builder.build2().expect("build2 should succeed");
|
||||
let request = match draft_or_request {
|
||||
MeshGenerationDraftOrRequest::Request(r) => r,
|
||||
_ => panic!("expected Request variant (Artcraft skips draft)"),
|
||||
};
|
||||
|
||||
let response = request.send_request(&client).await.expect("send_request should succeed");
|
||||
|
||||
match &response {
|
||||
GenerateMeshResponse::Artcraft(p) => {
|
||||
println!("inference_job_token={:?}", p.inference_job_token);
|
||||
println!("all_inference_job_tokens={:?}", p.all_inference_job_tokens);
|
||||
}
|
||||
other => println!("response: {:?}", other),
|
||||
}
|
||||
|
||||
response
|
||||
}
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
use enums::common::generation::common_mesh_model::CommonMeshModel as CommonMeshModelEnum;
|
||||
|
||||
use crate::errors::artcraft_router_error::ArtcraftRouterError;
|
||||
use crate::generate::generate_mesh::generate_mesh_request_builder::GenerateMeshRequestBuilder;
|
||||
use crate::generate::generate_mesh::mesh_generation_draft_or_request::MeshGenerationDraftOrRequest;
|
||||
use crate::generate::generate_mesh::mesh_generation_request::MeshGenerationRequest;
|
||||
use crate::generate::generate_mesh::providers::artcraft::build_common::build_artcraft_omni_mesh_request;
|
||||
use crate::generate::generate_mesh::providers::artcraft::hunyuan_3d_3p1_smart_topology::request::ArtcraftHunyuan3d3p1SmartTopologyRequestState;
|
||||
|
||||
pub fn build_artcraft_hunyuan_3d_3p1_smart_topology(builder: GenerateMeshRequestBuilder) -> Result<MeshGenerationDraftOrRequest, ArtcraftRouterError> {
|
||||
let request = build_artcraft_omni_mesh_request(builder, CommonMeshModelEnum::Hunyuan3d3p1SmartTopology)?;
|
||||
let state = ArtcraftHunyuan3d3p1SmartTopologyRequestState { request };
|
||||
Ok(MeshGenerationDraftOrRequest::Request(MeshGenerationRequest::ArtcraftHunyuan3d3p1SmartTopology(state)))
|
||||
}
|
||||
+78
@@ -0,0 +1,78 @@
|
||||
use crate::generate::generate_mesh::mesh_generation_cost_estimate::MeshGenerationCostEstimate;
|
||||
use crate::generate::generate_mesh::providers::artcraft::hunyuan_3d_3p1_smart_topology::request::ArtcraftHunyuan3d3p1SmartTopologyRequestState;
|
||||
|
||||
const FLAT_COST_IN_USD_CENTS: u64 = 98;
|
||||
|
||||
/// Hunyuan 3D v3.1 Smart Topology via Artcraft: flat 98¢ per generation,
|
||||
/// with no add-ons.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct ArtcraftHunyuan3d3p1SmartTopologyCostState {
|
||||
pub cost_in_usd_cents: u64,
|
||||
}
|
||||
|
||||
impl ArtcraftHunyuan3d3p1SmartTopologyCostState {
|
||||
pub fn from_request(_request: &ArtcraftHunyuan3d3p1SmartTopologyRequestState) -> Self {
|
||||
Self { cost_in_usd_cents: FLAT_COST_IN_USD_CENTS }
|
||||
}
|
||||
|
||||
pub fn estimate_cost(&self) -> MeshGenerationCostEstimate {
|
||||
MeshGenerationCostEstimate {
|
||||
cost_in_credits: Some(self.cost_in_usd_cents),
|
||||
cost_in_usd_cents: Some(self.cost_in_usd_cents),
|
||||
is_free: false,
|
||||
is_unlimited: false,
|
||||
is_rate_limited: false,
|
||||
has_watermark: false,
|
||||
failures_are_refunded: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use enums::common::generation::common_polygon_type::CommonPolygonType;
|
||||
use tokens::tokens::media_files::MediaFileToken;
|
||||
|
||||
use crate::api::mesh_ref::MeshRef;
|
||||
use crate::api::router_mesh_model::RouterMeshModel;
|
||||
use crate::api::router_provider::RouterProvider;
|
||||
use crate::generate::generate_mesh::generate_mesh_request_builder::GenerateMeshRequestBuilder;
|
||||
|
||||
mod cost_table {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn cost_is_a_flat_ninety_eight_cents() {
|
||||
assert_eq!(estimate_usd_cents(base_builder()), 98);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn polygon_type_does_not_change_the_price() {
|
||||
let builder = GenerateMeshRequestBuilder {
|
||||
polygon_type: Some(CommonPolygonType::Quad),
|
||||
..base_builder()
|
||||
};
|
||||
assert_eq!(estimate_usd_cents(builder), 98);
|
||||
}
|
||||
}
|
||||
|
||||
// ── Helpers ──
|
||||
|
||||
fn base_builder() -> GenerateMeshRequestBuilder {
|
||||
GenerateMeshRequestBuilder {
|
||||
model: RouterMeshModel::Hunyuan3d3p1SmartTopology,
|
||||
provider: RouterProvider::Artcraft,
|
||||
input_mesh: Some(MeshRef::MediaFileToken(MediaFileToken::new("mf_mesh".to_string()))),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
fn estimate_usd_cents(builder: GenerateMeshRequestBuilder) -> u64 {
|
||||
builder.build2()
|
||||
.expect("build should succeed")
|
||||
.estimate_cost()
|
||||
.expect("estimate should succeed")
|
||||
.cost_in_usd_cents
|
||||
.expect("cost should be present")
|
||||
}
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
pub mod build;
|
||||
pub mod cost;
|
||||
pub mod request;
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
use artcraft_api_defs::omni_gen::cost_and_generate_requests::omni_gen_mesh_cost_and_generate_request::OmniGenMeshCostAndGenerateRequest;
|
||||
|
||||
use crate::client::router_artcraft_client::RouterArtcraftClient;
|
||||
use crate::errors::artcraft_router_error::ArtcraftRouterError;
|
||||
use crate::generate::generate_mesh::generate_mesh_response::GenerateMeshResponse;
|
||||
use crate::generate::generate_mesh::providers::artcraft::request_common::send_artcraft_omni_mesh_request;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct ArtcraftHunyuan3d3p1SmartTopologyRequestState {
|
||||
pub request: OmniGenMeshCostAndGenerateRequest,
|
||||
}
|
||||
|
||||
impl ArtcraftHunyuan3d3p1SmartTopologyRequestState {
|
||||
pub async fn send(&self, client: &RouterArtcraftClient) -> Result<GenerateMeshResponse, ArtcraftRouterError> {
|
||||
send_artcraft_omni_mesh_request(&self.request, client).await
|
||||
}
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
use enums::common::generation::common_mesh_model::CommonMeshModel as CommonMeshModelEnum;
|
||||
|
||||
use crate::errors::artcraft_router_error::ArtcraftRouterError;
|
||||
use crate::generate::generate_mesh::generate_mesh_request_builder::GenerateMeshRequestBuilder;
|
||||
use crate::generate::generate_mesh::mesh_generation_draft_or_request::MeshGenerationDraftOrRequest;
|
||||
use crate::generate::generate_mesh::mesh_generation_request::MeshGenerationRequest;
|
||||
use crate::generate::generate_mesh::providers::artcraft::build_common::build_artcraft_omni_mesh_request;
|
||||
use crate::generate::generate_mesh::providers::artcraft::meshy_v6::request::ArtcraftMeshyV6RequestState;
|
||||
|
||||
pub fn build_artcraft_meshy_v6(builder: GenerateMeshRequestBuilder) -> Result<MeshGenerationDraftOrRequest, ArtcraftRouterError> {
|
||||
let request = build_artcraft_omni_mesh_request(builder, CommonMeshModelEnum::MeshyV6)?;
|
||||
let state = ArtcraftMeshyV6RequestState { request };
|
||||
Ok(MeshGenerationDraftOrRequest::Request(MeshGenerationRequest::ArtcraftMeshyV6(state)))
|
||||
}
|
||||
+100
@@ -0,0 +1,100 @@
|
||||
use crate::generate::generate_mesh::mesh_generation_cost_estimate::MeshGenerationCostEstimate;
|
||||
use crate::generate::generate_mesh::providers::artcraft::meshy_v6::request::ArtcraftMeshyV6RequestState;
|
||||
|
||||
const FLAT_COST_IN_USD_CENTS: u64 = 104;
|
||||
|
||||
/// Meshy 6 via Artcraft: flat price per generation, regardless of options
|
||||
/// or input mode (text or image).
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct ArtcraftMeshyV6CostState {
|
||||
pub cost_in_usd_cents: u64,
|
||||
}
|
||||
|
||||
impl ArtcraftMeshyV6CostState {
|
||||
pub fn from_request(_request: &ArtcraftMeshyV6RequestState) -> Self {
|
||||
Self { cost_in_usd_cents: FLAT_COST_IN_USD_CENTS }
|
||||
}
|
||||
|
||||
pub fn estimate_cost(&self) -> MeshGenerationCostEstimate {
|
||||
MeshGenerationCostEstimate {
|
||||
cost_in_credits: Some(self.cost_in_usd_cents),
|
||||
cost_in_usd_cents: Some(self.cost_in_usd_cents),
|
||||
is_free: false,
|
||||
is_unlimited: false,
|
||||
is_rate_limited: false,
|
||||
has_watermark: false,
|
||||
failures_are_refunded: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use enums::common::generation::common_mesh_output_type::CommonMeshOutputType;
|
||||
use enums::common::generation::common_polygon_type::CommonPolygonType;
|
||||
use tokens::tokens::media_files::MediaFileToken;
|
||||
|
||||
use crate::api::image_list_ref::ImageListRef;
|
||||
use crate::api::router_mesh_model::RouterMeshModel;
|
||||
use crate::api::router_provider::RouterProvider;
|
||||
use crate::generate::generate_mesh::generate_mesh_request_builder::GenerateMeshRequestBuilder;
|
||||
|
||||
use super::*;
|
||||
|
||||
mod flat_pricing {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn image_mode_is_one_hundred_four_cents() {
|
||||
assert_eq!(estimate_usd_cents(image_builder()), 104);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn text_mode_is_one_hundred_four_cents() {
|
||||
assert_eq!(estimate_usd_cents(text_builder()), 104);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn options_do_not_change_the_price() {
|
||||
let builder = GenerateMeshRequestBuilder {
|
||||
mesh_output_type: Some(CommonMeshOutputType::LowPoly),
|
||||
polygon_type: Some(CommonPolygonType::Quad),
|
||||
face_count: Some(100_000),
|
||||
enable_pbr: Some(true),
|
||||
..image_builder()
|
||||
};
|
||||
assert_eq!(estimate_usd_cents(builder), 104);
|
||||
}
|
||||
}
|
||||
|
||||
// ── Helpers ──
|
||||
|
||||
fn image_builder() -> GenerateMeshRequestBuilder {
|
||||
GenerateMeshRequestBuilder {
|
||||
model: RouterMeshModel::MeshyV6,
|
||||
provider: RouterProvider::Artcraft,
|
||||
reference_images: Some(ImageListRef::MediaFileTokens(vec![
|
||||
MediaFileToken::new("mf_front".to_string()),
|
||||
])),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
fn text_builder() -> GenerateMeshRequestBuilder {
|
||||
GenerateMeshRequestBuilder {
|
||||
model: RouterMeshModel::MeshyV6,
|
||||
provider: RouterProvider::Artcraft,
|
||||
prompt: Some("a red ceramic teapot".to_string()),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
fn estimate_usd_cents(builder: GenerateMeshRequestBuilder) -> u64 {
|
||||
builder.build2()
|
||||
.expect("build should succeed")
|
||||
.estimate_cost()
|
||||
.expect("estimate should succeed")
|
||||
.cost_in_usd_cents
|
||||
.expect("cost should be present")
|
||||
}
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
pub mod build;
|
||||
pub mod cost;
|
||||
pub mod request;
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
use artcraft_api_defs::omni_gen::cost_and_generate_requests::omni_gen_mesh_cost_and_generate_request::OmniGenMeshCostAndGenerateRequest;
|
||||
|
||||
use crate::client::router_artcraft_client::RouterArtcraftClient;
|
||||
use crate::errors::artcraft_router_error::ArtcraftRouterError;
|
||||
use crate::generate::generate_mesh::generate_mesh_response::GenerateMeshResponse;
|
||||
use crate::generate::generate_mesh::providers::artcraft::request_common::send_artcraft_omni_mesh_request;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct ArtcraftMeshyV6RequestState {
|
||||
pub request: OmniGenMeshCostAndGenerateRequest,
|
||||
}
|
||||
|
||||
impl ArtcraftMeshyV6RequestState {
|
||||
pub async fn send(&self, client: &RouterArtcraftClient) -> Result<GenerateMeshResponse, ArtcraftRouterError> {
|
||||
send_artcraft_omni_mesh_request(&self.request, client).await
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::api::router_mesh_model::RouterMeshModel;
|
||||
use crate::api::router_provider::RouterProvider;
|
||||
use crate::generate::generate_mesh::generate_mesh_request_builder::GenerateMeshRequestBuilder;
|
||||
use crate::generate::generate_mesh::generate_mesh_response::GenerateMeshResponse;
|
||||
use crate::generate::generate_mesh::mesh_generation_draft_or_request::MeshGenerationDraftOrRequest;
|
||||
use crate::test_helpers::get_artcraft_client;
|
||||
|
||||
#[tokio::test]
|
||||
#[ignore] // sends a real generation to the Artcraft backend, incurs cost
|
||||
async fn text_to_mesh_teapot() {
|
||||
let response = run_pipeline(GenerateMeshRequestBuilder {
|
||||
model: RouterMeshModel::MeshyV6,
|
||||
provider: RouterProvider::Artcraft,
|
||||
prompt: Some("A red ceramic teapot with a curved spout.".to_string()),
|
||||
..Default::default()
|
||||
}).await;
|
||||
assert!(matches!(response, GenerateMeshResponse::Artcraft(_)));
|
||||
assert_eq!(1, 2, "Inspect output above");
|
||||
}
|
||||
|
||||
// ── Helpers ──
|
||||
|
||||
async fn run_pipeline(builder: GenerateMeshRequestBuilder) -> GenerateMeshResponse {
|
||||
let client = get_artcraft_client();
|
||||
|
||||
let draft_or_request = builder.build2().expect("build2 should succeed");
|
||||
let request = match draft_or_request {
|
||||
MeshGenerationDraftOrRequest::Request(r) => r,
|
||||
_ => panic!("expected Request variant (Artcraft skips draft)"),
|
||||
};
|
||||
|
||||
let response = request.send_request(&client).await.expect("send_request should succeed");
|
||||
|
||||
match &response {
|
||||
GenerateMeshResponse::Artcraft(p) => {
|
||||
println!("inference_job_token={:?}", p.inference_job_token);
|
||||
println!("all_inference_job_tokens={:?}", p.all_inference_job_tokens);
|
||||
}
|
||||
other => println!("response: {:?}", other),
|
||||
}
|
||||
|
||||
response
|
||||
}
|
||||
}
|
||||
+7
@@ -5,3 +5,10 @@ pub mod hunyuan3d_3;
|
||||
pub mod hunyuan3d_3_sketch;
|
||||
pub mod hunyuan_3d_2p0;
|
||||
pub mod hunyuan_3d_2p1;
|
||||
pub mod hunyuan_3d_3p1_part;
|
||||
pub mod hunyuan_3d_3p1_pro;
|
||||
pub mod hunyuan_3d_3p1_rapid;
|
||||
pub mod hunyuan_3d_3p1_smart_topology;
|
||||
pub mod meshy_v6;
|
||||
pub mod rodin_2p5_fast;
|
||||
pub mod tripo3d_h3p1;
|
||||
|
||||
+13
@@ -6,6 +6,7 @@ use tokens::tokens::media_files::MediaFileToken;
|
||||
|
||||
use crate::api::image_list_ref::ImageListRef;
|
||||
use crate::api::image_ref::ImageRef;
|
||||
use crate::api::mesh_ref::MeshRef;
|
||||
use crate::errors::artcraft_router_error::ArtcraftRouterError;
|
||||
use crate::errors::client_error::ClientError;
|
||||
|
||||
@@ -32,3 +33,15 @@ pub(super) fn resolve_image_ref(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn resolve_mesh_ref(
|
||||
mesh_ref: Option<MeshRef>,
|
||||
) -> Result<Option<MediaFileToken>, ArtcraftRouterError> {
|
||||
match mesh_ref {
|
||||
None => Ok(None),
|
||||
Some(MeshRef::MediaFileToken(token)) => Ok(Some(token)),
|
||||
Some(MeshRef::Url(_)) => {
|
||||
Err(ArtcraftRouterError::Client(ClientError::ArtcraftOnlySupportsMediaTokens))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
use enums::common::generation::common_mesh_model::CommonMeshModel as CommonMeshModelEnum;
|
||||
|
||||
use crate::errors::artcraft_router_error::ArtcraftRouterError;
|
||||
use crate::generate::generate_mesh::generate_mesh_request_builder::GenerateMeshRequestBuilder;
|
||||
use crate::generate::generate_mesh::mesh_generation_draft_or_request::MeshGenerationDraftOrRequest;
|
||||
use crate::generate::generate_mesh::mesh_generation_request::MeshGenerationRequest;
|
||||
use crate::generate::generate_mesh::providers::artcraft::build_common::build_artcraft_omni_mesh_request;
|
||||
use crate::generate::generate_mesh::providers::artcraft::rodin_2p5_fast::request::ArtcraftRodin2p5FastRequestState;
|
||||
|
||||
pub fn build_artcraft_rodin_2p5_fast(builder: GenerateMeshRequestBuilder) -> Result<MeshGenerationDraftOrRequest, ArtcraftRouterError> {
|
||||
let request = build_artcraft_omni_mesh_request(builder, CommonMeshModelEnum::Rodin2p5Fast)?;
|
||||
let state = ArtcraftRodin2p5FastRequestState { request };
|
||||
Ok(MeshGenerationDraftOrRequest::Request(MeshGenerationRequest::ArtcraftRodin2p5Fast(state)))
|
||||
}
|
||||
+97
@@ -0,0 +1,97 @@
|
||||
use crate::generate::generate_mesh::mesh_generation_cost_estimate::MeshGenerationCostEstimate;
|
||||
use crate::generate::generate_mesh::providers::artcraft::rodin_2p5_fast::request::ArtcraftRodin2p5FastRequestState;
|
||||
|
||||
const FLAT_COST_IN_USD_CENTS: u64 = 13;
|
||||
|
||||
/// Hyper3D Rodin v2.5 Fast via Artcraft: flat price per generation,
|
||||
/// regardless of options or input mode (text or image).
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct ArtcraftRodin2p5FastCostState {
|
||||
pub cost_in_usd_cents: u64,
|
||||
}
|
||||
|
||||
impl ArtcraftRodin2p5FastCostState {
|
||||
pub fn from_request(_request: &ArtcraftRodin2p5FastRequestState) -> Self {
|
||||
Self { cost_in_usd_cents: FLAT_COST_IN_USD_CENTS }
|
||||
}
|
||||
|
||||
pub fn estimate_cost(&self) -> MeshGenerationCostEstimate {
|
||||
MeshGenerationCostEstimate {
|
||||
cost_in_credits: Some(self.cost_in_usd_cents),
|
||||
cost_in_usd_cents: Some(self.cost_in_usd_cents),
|
||||
is_free: false,
|
||||
is_unlimited: false,
|
||||
is_rate_limited: false,
|
||||
has_watermark: false,
|
||||
failures_are_refunded: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use enums::common::generation::common_mesh_output_type::CommonMeshOutputType;
|
||||
use tokens::tokens::media_files::MediaFileToken;
|
||||
|
||||
use crate::api::image_list_ref::ImageListRef;
|
||||
use crate::api::router_mesh_model::RouterMeshModel;
|
||||
use crate::api::router_provider::RouterProvider;
|
||||
use crate::generate::generate_mesh::generate_mesh_request_builder::GenerateMeshRequestBuilder;
|
||||
|
||||
use super::*;
|
||||
|
||||
mod flat_pricing {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn image_mode_is_thirteen_cents() {
|
||||
assert_eq!(estimate_usd_cents(image_builder()), 13);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn text_mode_is_thirteen_cents() {
|
||||
assert_eq!(estimate_usd_cents(text_builder()), 13);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn options_do_not_change_the_price() {
|
||||
let builder = GenerateMeshRequestBuilder {
|
||||
mesh_output_type: Some(CommonMeshOutputType::Geometry),
|
||||
enable_pbr: Some(true),
|
||||
..image_builder()
|
||||
};
|
||||
assert_eq!(estimate_usd_cents(builder), 13);
|
||||
}
|
||||
}
|
||||
|
||||
// ── Helpers ──
|
||||
|
||||
fn image_builder() -> GenerateMeshRequestBuilder {
|
||||
GenerateMeshRequestBuilder {
|
||||
model: RouterMeshModel::Rodin2p5Fast,
|
||||
provider: RouterProvider::Artcraft,
|
||||
reference_images: Some(ImageListRef::MediaFileTokens(vec![
|
||||
MediaFileToken::new("mf_front".to_string()),
|
||||
])),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
fn text_builder() -> GenerateMeshRequestBuilder {
|
||||
GenerateMeshRequestBuilder {
|
||||
model: RouterMeshModel::Rodin2p5Fast,
|
||||
provider: RouterProvider::Artcraft,
|
||||
prompt: Some("a red ceramic teapot".to_string()),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
fn estimate_usd_cents(builder: GenerateMeshRequestBuilder) -> u64 {
|
||||
builder.build2()
|
||||
.expect("build should succeed")
|
||||
.estimate_cost()
|
||||
.expect("estimate should succeed")
|
||||
.cost_in_usd_cents
|
||||
.expect("cost should be present")
|
||||
}
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
pub mod build;
|
||||
pub mod cost;
|
||||
pub mod request;
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
use artcraft_api_defs::omni_gen::cost_and_generate_requests::omni_gen_mesh_cost_and_generate_request::OmniGenMeshCostAndGenerateRequest;
|
||||
|
||||
use crate::client::router_artcraft_client::RouterArtcraftClient;
|
||||
use crate::errors::artcraft_router_error::ArtcraftRouterError;
|
||||
use crate::generate::generate_mesh::generate_mesh_response::GenerateMeshResponse;
|
||||
use crate::generate::generate_mesh::providers::artcraft::request_common::send_artcraft_omni_mesh_request;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct ArtcraftRodin2p5FastRequestState {
|
||||
pub request: OmniGenMeshCostAndGenerateRequest,
|
||||
}
|
||||
|
||||
impl ArtcraftRodin2p5FastRequestState {
|
||||
pub async fn send(&self, client: &RouterArtcraftClient) -> Result<GenerateMeshResponse, ArtcraftRouterError> {
|
||||
send_artcraft_omni_mesh_request(&self.request, client).await
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::api::router_mesh_model::RouterMeshModel;
|
||||
use crate::api::router_provider::RouterProvider;
|
||||
use crate::generate::generate_mesh::generate_mesh_request_builder::GenerateMeshRequestBuilder;
|
||||
use crate::generate::generate_mesh::generate_mesh_response::GenerateMeshResponse;
|
||||
use crate::generate::generate_mesh::mesh_generation_draft_or_request::MeshGenerationDraftOrRequest;
|
||||
use crate::test_helpers::get_artcraft_client;
|
||||
|
||||
#[tokio::test]
|
||||
#[ignore] // sends a real generation to the Artcraft backend, incurs cost
|
||||
async fn text_to_mesh_teapot() {
|
||||
let response = run_pipeline(GenerateMeshRequestBuilder {
|
||||
model: RouterMeshModel::Rodin2p5Fast,
|
||||
provider: RouterProvider::Artcraft,
|
||||
prompt: Some("A red ceramic teapot with a curved spout.".to_string()),
|
||||
..Default::default()
|
||||
}).await;
|
||||
assert!(matches!(response, GenerateMeshResponse::Artcraft(_)));
|
||||
assert_eq!(1, 2, "Inspect output above");
|
||||
}
|
||||
|
||||
// ── Helpers ──
|
||||
|
||||
async fn run_pipeline(builder: GenerateMeshRequestBuilder) -> GenerateMeshResponse {
|
||||
let client = get_artcraft_client();
|
||||
|
||||
let draft_or_request = builder.build2().expect("build2 should succeed");
|
||||
let request = match draft_or_request {
|
||||
MeshGenerationDraftOrRequest::Request(r) => r,
|
||||
_ => panic!("expected Request variant (Artcraft skips draft)"),
|
||||
};
|
||||
|
||||
let response = request.send_request(&client).await.expect("send_request should succeed");
|
||||
|
||||
match &response {
|
||||
GenerateMeshResponse::Artcraft(p) => {
|
||||
println!("inference_job_token={:?}", p.inference_job_token);
|
||||
println!("all_inference_job_tokens={:?}", p.all_inference_job_tokens);
|
||||
}
|
||||
other => println!("response: {:?}", other),
|
||||
}
|
||||
|
||||
response
|
||||
}
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
use enums::common::generation::common_mesh_model::CommonMeshModel as CommonMeshModelEnum;
|
||||
|
||||
use crate::errors::artcraft_router_error::ArtcraftRouterError;
|
||||
use crate::generate::generate_mesh::generate_mesh_request_builder::GenerateMeshRequestBuilder;
|
||||
use crate::generate::generate_mesh::mesh_generation_draft_or_request::MeshGenerationDraftOrRequest;
|
||||
use crate::generate::generate_mesh::mesh_generation_request::MeshGenerationRequest;
|
||||
use crate::generate::generate_mesh::providers::artcraft::build_common::build_artcraft_omni_mesh_request;
|
||||
use crate::generate::generate_mesh::providers::artcraft::tripo3d_h3p1::request::ArtcraftTripo3dH3p1RequestState;
|
||||
|
||||
pub fn build_artcraft_tripo3d_h3p1(builder: GenerateMeshRequestBuilder) -> Result<MeshGenerationDraftOrRequest, ArtcraftRouterError> {
|
||||
let request = build_artcraft_omni_mesh_request(builder, CommonMeshModelEnum::Tripo3dH3p1)?;
|
||||
let state = ArtcraftTripo3dH3p1RequestState { request };
|
||||
Ok(MeshGenerationDraftOrRequest::Request(MeshGenerationRequest::ArtcraftTripo3dH3p1(state)))
|
||||
}
|
||||
+338
@@ -0,0 +1,338 @@
|
||||
use enums::common::generation::common_mesh_output_type::CommonMeshOutputType;
|
||||
use enums::common::generation::common_mesh_quality::CommonMeshQuality;
|
||||
use enums::common::generation::common_polygon_type::CommonPolygonType;
|
||||
|
||||
use crate::generate::generate_mesh::mesh_generation_cost_estimate::MeshGenerationCostEstimate;
|
||||
use crate::generate::generate_mesh::providers::artcraft::tripo3d_h3p1::request::ArtcraftTripo3dH3p1RequestState;
|
||||
|
||||
// Text and multi-view generations share a price tier; single-image
|
||||
// generations price higher.
|
||||
const TEXT_OR_MULTIVIEW_UNTEXTURED_COST_IN_USD_CENTS: u64 = 13;
|
||||
const TEXT_OR_MULTIVIEW_STANDARD_TEXTURE_COST_IN_USD_CENTS: u64 = 26;
|
||||
const TEXT_OR_MULTIVIEW_DETAILED_TEXTURE_COST_IN_USD_CENTS: u64 = 39;
|
||||
const IMAGE_UNTEXTURED_COST_IN_USD_CENTS: u64 = 26;
|
||||
const IMAGE_STANDARD_TEXTURE_COST_IN_USD_CENTS: u64 = 39;
|
||||
const IMAGE_DETAILED_TEXTURE_COST_IN_USD_CENTS: u64 = 52;
|
||||
const DETAILED_GEOMETRY_ADD_ON_COST_IN_USD_CENTS: u64 = 26;
|
||||
const QUAD_ADD_ON_COST_IN_USD_CENTS: u64 = 7;
|
||||
|
||||
/// Tripo3D H3.1 via Artcraft: base price by input mode (text and multi-view
|
||||
/// share a tier; single-image is higher) and texture tier (untextured,
|
||||
/// standard, or detailed), plus flat add-ons for detailed geometry and quad
|
||||
/// topology.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct ArtcraftTripo3dH3p1CostState {
|
||||
pub cost_in_usd_cents: u64,
|
||||
}
|
||||
|
||||
impl ArtcraftTripo3dH3p1CostState {
|
||||
pub fn from_request(request: &ArtcraftTripo3dH3p1RequestState) -> Self {
|
||||
let request = &request.request;
|
||||
|
||||
let uses_multi_view = request.back_image_media_token.is_some()
|
||||
|| request.left_image_media_token.is_some()
|
||||
|| request.right_image_media_token.is_some();
|
||||
let has_primary_image = request.front_image_media_token.is_some()
|
||||
|| request.reference_image_media_tokens.as_ref().is_some_and(|tokens| !tokens.is_empty());
|
||||
let uses_single_image = !uses_multi_view && has_primary_image;
|
||||
|
||||
// The untextured tier requires that PBR isn't explicitly requested,
|
||||
// since PBR implies texturing.
|
||||
let texture_off = (matches!(request.mesh_output_type, Some(CommonMeshOutputType::Geometry))
|
||||
|| request.enable_texture == Some(false))
|
||||
&& request.enable_pbr != Some(true);
|
||||
|
||||
let (untextured, standard, detailed) = if uses_single_image {
|
||||
(
|
||||
IMAGE_UNTEXTURED_COST_IN_USD_CENTS,
|
||||
IMAGE_STANDARD_TEXTURE_COST_IN_USD_CENTS,
|
||||
IMAGE_DETAILED_TEXTURE_COST_IN_USD_CENTS,
|
||||
)
|
||||
} else {
|
||||
(
|
||||
TEXT_OR_MULTIVIEW_UNTEXTURED_COST_IN_USD_CENTS,
|
||||
TEXT_OR_MULTIVIEW_STANDARD_TEXTURE_COST_IN_USD_CENTS,
|
||||
TEXT_OR_MULTIVIEW_DETAILED_TEXTURE_COST_IN_USD_CENTS,
|
||||
)
|
||||
};
|
||||
|
||||
let mut cost = if texture_off {
|
||||
untextured
|
||||
} else if matches!(request.texture_quality, Some(CommonMeshQuality::Detailed)) {
|
||||
detailed
|
||||
} else {
|
||||
standard
|
||||
};
|
||||
|
||||
if matches!(request.geometry_quality, Some(CommonMeshQuality::Detailed)) {
|
||||
cost += DETAILED_GEOMETRY_ADD_ON_COST_IN_USD_CENTS;
|
||||
}
|
||||
if matches!(request.polygon_type, Some(CommonPolygonType::Quad)) {
|
||||
cost += QUAD_ADD_ON_COST_IN_USD_CENTS;
|
||||
}
|
||||
|
||||
Self { cost_in_usd_cents: cost }
|
||||
}
|
||||
|
||||
pub fn estimate_cost(&self) -> MeshGenerationCostEstimate {
|
||||
MeshGenerationCostEstimate {
|
||||
cost_in_credits: Some(self.cost_in_usd_cents),
|
||||
cost_in_usd_cents: Some(self.cost_in_usd_cents),
|
||||
is_free: false,
|
||||
is_unlimited: false,
|
||||
is_rate_limited: false,
|
||||
has_watermark: false,
|
||||
failures_are_refunded: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use tokens::tokens::media_files::MediaFileToken;
|
||||
|
||||
use crate::api::image_list_ref::ImageListRef;
|
||||
use crate::api::image_ref::ImageRef;
|
||||
use crate::api::router_mesh_model::RouterMeshModel;
|
||||
use crate::api::router_provider::RouterProvider;
|
||||
use crate::generate::generate_mesh::generate_mesh_request_builder::GenerateMeshRequestBuilder;
|
||||
|
||||
use super::*;
|
||||
|
||||
mod text_mode_costs {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn default_is_standard_textures() {
|
||||
assert_eq!(estimate_usd_cents(text_builder()), 26);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn untextured_via_enable_texture_off() {
|
||||
let builder = GenerateMeshRequestBuilder {
|
||||
enable_texture: Some(false),
|
||||
..text_builder()
|
||||
};
|
||||
assert_eq!(estimate_usd_cents(builder), 13);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn untextured_via_geometry_output() {
|
||||
let builder = GenerateMeshRequestBuilder {
|
||||
mesh_output_type: Some(CommonMeshOutputType::Geometry),
|
||||
..text_builder()
|
||||
};
|
||||
assert_eq!(estimate_usd_cents(builder), 13);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn texture_off_with_pbr_keeps_textured_tier() {
|
||||
let builder = GenerateMeshRequestBuilder {
|
||||
enable_texture: Some(false),
|
||||
enable_pbr: Some(true),
|
||||
..text_builder()
|
||||
};
|
||||
assert_eq!(estimate_usd_cents(builder), 26);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn detailed_texture() {
|
||||
let builder = GenerateMeshRequestBuilder {
|
||||
texture_quality: Some(CommonMeshQuality::Detailed),
|
||||
..text_builder()
|
||||
};
|
||||
assert_eq!(estimate_usd_cents(builder), 39);
|
||||
}
|
||||
}
|
||||
|
||||
mod image_mode_costs {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn default_is_standard_textures() {
|
||||
assert_eq!(estimate_usd_cents(image_builder()), 39);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn untextured_via_enable_texture_off() {
|
||||
let builder = GenerateMeshRequestBuilder {
|
||||
enable_texture: Some(false),
|
||||
..image_builder()
|
||||
};
|
||||
assert_eq!(estimate_usd_cents(builder), 26);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn untextured_via_geometry_output() {
|
||||
let builder = GenerateMeshRequestBuilder {
|
||||
mesh_output_type: Some(CommonMeshOutputType::Geometry),
|
||||
..image_builder()
|
||||
};
|
||||
assert_eq!(estimate_usd_cents(builder), 26);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn detailed_texture() {
|
||||
let builder = GenerateMeshRequestBuilder {
|
||||
texture_quality: Some(CommonMeshQuality::Detailed),
|
||||
..image_builder()
|
||||
};
|
||||
assert_eq!(estimate_usd_cents(builder), 52);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn front_image_token_prices_as_image_mode() {
|
||||
let builder = GenerateMeshRequestBuilder {
|
||||
front_image: Some(ImageRef::MediaFileToken(MediaFileToken::new("mf_front".to_string()))),
|
||||
..base_builder()
|
||||
};
|
||||
assert_eq!(estimate_usd_cents(builder), 39);
|
||||
}
|
||||
}
|
||||
|
||||
mod multiview_mode_costs {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn default_is_standard_textures() {
|
||||
assert_eq!(estimate_usd_cents(multiview_builder()), 26);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn each_side_view_prices_as_multiview() {
|
||||
for side in ["back", "left", "right"] {
|
||||
let mut builder = image_builder();
|
||||
let image = Some(ImageRef::MediaFileToken(MediaFileToken::new(format!("mf_{side}"))));
|
||||
match side {
|
||||
"back" => builder.back_image = image,
|
||||
"left" => builder.left_image = image,
|
||||
_ => builder.right_image = image,
|
||||
}
|
||||
assert_eq!(estimate_usd_cents(builder), 26, "for {side} image");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn untextured_via_enable_texture_off() {
|
||||
let builder = GenerateMeshRequestBuilder {
|
||||
enable_texture: Some(false),
|
||||
..multiview_builder()
|
||||
};
|
||||
assert_eq!(estimate_usd_cents(builder), 13);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn detailed_texture() {
|
||||
let builder = GenerateMeshRequestBuilder {
|
||||
texture_quality: Some(CommonMeshQuality::Detailed),
|
||||
..multiview_builder()
|
||||
};
|
||||
assert_eq!(estimate_usd_cents(builder), 39);
|
||||
}
|
||||
}
|
||||
|
||||
mod add_ons {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn detailed_geometry_adds_twenty_six_cents() {
|
||||
let builder = GenerateMeshRequestBuilder {
|
||||
geometry_quality: Some(CommonMeshQuality::Detailed),
|
||||
..image_builder()
|
||||
};
|
||||
assert_eq!(estimate_usd_cents(builder), 39 + 26);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn quad_adds_seven_cents() {
|
||||
let builder = GenerateMeshRequestBuilder {
|
||||
polygon_type: Some(CommonPolygonType::Quad),
|
||||
..image_builder()
|
||||
};
|
||||
assert_eq!(estimate_usd_cents(builder), 39 + 7);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn triangle_polygon_adds_nothing() {
|
||||
let builder = GenerateMeshRequestBuilder {
|
||||
polygon_type: Some(CommonPolygonType::Triangle),
|
||||
..image_builder()
|
||||
};
|
||||
assert_eq!(estimate_usd_cents(builder), 39);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn standard_qualities_add_nothing() {
|
||||
let builder = GenerateMeshRequestBuilder {
|
||||
texture_quality: Some(CommonMeshQuality::Standard),
|
||||
geometry_quality: Some(CommonMeshQuality::Standard),
|
||||
..image_builder()
|
||||
};
|
||||
assert_eq!(estimate_usd_cents(builder), 39);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn all_add_ons_stack() {
|
||||
let builder = GenerateMeshRequestBuilder {
|
||||
texture_quality: Some(CommonMeshQuality::Detailed),
|
||||
geometry_quality: Some(CommonMeshQuality::Detailed),
|
||||
polygon_type: Some(CommonPolygonType::Quad),
|
||||
..image_builder()
|
||||
};
|
||||
assert_eq!(estimate_usd_cents(builder), 52 + 26 + 7);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn add_ons_stack_on_the_untextured_tier() {
|
||||
let builder = GenerateMeshRequestBuilder {
|
||||
enable_texture: Some(false),
|
||||
geometry_quality: Some(CommonMeshQuality::Detailed),
|
||||
polygon_type: Some(CommonPolygonType::Quad),
|
||||
..text_builder()
|
||||
};
|
||||
assert_eq!(estimate_usd_cents(builder), 13 + 26 + 7);
|
||||
}
|
||||
}
|
||||
|
||||
// ── Helpers ──
|
||||
|
||||
fn base_builder() -> GenerateMeshRequestBuilder {
|
||||
GenerateMeshRequestBuilder {
|
||||
model: RouterMeshModel::Tripo3dH3p1,
|
||||
provider: RouterProvider::Artcraft,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
fn text_builder() -> GenerateMeshRequestBuilder {
|
||||
GenerateMeshRequestBuilder {
|
||||
prompt: Some("a red ceramic teapot".to_string()),
|
||||
..base_builder()
|
||||
}
|
||||
}
|
||||
|
||||
fn image_builder() -> GenerateMeshRequestBuilder {
|
||||
GenerateMeshRequestBuilder {
|
||||
reference_images: Some(ImageListRef::MediaFileTokens(vec![
|
||||
MediaFileToken::new("mf_front".to_string()),
|
||||
])),
|
||||
..base_builder()
|
||||
}
|
||||
}
|
||||
|
||||
fn multiview_builder() -> GenerateMeshRequestBuilder {
|
||||
GenerateMeshRequestBuilder {
|
||||
back_image: Some(ImageRef::MediaFileToken(MediaFileToken::new("mf_back".to_string()))),
|
||||
..image_builder()
|
||||
}
|
||||
}
|
||||
|
||||
fn estimate_usd_cents(builder: GenerateMeshRequestBuilder) -> u64 {
|
||||
builder.build2()
|
||||
.expect("build should succeed")
|
||||
.estimate_cost()
|
||||
.expect("estimate should succeed")
|
||||
.cost_in_usd_cents
|
||||
.expect("cost should be present")
|
||||
}
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
pub mod build;
|
||||
pub mod cost;
|
||||
pub mod request;
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
use artcraft_api_defs::omni_gen::cost_and_generate_requests::omni_gen_mesh_cost_and_generate_request::OmniGenMeshCostAndGenerateRequest;
|
||||
|
||||
use crate::client::router_artcraft_client::RouterArtcraftClient;
|
||||
use crate::errors::artcraft_router_error::ArtcraftRouterError;
|
||||
use crate::generate::generate_mesh::generate_mesh_response::GenerateMeshResponse;
|
||||
use crate::generate::generate_mesh::providers::artcraft::request_common::send_artcraft_omni_mesh_request;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct ArtcraftTripo3dH3p1RequestState {
|
||||
pub request: OmniGenMeshCostAndGenerateRequest,
|
||||
}
|
||||
|
||||
impl ArtcraftTripo3dH3p1RequestState {
|
||||
pub async fn send(&self, client: &RouterArtcraftClient) -> Result<GenerateMeshResponse, ArtcraftRouterError> {
|
||||
send_artcraft_omni_mesh_request(&self.request, client).await
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::api::router_mesh_model::RouterMeshModel;
|
||||
use crate::api::router_provider::RouterProvider;
|
||||
use crate::generate::generate_mesh::generate_mesh_request_builder::GenerateMeshRequestBuilder;
|
||||
use crate::generate::generate_mesh::generate_mesh_response::GenerateMeshResponse;
|
||||
use crate::generate::generate_mesh::mesh_generation_draft_or_request::MeshGenerationDraftOrRequest;
|
||||
use crate::test_helpers::get_artcraft_client;
|
||||
|
||||
#[tokio::test]
|
||||
#[ignore] // sends a real generation to the Artcraft backend, incurs cost
|
||||
async fn text_to_mesh_teapot() {
|
||||
let response = run_pipeline(GenerateMeshRequestBuilder {
|
||||
model: RouterMeshModel::Tripo3dH3p1,
|
||||
provider: RouterProvider::Artcraft,
|
||||
prompt: Some("A red ceramic teapot with a curved spout.".to_string()),
|
||||
..Default::default()
|
||||
}).await;
|
||||
assert!(matches!(response, GenerateMeshResponse::Artcraft(_)));
|
||||
assert_eq!(1, 2, "Inspect output above");
|
||||
}
|
||||
|
||||
// ── Helpers ──
|
||||
|
||||
async fn run_pipeline(builder: GenerateMeshRequestBuilder) -> GenerateMeshResponse {
|
||||
let client = get_artcraft_client();
|
||||
|
||||
let draft_or_request = builder.build2().expect("build2 should succeed");
|
||||
let request = match draft_or_request {
|
||||
MeshGenerationDraftOrRequest::Request(r) => r,
|
||||
_ => panic!("expected Request variant (Artcraft skips draft)"),
|
||||
};
|
||||
|
||||
let response = request.send_request(&client).await.expect("send_request should succeed");
|
||||
|
||||
match &response {
|
||||
GenerateMeshResponse::Artcraft(p) => {
|
||||
println!("inference_job_token={:?}", p.inference_job_token);
|
||||
println!("all_inference_job_tokens={:?}", p.all_inference_job_tokens);
|
||||
}
|
||||
other => println!("response: {:?}", other),
|
||||
}
|
||||
|
||||
response
|
||||
}
|
||||
}
|
||||
+152
@@ -0,0 +1,152 @@
|
||||
use fal_client::requests::api::mesh::part::hunyuan_3d_3p1_part::api::Hunyuan3d3p1PartRequest;
|
||||
|
||||
use crate::errors::artcraft_router_error::ArtcraftRouterError;
|
||||
use crate::generate::generate_mesh::generate_mesh_request_builder::GenerateMeshRequestBuilder;
|
||||
use crate::generate::generate_mesh::mesh_generation_draft_or_request::MeshGenerationDraftOrRequest;
|
||||
use crate::generate::generate_mesh::mesh_generation_request::MeshGenerationRequest;
|
||||
use crate::generate::generate_mesh::providers::fal::hunyuan_3d_3p1_part::request::FalHunyuan3d3p1PartRequestState;
|
||||
use crate::generate::generate_mesh::providers::fal::resolve::plan_input_mesh_url;
|
||||
use crate::generate::generate_mesh::providers::reject_unsupported::{
|
||||
reject_unsupported_image_ref, reject_unsupported_option,
|
||||
};
|
||||
|
||||
/// Hunyuan 3D v3.1 Part splits an existing mesh into semantic parts. It takes
|
||||
/// only a mesh file (FBX) as input — text, images, and generation options are
|
||||
/// all unsupported.
|
||||
pub fn build_fal_hunyuan_3d_3p1_part(builder: GenerateMeshRequestBuilder) -> Result<MeshGenerationDraftOrRequest, ArtcraftRouterError> {
|
||||
let state = build_fal_hunyuan_3d_3p1_part_state(builder)?;
|
||||
Ok(MeshGenerationDraftOrRequest::Request(MeshGenerationRequest::FalHunyuan3d3p1Part(state)))
|
||||
}
|
||||
|
||||
pub(crate) fn build_fal_hunyuan_3d_3p1_part_state(
|
||||
mut builder: GenerateMeshRequestBuilder,
|
||||
) -> Result<FalHunyuan3d3p1PartRequestState, ArtcraftRouterError> {
|
||||
let strategy = builder.request_mismatch_mitigation_strategy;
|
||||
|
||||
let input_file_url = plan_input_mesh_url(builder.input_mesh.take())?.ok_or_else(|| {
|
||||
ArtcraftRouterError::InvalidInput(
|
||||
"Hunyuan 3D v3.1 Part requires an input mesh file (FBX)".to_string(),
|
||||
)
|
||||
})?;
|
||||
|
||||
// Part splitting takes only the mesh file; every other option is
|
||||
// unsupported.
|
||||
reject_unsupported_option("prompt", builder.prompt.as_ref(), strategy)?;
|
||||
reject_unsupported_option("reference_images", builder.reference_images.as_ref(), strategy)?;
|
||||
reject_unsupported_image_ref("front_image", builder.front_image.as_ref(), strategy)?;
|
||||
reject_unsupported_image_ref("back_image", builder.back_image.as_ref(), strategy)?;
|
||||
reject_unsupported_image_ref("left_image", builder.left_image.as_ref(), strategy)?;
|
||||
reject_unsupported_image_ref("right_image", builder.right_image.as_ref(), strategy)?;
|
||||
reject_unsupported_option("mesh_output_type", builder.mesh_output_type.as_ref(), strategy)?;
|
||||
reject_unsupported_option("polygon_type", builder.polygon_type.as_ref(), strategy)?;
|
||||
reject_unsupported_option("face_count", builder.face_count.as_ref(), strategy)?;
|
||||
reject_unsupported_option("enable_pbr", builder.enable_pbr.as_ref(), strategy)?;
|
||||
reject_unsupported_option("enable_texture", builder.enable_texture.as_ref(), strategy)?;
|
||||
reject_unsupported_option("texture_quality", builder.texture_quality.as_ref(), strategy)?;
|
||||
reject_unsupported_option("geometry_quality", builder.geometry_quality.as_ref(), strategy)?;
|
||||
|
||||
let request = Hunyuan3d3p1PartRequest { input_file_url };
|
||||
Ok(FalHunyuan3d3p1PartRequestState { request })
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use enums::common::generation::common_mesh_output_type::CommonMeshOutputType;
|
||||
use enums::common::generation::common_polygon_type::CommonPolygonType;
|
||||
use tokens::tokens::media_files::MediaFileToken;
|
||||
|
||||
use crate::api::image_ref::ImageRef;
|
||||
use crate::api::mesh_ref::MeshRef;
|
||||
use crate::api::router_mesh_model::RouterMeshModel;
|
||||
use crate::api::router_provider::RouterProvider;
|
||||
use crate::client::request_mismatch_mitigation_strategy::RequestMismatchMitigationStrategy;
|
||||
use crate::errors::client_error::ClientError;
|
||||
|
||||
use super::*;
|
||||
|
||||
const MESH_URL: &str = "https://example.com/model.fbx";
|
||||
|
||||
mod shape_dispatch {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn input_mesh_url_builds_the_request() {
|
||||
let state = build_fal_hunyuan_3d_3p1_part_state(mesh_builder()).expect("build");
|
||||
assert_eq!(state.request.input_file_url, MESH_URL);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn missing_input_mesh_is_rejected() {
|
||||
let result = build_fal_hunyuan_3d_3p1_part_state(base_builder());
|
||||
assert!(matches!(result, Err(ArtcraftRouterError::InvalidInput(_))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn media_token_input_mesh_is_rejected() {
|
||||
let builder = GenerateMeshRequestBuilder {
|
||||
input_mesh: Some(MeshRef::MediaFileToken(MediaFileToken::new("mf_mesh".to_string()))),
|
||||
..base_builder()
|
||||
};
|
||||
assert!(matches!(
|
||||
build_fal_hunyuan_3d_3p1_part_state(builder),
|
||||
Err(ArtcraftRouterError::Client(ClientError::FalOnlySupportsUrls))
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
mod unsupported_options {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn each_unsupported_option_errors_out_under_error_out() {
|
||||
let cases: Vec<fn(&mut GenerateMeshRequestBuilder)> = vec![
|
||||
|b| b.prompt = Some("a red ceramic teapot".to_string()),
|
||||
|b| b.front_image = Some(ImageRef::Url("https://example.com/front.png".to_string())),
|
||||
|b| b.back_image = Some(ImageRef::Url("https://example.com/back.png".to_string())),
|
||||
|b| b.mesh_output_type = Some(CommonMeshOutputType::Normal),
|
||||
|b| b.polygon_type = Some(CommonPolygonType::Quad),
|
||||
|b| b.face_count = Some(100_000),
|
||||
|b| b.enable_pbr = Some(true),
|
||||
|b| b.enable_texture = Some(false),
|
||||
];
|
||||
for (index, set) in cases.into_iter().enumerate() {
|
||||
let mut builder = GenerateMeshRequestBuilder {
|
||||
request_mismatch_mitigation_strategy: RequestMismatchMitigationStrategy::ErrorOut,
|
||||
..mesh_builder()
|
||||
};
|
||||
set(&mut builder);
|
||||
assert!(build_fal_hunyuan_3d_3p1_part_state(builder).is_err(), "for case {index}");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unsupported_options_are_dropped_under_lenient_strategies() {
|
||||
let builder = GenerateMeshRequestBuilder {
|
||||
prompt: Some("a red ceramic teapot".to_string()),
|
||||
mesh_output_type: Some(CommonMeshOutputType::Normal),
|
||||
face_count: Some(100_000),
|
||||
enable_pbr: Some(true),
|
||||
..mesh_builder()
|
||||
};
|
||||
let state = build_fal_hunyuan_3d_3p1_part_state(builder).expect("build");
|
||||
assert_eq!(state.request.input_file_url, MESH_URL);
|
||||
}
|
||||
}
|
||||
|
||||
// ── Helpers ──
|
||||
|
||||
fn base_builder() -> GenerateMeshRequestBuilder {
|
||||
GenerateMeshRequestBuilder {
|
||||
model: RouterMeshModel::Hunyuan3d3p1Part,
|
||||
provider: RouterProvider::Fal,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
fn mesh_builder() -> GenerateMeshRequestBuilder {
|
||||
GenerateMeshRequestBuilder {
|
||||
input_mesh: Some(MeshRef::Url(MESH_URL.to_string())),
|
||||
..base_builder()
|
||||
}
|
||||
}
|
||||
}
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
use fal_client::requests::traits::fal_request_cost_calculator_trait::FalRequestCostCalculator;
|
||||
|
||||
use crate::generate::generate_mesh::mesh_generation_cost_estimate::MeshGenerationCostEstimate;
|
||||
use crate::generate::generate_mesh::providers::fal::hunyuan3d_3::cost::fal_mesh_cost_estimate;
|
||||
use crate::generate::generate_mesh::providers::fal::hunyuan_3d_3p1_part::request::FalHunyuan3d3p1PartRequestState;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct FalHunyuan3d3p1PartCostState {
|
||||
pub cost_in_usd_cents: u64,
|
||||
}
|
||||
|
||||
impl FalHunyuan3d3p1PartCostState {
|
||||
pub fn from_request(request: &FalHunyuan3d3p1PartRequestState) -> Self {
|
||||
// Cost math is owned by fal_client's per-endpoint
|
||||
// `FalRequestCostCalculator` implementations. The router state just
|
||||
// forwards the result so router cost ≡ fal_client cost by construction.
|
||||
Self {
|
||||
cost_in_usd_cents: request.request.calculate_cost_in_cents(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn estimate_cost(&self) -> MeshGenerationCostEstimate {
|
||||
fal_mesh_cost_estimate(self.cost_in_usd_cents)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::api::mesh_ref::MeshRef;
|
||||
use crate::api::router_mesh_model::RouterMeshModel;
|
||||
use crate::api::router_provider::RouterProvider;
|
||||
use crate::generate::generate_mesh::generate_mesh_request_builder::GenerateMeshRequestBuilder;
|
||||
|
||||
const MESH_URL: &str = "https://example.com/model.fbx";
|
||||
|
||||
#[test]
|
||||
fn cost_is_a_flat_forty_five_cents() {
|
||||
assert_eq!(estimate_usd_cents(mesh_builder()), 45);
|
||||
}
|
||||
|
||||
// ── Helpers ──
|
||||
|
||||
fn mesh_builder() -> GenerateMeshRequestBuilder {
|
||||
GenerateMeshRequestBuilder {
|
||||
model: RouterMeshModel::Hunyuan3d3p1Part,
|
||||
provider: RouterProvider::Fal,
|
||||
input_mesh: Some(MeshRef::Url(MESH_URL.to_string())),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
fn estimate_usd_cents(builder: GenerateMeshRequestBuilder) -> u64 {
|
||||
builder.build2()
|
||||
.expect("build should succeed")
|
||||
.estimate_cost()
|
||||
.expect("estimate should succeed")
|
||||
.cost_in_usd_cents
|
||||
.expect("cost should be present")
|
||||
}
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
pub mod build;
|
||||
pub mod cost;
|
||||
pub mod request;
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
use fal_client::requests::api::mesh::part::hunyuan_3d_3p1_part::api::Hunyuan3d3p1PartRequest;
|
||||
|
||||
use crate::client::router_fal_client::RouterFalClient;
|
||||
use crate::errors::artcraft_router_error::ArtcraftRouterError;
|
||||
use crate::generate::generate_mesh::generate_mesh_response::GenerateMeshResponse;
|
||||
use crate::generate::generate_mesh::providers::fal::hunyuan3d_3::request::send_fal_mesh_request;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct FalHunyuan3d3p1PartRequestState {
|
||||
/// Final materialized request; ready to fire.
|
||||
pub request: Hunyuan3d3p1PartRequest,
|
||||
}
|
||||
|
||||
impl FalHunyuan3d3p1PartRequestState {
|
||||
pub async fn send(&self, client: &RouterFalClient) -> Result<GenerateMeshResponse, ArtcraftRouterError> {
|
||||
send_fal_mesh_request(&self.request, client).await
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::api::mesh_ref::MeshRef;
|
||||
use crate::api::router_mesh_model::RouterMeshModel;
|
||||
use crate::api::router_provider::RouterProvider;
|
||||
use crate::generate::generate_mesh::generate_mesh_request_builder::GenerateMeshRequestBuilder;
|
||||
use crate::generate::generate_mesh::mesh_generation_draft_or_request::MeshGenerationDraftOrRequest;
|
||||
use crate::test_helpers::get_fal_client;
|
||||
|
||||
#[tokio::test]
|
||||
#[ignore] // requires real API key and a real FBX URL, incurs cost
|
||||
async fn send_part_split() {
|
||||
let client = get_fal_client();
|
||||
let builder = GenerateMeshRequestBuilder {
|
||||
model: RouterMeshModel::Hunyuan3d3p1Part,
|
||||
provider: RouterProvider::Fal,
|
||||
input_mesh: Some(MeshRef::Url("https://example.com/model.fbx".to_string())),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let draft_or_request = builder.build2().expect("build2 should succeed");
|
||||
let request = match draft_or_request {
|
||||
MeshGenerationDraftOrRequest::Request(r) => r,
|
||||
_ => panic!("expected Request variant (Fal mesh skips draft)"),
|
||||
};
|
||||
|
||||
let response = request.send_request(&client).await.expect("send_request should succeed");
|
||||
println!("response: {:?}", response);
|
||||
let payload = response.get_fal_payload().expect("expected Fal payload");
|
||||
assert!(payload.request_id.is_some() || payload.gateway_request_id.is_some());
|
||||
assert_eq!(1, 2, "Inspect output above");
|
||||
}
|
||||
}
|
||||
+411
@@ -0,0 +1,411 @@
|
||||
use enums::common::generation::common_mesh_output_type::CommonMeshOutputType;
|
||||
use fal_client::requests::api::mesh::image::hunyuan_3d_3p1_pro_image_to_mesh::api::{
|
||||
Hunyuan3d3p1ProImageToMeshGenerateType, Hunyuan3d3p1ProImageToMeshRequest,
|
||||
};
|
||||
use fal_client::requests::api::mesh::text::hunyuan_3d_3p1_pro_text_to_mesh::api::{
|
||||
Hunyuan3d3p1ProTextToMeshGenerateType, Hunyuan3d3p1ProTextToMeshRequest,
|
||||
};
|
||||
|
||||
use crate::client::request_mismatch_mitigation_strategy::RequestMismatchMitigationStrategy;
|
||||
use crate::errors::artcraft_router_error::ArtcraftRouterError;
|
||||
use crate::generate::generate_mesh::generate_mesh_request_builder::GenerateMeshRequestBuilder;
|
||||
use crate::generate::generate_mesh::mesh_generation_draft_or_request::MeshGenerationDraftOrRequest;
|
||||
use crate::generate::generate_mesh::mesh_generation_request::MeshGenerationRequest;
|
||||
use crate::generate::generate_mesh::providers::fal::hunyuan_3d_3p1_pro::request::{
|
||||
FalHunyuan3d3p1ProImageRequestState, FalHunyuan3d3p1ProTextRequestState,
|
||||
};
|
||||
use crate::generate::generate_mesh::providers::fal::resolve::{
|
||||
plan_face_count, plan_primary_image_url, plan_side_image_url,
|
||||
};
|
||||
use crate::generate::generate_mesh::providers::reject_unsupported::{
|
||||
reject_unsupported_image_ref, reject_unsupported_option,
|
||||
};
|
||||
|
||||
/// Hunyuan 3D v3.1 Pro combines fal's image-to-3d and text-to-3d endpoints
|
||||
/// under a single router model. The request shape picks the endpoint: any
|
||||
/// image input (reference images or `front_image`) dispatches to image-to-3d;
|
||||
/// otherwise a prompt dispatches to text-to-3d.
|
||||
pub fn build_fal_hunyuan_3d_3p1_pro(builder: GenerateMeshRequestBuilder) -> Result<MeshGenerationDraftOrRequest, ArtcraftRouterError> {
|
||||
let state = build_fal_hunyuan_3d_3p1_pro_state(builder)?;
|
||||
let request = match state {
|
||||
FalHunyuan3d3p1ProState::Image(state) => MeshGenerationRequest::FalHunyuan3d3p1ProImage(state),
|
||||
FalHunyuan3d3p1ProState::Text(state) => MeshGenerationRequest::FalHunyuan3d3p1ProText(state),
|
||||
};
|
||||
Ok(MeshGenerationDraftOrRequest::Request(request))
|
||||
}
|
||||
|
||||
/// The endpoint selected by the request shape.
|
||||
#[derive(Clone, Debug)]
|
||||
pub(crate) enum FalHunyuan3d3p1ProState {
|
||||
Image(FalHunyuan3d3p1ProImageRequestState),
|
||||
Text(FalHunyuan3d3p1ProTextRequestState),
|
||||
}
|
||||
|
||||
pub(crate) fn build_fal_hunyuan_3d_3p1_pro_state(
|
||||
mut builder: GenerateMeshRequestBuilder,
|
||||
) -> Result<FalHunyuan3d3p1ProState, ArtcraftRouterError> {
|
||||
let strategy = builder.request_mismatch_mitigation_strategy;
|
||||
|
||||
// v3.1 Pro has no polygon-type, texture, or input-mesh parameters.
|
||||
reject_unsupported_option("polygon_type", builder.polygon_type.as_ref(), strategy)?;
|
||||
reject_unsupported_option("enable_texture", builder.enable_texture.as_ref(), strategy)?;
|
||||
reject_unsupported_option("texture_quality", builder.texture_quality.as_ref(), strategy)?;
|
||||
reject_unsupported_option("geometry_quality", builder.geometry_quality.as_ref(), strategy)?;
|
||||
reject_unsupported_option("input_mesh", builder.input_mesh.as_ref(), strategy)?;
|
||||
|
||||
let maybe_front_url = plan_primary_image_url(
|
||||
builder.reference_images.take(),
|
||||
builder.front_image.take(),
|
||||
strategy,
|
||||
)?;
|
||||
let face_count = plan_face_count(builder.face_count, strategy)?;
|
||||
|
||||
match maybe_front_url {
|
||||
Some(image_url) => {
|
||||
// Image mode. The image endpoint has no prompt parameter.
|
||||
reject_unsupported_option("prompt", builder.prompt.as_ref(), strategy)?;
|
||||
|
||||
let request = Hunyuan3d3p1ProImageToMeshRequest {
|
||||
image_url,
|
||||
back_image_url: plan_side_image_url(builder.back_image.take())?,
|
||||
left_image_url: plan_side_image_url(builder.left_image.take())?,
|
||||
right_image_url: plan_side_image_url(builder.right_image.take())?,
|
||||
// The builder has no fields for the v3.1-exclusive extra views.
|
||||
top_image_url: None,
|
||||
bottom_image_url: None,
|
||||
left_front_image_url: None,
|
||||
right_front_image_url: None,
|
||||
generate_type: plan_image_generate_type(builder.mesh_output_type, strategy)?,
|
||||
face_count,
|
||||
enable_pbr: builder.enable_pbr,
|
||||
};
|
||||
Ok(FalHunyuan3d3p1ProState::Image(FalHunyuan3d3p1ProImageRequestState { request }))
|
||||
}
|
||||
None => {
|
||||
// Text mode. Multi-view side images only make sense alongside a front
|
||||
// image, so they're unsupported here.
|
||||
reject_unsupported_image_ref("back_image", builder.back_image.as_ref(), strategy)?;
|
||||
reject_unsupported_image_ref("left_image", builder.left_image.as_ref(), strategy)?;
|
||||
reject_unsupported_image_ref("right_image", builder.right_image.as_ref(), strategy)?;
|
||||
|
||||
let prompt = builder.prompt.take().ok_or_else(|| {
|
||||
ArtcraftRouterError::InvalidInput(
|
||||
"Hunyuan 3D v3.1 Pro requires an input image or a prompt".to_string(),
|
||||
)
|
||||
})?;
|
||||
|
||||
let request = Hunyuan3d3p1ProTextToMeshRequest {
|
||||
prompt,
|
||||
generate_type: plan_text_generate_type(builder.mesh_output_type, strategy)?,
|
||||
face_count,
|
||||
enable_pbr: builder.enable_pbr,
|
||||
};
|
||||
Ok(FalHunyuan3d3p1ProState::Text(FalHunyuan3d3p1ProTextRequestState { request }))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ── Enum mapping helpers ──
|
||||
|
||||
/// v3.1 drops the v3 `LowPoly` generation type: `LowPoly` rejects under
|
||||
/// `ErrorOut` and drops to the endpoint default otherwise.
|
||||
fn plan_image_generate_type(
|
||||
mesh_output_type: Option<CommonMeshOutputType>,
|
||||
strategy: RequestMismatchMitigationStrategy,
|
||||
) -> Result<Option<Hunyuan3d3p1ProImageToMeshGenerateType>, ArtcraftRouterError> {
|
||||
match mesh_output_type {
|
||||
None => Ok(None),
|
||||
Some(CommonMeshOutputType::Normal) => Ok(Some(Hunyuan3d3p1ProImageToMeshGenerateType::Normal)),
|
||||
Some(CommonMeshOutputType::Geometry) => Ok(Some(Hunyuan3d3p1ProImageToMeshGenerateType::Geometry)),
|
||||
Some(CommonMeshOutputType::LowPoly) => {
|
||||
reject_unsupported_option("mesh_output_type", Some(&CommonMeshOutputType::LowPoly), strategy)?;
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn plan_text_generate_type(
|
||||
mesh_output_type: Option<CommonMeshOutputType>,
|
||||
strategy: RequestMismatchMitigationStrategy,
|
||||
) -> Result<Option<Hunyuan3d3p1ProTextToMeshGenerateType>, ArtcraftRouterError> {
|
||||
match mesh_output_type {
|
||||
None => Ok(None),
|
||||
Some(CommonMeshOutputType::Normal) => Ok(Some(Hunyuan3d3p1ProTextToMeshGenerateType::Normal)),
|
||||
Some(CommonMeshOutputType::Geometry) => Ok(Some(Hunyuan3d3p1ProTextToMeshGenerateType::Geometry)),
|
||||
Some(CommonMeshOutputType::LowPoly) => {
|
||||
reject_unsupported_option("mesh_output_type", Some(&CommonMeshOutputType::LowPoly), strategy)?;
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use enums::common::generation::common_mesh_quality::CommonMeshQuality;
|
||||
use enums::common::generation::common_polygon_type::CommonPolygonType;
|
||||
|
||||
use crate::api::image_list_ref::ImageListRef;
|
||||
use crate::api::image_ref::ImageRef;
|
||||
use crate::api::mesh_ref::MeshRef;
|
||||
use crate::api::router_mesh_model::RouterMeshModel;
|
||||
use crate::api::router_provider::RouterProvider;
|
||||
|
||||
use super::*;
|
||||
|
||||
const FRONT_URL: &str = "https://example.com/front.png";
|
||||
const BACK_URL: &str = "https://example.com/back.png";
|
||||
|
||||
mod shape_dispatch {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn reference_image_dispatches_to_image_mode() {
|
||||
let state = build_fal_hunyuan_3d_3p1_pro_state(image_builder()).expect("build");
|
||||
let image = expect_image(state);
|
||||
assert_eq!(image.request.image_url, FRONT_URL);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn front_image_dispatches_to_image_mode() {
|
||||
let builder = GenerateMeshRequestBuilder {
|
||||
reference_images: None,
|
||||
front_image: Some(ImageRef::Url(FRONT_URL.to_string())),
|
||||
..base_builder()
|
||||
};
|
||||
let state = build_fal_hunyuan_3d_3p1_pro_state(builder).expect("build");
|
||||
let image = expect_image(state);
|
||||
assert_eq!(image.request.image_url, FRONT_URL);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn prompt_only_dispatches_to_text_mode() {
|
||||
let state = build_fal_hunyuan_3d_3p1_pro_state(text_builder()).expect("build");
|
||||
let text = expect_text(state);
|
||||
assert_eq!(text.request.prompt, "a red ceramic teapot");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn neither_image_nor_prompt_is_rejected() {
|
||||
let result = build_fal_hunyuan_3d_3p1_pro_state(base_builder());
|
||||
assert!(matches!(result, Err(ArtcraftRouterError::InvalidInput(_))));
|
||||
}
|
||||
}
|
||||
|
||||
mod image_mode {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn side_images_map_to_multi_view_params() {
|
||||
let builder = GenerateMeshRequestBuilder {
|
||||
back_image: Some(ImageRef::Url(BACK_URL.to_string())),
|
||||
left_image: Some(ImageRef::Url("https://example.com/left.png".to_string())),
|
||||
right_image: Some(ImageRef::Url("https://example.com/right.png".to_string())),
|
||||
..image_builder()
|
||||
};
|
||||
let image = expect_image(build_fal_hunyuan_3d_3p1_pro_state(builder).expect("build"));
|
||||
assert_eq!(image.request.back_image_url.as_deref(), Some(BACK_URL));
|
||||
assert_eq!(image.request.left_image_url.as_deref(), Some("https://example.com/left.png"));
|
||||
assert_eq!(image.request.right_image_url.as_deref(), Some("https://example.com/right.png"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn v3p1_exclusive_extra_views_are_left_unset() {
|
||||
let image = expect_image(build_fal_hunyuan_3d_3p1_pro_state(image_builder()).expect("build"));
|
||||
assert!(image.request.top_image_url.is_none());
|
||||
assert!(image.request.bottom_image_url.is_none());
|
||||
assert!(image.request.left_front_image_url.is_none());
|
||||
assert!(image.request.right_front_image_url.is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn options_map_through() {
|
||||
let builder = GenerateMeshRequestBuilder {
|
||||
mesh_output_type: Some(CommonMeshOutputType::Geometry),
|
||||
face_count: Some(100_000),
|
||||
enable_pbr: Some(true),
|
||||
..image_builder()
|
||||
};
|
||||
let image = expect_image(build_fal_hunyuan_3d_3p1_pro_state(builder).expect("build"));
|
||||
assert!(matches!(image.request.generate_type, Some(Hunyuan3d3p1ProImageToMeshGenerateType::Geometry)));
|
||||
assert_eq!(image.request.face_count, Some(100_000));
|
||||
assert_eq!(image.request.enable_pbr, Some(true));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn low_poly_errors_out_under_error_out() {
|
||||
let builder = GenerateMeshRequestBuilder {
|
||||
mesh_output_type: Some(CommonMeshOutputType::LowPoly),
|
||||
request_mismatch_mitigation_strategy: RequestMismatchMitigationStrategy::ErrorOut,
|
||||
..image_builder()
|
||||
};
|
||||
assert!(build_fal_hunyuan_3d_3p1_pro_state(builder).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn low_poly_drops_to_default_under_lenient_strategies() {
|
||||
let builder = GenerateMeshRequestBuilder {
|
||||
mesh_output_type: Some(CommonMeshOutputType::LowPoly),
|
||||
..image_builder()
|
||||
};
|
||||
let image = expect_image(build_fal_hunyuan_3d_3p1_pro_state(builder).expect("build"));
|
||||
assert!(image.request.generate_type.is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn prompt_with_image_errors_out_under_error_out() {
|
||||
let builder = GenerateMeshRequestBuilder {
|
||||
prompt: Some("a red ceramic teapot".to_string()),
|
||||
request_mismatch_mitigation_strategy: RequestMismatchMitigationStrategy::ErrorOut,
|
||||
..image_builder()
|
||||
};
|
||||
assert!(build_fal_hunyuan_3d_3p1_pro_state(builder).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn prompt_with_image_is_dropped_under_lenient_strategies() {
|
||||
let builder = GenerateMeshRequestBuilder {
|
||||
prompt: Some("a red ceramic teapot".to_string()),
|
||||
..image_builder()
|
||||
};
|
||||
assert!(matches!(
|
||||
build_fal_hunyuan_3d_3p1_pro_state(builder).expect("build"),
|
||||
FalHunyuan3d3p1ProState::Image(_)
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
mod text_mode {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn options_map_through() {
|
||||
let builder = GenerateMeshRequestBuilder {
|
||||
mesh_output_type: Some(CommonMeshOutputType::Normal),
|
||||
face_count: Some(40_000),
|
||||
enable_pbr: Some(false),
|
||||
..text_builder()
|
||||
};
|
||||
let text = expect_text(build_fal_hunyuan_3d_3p1_pro_state(builder).expect("build"));
|
||||
assert!(matches!(text.request.generate_type, Some(Hunyuan3d3p1ProTextToMeshGenerateType::Normal)));
|
||||
assert_eq!(text.request.face_count, Some(40_000));
|
||||
assert_eq!(text.request.enable_pbr, Some(false));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn side_images_without_front_error_out_under_error_out() {
|
||||
let builder = GenerateMeshRequestBuilder {
|
||||
back_image: Some(ImageRef::Url(BACK_URL.to_string())),
|
||||
request_mismatch_mitigation_strategy: RequestMismatchMitigationStrategy::ErrorOut,
|
||||
..text_builder()
|
||||
};
|
||||
assert!(build_fal_hunyuan_3d_3p1_pro_state(builder).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn side_images_without_front_are_dropped_under_lenient_strategies() {
|
||||
let builder = GenerateMeshRequestBuilder {
|
||||
back_image: Some(ImageRef::Url(BACK_URL.to_string())),
|
||||
..text_builder()
|
||||
};
|
||||
assert!(matches!(
|
||||
build_fal_hunyuan_3d_3p1_pro_state(builder).expect("build"),
|
||||
FalHunyuan3d3p1ProState::Text(_)
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
mod unsupported_options {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn polygon_type_errors_out_under_error_out() {
|
||||
let builder = GenerateMeshRequestBuilder {
|
||||
polygon_type: Some(CommonPolygonType::Quad),
|
||||
request_mismatch_mitigation_strategy: RequestMismatchMitigationStrategy::ErrorOut,
|
||||
..image_builder()
|
||||
};
|
||||
assert!(build_fal_hunyuan_3d_3p1_pro_state(builder).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn texture_options_error_out_under_error_out() {
|
||||
let cases: Vec<fn(&mut GenerateMeshRequestBuilder)> = vec![
|
||||
|b| b.enable_texture = Some(false),
|
||||
|b| b.texture_quality = Some(CommonMeshQuality::Detailed),
|
||||
|b| b.geometry_quality = Some(CommonMeshQuality::Detailed),
|
||||
];
|
||||
for (index, set) in cases.into_iter().enumerate() {
|
||||
let mut builder = GenerateMeshRequestBuilder {
|
||||
request_mismatch_mitigation_strategy: RequestMismatchMitigationStrategy::ErrorOut,
|
||||
..image_builder()
|
||||
};
|
||||
set(&mut builder);
|
||||
assert!(build_fal_hunyuan_3d_3p1_pro_state(builder).is_err(), "for case {index}");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn input_mesh_errors_out_under_error_out() {
|
||||
let builder = GenerateMeshRequestBuilder {
|
||||
input_mesh: Some(MeshRef::Url("https://example.com/model.glb".to_string())),
|
||||
request_mismatch_mitigation_strategy: RequestMismatchMitigationStrategy::ErrorOut,
|
||||
..text_builder()
|
||||
};
|
||||
assert!(build_fal_hunyuan_3d_3p1_pro_state(builder).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unsupported_options_are_dropped_under_lenient_strategies() {
|
||||
let builder = GenerateMeshRequestBuilder {
|
||||
polygon_type: Some(CommonPolygonType::Quad),
|
||||
enable_texture: Some(false),
|
||||
texture_quality: Some(CommonMeshQuality::Detailed),
|
||||
geometry_quality: Some(CommonMeshQuality::Detailed),
|
||||
input_mesh: Some(MeshRef::Url("https://example.com/model.glb".to_string())),
|
||||
..image_builder()
|
||||
};
|
||||
assert!(matches!(
|
||||
build_fal_hunyuan_3d_3p1_pro_state(builder).expect("build"),
|
||||
FalHunyuan3d3p1ProState::Image(_)
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
// ── Helpers ──
|
||||
|
||||
fn base_builder() -> GenerateMeshRequestBuilder {
|
||||
GenerateMeshRequestBuilder {
|
||||
model: RouterMeshModel::Hunyuan3d3p1Pro,
|
||||
provider: RouterProvider::Fal,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
fn image_builder() -> GenerateMeshRequestBuilder {
|
||||
GenerateMeshRequestBuilder {
|
||||
reference_images: Some(ImageListRef::Urls(vec![FRONT_URL.to_string()])),
|
||||
..base_builder()
|
||||
}
|
||||
}
|
||||
|
||||
fn text_builder() -> GenerateMeshRequestBuilder {
|
||||
GenerateMeshRequestBuilder {
|
||||
prompt: Some("a red ceramic teapot".to_string()),
|
||||
..base_builder()
|
||||
}
|
||||
}
|
||||
|
||||
fn expect_image(state: FalHunyuan3d3p1ProState) -> FalHunyuan3d3p1ProImageRequestState {
|
||||
match state {
|
||||
FalHunyuan3d3p1ProState::Image(image) => image,
|
||||
FalHunyuan3d3p1ProState::Text(text) => panic!("expected image mode, got text: {text:?}"),
|
||||
}
|
||||
}
|
||||
|
||||
fn expect_text(state: FalHunyuan3d3p1ProState) -> FalHunyuan3d3p1ProTextRequestState {
|
||||
match state {
|
||||
FalHunyuan3d3p1ProState::Text(text) => text,
|
||||
FalHunyuan3d3p1ProState::Image(image) => panic!("expected text mode, got image: {image:?}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
+159
@@ -0,0 +1,159 @@
|
||||
use fal_client::requests::traits::fal_request_cost_calculator_trait::FalRequestCostCalculator;
|
||||
|
||||
use crate::generate::generate_mesh::mesh_generation_cost_estimate::MeshGenerationCostEstimate;
|
||||
use crate::generate::generate_mesh::providers::fal::hunyuan3d_3::cost::fal_mesh_cost_estimate;
|
||||
use crate::generate::generate_mesh::providers::fal::hunyuan_3d_3p1_pro::request::{
|
||||
FalHunyuan3d3p1ProImageRequestState, FalHunyuan3d3p1ProTextRequestState,
|
||||
};
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct FalHunyuan3d3p1ProImageCostState {
|
||||
pub cost_in_usd_cents: u64,
|
||||
}
|
||||
|
||||
impl FalHunyuan3d3p1ProImageCostState {
|
||||
pub fn from_request(request: &FalHunyuan3d3p1ProImageRequestState) -> Self {
|
||||
// Cost math is owned by fal_client's per-endpoint
|
||||
// `FalRequestCostCalculator` implementations. The router state just
|
||||
// forwards the result so router cost ≡ fal_client cost by construction.
|
||||
Self {
|
||||
cost_in_usd_cents: request.request.calculate_cost_in_cents(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn estimate_cost(&self) -> MeshGenerationCostEstimate {
|
||||
fal_mesh_cost_estimate(self.cost_in_usd_cents)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct FalHunyuan3d3p1ProTextCostState {
|
||||
pub cost_in_usd_cents: u64,
|
||||
}
|
||||
|
||||
impl FalHunyuan3d3p1ProTextCostState {
|
||||
pub fn from_request(request: &FalHunyuan3d3p1ProTextRequestState) -> Self {
|
||||
Self {
|
||||
cost_in_usd_cents: request.request.calculate_cost_in_cents(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn estimate_cost(&self) -> MeshGenerationCostEstimate {
|
||||
fal_mesh_cost_estimate(self.cost_in_usd_cents)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use enums::common::generation::common_mesh_output_type::CommonMeshOutputType;
|
||||
|
||||
use crate::api::image_list_ref::ImageListRef;
|
||||
use crate::api::image_ref::ImageRef;
|
||||
use crate::api::router_mesh_model::RouterMeshModel;
|
||||
use crate::api::router_provider::RouterProvider;
|
||||
use crate::generate::generate_mesh::generate_mesh_request_builder::GenerateMeshRequestBuilder;
|
||||
|
||||
const FRONT_URL: &str = "https://example.com/front.png";
|
||||
|
||||
mod image_mode_costs {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn base_cost_table() {
|
||||
let cases = [
|
||||
(None, 38),
|
||||
(Some(CommonMeshOutputType::Normal), 38),
|
||||
(Some(CommonMeshOutputType::Geometry), 38),
|
||||
];
|
||||
for (output_type, expected) in cases {
|
||||
let builder = GenerateMeshRequestBuilder {
|
||||
mesh_output_type: output_type,
|
||||
..image_builder()
|
||||
};
|
||||
assert_eq!(estimate_usd_cents(builder), expected, "for {output_type:?}");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn multi_view_adds_fifteen_cents() {
|
||||
let builder = GenerateMeshRequestBuilder {
|
||||
back_image: Some(ImageRef::Url("https://example.com/back.png".to_string())),
|
||||
..image_builder()
|
||||
};
|
||||
assert_eq!(estimate_usd_cents(builder), 38 + 15);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn add_ons_stack() {
|
||||
let builder = GenerateMeshRequestBuilder {
|
||||
mesh_output_type: Some(CommonMeshOutputType::Geometry),
|
||||
enable_pbr: Some(true),
|
||||
face_count: Some(100_000),
|
||||
back_image: Some(ImageRef::Url("https://example.com/back.png".to_string())),
|
||||
..image_builder()
|
||||
};
|
||||
// base(38) + PBR(15) + face_count(15) + multi_view(15)
|
||||
assert_eq!(estimate_usd_cents(builder), 83);
|
||||
}
|
||||
}
|
||||
|
||||
mod text_mode_costs {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn base_cost_table() {
|
||||
let cases = [
|
||||
(None, 38),
|
||||
(Some(CommonMeshOutputType::Normal), 38),
|
||||
(Some(CommonMeshOutputType::Geometry), 38),
|
||||
];
|
||||
for (output_type, expected) in cases {
|
||||
let builder = GenerateMeshRequestBuilder {
|
||||
mesh_output_type: output_type,
|
||||
..text_builder()
|
||||
};
|
||||
assert_eq!(estimate_usd_cents(builder), expected, "for {output_type:?}");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn add_ons_stack() {
|
||||
let builder = GenerateMeshRequestBuilder {
|
||||
enable_pbr: Some(true),
|
||||
face_count: Some(40_000),
|
||||
..text_builder()
|
||||
};
|
||||
// base(38) + PBR(15) + face_count(15)
|
||||
assert_eq!(estimate_usd_cents(builder), 68);
|
||||
}
|
||||
}
|
||||
|
||||
// ── Helpers ──
|
||||
|
||||
fn image_builder() -> GenerateMeshRequestBuilder {
|
||||
GenerateMeshRequestBuilder {
|
||||
model: RouterMeshModel::Hunyuan3d3p1Pro,
|
||||
provider: RouterProvider::Fal,
|
||||
reference_images: Some(ImageListRef::Urls(vec![FRONT_URL.to_string()])),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
fn text_builder() -> GenerateMeshRequestBuilder {
|
||||
GenerateMeshRequestBuilder {
|
||||
model: RouterMeshModel::Hunyuan3d3p1Pro,
|
||||
provider: RouterProvider::Fal,
|
||||
prompt: Some("a red ceramic teapot".to_string()),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
fn estimate_usd_cents(builder: GenerateMeshRequestBuilder) -> u64 {
|
||||
builder.build2()
|
||||
.expect("build should succeed")
|
||||
.estimate_cost()
|
||||
.expect("estimate should succeed")
|
||||
.cost_in_usd_cents
|
||||
.expect("cost should be present")
|
||||
}
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
pub mod build;
|
||||
pub mod cost;
|
||||
pub mod request;
|
||||
+92
@@ -0,0 +1,92 @@
|
||||
use fal_client::requests::api::mesh::image::hunyuan_3d_3p1_pro_image_to_mesh::api::Hunyuan3d3p1ProImageToMeshRequest;
|
||||
use fal_client::requests::api::mesh::text::hunyuan_3d_3p1_pro_text_to_mesh::api::Hunyuan3d3p1ProTextToMeshRequest;
|
||||
|
||||
use crate::client::router_fal_client::RouterFalClient;
|
||||
use crate::errors::artcraft_router_error::ArtcraftRouterError;
|
||||
use crate::generate::generate_mesh::generate_mesh_response::GenerateMeshResponse;
|
||||
use crate::generate::generate_mesh::providers::fal::hunyuan3d_3::request::send_fal_mesh_request;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct FalHunyuan3d3p1ProImageRequestState {
|
||||
/// Final materialized request; ready to fire.
|
||||
pub request: Hunyuan3d3p1ProImageToMeshRequest,
|
||||
}
|
||||
|
||||
impl FalHunyuan3d3p1ProImageRequestState {
|
||||
pub async fn send(&self, client: &RouterFalClient) -> Result<GenerateMeshResponse, ArtcraftRouterError> {
|
||||
send_fal_mesh_request(&self.request, client).await
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct FalHunyuan3d3p1ProTextRequestState {
|
||||
/// Final materialized request; ready to fire.
|
||||
pub request: Hunyuan3d3p1ProTextToMeshRequest,
|
||||
}
|
||||
|
||||
impl FalHunyuan3d3p1ProTextRequestState {
|
||||
pub async fn send(&self, client: &RouterFalClient) -> Result<GenerateMeshResponse, ArtcraftRouterError> {
|
||||
send_fal_mesh_request(&self.request, client).await
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use test_data::web::image_urls::ERNEST_SCARED_STUPID_IMAGE_URL;
|
||||
|
||||
use crate::api::image_list_ref::ImageListRef;
|
||||
use crate::api::router_mesh_model::RouterMeshModel;
|
||||
use crate::api::router_provider::RouterProvider;
|
||||
use crate::generate::generate_mesh::generate_mesh_request_builder::GenerateMeshRequestBuilder;
|
||||
use crate::generate::generate_mesh::generate_mesh_response::GenerateMeshResponse;
|
||||
use crate::generate::generate_mesh::mesh_generation_draft_or_request::MeshGenerationDraftOrRequest;
|
||||
use crate::test_helpers::get_fal_client;
|
||||
|
||||
#[tokio::test]
|
||||
#[ignore] // requires real API key, incurs cost
|
||||
async fn send_pro_image_to_mesh() {
|
||||
let response = run_pipeline(GenerateMeshRequestBuilder {
|
||||
reference_images: Some(ImageListRef::Urls(vec![ERNEST_SCARED_STUPID_IMAGE_URL.to_string()])),
|
||||
..pro_builder()
|
||||
}).await;
|
||||
let payload = response.get_fal_payload().expect("expected Fal payload");
|
||||
assert!(payload.request_id.is_some() || payload.gateway_request_id.is_some());
|
||||
assert_eq!(1, 2, "Inspect output above");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
#[ignore] // requires real API key, incurs cost
|
||||
async fn send_pro_text_to_mesh() {
|
||||
let response = run_pipeline(GenerateMeshRequestBuilder {
|
||||
prompt: Some("A velociraptor with an open mouth full of sharp teeth.".to_string()),
|
||||
..pro_builder()
|
||||
}).await;
|
||||
let payload = response.get_fal_payload().expect("expected Fal payload");
|
||||
assert!(payload.request_id.is_some() || payload.gateway_request_id.is_some());
|
||||
assert_eq!(1, 2, "Inspect output above");
|
||||
}
|
||||
|
||||
// ── Helpers ──
|
||||
|
||||
fn pro_builder() -> GenerateMeshRequestBuilder {
|
||||
GenerateMeshRequestBuilder {
|
||||
model: RouterMeshModel::Hunyuan3d3p1Pro,
|
||||
provider: RouterProvider::Fal,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
async fn run_pipeline(builder: GenerateMeshRequestBuilder) -> GenerateMeshResponse {
|
||||
let client = get_fal_client();
|
||||
|
||||
let draft_or_request = builder.build2().expect("build2 should succeed");
|
||||
let request = match draft_or_request {
|
||||
MeshGenerationDraftOrRequest::Request(r) => r,
|
||||
_ => panic!("expected Request variant (Fal mesh skips draft)"),
|
||||
};
|
||||
|
||||
let response = request.send_request(&client).await.expect("send_request should succeed");
|
||||
println!("response: {:?}", response);
|
||||
response
|
||||
}
|
||||
}
|
||||
+323
@@ -0,0 +1,323 @@
|
||||
use enums::common::generation::common_mesh_output_type::CommonMeshOutputType;
|
||||
use fal_client::requests::api::mesh::image::hunyuan_3d_3p1_rapid_image_to_mesh::api::Hunyuan3d3p1RapidImageToMeshRequest;
|
||||
use fal_client::requests::api::mesh::text::hunyuan_3d_3p1_rapid_text_to_mesh::api::Hunyuan3d3p1RapidTextToMeshRequest;
|
||||
|
||||
use crate::client::request_mismatch_mitigation_strategy::RequestMismatchMitigationStrategy;
|
||||
use crate::errors::artcraft_router_error::ArtcraftRouterError;
|
||||
use crate::generate::generate_mesh::generate_mesh_request_builder::GenerateMeshRequestBuilder;
|
||||
use crate::generate::generate_mesh::mesh_generation_draft_or_request::MeshGenerationDraftOrRequest;
|
||||
use crate::generate::generate_mesh::mesh_generation_request::MeshGenerationRequest;
|
||||
use crate::generate::generate_mesh::providers::fal::hunyuan_3d_3p1_rapid::request::{
|
||||
FalHunyuan3d3p1RapidImageRequestState, FalHunyuan3d3p1RapidTextRequestState,
|
||||
};
|
||||
use crate::generate::generate_mesh::providers::fal::resolve::plan_primary_image_url;
|
||||
use crate::generate::generate_mesh::providers::reject_unsupported::{
|
||||
reject_unsupported_image_ref, reject_unsupported_option,
|
||||
};
|
||||
|
||||
/// Hunyuan 3D v3.1 Rapid combines fal's rapid image-to-3d and text-to-3d
|
||||
/// endpoints under a single router model. The request shape picks the
|
||||
/// endpoint: any image input (reference images or `front_image`) dispatches
|
||||
/// to image-to-3d; otherwise a prompt dispatches to text-to-3d.
|
||||
pub fn build_fal_hunyuan_3d_3p1_rapid(builder: GenerateMeshRequestBuilder) -> Result<MeshGenerationDraftOrRequest, ArtcraftRouterError> {
|
||||
let state = build_fal_hunyuan_3d_3p1_rapid_state(builder)?;
|
||||
let request = match state {
|
||||
FalHunyuan3d3p1RapidState::Image(state) => MeshGenerationRequest::FalHunyuan3d3p1RapidImage(state),
|
||||
FalHunyuan3d3p1RapidState::Text(state) => MeshGenerationRequest::FalHunyuan3d3p1RapidText(state),
|
||||
};
|
||||
Ok(MeshGenerationDraftOrRequest::Request(request))
|
||||
}
|
||||
|
||||
/// The endpoint selected by the request shape.
|
||||
#[derive(Clone, Debug)]
|
||||
pub(crate) enum FalHunyuan3d3p1RapidState {
|
||||
Image(FalHunyuan3d3p1RapidImageRequestState),
|
||||
Text(FalHunyuan3d3p1RapidTextRequestState),
|
||||
}
|
||||
|
||||
pub(crate) fn build_fal_hunyuan_3d_3p1_rapid_state(
|
||||
mut builder: GenerateMeshRequestBuilder,
|
||||
) -> Result<FalHunyuan3d3p1RapidState, ArtcraftRouterError> {
|
||||
let strategy = builder.request_mismatch_mitigation_strategy;
|
||||
|
||||
// The rapid schema is minimal: no polygon-type, face-count, texture, or
|
||||
// input-mesh parameters.
|
||||
reject_unsupported_option("polygon_type", builder.polygon_type.as_ref(), strategy)?;
|
||||
reject_unsupported_option("face_count", builder.face_count.as_ref(), strategy)?;
|
||||
reject_unsupported_option("enable_texture", builder.enable_texture.as_ref(), strategy)?;
|
||||
reject_unsupported_option("texture_quality", builder.texture_quality.as_ref(), strategy)?;
|
||||
reject_unsupported_option("geometry_quality", builder.geometry_quality.as_ref(), strategy)?;
|
||||
reject_unsupported_option("input_mesh", builder.input_mesh.as_ref(), strategy)?;
|
||||
|
||||
// Rapid is single-image only — no multi-view side images in either mode.
|
||||
reject_unsupported_image_ref("back_image", builder.back_image.as_ref(), strategy)?;
|
||||
reject_unsupported_image_ref("left_image", builder.left_image.as_ref(), strategy)?;
|
||||
reject_unsupported_image_ref("right_image", builder.right_image.as_ref(), strategy)?;
|
||||
|
||||
let maybe_front_url = plan_primary_image_url(
|
||||
builder.reference_images.take(),
|
||||
builder.front_image.take(),
|
||||
strategy,
|
||||
)?;
|
||||
let enable_geometry = plan_enable_geometry(builder.mesh_output_type, strategy)?;
|
||||
|
||||
match maybe_front_url {
|
||||
Some(image_url) => {
|
||||
// Image mode. The image endpoint has no prompt parameter.
|
||||
reject_unsupported_option("prompt", builder.prompt.as_ref(), strategy)?;
|
||||
|
||||
let request = Hunyuan3d3p1RapidImageToMeshRequest {
|
||||
image_url,
|
||||
enable_pbr: builder.enable_pbr,
|
||||
enable_geometry,
|
||||
};
|
||||
Ok(FalHunyuan3d3p1RapidState::Image(FalHunyuan3d3p1RapidImageRequestState { request }))
|
||||
}
|
||||
None => {
|
||||
let prompt = builder.prompt.take().ok_or_else(|| {
|
||||
ArtcraftRouterError::InvalidInput(
|
||||
"Hunyuan 3D v3.1 Rapid requires an input image or a prompt".to_string(),
|
||||
)
|
||||
})?;
|
||||
|
||||
let request = Hunyuan3d3p1RapidTextToMeshRequest {
|
||||
prompt,
|
||||
enable_pbr: builder.enable_pbr,
|
||||
enable_geometry,
|
||||
};
|
||||
Ok(FalHunyuan3d3p1RapidState::Text(FalHunyuan3d3p1RapidTextRequestState { request }))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ── Enum mapping helpers ──
|
||||
|
||||
/// Rapid has no generation-type enum — geometry-only output is a boolean.
|
||||
/// `Geometry` maps to `enable_geometry: true`; `Normal` (or unset) leaves it
|
||||
/// unset (fal defaults to false); `LowPoly` rejects under `ErrorOut` and
|
||||
/// drops otherwise.
|
||||
fn plan_enable_geometry(
|
||||
mesh_output_type: Option<CommonMeshOutputType>,
|
||||
strategy: RequestMismatchMitigationStrategy,
|
||||
) -> Result<Option<bool>, ArtcraftRouterError> {
|
||||
match mesh_output_type {
|
||||
None | Some(CommonMeshOutputType::Normal) => Ok(None),
|
||||
Some(CommonMeshOutputType::Geometry) => Ok(Some(true)),
|
||||
Some(CommonMeshOutputType::LowPoly) => {
|
||||
reject_unsupported_option("mesh_output_type", Some(&CommonMeshOutputType::LowPoly), strategy)?;
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use enums::common::generation::common_polygon_type::CommonPolygonType;
|
||||
|
||||
use crate::api::image_list_ref::ImageListRef;
|
||||
use crate::api::image_ref::ImageRef;
|
||||
use crate::api::mesh_ref::MeshRef;
|
||||
use crate::api::router_mesh_model::RouterMeshModel;
|
||||
use crate::api::router_provider::RouterProvider;
|
||||
|
||||
use super::*;
|
||||
|
||||
const FRONT_URL: &str = "https://example.com/front.png";
|
||||
const BACK_URL: &str = "https://example.com/back.png";
|
||||
|
||||
mod shape_dispatch {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn reference_image_dispatches_to_image_mode() {
|
||||
let state = build_fal_hunyuan_3d_3p1_rapid_state(image_builder()).expect("build");
|
||||
let image = expect_image(state);
|
||||
assert_eq!(image.request.image_url, FRONT_URL);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn front_image_dispatches_to_image_mode() {
|
||||
let builder = GenerateMeshRequestBuilder {
|
||||
reference_images: None,
|
||||
front_image: Some(ImageRef::Url(FRONT_URL.to_string())),
|
||||
..base_builder()
|
||||
};
|
||||
let state = build_fal_hunyuan_3d_3p1_rapid_state(builder).expect("build");
|
||||
let image = expect_image(state);
|
||||
assert_eq!(image.request.image_url, FRONT_URL);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn prompt_only_dispatches_to_text_mode() {
|
||||
let state = build_fal_hunyuan_3d_3p1_rapid_state(text_builder()).expect("build");
|
||||
let text = expect_text(state);
|
||||
assert_eq!(text.request.prompt, "a red ceramic teapot");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn neither_image_nor_prompt_is_rejected() {
|
||||
let result = build_fal_hunyuan_3d_3p1_rapid_state(base_builder());
|
||||
assert!(matches!(result, Err(ArtcraftRouterError::InvalidInput(_))));
|
||||
}
|
||||
}
|
||||
|
||||
mod option_mapping {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn geometry_output_maps_to_enable_geometry() {
|
||||
let builder = GenerateMeshRequestBuilder {
|
||||
mesh_output_type: Some(CommonMeshOutputType::Geometry),
|
||||
..image_builder()
|
||||
};
|
||||
let image = expect_image(build_fal_hunyuan_3d_3p1_rapid_state(builder).expect("build"));
|
||||
assert_eq!(image.request.enable_geometry, Some(true));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn normal_output_leaves_enable_geometry_unset() {
|
||||
let builder = GenerateMeshRequestBuilder {
|
||||
mesh_output_type: Some(CommonMeshOutputType::Normal),
|
||||
..image_builder()
|
||||
};
|
||||
let image = expect_image(build_fal_hunyuan_3d_3p1_rapid_state(builder).expect("build"));
|
||||
assert!(image.request.enable_geometry.is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn enable_pbr_passes_through_in_both_modes() {
|
||||
let image_builder = GenerateMeshRequestBuilder {
|
||||
enable_pbr: Some(true),
|
||||
..image_builder()
|
||||
};
|
||||
let image = expect_image(build_fal_hunyuan_3d_3p1_rapid_state(image_builder).expect("build"));
|
||||
assert_eq!(image.request.enable_pbr, Some(true));
|
||||
|
||||
let text_builder = GenerateMeshRequestBuilder {
|
||||
enable_pbr: Some(true),
|
||||
..text_builder()
|
||||
};
|
||||
let text = expect_text(build_fal_hunyuan_3d_3p1_rapid_state(text_builder).expect("build"));
|
||||
assert_eq!(text.request.enable_pbr, Some(true));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn low_poly_errors_out_under_error_out() {
|
||||
let builder = GenerateMeshRequestBuilder {
|
||||
mesh_output_type: Some(CommonMeshOutputType::LowPoly),
|
||||
request_mismatch_mitigation_strategy: RequestMismatchMitigationStrategy::ErrorOut,
|
||||
..image_builder()
|
||||
};
|
||||
assert!(build_fal_hunyuan_3d_3p1_rapid_state(builder).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn low_poly_drops_to_default_under_lenient_strategies() {
|
||||
let builder = GenerateMeshRequestBuilder {
|
||||
mesh_output_type: Some(CommonMeshOutputType::LowPoly),
|
||||
..image_builder()
|
||||
};
|
||||
let image = expect_image(build_fal_hunyuan_3d_3p1_rapid_state(builder).expect("build"));
|
||||
assert!(image.request.enable_geometry.is_none());
|
||||
}
|
||||
}
|
||||
|
||||
mod unsupported_options {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn each_unsupported_option_errors_out_under_error_out() {
|
||||
let cases: Vec<fn(&mut GenerateMeshRequestBuilder)> = vec![
|
||||
|b| b.polygon_type = Some(CommonPolygonType::Quad),
|
||||
|b| b.face_count = Some(100_000),
|
||||
|b| b.enable_texture = Some(false),
|
||||
|b| b.input_mesh = Some(MeshRef::Url("https://example.com/model.glb".to_string())),
|
||||
|b| b.back_image = Some(ImageRef::Url(BACK_URL.to_string())),
|
||||
|b| b.left_image = Some(ImageRef::Url(BACK_URL.to_string())),
|
||||
|b| b.right_image = Some(ImageRef::Url(BACK_URL.to_string())),
|
||||
];
|
||||
for (index, set) in cases.into_iter().enumerate() {
|
||||
let mut builder = GenerateMeshRequestBuilder {
|
||||
request_mismatch_mitigation_strategy: RequestMismatchMitigationStrategy::ErrorOut,
|
||||
..image_builder()
|
||||
};
|
||||
set(&mut builder);
|
||||
assert!(build_fal_hunyuan_3d_3p1_rapid_state(builder).is_err(), "for case {index}");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unsupported_options_are_dropped_under_lenient_strategies() {
|
||||
let builder = GenerateMeshRequestBuilder {
|
||||
polygon_type: Some(CommonPolygonType::Quad),
|
||||
face_count: Some(100_000),
|
||||
back_image: Some(ImageRef::Url(BACK_URL.to_string())),
|
||||
..image_builder()
|
||||
};
|
||||
assert!(matches!(
|
||||
build_fal_hunyuan_3d_3p1_rapid_state(builder).expect("build"),
|
||||
FalHunyuan3d3p1RapidState::Image(_)
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn prompt_with_image_errors_out_under_error_out() {
|
||||
let builder = GenerateMeshRequestBuilder {
|
||||
prompt: Some("a red ceramic teapot".to_string()),
|
||||
request_mismatch_mitigation_strategy: RequestMismatchMitigationStrategy::ErrorOut,
|
||||
..image_builder()
|
||||
};
|
||||
assert!(build_fal_hunyuan_3d_3p1_rapid_state(builder).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn prompt_with_image_is_dropped_under_lenient_strategies() {
|
||||
let builder = GenerateMeshRequestBuilder {
|
||||
prompt: Some("a red ceramic teapot".to_string()),
|
||||
..image_builder()
|
||||
};
|
||||
assert!(matches!(
|
||||
build_fal_hunyuan_3d_3p1_rapid_state(builder).expect("build"),
|
||||
FalHunyuan3d3p1RapidState::Image(_)
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
// ── Helpers ──
|
||||
|
||||
fn base_builder() -> GenerateMeshRequestBuilder {
|
||||
GenerateMeshRequestBuilder {
|
||||
model: RouterMeshModel::Hunyuan3d3p1Rapid,
|
||||
provider: RouterProvider::Fal,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
fn image_builder() -> GenerateMeshRequestBuilder {
|
||||
GenerateMeshRequestBuilder {
|
||||
reference_images: Some(ImageListRef::Urls(vec![FRONT_URL.to_string()])),
|
||||
..base_builder()
|
||||
}
|
||||
}
|
||||
|
||||
fn text_builder() -> GenerateMeshRequestBuilder {
|
||||
GenerateMeshRequestBuilder {
|
||||
prompt: Some("a red ceramic teapot".to_string()),
|
||||
..base_builder()
|
||||
}
|
||||
}
|
||||
|
||||
fn expect_image(state: FalHunyuan3d3p1RapidState) -> FalHunyuan3d3p1RapidImageRequestState {
|
||||
match state {
|
||||
FalHunyuan3d3p1RapidState::Image(image) => image,
|
||||
FalHunyuan3d3p1RapidState::Text(text) => panic!("expected image mode, got text: {text:?}"),
|
||||
}
|
||||
}
|
||||
|
||||
fn expect_text(state: FalHunyuan3d3p1RapidState) -> FalHunyuan3d3p1RapidTextRequestState {
|
||||
match state {
|
||||
FalHunyuan3d3p1RapidState::Text(text) => text,
|
||||
FalHunyuan3d3p1RapidState::Image(image) => panic!("expected text mode, got image: {image:?}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user