Compare commits

..
Author SHA1 Message Date
Brandon Thomas 41d209d46f fix models 2026-07-07 20:28:35 -04:00
471 changed files with 5208 additions and 25530 deletions
@@ -1,223 +0,0 @@
{
"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"
}
@@ -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 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"
}
@@ -0,0 +1,12 @@
{
"db_name": "MySQL",
"query": "\nINSERT INTO media_files\nSET\n token = ?,\n\n media_class = ?,\n media_type = ?,\n\n origin_category = ?,\n origin_product_category = ?,\n\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"
}
@@ -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_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_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 ",
"describe": {
"columns": [
{
@@ -55,7 +55,7 @@
},
{
"ordinal": 5,
"name": "maybe_mime_type",
"name": "maybe_media_subtype: enums::by_table::media_files::media_file_subtype::MediaFileSubtype",
"type_info": {
"type": "VarString",
"flags": "BINARY",
@@ -65,6 +65,16 @@
},
{
"ordinal": 6,
"name": "maybe_mime_type",
"type_info": {
"type": "VarString",
"flags": "BINARY",
"char_set": 224,
"max_size": 128
}
},
{
"ordinal": 7,
"name": "maybe_creator_user_token: tokens::tokens::users::UserToken",
"type_info": {
"type": "VarString",
@@ -74,7 +84,7 @@
}
},
{
"ordinal": 7,
"ordinal": 8,
"name": "maybe_creator_username",
"type_info": {
"type": "VarString",
@@ -84,7 +94,7 @@
}
},
{
"ordinal": 8,
"ordinal": 9,
"name": "maybe_creator_display_name",
"type_info": {
"type": "VarString",
@@ -94,7 +104,7 @@
}
},
{
"ordinal": 9,
"ordinal": 10,
"name": "maybe_creator_gravatar_hash",
"type_info": {
"type": "String",
@@ -104,7 +114,7 @@
}
},
{
"ordinal": 10,
"ordinal": 11,
"name": "maybe_creator_anonymous_visitor_token: tokens::tokens::anonymous_visitor_tracking::AnonymousVisitorTrackingToken",
"type_info": {
"type": "VarString",
@@ -114,7 +124,7 @@
}
},
{
"ordinal": 11,
"ordinal": 12,
"name": "maybe_batch_token: tokens::tokens::batch_generations::BatchGenerationToken",
"type_info": {
"type": "VarString",
@@ -124,7 +134,7 @@
}
},
{
"ordinal": 12,
"ordinal": 13,
"name": "maybe_style_transfer_source_media_file_token: tokens::tokens::media_files::MediaFileToken",
"type_info": {
"type": "VarString",
@@ -134,7 +144,7 @@
}
},
{
"ordinal": 13,
"ordinal": 14,
"name": "maybe_scene_source_media_file_token: tokens::tokens::media_files::MediaFileToken",
"type_info": {
"type": "VarString",
@@ -144,7 +154,7 @@
}
},
{
"ordinal": 14,
"ordinal": 15,
"name": "maybe_title",
"type_info": {
"type": "VarString",
@@ -154,7 +164,7 @@
}
},
{
"ordinal": 15,
"ordinal": 16,
"name": "maybe_text_transcript",
"type_info": {
"type": "Blob",
@@ -164,7 +174,7 @@
}
},
{
"ordinal": 16,
"ordinal": 17,
"name": "maybe_origin_filename",
"type_info": {
"type": "VarString",
@@ -174,7 +184,7 @@
}
},
{
"ordinal": 17,
"ordinal": 18,
"name": "maybe_duration_millis",
"type_info": {
"type": "Long",
@@ -184,7 +194,7 @@
}
},
{
"ordinal": 18,
"ordinal": 19,
"name": "maybe_prompt_token: tokens::tokens::prompts::PromptToken",
"type_info": {
"type": "VarString",
@@ -194,7 +204,7 @@
}
},
{
"ordinal": 19,
"ordinal": 20,
"name": "maybe_other_prompt_args",
"type_info": {
"type": "Blob",
@@ -204,7 +214,7 @@
}
},
{
"ordinal": 20,
"ordinal": 21,
"name": "maybe_file_cover_image_public_bucket_hash",
"type_info": {
"type": "VarString",
@@ -214,7 +224,7 @@
}
},
{
"ordinal": 21,
"ordinal": 22,
"name": "maybe_file_cover_image_public_bucket_prefix",
"type_info": {
"type": "VarString",
@@ -224,7 +234,7 @@
}
},
{
"ordinal": 22,
"ordinal": 23,
"name": "maybe_file_cover_image_public_bucket_extension",
"type_info": {
"type": "VarString",
@@ -234,7 +244,7 @@
}
},
{
"ordinal": 23,
"ordinal": 24,
"name": "creator_set_visibility: enums::common::visibility::Visibility",
"type_info": {
"type": "String",
@@ -244,7 +254,7 @@
}
},
{
"ordinal": 24,
"ordinal": 25,
"name": "is_user_upload",
"type_info": {
"type": "Tiny",
@@ -254,7 +264,7 @@
}
},
{
"ordinal": 25,
"ordinal": 26,
"name": "is_intermediate_system_file",
"type_info": {
"type": "Tiny",
@@ -264,7 +274,7 @@
}
},
{
"ordinal": 26,
"ordinal": 27,
"name": "maybe_model_weights_token: tokens::tokens::model_weights::ModelWeightToken",
"type_info": {
"type": "VarString",
@@ -274,7 +284,7 @@
}
},
{
"ordinal": 27,
"ordinal": 28,
"name": "maybe_model_weights_title",
"type_info": {
"type": "VarString",
@@ -284,7 +294,7 @@
}
},
{
"ordinal": 28,
"ordinal": 29,
"name": "maybe_model_weights_type: enums::by_table::model_weights::weights_types::WeightsType",
"type_info": {
"type": "VarString",
@@ -294,7 +304,7 @@
}
},
{
"ordinal": 29,
"ordinal": 30,
"name": "maybe_model_weights_category: enums::by_table::model_weights::weights_category::WeightsCategory",
"type_info": {
"type": "VarString",
@@ -304,7 +314,7 @@
}
},
{
"ordinal": 30,
"ordinal": 31,
"name": "maybe_model_cover_image_public_bucket_hash",
"type_info": {
"type": "VarString",
@@ -314,7 +324,7 @@
}
},
{
"ordinal": 31,
"ordinal": 32,
"name": "maybe_model_cover_image_public_bucket_prefix",
"type_info": {
"type": "VarString",
@@ -324,7 +334,7 @@
}
},
{
"ordinal": 32,
"ordinal": 33,
"name": "maybe_model_cover_image_public_bucket_extension",
"type_info": {
"type": "VarString",
@@ -334,7 +344,7 @@
}
},
{
"ordinal": 33,
"ordinal": 34,
"name": "maybe_model_weight_creator_user_token: tokens::tokens::users::UserToken",
"type_info": {
"type": "VarString",
@@ -344,7 +354,7 @@
}
},
{
"ordinal": 34,
"ordinal": 35,
"name": "maybe_model_weight_creator_username",
"type_info": {
"type": "VarString",
@@ -354,7 +364,7 @@
}
},
{
"ordinal": 35,
"ordinal": 36,
"name": "maybe_model_weight_creator_display_name",
"type_info": {
"type": "VarString",
@@ -364,7 +374,7 @@
}
},
{
"ordinal": 36,
"ordinal": 37,
"name": "maybe_model_weight_creator_gravatar_hash",
"type_info": {
"type": "String",
@@ -374,7 +384,7 @@
}
},
{
"ordinal": 37,
"ordinal": 38,
"name": "extra_file_modification_info",
"type_info": {
"type": "Blob",
@@ -384,7 +394,7 @@
}
},
{
"ordinal": 38,
"ordinal": 39,
"name": "public_bucket_directory_hash",
"type_info": {
"type": "VarString",
@@ -394,7 +404,7 @@
}
},
{
"ordinal": 39,
"ordinal": 40,
"name": "maybe_public_bucket_prefix",
"type_info": {
"type": "VarString",
@@ -404,7 +414,7 @@
}
},
{
"ordinal": 40,
"ordinal": 41,
"name": "maybe_public_bucket_extension",
"type_info": {
"type": "VarString",
@@ -414,7 +424,7 @@
}
},
{
"ordinal": 41,
"ordinal": 42,
"name": "maybe_ratings_positive_count",
"type_info": {
"type": "Long",
@@ -424,7 +434,7 @@
}
},
{
"ordinal": 42,
"ordinal": 43,
"name": "maybe_ratings_negative_count",
"type_info": {
"type": "Long",
@@ -434,7 +444,7 @@
}
},
{
"ordinal": 43,
"ordinal": 44,
"name": "maybe_bookmark_count",
"type_info": {
"type": "Long",
@@ -444,7 +454,7 @@
}
},
{
"ordinal": 44,
"ordinal": 45,
"name": "is_featured",
"type_info": {
"type": "LongLong",
@@ -454,7 +464,7 @@
}
},
{
"ordinal": 45,
"ordinal": 46,
"name": "created_at",
"type_info": {
"type": "Timestamp",
@@ -464,7 +474,7 @@
}
},
{
"ordinal": 46,
"ordinal": 47,
"name": "updated_at",
"type_info": {
"type": "Timestamp",
@@ -501,6 +511,7 @@
true,
true,
true,
true,
false,
false,
false,
@@ -527,5 +538,5 @@
false
]
},
"hash": "784ca68c6cb61d481b605bfe6ee3248a5b44499038c71b57fc9ea6c4722d170f"
"hash": "57fa22e6cd4e82b801605c96eac21babd78df3df361b3be566993b78a46efef2"
}
@@ -1,223 +0,0 @@
{
"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"
}
@@ -0,0 +1,12 @@
{
"db_name": "MySQL",
"query": "\nINSERT INTO media_files\nSET\n token = ?,\n\n media_class = ?,\n media_type = ?,\n\n origin_category = ?,\n origin_product_category = ?,\n\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"
}
@@ -1,58 +0,0 @@
{
"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"
}
@@ -1,223 +0,0 @@
{
"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"
}
@@ -1,12 +0,0 @@
{
"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"
}
@@ -1,12 +0,0 @@
{
"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"
}
@@ -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 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"
}
@@ -0,0 +1,12 @@
{
"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"
}
@@ -1,223 +0,0 @@
{
"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"
}
@@ -1,12 +0,0 @@
{
"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"
}
@@ -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_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_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 ",
"describe": {
"columns": [
{
@@ -55,7 +55,7 @@
},
{
"ordinal": 5,
"name": "maybe_mime_type",
"name": "maybe_media_subtype: enums::by_table::media_files::media_file_subtype::MediaFileSubtype",
"type_info": {
"type": "VarString",
"flags": "BINARY",
@@ -65,6 +65,16 @@
},
{
"ordinal": 6,
"name": "maybe_mime_type",
"type_info": {
"type": "VarString",
"flags": "BINARY",
"char_set": 224,
"max_size": 128
}
},
{
"ordinal": 7,
"name": "maybe_creator_user_token: tokens::tokens::users::UserToken",
"type_info": {
"type": "VarString",
@@ -74,7 +84,7 @@
}
},
{
"ordinal": 7,
"ordinal": 8,
"name": "maybe_creator_username",
"type_info": {
"type": "VarString",
@@ -84,7 +94,7 @@
}
},
{
"ordinal": 8,
"ordinal": 9,
"name": "maybe_creator_display_name",
"type_info": {
"type": "VarString",
@@ -94,7 +104,7 @@
}
},
{
"ordinal": 9,
"ordinal": 10,
"name": "maybe_creator_gravatar_hash",
"type_info": {
"type": "String",
@@ -104,7 +114,7 @@
}
},
{
"ordinal": 10,
"ordinal": 11,
"name": "maybe_creator_anonymous_visitor_token: tokens::tokens::anonymous_visitor_tracking::AnonymousVisitorTrackingToken",
"type_info": {
"type": "VarString",
@@ -114,7 +124,7 @@
}
},
{
"ordinal": 11,
"ordinal": 12,
"name": "maybe_batch_token: tokens::tokens::batch_generations::BatchGenerationToken",
"type_info": {
"type": "VarString",
@@ -124,7 +134,7 @@
}
},
{
"ordinal": 12,
"ordinal": 13,
"name": "maybe_style_transfer_source_media_file_token: tokens::tokens::media_files::MediaFileToken",
"type_info": {
"type": "VarString",
@@ -134,7 +144,7 @@
}
},
{
"ordinal": 13,
"ordinal": 14,
"name": "maybe_scene_source_media_file_token: tokens::tokens::media_files::MediaFileToken",
"type_info": {
"type": "VarString",
@@ -144,7 +154,7 @@
}
},
{
"ordinal": 14,
"ordinal": 15,
"name": "maybe_title",
"type_info": {
"type": "VarString",
@@ -154,7 +164,7 @@
}
},
{
"ordinal": 15,
"ordinal": 16,
"name": "maybe_text_transcript",
"type_info": {
"type": "Blob",
@@ -164,7 +174,7 @@
}
},
{
"ordinal": 16,
"ordinal": 17,
"name": "maybe_origin_filename",
"type_info": {
"type": "VarString",
@@ -174,7 +184,7 @@
}
},
{
"ordinal": 17,
"ordinal": 18,
"name": "maybe_duration_millis",
"type_info": {
"type": "Long",
@@ -184,7 +194,7 @@
}
},
{
"ordinal": 18,
"ordinal": 19,
"name": "maybe_prompt_token: tokens::tokens::prompts::PromptToken",
"type_info": {
"type": "VarString",
@@ -194,7 +204,7 @@
}
},
{
"ordinal": 19,
"ordinal": 20,
"name": "maybe_other_prompt_args",
"type_info": {
"type": "Blob",
@@ -204,7 +214,7 @@
}
},
{
"ordinal": 20,
"ordinal": 21,
"name": "maybe_file_cover_image_public_bucket_hash",
"type_info": {
"type": "VarString",
@@ -214,7 +224,7 @@
}
},
{
"ordinal": 21,
"ordinal": 22,
"name": "maybe_file_cover_image_public_bucket_prefix",
"type_info": {
"type": "VarString",
@@ -224,7 +234,7 @@
}
},
{
"ordinal": 22,
"ordinal": 23,
"name": "maybe_file_cover_image_public_bucket_extension",
"type_info": {
"type": "VarString",
@@ -234,7 +244,7 @@
}
},
{
"ordinal": 23,
"ordinal": 24,
"name": "creator_set_visibility: enums::common::visibility::Visibility",
"type_info": {
"type": "String",
@@ -244,7 +254,7 @@
}
},
{
"ordinal": 24,
"ordinal": 25,
"name": "is_user_upload",
"type_info": {
"type": "Tiny",
@@ -254,7 +264,7 @@
}
},
{
"ordinal": 25,
"ordinal": 26,
"name": "is_intermediate_system_file",
"type_info": {
"type": "Tiny",
@@ -264,7 +274,7 @@
}
},
{
"ordinal": 26,
"ordinal": 27,
"name": "maybe_model_weights_token: tokens::tokens::model_weights::ModelWeightToken",
"type_info": {
"type": "VarString",
@@ -274,7 +284,7 @@
}
},
{
"ordinal": 27,
"ordinal": 28,
"name": "maybe_model_weights_title",
"type_info": {
"type": "VarString",
@@ -284,7 +294,7 @@
}
},
{
"ordinal": 28,
"ordinal": 29,
"name": "maybe_model_weights_type: enums::by_table::model_weights::weights_types::WeightsType",
"type_info": {
"type": "VarString",
@@ -294,7 +304,7 @@
}
},
{
"ordinal": 29,
"ordinal": 30,
"name": "maybe_model_weights_category: enums::by_table::model_weights::weights_category::WeightsCategory",
"type_info": {
"type": "VarString",
@@ -304,7 +314,7 @@
}
},
{
"ordinal": 30,
"ordinal": 31,
"name": "maybe_model_cover_image_public_bucket_hash",
"type_info": {
"type": "VarString",
@@ -314,7 +324,7 @@
}
},
{
"ordinal": 31,
"ordinal": 32,
"name": "maybe_model_cover_image_public_bucket_prefix",
"type_info": {
"type": "VarString",
@@ -324,7 +334,7 @@
}
},
{
"ordinal": 32,
"ordinal": 33,
"name": "maybe_model_cover_image_public_bucket_extension",
"type_info": {
"type": "VarString",
@@ -334,7 +344,7 @@
}
},
{
"ordinal": 33,
"ordinal": 34,
"name": "maybe_model_weight_creator_user_token: tokens::tokens::users::UserToken",
"type_info": {
"type": "VarString",
@@ -344,7 +354,7 @@
}
},
{
"ordinal": 34,
"ordinal": 35,
"name": "maybe_model_weight_creator_username",
"type_info": {
"type": "VarString",
@@ -354,7 +364,7 @@
}
},
{
"ordinal": 35,
"ordinal": 36,
"name": "maybe_model_weight_creator_display_name",
"type_info": {
"type": "VarString",
@@ -364,7 +374,7 @@
}
},
{
"ordinal": 36,
"ordinal": 37,
"name": "maybe_model_weight_creator_gravatar_hash",
"type_info": {
"type": "String",
@@ -374,7 +384,7 @@
}
},
{
"ordinal": 37,
"ordinal": 38,
"name": "extra_file_modification_info",
"type_info": {
"type": "Blob",
@@ -384,7 +394,7 @@
}
},
{
"ordinal": 38,
"ordinal": 39,
"name": "public_bucket_directory_hash",
"type_info": {
"type": "VarString",
@@ -394,7 +404,7 @@
}
},
{
"ordinal": 39,
"ordinal": 40,
"name": "maybe_public_bucket_prefix",
"type_info": {
"type": "VarString",
@@ -404,7 +414,7 @@
}
},
{
"ordinal": 40,
"ordinal": 41,
"name": "maybe_public_bucket_extension",
"type_info": {
"type": "VarString",
@@ -414,7 +424,7 @@
}
},
{
"ordinal": 41,
"ordinal": 42,
"name": "maybe_ratings_positive_count",
"type_info": {
"type": "Long",
@@ -424,7 +434,7 @@
}
},
{
"ordinal": 42,
"ordinal": 43,
"name": "maybe_ratings_negative_count",
"type_info": {
"type": "Long",
@@ -434,7 +444,7 @@
}
},
{
"ordinal": 43,
"ordinal": 44,
"name": "maybe_bookmark_count",
"type_info": {
"type": "Long",
@@ -444,7 +454,7 @@
}
},
{
"ordinal": 44,
"ordinal": 45,
"name": "is_featured",
"type_info": {
"type": "LongLong",
@@ -454,7 +464,7 @@
}
},
{
"ordinal": 45,
"ordinal": 46,
"name": "created_at",
"type_info": {
"type": "Timestamp",
@@ -464,7 +474,7 @@
}
},
{
"ordinal": 46,
"ordinal": 47,
"name": "updated_at",
"type_info": {
"type": "Timestamp",
@@ -501,6 +511,7 @@
true,
true,
true,
true,
false,
false,
false,
@@ -527,5 +538,5 @@
false
]
},
"hash": "ad217c26087b2ebed6f2c9f79b16ead63727107dfce3366f3c1d12bc9d565c2c"
"hash": "f6c37066ab3e508050e81cf3bea38cc708b05c5c59ba1c322dcfb775e4b9e7a6"
}
-17
View File
@@ -77,20 +77,3 @@ 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.
@@ -1,7 +0,0 @@
-- noinspection SqlDialectInspectionForFile
-- noinspection SqlNoDataSourceInspectionForFile
-- noinspection SqlResolveForFile
ALTER TABLE media_files
ADD COLUMN has_watermark BOOLEAN NOT NULL DEFAULT FALSE
AFTER checksum_sha2;
@@ -1,10 +0,0 @@
-- 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;
@@ -1,10 +0,0 @@
-- 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);
@@ -1,7 +0,0 @@
-- noinspection SqlDialectInspectionForFile
-- noinspection SqlNoDataSourceInspectionForFile
-- noinspection SqlResolveForFile
ALTER TABLE media_files
DROP INDEX index_is_in_aws,
DROP COLUMN is_in_aws;
@@ -1,9 +0,0 @@
-- 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;
@@ -1,6 +0,0 @@
-- noinspection SqlDialectInspectionForFile
-- noinspection SqlNoDataSourceInspectionForFile
-- noinspection SqlResolveForFile
ALTER TABLE media_files
DROP COLUMN is_not_in_gcp;
@@ -1,10 +0,0 @@
-- 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);
@@ -1,7 +0,0 @@
-- noinspection SqlDialectInspectionForFile
-- noinspection SqlNoDataSourceInspectionForFile
-- noinspection SqlResolveForFile
ALTER TABLE media_files
DROP INDEX index_nsfw_status,
DROP COLUMN nsfw_status;
@@ -1,7 +0,0 @@
-- noinspection SqlDialectInspectionForFile
-- noinspection SqlNoDataSourceInspectionForFile
-- noinspection SqlResolveForFile
ALTER TABLE media_files
ADD COLUMN is_batch_generated BOOLEAN NOT NULL DEFAULT FALSE
AFTER maybe_origin_filename;
@@ -1,6 +0,0 @@
-- noinspection SqlDialectInspectionForFile
-- noinspection SqlNoDataSourceInspectionForFile
-- noinspection SqlResolveForFile
ALTER TABLE media_files
DROP COLUMN is_batch_generated;
@@ -1,7 +0,0 @@
-- noinspection SqlDialectInspectionForFile
-- noinspection SqlNoDataSourceInspectionForFile
-- noinspection SqlResolveForFile
ALTER TABLE media_files
ADD COLUMN maybe_media_subtype VARCHAR(32) DEFAULT NULL
AFTER media_class;
@@ -1,6 +0,0 @@
-- noinspection SqlDialectInspectionForFile
-- noinspection SqlNoDataSourceInspectionForFile
-- noinspection SqlResolveForFile
ALTER TABLE media_files
DROP COLUMN maybe_media_subtype;
@@ -1,7 +0,0 @@
-- noinspection SqlDialectInspectionForFile
-- noinspection SqlNoDataSourceInspectionForFile
-- noinspection SqlResolveForFile
ALTER TABLE media_files
DROP INDEX index_maybe_project_type,
DROP COLUMN maybe_project_type;
@@ -1,16 +0,0 @@
-- 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,6 +99,12 @@ 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,
@@ -132,6 +138,9 @@ 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:
@@ -157,19 +166,20 @@ 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 image types.
-- * 'image' for a variety of video types.
-- * 'video' for a variety of video 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)
-- * 'dimensional' for a variety of 3d types
media_class VARCHAR(16) NOT NULL DEFAULT "unknown",
-- 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,
-- 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,
-- The file's mime type.
maybe_mime_type VARCHAR(32) DEFAULT NULL,
@@ -217,6 +227,10 @@ 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.
@@ -245,6 +259,13 @@ 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
@@ -330,12 +351,13 @@ 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),
@@ -0,0 +1,267 @@
{
"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"
}
@@ -0,0 +1,267 @@
{
"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"
}
@@ -0,0 +1,267 @@
{
"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"
}
@@ -0,0 +1,267 @@
{
"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"
}
@@ -0,0 +1,267 @@
{
"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"
}
@@ -0,0 +1,267 @@
{
"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,7 +2,6 @@ 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;
@@ -1,22 +0,0 @@
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,
}
@@ -1,78 +0,0 @@
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,4 +1,2 @@
pub mod delete_media_file;
pub mod list_batch_generated_media_files;
pub mod list_session_project_media_files;
pub mod project;
pub mod list_batch_generated_media_files;
@@ -1,8 +0,0 @@
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;
@@ -1,16 +0,0 @@
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,
}
@@ -1,16 +0,0 @@
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,
}
@@ -1,16 +0,0 @@
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,
}
@@ -1,16 +0,0 @@
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,
}
@@ -1,21 +0,0 @@
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,
}
@@ -1,21 +0,0 @@
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,
}
@@ -1,21 +0,0 @@
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,
}
@@ -1,21 +0,0 @@
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,
}
@@ -3,7 +3,6 @@ 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;
@@ -39,12 +38,6 @@ 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>,
@@ -56,18 +49,4 @@ 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>,
}
@@ -2,7 +2,7 @@ use serde_derive::{Deserialize, Serialize};
use utoipa::ToSchema;
/// Response body for the omni-gen video cost estimate endpoint.
#[derive(Debug, Serialize, Deserialize, ToSchema)]
#[derive(Serialize, Deserialize, ToSchema)]
pub struct OmniGenVideoCostResponse {
pub success: bool,
@@ -71,11 +71,6 @@ 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>>,
@@ -92,18 +87,6 @@ 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>,
}
@@ -120,14 +103,10 @@ 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,
}
}
@@ -76,6 +76,11 @@ 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>,
@@ -1,9 +0,0 @@
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,7 +2,6 @@ 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,37 +18,6 @@ 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)]
@@ -63,13 +32,6 @@ 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]
@@ -81,13 +43,6 @@ 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,10 +24,6 @@ pub enum RouterSplatModel {
#[serde(rename = "marble_1p1_plus")]
Marble1p1Plus,
/// TripoSplat: single-image to 3D Gaussian splat (via Fal).
#[serde(rename = "triposplat")]
TripoSplat,
}
#[cfg(test)]
@@ -44,7 +40,6 @@ 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]
@@ -58,7 +53,6 @@ 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();
@@ -1,10 +1,8 @@
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;
@@ -14,24 +12,10 @@ 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
@@ -65,9 +49,6 @@ 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>,
@@ -80,16 +61,6 @@ 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,
@@ -110,14 +81,10 @@ 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,
}
}
@@ -132,25 +99,11 @@ 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(),
}
}
@@ -11,20 +11,6 @@ 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,
};
@@ -37,40 +23,6 @@ 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 {
@@ -78,101 +30,41 @@ 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(_)
| 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,
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,
}
}
/// Return a cost estimate to fulfill the request.
pub fn estimate_cost(&self) -> Result<MeshGenerationCostEstimate, ArtcraftRouterError> {
match self {
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()),
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()),
}
}
@@ -180,35 +72,42 @@ impl MeshGenerationRequest {
/// If successful, returns the job IDs.
pub async fn send_request(&self, client: &RouterClient) -> Result<GenerateMeshResponse, ArtcraftRouterError> {
match self {
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,
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
},
}
}
}
@@ -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_mesh_ref,
resolve_image_list_ref, resolve_image_ref,
};
/// Build an `OmniGenMeshCostAndGenerateRequest` from the builder. The
@@ -20,7 +20,6 @@ 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 {
@@ -32,14 +31,10 @@ 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,
})
}
@@ -1,14 +0,0 @@
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)))
}
@@ -1,78 +0,0 @@
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")
}
}
@@ -1,3 +0,0 @@
pub mod build;
pub mod cost;
pub mod request;
@@ -1,17 +0,0 @@
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
}
}
@@ -1,14 +0,0 @@
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)))
}
@@ -1,183 +0,0 @@
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")
}
}
@@ -1,3 +0,0 @@
pub mod build;
pub mod cost;
pub mod request;
@@ -1,64 +0,0 @@
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
}
}
@@ -1,14 +0,0 @@
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)))
}
@@ -1,124 +0,0 @@
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")
}
}
@@ -1,3 +0,0 @@
pub mod build;
pub mod cost;
pub mod request;
@@ -1,64 +0,0 @@
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
}
}
@@ -1,14 +0,0 @@
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)))
}
@@ -1,78 +0,0 @@
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")
}
}
@@ -1,3 +0,0 @@
pub mod build;
pub mod cost;
pub mod request;
@@ -1,17 +0,0 @@
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
}
}
@@ -1,14 +0,0 @@
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)))
}
@@ -1,100 +0,0 @@
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")
}
}
@@ -1,3 +0,0 @@
pub mod build;
pub mod cost;
pub mod request;
@@ -1,64 +0,0 @@
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
}
}
@@ -5,10 +5,3 @@ 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;
@@ -6,7 +6,6 @@ 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;
@@ -33,15 +32,3 @@ 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))
}
}
}
@@ -1,14 +0,0 @@
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)))
}
@@ -1,97 +0,0 @@
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")
}
}
@@ -1,3 +0,0 @@
pub mod build;
pub mod cost;
pub mod request;
@@ -1,64 +0,0 @@
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
}
}
@@ -1,14 +0,0 @@
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)))
}
@@ -1,338 +0,0 @@
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")
}
}
@@ -1,3 +0,0 @@
pub mod build;
pub mod cost;
pub mod request;
@@ -1,64 +0,0 @@
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
}
}
@@ -1,152 +0,0 @@
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()
}
}
}
@@ -1,60 +0,0 @@
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")
}
}
@@ -1,3 +0,0 @@
pub mod build;
pub mod cost;
pub mod request;
@@ -1,52 +0,0 @@
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");
}
}
@@ -1,411 +0,0 @@
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:?}"),
}
}
}
@@ -1,159 +0,0 @@
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")
}
}
@@ -1,3 +0,0 @@
pub mod build;
pub mod cost;
pub mod request;
@@ -1,92 +0,0 @@
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
}
}
@@ -1,323 +0,0 @@
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