enrich prompts (#1197)

This commit is contained in:
Brandon Thomas
2026-03-23 21:50:18 -04:00
committed by GitHub
parent 0db5646deb
commit 95eeafd6f0
80 changed files with 2399 additions and 1022 deletions
@@ -1,12 +0,0 @@
{
"db_name": "MySQL",
"query": "\nINSERT INTO prompts\nSET\n token = ?,\n prompt_type = ?,\n\n maybe_creator_user_token = ?,\n \n maybe_model_type = ?,\n maybe_generation_provider = ?,\n\n maybe_positive_prompt = ?,\n maybe_negative_prompt = ?,\n \n maybe_other_args = ?,\n\n creator_ip_address = ?\n ",
"describe": {
"columns": [],
"parameters": {
"Right": 9
},
"nullable": []
},
"hash": "08f52f15acf6afc7091a525e3dc88f706c71c3579e5e81aa9903807d85f469ef"
}
@@ -0,0 +1,12 @@
{
"db_name": "MySQL",
"query": "\nINSERT INTO prompts\nSET\n token = ?,\n prompt_type = ?,\n\n maybe_creator_user_token = ?,\n \n maybe_model_type = ?,\n maybe_generation_provider = ?,\n\n maybe_positive_prompt = ?,\n maybe_negative_prompt = ?,\n\n maybe_generation_mode = ?,\n maybe_aspect_ratio = ?,\n maybe_resolution = ?,\n maybe_batch_count = ?,\n maybe_generate_audio = ?,\n \n maybe_other_args = ?,\n\n creator_ip_address = ?\n ",
"describe": {
"columns": [],
"parameters": {
"Right": 14
},
"nullable": []
},
"hash": "aa2f2e0b259e5512b16aa7c4db1d6e76b5313ed90ffeeed5d70ccaf2953b513f"
}
@@ -1,6 +1,6 @@
{
"db_name": "MySQL",
"query": "\nSELECT\n p.token as `token: tokens::tokens::prompts::PromptToken`,\n\n p.prompt_type as `prompt_type: enums::by_table::prompts::prompt_type::PromptType`,\n\n p.maybe_model_type as `maybe_model_type: enums::common::model_type::ModelType`,\n p.maybe_generation_provider as `maybe_generation_provider: enums::common::generation_provider::GenerationProvider`,\n\n p.maybe_creator_user_token as `maybe_creator_user_token: tokens::tokens::users::UserToken`,\n\n p.maybe_positive_prompt,\n p.maybe_negative_prompt,\n p.maybe_other_args,\n\n p.creator_ip_address,\n p.created_at\n\nFROM prompts as p\nWHERE\n p.token = ?\n ",
"query": "\nSELECT\n p.token as `token: tokens::tokens::prompts::PromptToken`,\n\n p.prompt_type as `prompt_type: enums::by_table::prompts::prompt_type::PromptType`,\n\n p.maybe_model_type as `maybe_model_type: enums::common::generation::common_model_type::CommonModelType`,\n p.maybe_generation_provider as `maybe_generation_provider: enums::common::generation_provider::GenerationProvider`,\n\n p.maybe_creator_user_token as `maybe_creator_user_token: tokens::tokens::users::UserToken`,\n\n p.maybe_positive_prompt,\n p.maybe_negative_prompt,\n\n p.maybe_generation_mode as `maybe_generation_mode: enums::common::generation::common_generation_mode::CommonGenerationMode`,\n p.maybe_aspect_ratio as `maybe_aspect_ratio: enums::common::generation::common_aspect_ratio::CommonAspectRatio`,\n p.maybe_resolution as `maybe_resolution: enums::common::generation::common_resolution::CommonResolution`,\n p.maybe_batch_count as `maybe_batch_count: u8`,\n p.maybe_generate_audio as `maybe_generate_audio: bool`,\n\n p.maybe_other_args,\n\n p.creator_ip_address,\n p.created_at\n\nFROM prompts as p\nWHERE\n p.token = ?\n ",
"describe": {
"columns": [
{
@@ -25,7 +25,7 @@
},
{
"ordinal": 2,
"name": "maybe_model_type: enums::common::model_type::ModelType",
"name": "maybe_model_type: enums::common::generation::common_model_type::CommonModelType",
"type_info": {
"type": "VarString",
"flags": "BINARY",
@@ -75,6 +75,56 @@
},
{
"ordinal": 7,
"name": "maybe_generation_mode: enums::common::generation::common_generation_mode::CommonGenerationMode",
"type_info": {
"type": "VarString",
"flags": "BINARY",
"char_set": 224,
"max_size": 96
}
},
{
"ordinal": 8,
"name": "maybe_aspect_ratio: enums::common::generation::common_aspect_ratio::CommonAspectRatio",
"type_info": {
"type": "VarString",
"flags": "BINARY",
"char_set": 224,
"max_size": 96
}
},
{
"ordinal": 9,
"name": "maybe_resolution: enums::common::generation::common_resolution::CommonResolution",
"type_info": {
"type": "VarString",
"flags": "BINARY",
"char_set": 224,
"max_size": 64
}
},
{
"ordinal": 10,
"name": "maybe_batch_count: u8",
"type_info": {
"type": "Tiny",
"flags": "UNSIGNED",
"char_set": 63,
"max_size": 3
}
},
{
"ordinal": 11,
"name": "maybe_generate_audio: bool",
"type_info": {
"type": "Tiny",
"flags": "",
"char_set": 63,
"max_size": 1
}
},
{
"ordinal": 12,
"name": "maybe_other_args",
"type_info": {
"type": "Blob",
@@ -84,7 +134,7 @@
}
},
{
"ordinal": 8,
"ordinal": 13,
"name": "creator_ip_address",
"type_info": {
"type": "VarString",
@@ -94,7 +144,7 @@
}
},
{
"ordinal": 9,
"ordinal": 14,
"name": "created_at",
"type_info": {
"type": "Timestamp",
@@ -116,9 +166,14 @@
true,
true,
true,
true,
true,
true,
true,
true,
false,
false
]
},
"hash": "b46b0d022790f4e10f6e47126df0fdc55172bf8cda77d4e0b2afac9e6ebd1630"
"hash": "e674d80b86990bf3266eb81dd9445adf7a85646596c76ac975921c42a23005ea"
}
Generated
+1
View File
@@ -3781,6 +3781,7 @@ name = "enums"
version = "0.0.1"
dependencies = [
"log",
"regex",
"serde",
"serde_derive",
"serde_json",
@@ -27,6 +27,8 @@ CREATE TABLE prompts (
-- What type of model was used to generate the result.
-- NB: We're conflating prompts with outputs, but in a
-- sense the model is inextricably part of the prompt.
--
-- Represented by the `CommonModelType` enum.
maybe_model_type VARCHAR(32) DEFAULT NULL,
-- What service provided the generation.
@@ -54,15 +56,19 @@ CREATE TABLE prompts (
-- ========== GENERATION PARAMETERS ==========
-- Represented by the enum `CommonGenerationMode`.
-- The generation mode (eg. keyframe, reference, inpaint, etc.)
maybe_generation_mode VARCHAR(24) DEFAULT NULL,
-- Represented by the enum `CommonAspectRatio`.
-- The aspect ratio for the prompt (eg. square, auto, wide_three_by_two, etc.)
maybe_aspect_ratio VARCHAR(24) DEFAULT NULL,
-- Represented by the enum `CommonResolution`.
-- The resolution for the prompt (eg. four_k, 720p, etc.)
maybe_resolution VARCHAR(16) DEFAULT NULL,
-- TODO: Let's change this to an integer before writing to it.
-- The duration (eg. 8s, 16s, 2m, etc.)
-- Not an integer because we want to potentially support other units
maybe_duration VARCHAR(8) DEFAULT NULL,
@@ -3,7 +3,7 @@ use utoipa::ToSchema;
use enums::common::generation::common_aspect_ratio::CommonAspectRatio;
use enums::common::generation::common_image_model::CommonImageModel;
use enums::common::generation::common_video_resolution::CommonVideoResolution;
use enums::common::generation::common_resolution::CommonResolution;
use enums::common::generation_provider::GenerationProvider;
pub const ESTIMATE_IMAGE_COST_PATH: &str = "/v1/generate/cost_estimate/image";
@@ -27,7 +27,7 @@ pub struct EstimateImageCostRequest {
pub aspect_ratio: Option<CommonAspectRatio>,
/// Optional resolution.
pub resolution: Option<CommonVideoResolution>,
pub resolution: Option<CommonResolution>,
/// Number of images to generate in parallel.
pub image_batch_count: Option<u16>,
@@ -3,7 +3,7 @@ use utoipa::ToSchema;
use enums::common::generation::common_aspect_ratio::CommonAspectRatio;
use enums::common::generation::common_video_model::CommonVideoModel;
use enums::common::generation::common_video_resolution::CommonVideoResolution;
use enums::common::generation::common_resolution::CommonResolution;
use enums::common::generation_provider::GenerationProvider;
pub const ESTIMATE_VIDEO_COST_PATH: &str = "/v1/generate/cost_estimate/video";
@@ -30,7 +30,7 @@ pub struct EstimateVideoCostRequest {
pub aspect_ratio: Option<CommonAspectRatio>,
/// Optional resolution.
pub resolution: Option<CommonVideoResolution>,
pub resolution: Option<CommonResolution>,
/// Duration in seconds.
pub duration_seconds: Option<u16>,
@@ -1,8 +1,11 @@
use enums::common::generation::common_aspect_ratio::CommonAspectRatio;
use enums::common::generation::common_generation_mode::CommonGenerationMode;
use enums::common::generation::common_model_type::CommonModelType;
use enums::common::generation::common_resolution::CommonResolution;
use enums::common::generation_provider::GenerationProvider;
use serde_derive::{Deserialize, Serialize};
use tokens::tokens::prompts::PromptToken;
use utoipa::ToSchema;
use enums::common::generation_provider::GenerationProvider;
use enums::common::model_type::ModelType;
pub const CREATE_PROMPT_PATH: &str = "/v1/prompts/create";
@@ -14,14 +17,29 @@ pub struct CreatePromptRequest {
/// OPTIONAL. The positive prompt.
pub positive_prompt: Option<String>,
/// OPTIONAL. The positive prompt.
/// OPTIONAL. The negative prompt.
pub negative_prompt: Option<String>,
/// OPTIONAL. The model type.
pub model_type: Option<ModelType>,
pub model_type: Option<CommonModelType>,
/// OPTIONAL. The service used.
pub generation_provider: Option<GenerationProvider>,
/// OPTIONAL. The generation mode (eg. keyframe, reference, inpaint, etc.)
pub maybe_generation_mode: Option<CommonGenerationMode>,
/// OPTIONAL. The aspect ratio (eg. square, auto, wide_three_by_two, etc.)
pub maybe_aspect_ratio: Option<CommonAspectRatio>,
/// OPTIONAL. The resolution (eg. one_k, two_k, four_k, etc.)
pub maybe_resolution: Option<CommonResolution>,
/// OPTIONAL. The number of outputs to generate (0-255).
pub maybe_batch_count: Option<u8>,
/// OPTIONAL. Whether to generate audio.
pub maybe_generate_audio: Option<bool>,
}
#[derive(Serialize, Deserialize, ToSchema)]
@@ -0,0 +1,142 @@
use crate::common::responses::media_links::MediaLinks;
use chrono::{DateTime, Utc};
use enums::by_table::prompt_context_items::prompt_context_semantic_type::PromptContextSemanticType;
use enums::by_table::prompts::prompt_type::PromptType;
use enums::common::generation::common_aspect_ratio::CommonAspectRatio;
use enums::common::generation::common_generation_mode::CommonGenerationMode;
use enums::common::generation::common_model_type::CommonModelType;
use enums::common::generation::common_resolution::CommonResolution;
use enums::common::generation_provider::GenerationProvider;
use enums::no_table::style_transfer::style_transfer_name::StyleTransferName;
use serde_derive::{Deserialize, Serialize};
use tokens::tokens::media_files::MediaFileToken;
use tokens::tokens::prompts::PromptToken;
use utoipa::ToSchema;
pub const GET_PROMPT_PATH: &str = "/v1/prompts/{token}";
#[derive(Deserialize, ToSchema)]
pub struct GetPromptPathInfo {
pub token: PromptToken,
}
#[derive(Serialize, ToSchema)]
pub struct GetPromptSuccessResponse {
pub success: bool,
pub prompt: PromptInfo,
}
#[derive(Serialize, ToSchema)]
pub struct GetPromptImageContextItem {
pub media_token: MediaFileToken,
pub semantic: PromptContextSemanticType,
pub media_links: MediaLinks,
}
#[derive(Serialize, ToSchema)]
pub struct PromptInfo {
pub token: PromptToken,
/// The type of prompt.
/// Note: Prompts may or may not be compatible across systems.
pub prompt_type: PromptType,
/// The type of model used
pub maybe_model_type: Option<CommonModelType>,
/// The service provider used
pub maybe_generation_provider: Option<GenerationProvider>,
/// Positive prompt (technically optional, but usually present)
pub maybe_positive_prompt: Option<String>,
/// Negative prompt (optional)
pub maybe_negative_prompt: Option<String>,
/// OPTIONAL. The generation mode (eg. keyframe, reference, inpaint, etc.)
pub maybe_generation_mode: Option<CommonGenerationMode>,
/// OPTIONAL. The aspect ratio (eg. square, auto, wide_three_by_two, etc.)
pub maybe_aspect_ratio: Option<CommonAspectRatio>,
/// OPTIONAL. The resolution (eg. one_k, two_k, four_k, etc.)
pub maybe_resolution: Option<CommonResolution>,
/// OPTIONAL. The number of outputs to generate (0-255).
pub maybe_batch_count: Option<u8>,
/// OPTIONAL. Whether to generate audio.
pub maybe_generate_audio: Option<bool>,
/// Context images (optional)
pub maybe_context_images: Option<Vec<GetPromptImageContextItem>>,
/// Scheduled / travel prompt (optional)
pub maybe_travel_prompt: Option<String>,
/// If a "style" was used, this is the name of it.
/// This might not be present for all types of inference
/// and typically only applies to video style transfer.
pub maybe_style_name: Option<StyleTransferName>,
/// How many milliseconds it took to run generation.
pub maybe_inference_duration_millis: Option<u64>,
/// If a "strength" was used.
/// Typically only for video style transfer.
pub maybe_strength: Option<f32>,
/// If a frame skip setting was used.
pub maybe_frame_skip: Option<u8>,
/// If a face detailer was used.
/// This might not be present for all types of inference
/// and typically only applies to video style transfer.
pub used_face_detailer: bool,
/// If an upscaling pass was used.
/// This might not be present for all types of inference
/// and typically only applies to video style transfer.
pub used_upscaler: bool,
/// If lipsync was enabled.
/// This might not be present for all types of inference
/// and typically only applies to video style transfer.
pub lipsync_enabled: bool,
/// If LCM was disabled.
/// Only staff can do this for now.
pub lcm_disabled: bool,
/// If the cinematic workflow was used.
/// Only staff can do this for now.
pub use_cinematic: bool,
/// If a global IP Adapter Image was used, this is the details.
/// NB: We can't easily query for this without another DB round trip,
/// so the frontend should query for it instead.
pub maybe_global_ipa_image_token: Option<MediaFileToken>,
/// Fields that only moderators should see.
pub maybe_moderator_fields: Option<PromptInfoModeratorFields>,
pub created_at: DateTime<Utc>,
}
#[derive(Serialize, ToSchema)]
pub struct PromptInfoModeratorFields {
/// How many milliseconds it took to run generation.
pub maybe_inference_duration_millis: Option<u64>,
/// Version of Yae's workflow
/// Used for logging and debugging by the art team.
pub main_ipa_workflow: Option<String>,
/// Version of Yae's face detailer workflow
/// Used for logging and debugging by the art team.
pub face_detailer_workflow: Option<String>,
/// Version of Yae's upscaler workflow
/// Used for logging and debugging by the art team.
pub upscaler_workflow: Option<String>,
}
@@ -1 +1,2 @@
pub mod create_prompt;
pub mod create_prompt;
pub mod get_prompt;
@@ -4,7 +4,7 @@ use artcraft_router::errors::artcraft_router_error::ArtcraftRouterError;
use artcraft_router::errors::provider_error::ProviderError;
use base64::DecodeError;
use enums::common::generation_provider::GenerationProvider;
use enums::common::model_type::ModelType;
use enums::common::generation::common_model_type::CommonModelType;
use errors::AnyhowError;
use grok_client::error::grok_error::GrokError;
use midjourney_client::error::midjourney_error::MidjourneyError;
@@ -29,7 +29,7 @@ pub enum GenerateError {
/// Wrong provider for the model
BadProviderForModel {
provider: GenerationProvider,
model: ModelType,
model: CommonModelType,
},
/// If the response didn't contain job tokens to track.
@@ -1,19 +1,19 @@
use enums::common::model_type::ModelType;
use enums::common::generation::common_model_type::CommonModelType;
use crate::core::commands::enqueue::image_edit::enqueue_edit_image_command::ImageEditModel;
pub fn image_edit_model_to_model_type(model: ImageEditModel) -> ModelType {
pub fn image_edit_model_to_model_type(model: ImageEditModel) -> CommonModelType {
match model {
ImageEditModel::FluxProKontextMax => ModelType::FluxProKontextMax,
ImageEditModel::Gemini25Flash => ModelType::NanoBanana,
ImageEditModel::NanoBanana => ModelType::NanoBanana,
ImageEditModel::NanoBanana2 => ModelType::NanoBanana2,
ImageEditModel::NanoBananaPro => ModelType::NanoBananaPro,
ImageEditModel::GptImage1 => ModelType::GptImage1,
ImageEditModel::GptImage1p5 => ModelType::GptImage1p5,
ImageEditModel::Seedream4 => ModelType::Seedream4,
ImageEditModel::Seedream4p5 => ModelType::Seedream4p5,
ImageEditModel::Seedream5Lite => ModelType::Seedream5Lite,
ImageEditModel::QwenEdit2511Angles => ModelType::QwenEdit2511Angles,
ImageEditModel::Flux2LoraAngles => ModelType::Flux2LoraAngles,
ImageEditModel::FluxProKontextMax => CommonModelType::FluxProKontextMax,
ImageEditModel::Gemini25Flash => CommonModelType::NanoBanana,
ImageEditModel::NanoBanana => CommonModelType::NanoBanana,
ImageEditModel::NanoBanana2 => CommonModelType::NanoBanana2,
ImageEditModel::NanoBananaPro => CommonModelType::NanoBananaPro,
ImageEditModel::GptImage1 => CommonModelType::GptImage1,
ImageEditModel::GptImage1p5 => CommonModelType::GptImage1p5,
ImageEditModel::Seedream4 => CommonModelType::Seedream4,
ImageEditModel::Seedream4p5 => CommonModelType::Seedream4p5,
ImageEditModel::Seedream5Lite => CommonModelType::Seedream5Lite,
ImageEditModel::QwenEdit2511Angles => CommonModelType::QwenEdit2511Angles,
ImageEditModel::Flux2LoraAngles => CommonModelType::Flux2LoraAngles,
}
}
@@ -1,11 +1,11 @@
use crate::core::commands::enqueue::image_inpaint::enqueue_image_inpaint_command::ImageInpaintModel;
use enums::common::model_type::ModelType;
use enums::common::generation::common_model_type::CommonModelType;
pub fn image_inpaint_model_to_model_type(model: ImageInpaintModel) -> ModelType {
pub fn image_inpaint_model_to_model_type(model: ImageInpaintModel) -> CommonModelType {
match model {
ImageInpaintModel::FluxDevJuggernaut => ModelType::FluxDevJuggernaut,
ImageInpaintModel::FluxPro1 => ModelType::FluxPro1,
ImageInpaintModel::FluxProKontextMax => ModelType::FluxProKontextMax,
ImageInpaintModel::Gemini25Flash => ModelType::NanoBanana,
ImageInpaintModel::FluxDevJuggernaut => CommonModelType::FluxDevJuggernaut,
ImageInpaintModel::FluxPro1 => CommonModelType::FluxPro1,
ImageInpaintModel::FluxProKontextMax => CommonModelType::FluxProKontextMax,
ImageInpaintModel::Gemini25Flash => CommonModelType::NanoBanana,
}
}
@@ -1,23 +1,23 @@
use crate::core::commands::enqueue::text_to_image::enqueue_text_to_image_command::TextToImageModel;
use enums::common::model_type::ModelType;
use enums::common::generation::common_model_type::CommonModelType;
pub fn text_to_image_model_to_model_type(model: TextToImageModel) -> ModelType {
pub fn text_to_image_model_to_model_type(model: TextToImageModel) -> CommonModelType {
match model {
TextToImageModel::Flux1Dev => ModelType::Flux1Dev,
TextToImageModel::Flux1Schnell => ModelType::Flux1Schnell,
TextToImageModel::FluxPro11 => ModelType::FluxPro11,
TextToImageModel::FluxPro11Ultra => ModelType::FluxPro11Ultra,
TextToImageModel::GrokImage => ModelType::GrokImage,
TextToImageModel::Recraft3 => ModelType::Recraft3,
TextToImageModel::GptImage1 => ModelType::GptImage1,
TextToImageModel::GptImage1p5 => ModelType::GptImage1p5,
TextToImageModel::Gemini25Flash => ModelType::NanoBanana,
TextToImageModel::NanoBanana => ModelType::NanoBanana,
TextToImageModel::NanoBanana2 => ModelType::NanoBanana2,
TextToImageModel::NanoBananaPro => ModelType::NanoBananaPro,
TextToImageModel::Seedream4 => ModelType::Seedream4,
TextToImageModel::Seedream4p5 => ModelType::Seedream4p5,
TextToImageModel::Seedream5Lite => ModelType::Seedream5Lite,
TextToImageModel::Midjourney => ModelType::Midjourney,
TextToImageModel::Flux1Dev => CommonModelType::Flux1Dev,
TextToImageModel::Flux1Schnell => CommonModelType::Flux1Schnell,
TextToImageModel::FluxPro11 => CommonModelType::FluxPro11,
TextToImageModel::FluxPro11Ultra => CommonModelType::FluxPro11Ultra,
TextToImageModel::GrokImage => CommonModelType::GrokImage,
TextToImageModel::Recraft3 => CommonModelType::Recraft3,
TextToImageModel::GptImage1 => CommonModelType::GptImage1,
TextToImageModel::GptImage1p5 => CommonModelType::GptImage1p5,
TextToImageModel::Gemini25Flash => CommonModelType::NanoBanana,
TextToImageModel::NanoBanana => CommonModelType::NanoBanana,
TextToImageModel::NanoBanana2 => CommonModelType::NanoBanana2,
TextToImageModel::NanoBananaPro => CommonModelType::NanoBananaPro,
TextToImageModel::Seedream4 => CommonModelType::Seedream4,
TextToImageModel::Seedream4p5 => CommonModelType::Seedream4p5,
TextToImageModel::Seedream5Lite => CommonModelType::Seedream5Lite,
TextToImageModel::Midjourney => CommonModelType::Midjourney,
}
}
@@ -14,7 +14,7 @@ use anyhow::anyhow;
use artcraft_api_defs::prompts::create_prompt::CreatePromptRequest;
use artcraft_api_defs::utils::media_links_to_thumbnail_template::media_links_to_thumbnail_template;
use enums::common::generation_provider::GenerationProvider;
use enums::common::model_type::ModelType;
use enums::common::generation::common_model_type::CommonModelType;
use enums::tauri::tasks::task_media_file_class::TaskMediaFileClass;
use errors::AnyhowResult;
use grok_client::recipes::prompt_websocket_image_with_retry::{prompt_websocket_image_with_retry, PromptWebsocketImageWithRetryArgs};
@@ -182,8 +182,13 @@ async fn upload_images_to_storyteller(
uuid_idempotency_token: generate_random_uuid(),
positive_prompt: Some(prompt),
negative_prompt: None,
model_type: Some(ModelType::GrokImage),
model_type: Some(CommonModelType::GrokImage),
generation_provider: Some(GenerationProvider::Grok),
maybe_generation_mode: None,
maybe_aspect_ratio: None,
maybe_resolution: None,
maybe_batch_count: None,
maybe_generate_audio: None,
};
let prompt_response = create_prompt(
@@ -15,7 +15,7 @@ use artcraft_api_defs::prompts::create_prompt::CreatePromptRequest;
use artcraft_api_defs::utils::media_links_to_thumbnail_template::media_links_to_thumbnail_template;
use cookie_store::cookie_store::CookieStore;
use enums::common::generation_provider::GenerationProvider;
use enums::common::model_type::ModelType;
use enums::common::generation::common_model_type::CommonModelType;
use enums::tauri::tasks::task_media_file_class::TaskMediaFileClass;
use errors::AnyhowResult;
use grok_client::credentials::grok_full_credentials::GrokFullCredentials;
@@ -220,8 +220,13 @@ async fn upload_grok_video(
uuid_idempotency_token: generate_random_uuid(),
positive_prompt: grok_video_post.prompt.clone(),
negative_prompt: None,
model_type: Some(ModelType::GrokVideo),
model_type: Some(CommonModelType::GrokVideo),
generation_provider: Some(GenerationProvider::Grok),
maybe_generation_mode: None,
maybe_aspect_ratio: None,
maybe_resolution: None,
maybe_batch_count: None,
maybe_generate_audio: None,
};
let prompt_response = create_prompt(
@@ -14,7 +14,7 @@ use artcraft_api_defs::prompts::create_prompt::CreatePromptRequest;
use artcraft_api_defs::utils::media_links_to_thumbnail_template::media_links_to_thumbnail_template;
use cookie_store::cookie_store::CookieStore;
use enums::common::generation_provider::GenerationProvider;
use enums::common::model_type::ModelType;
use enums::common::generation::common_model_type::CommonModelType;
use enums::tauri::tasks::task_media_file_class::TaskMediaFileClass;
use errors::AnyhowResult;
use uuid_utils::uuid::generate_random_uuid;
@@ -231,18 +231,18 @@ async fn upload_midjourney_batch(
midjourney_item: &ImagineItem
) -> AnyhowResult<()> {
let model_type = match midjourney_item.job_type {
Some(MidjourneyJobType::V6Diffusion) => ModelType::MidjourneyV6,
Some(MidjourneyJobType::V6p1Diffusion) => ModelType::MidjourneyV6p1,
Some(MidjourneyJobType::V6p1RawDiffusion) => ModelType::MidjourneyV6p1Raw,
Some(MidjourneyJobType::V7Diffusion) => ModelType::MidjourneyV7,
Some(MidjourneyJobType::V7RawDiffusion) => ModelType::MidjourneyV7Raw,
Some(MidjourneyJobType::V7DraftDiffusion) => ModelType::MidjourneyV7Draft,
Some(MidjourneyJobType::V7DraftRawDiffusion) => ModelType::MidjourneyV7DraftRaw,
Some(MidjourneyJobType::V6Diffusion) => CommonModelType::MidjourneyV6,
Some(MidjourneyJobType::V6p1Diffusion) => CommonModelType::MidjourneyV6p1,
Some(MidjourneyJobType::V6p1RawDiffusion) => CommonModelType::MidjourneyV6p1Raw,
Some(MidjourneyJobType::V7Diffusion) => CommonModelType::MidjourneyV7,
Some(MidjourneyJobType::V7RawDiffusion) => CommonModelType::MidjourneyV7Raw,
Some(MidjourneyJobType::V7DraftDiffusion) => CommonModelType::MidjourneyV7Draft,
Some(MidjourneyJobType::V7DraftRawDiffusion) => CommonModelType::MidjourneyV7DraftRaw,
Some(MidjourneyJobType::Other(ref other)) => {
info!("Unknown Midjourney job type (for job id {}): {}", midjourney_job_id, other);
ModelType::Midjourney
CommonModelType::Midjourney
},
_ => ModelType::Midjourney,
_ => CommonModelType::Midjourney,
};
let request = CreatePromptRequest {
@@ -251,6 +251,11 @@ async fn upload_midjourney_batch(
negative_prompt: None,
model_type: Some(model_type),
generation_provider: Some(GenerationProvider::Midjourney),
maybe_generation_mode: None,
maybe_aspect_ratio: None,
maybe_resolution: None,
maybe_batch_count: None,
maybe_generate_audio: None,
};
let prompt_response = create_prompt(
@@ -16,7 +16,7 @@ use crate::services::storyteller::state::storyteller_credential_manager::Storyte
use artcraft_api_defs::prompts::create_prompt::CreatePromptRequest;
use artcraft_api_defs::utils::media_links_to_thumbnail_template::media_links_to_thumbnail_template;
use enums::common::generation_provider::GenerationProvider;
use enums::common::model_type::ModelType;
use enums::common::generation::common_model_type::CommonModelType;
use enums::tauri::tasks::task_media_file_class::TaskMediaFileClass;
use errors::AnyhowResult;
use uuid_utils::uuid::generate_random_uuid;
@@ -47,7 +47,7 @@ use url_utils::download_extension::extract_download_extension_from_url::{extract
pub struct SuccessfulGeneration {
pub prompt: Option<String>,
pub items: Vec<GenerationItem>,
pub model_type: ModelType,
pub model_type: CommonModelType,
}
pub struct GenerationItem {
@@ -74,8 +74,8 @@ pub async fn handle_classic_successful_generations(
info!("Task succeeded: {:?}", task_id);
let generation_type = match generation.model_type {
ModelType::GptImage1 => GenerationType::Image,
ModelType::Sora2 => GenerationType::Video,
CommonModelType::GptImage1 => GenerationType::Image,
CommonModelType::Sora2 => GenerationType::Video,
_ => {
// Fallback
warn!("Unexpected model type: {:?}", generation.model_type);
@@ -89,6 +89,11 @@ pub async fn handle_classic_successful_generations(
negative_prompt: None,
model_type: Some(generation.model_type),
generation_provider: Some(GenerationProvider::Sora),
maybe_generation_mode: None,
maybe_aspect_ratio: None,
maybe_resolution: None,
maybe_batch_count: None,
maybe_generate_audio: None,
};
let prompt_response = create_prompt(
@@ -15,7 +15,7 @@ use crate::services::sora::threads::sora_task_polling::helpers::handle_failed_ge
use crate::services::sora::threads::sora_task_polling::helpers::handle_successful_generations::{handle_classic_successful_generations, GenerationItem, SuccessfulGeneration};
use crate::services::storyteller::state::storyteller_credential_manager::StorytellerCredentialManager;
use artcraft_api_defs::prompts::create_prompt::CreatePromptRequest;
use enums::common::model_type::ModelType;
use enums::common::generation::common_model_type::CommonModelType;
use errors::AnyhowResult;
use uuid_utils::uuid::generate_random_uuid;
use log::{error, info, warn};
@@ -72,7 +72,7 @@ pub async fn poll_classic_sora_tasks(
task.id.clone(),
SuccessfulGeneration {
prompt: task.prompt.clone(),
model_type: ModelType::GptImage1,
model_type: CommonModelType::GptImage1,
items: task.generations.iter()
.map(|gen| {
GenerationItem {
@@ -15,7 +15,7 @@ use crate::services::sora::threads::sora_task_polling::helpers::handle_failed_ge
use crate::services::sora::threads::sora_task_polling::helpers::handle_successful_generations::{handle_classic_successful_generations, GenerationItem, SuccessfulGeneration};
use crate::services::storyteller::state::storyteller_credential_manager::StorytellerCredentialManager;
use artcraft_api_defs::prompts::create_prompt::CreatePromptRequest;
use enums::common::model_type::ModelType;
use enums::common::generation::common_model_type::CommonModelType;
use errors::AnyhowResult;
use uuid_utils::uuid::generate_random_uuid;
use log::{error, info, warn};
@@ -73,7 +73,7 @@ pub async fn poll_sora_2_tasks(
success_draft.task_id,
SuccessfulGeneration {
prompt: Some(success_draft.prompt),
model_type: ModelType::Sora2,
model_type: CommonModelType::Sora2,
items: vec![GenerationItem {
item_id: success_draft.id,
url: success_draft.url,
@@ -7,7 +7,10 @@ use anyhow::anyhow;
use chrono::{DateTime, Utc};
use enums::by_table::prompts::prompt_type::PromptType;
use enums::common::generation_provider::GenerationProvider;
use enums::common::model_type::ModelType;
use enums::common::generation::common_aspect_ratio::CommonAspectRatio;
use enums::common::generation::common_generation_mode::CommonGenerationMode;
use enums::common::generation::common_model_type::CommonModelType;
use enums::common::generation::common_resolution::CommonResolution;
use errors::AnyhowResult;
use sqlx::pool::PoolConnection;
use sqlx::{MySql, MySqlPool};
@@ -22,7 +25,7 @@ pub struct Prompt {
pub prompt_type: PromptType,
pub maybe_model_type: Option<ModelType>,
pub maybe_model_type: Option<CommonModelType>,
pub maybe_generation_provider: Option<GenerationProvider>,
pub maybe_creator_user_token: Option<UserToken>,
@@ -32,6 +35,12 @@ pub struct Prompt {
pub maybe_other_args: Option<PromptInnerPayload>,
pub maybe_generation_mode: Option<CommonGenerationMode>,
pub maybe_aspect_ratio: Option<CommonAspectRatio>,
pub maybe_resolution: Option<CommonResolution>,
pub maybe_batch_count: Option<u8>,
pub maybe_generate_audio: Option<bool>,
/// For moderators only
pub creator_ip_address: Option<String>,
@@ -44,7 +53,7 @@ pub struct PromptRaw {
pub prompt_type: PromptType,
pub maybe_model_type: Option<ModelType>,
pub maybe_model_type: Option<CommonModelType>,
pub maybe_generation_provider: Option<GenerationProvider>,
pub maybe_creator_user_token: Option<UserToken>,
@@ -54,6 +63,12 @@ pub struct PromptRaw {
pub maybe_other_args: Option<String>,
pub maybe_generation_mode: Option<CommonGenerationMode>,
pub maybe_aspect_ratio: Option<CommonAspectRatio>,
pub maybe_resolution: Option<CommonResolution>,
pub maybe_batch_count: Option<u8>,
pub maybe_generate_audio: Option<bool>,
/// For moderators only
pub creator_ip_address: Option<String>,
@@ -94,6 +109,11 @@ pub async fn get_prompt_from_connection(
maybe_creator_user_token: record.maybe_creator_user_token,
maybe_positive_prompt: record.maybe_positive_prompt,
maybe_negative_prompt: record.maybe_negative_prompt,
maybe_generation_mode: record.maybe_generation_mode,
maybe_aspect_ratio: record.maybe_aspect_ratio,
maybe_resolution: record.maybe_resolution,
maybe_batch_count: record.maybe_batch_count,
maybe_generate_audio: record.maybe_generate_audio,
maybe_other_args: record.maybe_other_args
.as_deref()
.map(|args| PromptInnerPayload::from_json(args))
@@ -115,13 +135,20 @@ SELECT
p.prompt_type as `prompt_type: enums::by_table::prompts::prompt_type::PromptType`,
p.maybe_model_type as `maybe_model_type: enums::common::model_type::ModelType`,
p.maybe_model_type as `maybe_model_type: enums::common::generation::common_model_type::CommonModelType`,
p.maybe_generation_provider as `maybe_generation_provider: enums::common::generation_provider::GenerationProvider`,
p.maybe_creator_user_token as `maybe_creator_user_token: tokens::tokens::users::UserToken`,
p.maybe_positive_prompt,
p.maybe_negative_prompt,
p.maybe_generation_mode as `maybe_generation_mode: enums::common::generation::common_generation_mode::CommonGenerationMode`,
p.maybe_aspect_ratio as `maybe_aspect_ratio: enums::common::generation::common_aspect_ratio::CommonAspectRatio`,
p.maybe_resolution as `maybe_resolution: enums::common::generation::common_resolution::CommonResolution`,
p.maybe_batch_count as `maybe_batch_count: u8`,
p.maybe_generate_audio as `maybe_generate_audio: bool`,
p.maybe_other_args,
p.creator_ip_address,
@@ -6,8 +6,11 @@ use sqlx;
use sqlx::{Executor, MySql};
use enums::by_table::prompts::prompt_type::PromptType;
use enums::common::generation::common_aspect_ratio::CommonAspectRatio;
use enums::common::generation::common_generation_mode::CommonGenerationMode;
use enums::common::generation::common_model_type::CommonModelType;
use enums::common::generation::common_resolution::CommonResolution;
use enums::common::generation_provider::GenerationProvider;
use enums::common::model_type::ModelType;
use errors::AnyhowResult;
use tokens::tokens::prompts::PromptToken;
use tokens::tokens::users::UserToken;
@@ -24,14 +27,18 @@ pub struct InsertPromptArgs<'e, 'c, E>
pub maybe_creator_user_token: Option<&'e UserToken>,
pub maybe_model_type: Option<ModelType>,
pub maybe_model_type: Option<CommonModelType>,
pub maybe_generation_provider: Option<GenerationProvider>,
pub maybe_positive_prompt: Option<&'e str>,
pub maybe_negative_prompt: Option<&'e str>,
pub maybe_generation_mode: Option<CommonGenerationMode>,
pub maybe_aspect_ratio: Option<CommonAspectRatio>,
pub maybe_resolution: Option<CommonResolution>,
pub maybe_batch_count: Option<u8>,
pub maybe_generate_audio: Option<bool>,
pub maybe_other_args: Option<&'e PromptInnerPayload>,
pub creator_ip_address: &'e str,
@@ -77,6 +84,12 @@ SET
maybe_positive_prompt = ?,
maybe_negative_prompt = ?,
maybe_generation_mode = ?,
maybe_aspect_ratio = ?,
maybe_resolution = ?,
maybe_batch_count = ?,
maybe_generate_audio = ?,
maybe_other_args = ?,
@@ -89,6 +102,11 @@ SET
args.maybe_generation_provider.map(|g| g.to_str()),
args.maybe_positive_prompt,
args.maybe_negative_prompt,
args.maybe_generation_mode.map(|m| m.to_str()),
args.maybe_aspect_ratio.map(|a| a.to_str()),
args.maybe_resolution.map(|r| r.to_str()),
args.maybe_batch_count,
args.maybe_generate_audio,
maybe_other_args,
args.creator_ip_address,
);
+1
View File
@@ -47,3 +47,4 @@ strum.workspace = true
# Test serialization
serde_json.workspace = true
toml.workspace = true
regex.workspace = true
@@ -1,8 +1,21 @@
use std::collections::BTreeSet;
#[cfg(test)]
use strum::EnumCount;
#[cfg(test)]
use strum::EnumIter;
use utoipa::ToSchema;
/// Common aspect ratios for video generation.
/// Mirrors artcraft_router::api::common_aspect_ratio::CommonAspectRatio.
#[derive(Copy, Clone, Debug, Serialize, Deserialize, ToSchema)]
use crate::error::enum_error::EnumError;
/// Maximum serialized string length for database storage.
pub const MAX_LENGTH: usize = 24;
/// Common aspect ratios for image and video generation.
///
/// NB: Keep the max serialized length to 24 characters.
#[cfg_attr(test, derive(EnumIter, EnumCount))]
#[derive(Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd, Serialize, Deserialize, ToSchema)]
#[serde(rename_all = "snake_case")]
pub enum CommonAspectRatio {
Auto,
@@ -19,7 +32,232 @@ pub enum CommonAspectRatio {
TallNineByTwentyOne,
Wide,
Tall,
#[serde(rename = "auto_2k")]
Auto2k,
#[serde(rename = "auto_4k")]
Auto4k,
SquareHd,
}
impl_enum_display_and_debug_using_to_str!(CommonAspectRatio);
impl_mysql_enum_coders!(CommonAspectRatio);
impl_mysql_from_row!(CommonAspectRatio);
impl CommonAspectRatio {
pub fn to_str(&self) -> &'static str {
match self {
Self::Auto => "auto",
Self::Square => "square",
Self::WideThreeByTwo => "wide_three_by_two",
Self::WideFourByThree => "wide_four_by_three",
Self::WideFiveByFour => "wide_five_by_four",
Self::WideSixteenByNine => "wide_sixteen_by_nine",
Self::WideTwentyOneByNine => "wide_twenty_one_by_nine",
Self::TallTwoByThree => "tall_two_by_three",
Self::TallThreeByFour => "tall_three_by_four",
Self::TallFourByFive => "tall_four_by_five",
Self::TallNineBySixteen => "tall_nine_by_sixteen",
Self::TallNineByTwentyOne => "tall_nine_by_twenty_one",
Self::Wide => "wide",
Self::Tall => "tall",
Self::Auto2k => "auto_2k",
Self::Auto4k => "auto_4k",
Self::SquareHd => "square_hd",
}
}
pub fn from_str(value: &str) -> Result<Self, EnumError> {
match value {
"auto" => Ok(Self::Auto),
"square" => Ok(Self::Square),
"wide_three_by_two" => Ok(Self::WideThreeByTwo),
"wide_four_by_three" => Ok(Self::WideFourByThree),
"wide_five_by_four" => Ok(Self::WideFiveByFour),
"wide_sixteen_by_nine" => Ok(Self::WideSixteenByNine),
"wide_twenty_one_by_nine" => Ok(Self::WideTwentyOneByNine),
"tall_two_by_three" => Ok(Self::TallTwoByThree),
"tall_three_by_four" => Ok(Self::TallThreeByFour),
"tall_four_by_five" => Ok(Self::TallFourByFive),
"tall_nine_by_sixteen" => Ok(Self::TallNineBySixteen),
"tall_nine_by_twenty_one" => Ok(Self::TallNineByTwentyOne),
"wide" => Ok(Self::Wide),
"tall" => Ok(Self::Tall),
"auto_2k" => Ok(Self::Auto2k),
"auto_4k" => Ok(Self::Auto4k),
"square_hd" => Ok(Self::SquareHd),
_ => Err(EnumError::CouldNotConvertFromString(value.to_string())),
}
}
pub fn all_variants() -> BTreeSet<Self> {
BTreeSet::from([
Self::Auto,
Self::Square,
Self::WideThreeByTwo,
Self::WideFourByThree,
Self::WideFiveByFour,
Self::WideSixteenByNine,
Self::WideTwentyOneByNine,
Self::TallTwoByThree,
Self::TallThreeByFour,
Self::TallFourByFive,
Self::TallNineBySixteen,
Self::TallNineByTwentyOne,
Self::Wide,
Self::Tall,
Self::Auto2k,
Self::Auto4k,
Self::SquareHd,
])
}
}
#[cfg(test)]
mod tests {
use crate::common::generation::common_aspect_ratio::CommonAspectRatio;
use crate::common::generation::common_aspect_ratio::MAX_LENGTH;
use crate::test_helpers::assert_serialization;
mod explicit_checks {
use super::*;
use crate::error::enum_error::EnumError;
#[test]
fn test_serialization() {
assert_serialization(CommonAspectRatio::Auto, "auto");
assert_serialization(CommonAspectRatio::Square, "square");
assert_serialization(CommonAspectRatio::WideThreeByTwo, "wide_three_by_two");
assert_serialization(CommonAspectRatio::WideFourByThree, "wide_four_by_three");
assert_serialization(CommonAspectRatio::WideFiveByFour, "wide_five_by_four");
assert_serialization(CommonAspectRatio::WideSixteenByNine, "wide_sixteen_by_nine");
assert_serialization(CommonAspectRatio::WideTwentyOneByNine, "wide_twenty_one_by_nine");
assert_serialization(CommonAspectRatio::TallTwoByThree, "tall_two_by_three");
assert_serialization(CommonAspectRatio::TallThreeByFour, "tall_three_by_four");
assert_serialization(CommonAspectRatio::TallFourByFive, "tall_four_by_five");
assert_serialization(CommonAspectRatio::TallNineBySixteen, "tall_nine_by_sixteen");
assert_serialization(CommonAspectRatio::TallNineByTwentyOne, "tall_nine_by_twenty_one");
assert_serialization(CommonAspectRatio::Wide, "wide");
assert_serialization(CommonAspectRatio::Tall, "tall");
assert_serialization(CommonAspectRatio::Auto2k, "auto_2k");
assert_serialization(CommonAspectRatio::Auto4k, "auto_4k");
assert_serialization(CommonAspectRatio::SquareHd, "square_hd");
}
#[test]
fn to_str() {
assert_eq!(CommonAspectRatio::Auto.to_str(), "auto");
assert_eq!(CommonAspectRatio::Square.to_str(), "square");
assert_eq!(CommonAspectRatio::WideThreeByTwo.to_str(), "wide_three_by_two");
assert_eq!(CommonAspectRatio::WideFourByThree.to_str(), "wide_four_by_three");
assert_eq!(CommonAspectRatio::WideFiveByFour.to_str(), "wide_five_by_four");
assert_eq!(CommonAspectRatio::WideSixteenByNine.to_str(), "wide_sixteen_by_nine");
assert_eq!(CommonAspectRatio::WideTwentyOneByNine.to_str(), "wide_twenty_one_by_nine");
assert_eq!(CommonAspectRatio::TallTwoByThree.to_str(), "tall_two_by_three");
assert_eq!(CommonAspectRatio::TallThreeByFour.to_str(), "tall_three_by_four");
assert_eq!(CommonAspectRatio::TallFourByFive.to_str(), "tall_four_by_five");
assert_eq!(CommonAspectRatio::TallNineBySixteen.to_str(), "tall_nine_by_sixteen");
assert_eq!(CommonAspectRatio::TallNineByTwentyOne.to_str(), "tall_nine_by_twenty_one");
assert_eq!(CommonAspectRatio::Wide.to_str(), "wide");
assert_eq!(CommonAspectRatio::Tall.to_str(), "tall");
assert_eq!(CommonAspectRatio::Auto2k.to_str(), "auto_2k");
assert_eq!(CommonAspectRatio::Auto4k.to_str(), "auto_4k");
assert_eq!(CommonAspectRatio::SquareHd.to_str(), "square_hd");
}
#[test]
fn from_str() {
assert_eq!(CommonAspectRatio::from_str("auto").unwrap(), CommonAspectRatio::Auto);
assert_eq!(CommonAspectRatio::from_str("square").unwrap(), CommonAspectRatio::Square);
assert_eq!(CommonAspectRatio::from_str("wide_three_by_two").unwrap(), CommonAspectRatio::WideThreeByTwo);
assert_eq!(CommonAspectRatio::from_str("wide_four_by_three").unwrap(), CommonAspectRatio::WideFourByThree);
assert_eq!(CommonAspectRatio::from_str("wide_five_by_four").unwrap(), CommonAspectRatio::WideFiveByFour);
assert_eq!(CommonAspectRatio::from_str("wide_sixteen_by_nine").unwrap(), CommonAspectRatio::WideSixteenByNine);
assert_eq!(CommonAspectRatio::from_str("wide_twenty_one_by_nine").unwrap(), CommonAspectRatio::WideTwentyOneByNine);
assert_eq!(CommonAspectRatio::from_str("tall_two_by_three").unwrap(), CommonAspectRatio::TallTwoByThree);
assert_eq!(CommonAspectRatio::from_str("tall_three_by_four").unwrap(), CommonAspectRatio::TallThreeByFour);
assert_eq!(CommonAspectRatio::from_str("tall_four_by_five").unwrap(), CommonAspectRatio::TallFourByFive);
assert_eq!(CommonAspectRatio::from_str("tall_nine_by_sixteen").unwrap(), CommonAspectRatio::TallNineBySixteen);
assert_eq!(CommonAspectRatio::from_str("tall_nine_by_twenty_one").unwrap(), CommonAspectRatio::TallNineByTwentyOne);
assert_eq!(CommonAspectRatio::from_str("wide").unwrap(), CommonAspectRatio::Wide);
assert_eq!(CommonAspectRatio::from_str("tall").unwrap(), CommonAspectRatio::Tall);
assert_eq!(CommonAspectRatio::from_str("auto_2k").unwrap(), CommonAspectRatio::Auto2k);
assert_eq!(CommonAspectRatio::from_str("auto_4k").unwrap(), CommonAspectRatio::Auto4k);
assert_eq!(CommonAspectRatio::from_str("square_hd").unwrap(), CommonAspectRatio::SquareHd);
}
#[test]
fn from_str_err() {
let result = CommonAspectRatio::from_str("invalid");
assert!(result.is_err());
if let Err(EnumError::CouldNotConvertFromString(value)) = result {
assert_eq!(value, "invalid");
} else {
panic!("Expected EnumError::CouldNotConvertFromString");
}
}
#[test]
fn all_variants() {
let variants = CommonAspectRatio::all_variants();
assert_eq!(variants.len(), 17);
}
}
mod mechanical_checks {
use super::*;
#[test]
fn variant_length() {
use strum::IntoEnumIterator;
assert_eq!(CommonAspectRatio::all_variants().len(), CommonAspectRatio::iter().len());
}
#[test]
fn round_trip() {
for variant in CommonAspectRatio::all_variants() {
assert_eq!(variant, CommonAspectRatio::from_str(variant.to_str()).unwrap());
assert_eq!(variant, CommonAspectRatio::from_str(&format!("{}", variant)).unwrap());
assert_eq!(variant, CommonAspectRatio::from_str(&format!("{:?}", variant)).unwrap());
}
}
#[test]
fn serialized_length_ok_for_database() {
for variant in CommonAspectRatio::all_variants() {
let serialized = variant.to_str();
assert!(serialized.len() > 0, "variant {:?} is too short", variant);
assert!(serialized.len() <= MAX_LENGTH, "variant {:?} is too long via to_str()", variant);
}
for variant in CommonAspectRatio::all_variants() {
let json = serde_json::to_string(&variant).unwrap().replace('"', "");
assert!(json.len() <= MAX_LENGTH, "variant {:?} is too long via JSON: {:?}", variant, json);
}
}
#[test]
fn serialized_names_must_not_contain_dots() {
for variant in CommonAspectRatio::all_variants() {
let to_str_value = variant.to_str();
assert!(!to_str_value.contains('.'), "to_str() for {:?} contains a dot: {:?}", variant, to_str_value);
let json_value = serde_json::to_string(&variant).unwrap().replace('"', "");
assert!(!json_value.contains('.'), "JSON serialization for {:?} contains a dot: {:?}", variant, json_value);
}
}
#[test]
fn serialized_names_must_only_contain_lowercase_alphanumeric_and_underscore() {
let valid_pattern = regex::Regex::new(r"^[a-z0-9_]+$").unwrap();
for variant in CommonAspectRatio::all_variants() {
let to_str_value = variant.to_str();
assert!(valid_pattern.is_match(to_str_value),
"to_str() for {:?} contains invalid characters: {:?} (only a-z, 0-9, _ allowed)", variant, to_str_value);
let json_value = serde_json::to_string(&variant).unwrap().replace('"', "");
assert!(valid_pattern.is_match(&json_value),
"JSON serialization for {:?} contains invalid characters: {:?} (only a-z, 0-9, _ allowed)", variant, json_value);
}
}
}
}
@@ -0,0 +1,205 @@
use std::collections::BTreeSet;
#[cfg(test)]
use strum::EnumCount;
#[cfg(test)]
use strum::EnumIter;
use utoipa::ToSchema;
use crate::error::enum_error::EnumError;
/// Maximum serialized string length for database storage.
/// Stored in the `prompts` table (`maybe_generation_mode` column) as VARCHAR(24).
pub const MAX_LENGTH: usize = 24;
/// The generation mode describes how the user wants to generate content.
///
/// Stored in the `prompts` table (`maybe_generation_mode` column).
///
/// NB: Keep the max serialized length to 24 characters.
#[cfg_attr(test, derive(EnumIter, EnumCount))]
#[derive(Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd, Serialize, Deserialize, ToSchema)]
#[serde(rename_all = "snake_case")]
pub enum CommonGenerationMode {
/// Generate from a keyframe (eg. image-to-video with a start/end frame)
Keyframe,
/// Generate using a reference image or style
Reference,
/// Edit an existing image or video
Edit,
/// Inpaint a masked region of an image
Inpaint,
/// Outpaint / extend beyond the borders of an image
Outpaint,
/// Generate from a text prompt only (no image input)
Text,
}
impl_enum_display_and_debug_using_to_str!(CommonGenerationMode);
impl_mysql_enum_coders!(CommonGenerationMode);
impl_mysql_from_row!(CommonGenerationMode);
impl CommonGenerationMode {
pub fn to_str(&self) -> &'static str {
match self {
Self::Keyframe => "keyframe",
Self::Reference => "reference",
Self::Edit => "edit",
Self::Inpaint => "inpaint",
Self::Outpaint => "outpaint",
Self::Text => "text",
}
}
pub fn from_str(value: &str) -> Result<Self, EnumError> {
match value {
"keyframe" => Ok(Self::Keyframe),
"reference" => Ok(Self::Reference),
"edit" => Ok(Self::Edit),
"inpaint" => Ok(Self::Inpaint),
"outpaint" => Ok(Self::Outpaint),
"text" => Ok(Self::Text),
_ => Err(EnumError::CouldNotConvertFromString(value.to_string())),
}
}
pub fn all_variants() -> BTreeSet<Self> {
BTreeSet::from([
Self::Keyframe,
Self::Reference,
Self::Edit,
Self::Inpaint,
Self::Outpaint,
Self::Text,
])
}
}
#[cfg(test)]
mod tests {
use crate::common::generation::common_generation_mode::CommonGenerationMode;
use crate::common::generation::common_generation_mode::MAX_LENGTH;
use crate::test_helpers::assert_serialization;
mod explicit_checks {
use super::*;
use crate::error::enum_error::EnumError;
#[test]
fn test_serialization() {
assert_serialization(CommonGenerationMode::Keyframe, "keyframe");
assert_serialization(CommonGenerationMode::Reference, "reference");
assert_serialization(CommonGenerationMode::Edit, "edit");
assert_serialization(CommonGenerationMode::Inpaint, "inpaint");
assert_serialization(CommonGenerationMode::Outpaint, "outpaint");
assert_serialization(CommonGenerationMode::Text, "text");
}
#[test]
fn to_str() {
assert_eq!(CommonGenerationMode::Keyframe.to_str(), "keyframe");
assert_eq!(CommonGenerationMode::Reference.to_str(), "reference");
assert_eq!(CommonGenerationMode::Edit.to_str(), "edit");
assert_eq!(CommonGenerationMode::Inpaint.to_str(), "inpaint");
assert_eq!(CommonGenerationMode::Outpaint.to_str(), "outpaint");
assert_eq!(CommonGenerationMode::Text.to_str(), "text");
}
#[test]
fn from_str() {
assert_eq!(CommonGenerationMode::from_str("keyframe").unwrap(), CommonGenerationMode::Keyframe);
assert_eq!(CommonGenerationMode::from_str("reference").unwrap(), CommonGenerationMode::Reference);
assert_eq!(CommonGenerationMode::from_str("edit").unwrap(), CommonGenerationMode::Edit);
assert_eq!(CommonGenerationMode::from_str("inpaint").unwrap(), CommonGenerationMode::Inpaint);
assert_eq!(CommonGenerationMode::from_str("outpaint").unwrap(), CommonGenerationMode::Outpaint);
assert_eq!(CommonGenerationMode::from_str("text").unwrap(), CommonGenerationMode::Text);
}
#[test]
fn from_str_err() {
let result = CommonGenerationMode::from_str("invalid");
assert!(result.is_err());
if let Err(EnumError::CouldNotConvertFromString(value)) = result {
assert_eq!(value, "invalid");
} else {
panic!("Expected EnumError::CouldNotConvertFromString");
}
}
#[test]
fn all_variants() {
let mut variants = CommonGenerationMode::all_variants();
assert_eq!(variants.len(), 6);
assert_eq!(variants.pop_first(), Some(CommonGenerationMode::Keyframe));
assert_eq!(variants.pop_first(), Some(CommonGenerationMode::Reference));
assert_eq!(variants.pop_first(), Some(CommonGenerationMode::Edit));
assert_eq!(variants.pop_first(), Some(CommonGenerationMode::Inpaint));
assert_eq!(variants.pop_first(), Some(CommonGenerationMode::Outpaint));
assert_eq!(variants.pop_first(), Some(CommonGenerationMode::Text));
assert_eq!(variants.pop_first(), None);
}
}
mod mechanical_checks {
use super::*;
#[test]
fn variant_length() {
use strum::IntoEnumIterator;
assert_eq!(CommonGenerationMode::all_variants().len(), CommonGenerationMode::iter().len());
}
#[test]
fn round_trip() {
for variant in CommonGenerationMode::all_variants() {
assert_eq!(variant, CommonGenerationMode::from_str(variant.to_str()).unwrap());
assert_eq!(variant, CommonGenerationMode::from_str(&format!("{}", variant)).unwrap());
assert_eq!(variant, CommonGenerationMode::from_str(&format!("{:?}", variant)).unwrap());
}
}
#[test]
fn serialized_length_ok_for_database() {
for variant in CommonGenerationMode::all_variants() {
let serialized = variant.to_str();
assert!(serialized.len() > 0, "variant {:?} is too short", variant);
assert!(serialized.len() <= MAX_LENGTH, "variant {:?} is too long via to_str()", variant);
}
for variant in CommonGenerationMode::all_variants() {
let json = serde_json::to_string(&variant).unwrap().replace('"', "");
assert!(json.len() <= MAX_LENGTH, "variant {:?} is too long via JSON: {:?}", variant, json);
}
}
#[test]
fn serialized_names_must_not_contain_dots() {
for variant in CommonGenerationMode::all_variants() {
let to_str_value = variant.to_str();
assert!(!to_str_value.contains('.'), "to_str() for {:?} contains a dot: {:?}", variant, to_str_value);
let json_value = serde_json::to_string(&variant).unwrap().replace('"', "");
assert!(!json_value.contains('.'), "JSON serialization for {:?} contains a dot: {:?}", variant, json_value);
}
}
#[test]
fn serialized_names_must_only_contain_lowercase_alphanumeric_and_underscore() {
let valid_pattern = regex::Regex::new(r"^[a-z0-9_]+$").unwrap();
for variant in CommonGenerationMode::all_variants() {
let to_str_value = variant.to_str();
assert!(valid_pattern.is_match(to_str_value),
"to_str() for {:?} contains invalid characters: {:?} (only a-z, 0-9, _ allowed)", variant, to_str_value);
let json_value = serde_json::to_string(&variant).unwrap().replace('"', "");
assert!(valid_pattern.is_match(&json_value),
"JSON serialization for {:?} contains invalid characters: {:?} (only a-z, 0-9, _ allowed)", variant, json_value);
}
}
}
}
@@ -0,0 +1,217 @@
use std::collections::BTreeSet;
#[cfg(test)]
use strum::EnumCount;
#[cfg(test)]
use strum::EnumIter;
use utoipa::ToSchema;
use crate::error::enum_error::EnumError;
/// Maximum serialized string length for future database storage.
pub const MAX_LENGTH: usize = 24;
/// The broad class of model (image, video, audio, 3D, etc.)
///
/// NB: This is not currently stored in the database, so it does not need
/// `impl_mysql_enum_coders!` or `impl_mysql_from_row!` macros. If it is
/// stored in the future, add those macros and a migration.
#[cfg_attr(test, derive(EnumIter, EnumCount))]
#[derive(Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd, Serialize, Deserialize, ToSchema)]
pub enum CommonModelClass {
#[serde(rename = "image")]
Image,
#[serde(rename = "video")]
Video,
#[serde(rename = "audio")]
Audio,
#[serde(rename = "audio_music")]
AudioMusic,
#[serde(rename = "audio_voice")]
AudioVoice,
#[serde(rename = "3d")]
Dimensional,
#[serde(rename = "3d_mesh")]
DimensionalMesh,
#[serde(rename = "3d_splat")]
DimensionalSplat,
}
impl_enum_display_and_debug_using_to_str!(CommonModelClass);
impl CommonModelClass {
pub fn to_str(&self) -> &'static str {
match self {
Self::Image => "image",
Self::Video => "video",
Self::Audio => "audio",
Self::AudioMusic => "audio_music",
Self::AudioVoice => "audio_voice",
Self::Dimensional => "3d",
Self::DimensionalMesh => "3d_mesh",
Self::DimensionalSplat => "3d_splat",
}
}
pub fn from_str(value: &str) -> Result<Self, EnumError> {
match value {
"image" => Ok(Self::Image),
"video" => Ok(Self::Video),
"audio" => Ok(Self::Audio),
"audio_music" => Ok(Self::AudioMusic),
"audio_voice" => Ok(Self::AudioVoice),
"3d" => Ok(Self::Dimensional),
"3d_mesh" => Ok(Self::DimensionalMesh),
"3d_splat" => Ok(Self::DimensionalSplat),
_ => Err(EnumError::CouldNotConvertFromString(value.to_string())),
}
}
pub fn all_variants() -> BTreeSet<Self> {
BTreeSet::from([
Self::Image,
Self::Video,
Self::Audio,
Self::AudioMusic,
Self::AudioVoice,
Self::Dimensional,
Self::DimensionalMesh,
Self::DimensionalSplat,
])
}
}
#[cfg(test)]
mod tests {
use crate::common::generation::common_model_class::CommonModelClass;
use crate::common::generation::common_model_class::MAX_LENGTH;
use crate::test_helpers::assert_serialization;
mod explicit_checks {
use super::*;
use crate::error::enum_error::EnumError;
#[test]
fn test_serialization() {
assert_serialization(CommonModelClass::Image, "image");
assert_serialization(CommonModelClass::Video, "video");
assert_serialization(CommonModelClass::Audio, "audio");
assert_serialization(CommonModelClass::AudioMusic, "audio_music");
assert_serialization(CommonModelClass::AudioVoice, "audio_voice");
assert_serialization(CommonModelClass::Dimensional, "3d");
assert_serialization(CommonModelClass::DimensionalMesh, "3d_mesh");
assert_serialization(CommonModelClass::DimensionalSplat, "3d_splat");
}
#[test]
fn to_str() {
assert_eq!(CommonModelClass::Image.to_str(), "image");
assert_eq!(CommonModelClass::Video.to_str(), "video");
assert_eq!(CommonModelClass::Audio.to_str(), "audio");
assert_eq!(CommonModelClass::AudioMusic.to_str(), "audio_music");
assert_eq!(CommonModelClass::AudioVoice.to_str(), "audio_voice");
assert_eq!(CommonModelClass::Dimensional.to_str(), "3d");
assert_eq!(CommonModelClass::DimensionalMesh.to_str(), "3d_mesh");
assert_eq!(CommonModelClass::DimensionalSplat.to_str(), "3d_splat");
}
#[test]
fn from_str() {
assert_eq!(CommonModelClass::from_str("image").unwrap(), CommonModelClass::Image);
assert_eq!(CommonModelClass::from_str("video").unwrap(), CommonModelClass::Video);
assert_eq!(CommonModelClass::from_str("audio").unwrap(), CommonModelClass::Audio);
assert_eq!(CommonModelClass::from_str("audio_music").unwrap(), CommonModelClass::AudioMusic);
assert_eq!(CommonModelClass::from_str("audio_voice").unwrap(), CommonModelClass::AudioVoice);
assert_eq!(CommonModelClass::from_str("3d").unwrap(), CommonModelClass::Dimensional);
assert_eq!(CommonModelClass::from_str("3d_mesh").unwrap(), CommonModelClass::DimensionalMesh);
assert_eq!(CommonModelClass::from_str("3d_splat").unwrap(), CommonModelClass::DimensionalSplat);
}
#[test]
fn from_str_err() {
let result = CommonModelClass::from_str("invalid");
assert!(result.is_err());
if let Err(EnumError::CouldNotConvertFromString(value)) = result {
assert_eq!(value, "invalid");
} else {
panic!("Expected EnumError::CouldNotConvertFromString");
}
}
#[test]
fn all_variants() {
let mut variants = CommonModelClass::all_variants();
assert_eq!(variants.len(), 8);
assert_eq!(variants.pop_first(), Some(CommonModelClass::Image));
assert_eq!(variants.pop_first(), Some(CommonModelClass::Video));
assert_eq!(variants.pop_first(), Some(CommonModelClass::Audio));
assert_eq!(variants.pop_first(), Some(CommonModelClass::AudioMusic));
assert_eq!(variants.pop_first(), Some(CommonModelClass::AudioVoice));
assert_eq!(variants.pop_first(), Some(CommonModelClass::Dimensional));
assert_eq!(variants.pop_first(), Some(CommonModelClass::DimensionalMesh));
assert_eq!(variants.pop_first(), Some(CommonModelClass::DimensionalSplat));
assert_eq!(variants.pop_first(), None);
}
}
mod mechanical_checks {
use super::*;
#[test]
fn variant_length() {
use strum::IntoEnumIterator;
assert_eq!(CommonModelClass::all_variants().len(), CommonModelClass::iter().len());
}
#[test]
fn round_trip() {
for variant in CommonModelClass::all_variants() {
assert_eq!(variant, CommonModelClass::from_str(variant.to_str()).unwrap());
assert_eq!(variant, CommonModelClass::from_str(&format!("{}", variant)).unwrap());
assert_eq!(variant, CommonModelClass::from_str(&format!("{:?}", variant)).unwrap());
}
}
#[test]
fn serialized_length_ok_for_database() {
for variant in CommonModelClass::all_variants() {
let serialized = variant.to_str();
assert!(serialized.len() > 0, "variant {:?} is too short", variant);
assert!(serialized.len() <= MAX_LENGTH, "variant {:?} is too long", variant);
}
}
#[test]
fn serialized_names_must_not_contain_dots() {
for variant in CommonModelClass::all_variants() {
let to_str_value = variant.to_str();
assert!(!to_str_value.contains('.'), "to_str() for {:?} contains a dot: {:?}", variant, to_str_value);
let json_value = serde_json::to_string(&variant).unwrap().replace('"', "");
assert!(!json_value.contains('.'), "JSON serialization for {:?} contains a dot: {:?}", variant, json_value);
}
}
#[test]
fn serialized_names_must_only_contain_lowercase_alphanumeric_and_underscore() {
let valid_pattern = regex::Regex::new(r"^[a-z0-9_]+$").unwrap();
for variant in CommonModelClass::all_variants() {
let to_str_value = variant.to_str();
assert!(valid_pattern.is_match(to_str_value),
"to_str() for {:?} contains invalid characters: {:?} (only a-z, 0-9, _ allowed)", variant, to_str_value);
let json_value = serde_json::to_string(&variant).unwrap().replace('"', "");
assert!(valid_pattern.is_match(&json_value),
"JSON serialization for {:?} contains invalid characters: {:?} (only a-z, 0-9, _ allowed)", variant, json_value);
}
}
}
}
@@ -0,0 +1,786 @@
use std::collections::BTreeSet;
#[cfg(test)]
use strum::EnumCount;
#[cfg(test)]
use strum::EnumIter;
use utoipa::ToSchema;
use crate::common::generation::common_model_class::CommonModelClass;
/// Maximum serialized string length for database storage.
/// Stored in the `prompts` table (`maybe_model_type` column) as VARCHAR(24).
pub const MAX_LENGTH: usize = 24;
/// Common model type enum used across image, video, 3D, and splat generation.
/// This forms part of our core data model for generative models.
///
/// This is used in the MySQL database, the HTTP API, and throughout
/// ArtCraft (the Tauri app).
///
/// Stored in the `prompts` table (`maybe_model_type` column).
/// Also used by other tables and the sqlite tasks table.
///
/// NB: This will be used by a variety of tables (MySQL and sqlite)!
/// Keep the max length to 24 characters.
#[cfg_attr(test, derive(EnumIter, EnumCount))]
#[derive(Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd, Serialize, Deserialize, ToSchema)]
#[serde(rename_all = "snake_case")]
pub enum CommonModelType {
// Image models
#[serde(rename = "flux_1_dev")]
Flux1Dev,
#[serde(rename = "flux_1_schnell")]
Flux1Schnell,
#[serde(rename = "flux_dev_juggernaut")]
FluxDevJuggernaut,
#[serde(rename = "flux_pro_1")]
FluxPro1,
#[serde(rename = "flux_pro_1p1")]
FluxPro11,
#[serde(rename = "flux_pro_1p1_ultra")]
FluxPro11Ultra,
#[serde(rename = "flux_pro_kontext_max")]
FluxProKontextMax,
#[serde(rename = "flux_2_lora_angles")]
Flux2LoraAngles,
#[serde(rename = "gpt_image_1")]
GptImage1,
#[serde(rename = "gpt_image_1p5")]
GptImage1p5,
// Generic grok image model without a version
#[serde(rename = "grok_image")]
GrokImage,
#[serde(rename = "recraft_3")]
Recraft3,
#[serde(rename = "seededit_3")]
SeedEdit3,
#[serde(rename = "qwen")]
Qwen,
#[serde(rename = "qwen_edit_2511_angles")]
QwenEdit2511Angles,
/// Gemini 2.5 Flash, AKA "Nano Banana"
#[serde(rename = "gemini_25_flash")]
Gemini25Flash,
#[serde(rename = "nano_banana")]
NanoBanana,
#[serde(rename = "nano_banana_2")]
NanoBanana2,
#[serde(rename = "nano_banana_pro")]
NanoBananaPro,
#[serde(rename = "seedream_4")]
Seedream4,
#[serde(rename = "seedream_4p5")]
Seedream4p5,
#[serde(rename = "seedream_5_lite")]
Seedream5Lite,
/// Midjourney without distinguishing a model type or version
#[serde(rename = "midjourney")]
Midjourney,
#[serde(rename = "midjourney_v6")]
MidjourneyV6,
#[serde(rename = "midjourney_v6p1")]
MidjourneyV6p1,
#[serde(rename = "midjourney_v6p1_raw")]
MidjourneyV6p1Raw,
#[serde(rename = "midjourney_v7")]
MidjourneyV7,
#[serde(rename = "midjourney_v7_draft")]
MidjourneyV7Draft,
#[serde(rename = "midjourney_v7_draft_raw")]
MidjourneyV7DraftRaw,
#[serde(rename = "midjourney_v7_raw")]
MidjourneyV7Raw,
//// Image Infill models
//#[serde(rename = "flux_pro_1_infill")]
//FluxPro1Infill,
// Video models
// Generic grok video model without a version
#[serde(rename = "grok_video")]
GrokVideo,
#[serde(rename = "kling_1p6_pro")]
Kling16Pro,
#[serde(rename = "kling_2p1_pro")]
Kling21Pro,
#[serde(rename = "kling_2p1_master")]
Kling21Master,
#[serde(rename = "kling_2p5_turbo_pro")]
Kling2p5TurboPro,
#[serde(rename = "kling_2p6_pro")]
Kling2p6Pro,
#[serde(rename = "kling_3p0_standard")]
Kling3p0Standard,
#[serde(rename = "kling_3p0_pro")]
Kling3p0Pro,
#[serde(rename = "seedance_1p0_lite")]
Seedance10Lite,
#[serde(rename = "seedance_1p0_pro")]
Seedance10Pro,
#[serde(rename = "seedance_1p5_pro")]
Seedance1p5Pro,
#[serde(rename = "seedance_2p0")]
Seedance2p0,
#[serde(rename = "sora_2")]
Sora2,
#[serde(rename = "sora_2_pro")]
Sora2Pro,
#[serde(rename = "veo_2")]
Veo2,
#[serde(rename = "veo_3")]
Veo3,
#[serde(rename = "veo_3_fast")]
Veo3Fast,
#[serde(rename = "veo_3p1")]
Veo3p1,
#[serde(rename = "veo_3p1_fast")]
Veo3p1Fast,
// 3D Object generation models
#[serde(rename = "hunyuan_3d_2p0")]
Hunyuan3d2_0,
#[serde(rename = "hunyuan_3d_2p1")]
Hunyuan3d2_1,
#[serde(rename = "hunyuan_3d_3")]
Hunyuan3d3,
// Splat generation models (World Labs)
#[serde(rename = "marble_0p1_mini")]
Marble0p1Mini,
#[serde(rename = "marble_0p1_plus")]
Marble0p1Plus,
}
impl_enum_display_and_debug_using_to_str!(CommonModelType);
impl_mysql_enum_coders!(CommonModelType);
impl_mysql_from_row!(CommonModelType);
// NB: We can derive `sqlx::Type` instead of using `impl_mysql_enum_coders`
impl CommonModelType {
pub fn to_str(&self) -> &'static str {
match self {
// Image models
Self::Flux1Dev => "flux_1_dev",
Self::Flux1Schnell => "flux_1_schnell",
Self::FluxDevJuggernaut => "flux_dev_juggernaut",
Self::FluxPro1 => "flux_pro_1",
Self::FluxPro11 => "flux_pro_1p1",
Self::FluxPro11Ultra => "flux_pro_1p1_ultra",
Self::FluxProKontextMax => "flux_pro_kontext_max",
Self::Flux2LoraAngles => "flux_2_lora_angles",
Self::GptImage1 => "gpt_image_1",
Self::GptImage1p5 => "gpt_image_1p5",
Self::GrokImage => "grok_image",
Self::Recraft3 => "recraft_3",
Self::SeedEdit3 => "seededit_3",
Self::Qwen => "qwen",
Self::QwenEdit2511Angles => "qwen_edit_2511_angles",
Self::Gemini25Flash => "gemini_25_flash",
Self::NanoBanana => "nano_banana",
Self::NanoBanana2 => "nano_banana_2",
Self::NanoBananaPro => "nano_banana_pro",
Self::Seedream4 => "seedream_4",
Self::Seedream4p5 => "seedream_4p5",
Self::Seedream5Lite => "seedream_5_lite",
Self::Midjourney => "midjourney",
Self::MidjourneyV6 => "midjourney_v6",
Self::MidjourneyV6p1 => "midjourney_v6p1",
Self::MidjourneyV6p1Raw => "midjourney_v6p1_raw",
Self::MidjourneyV7 => "midjourney_v7",
Self::MidjourneyV7Draft => "midjourney_v7_draft",
Self::MidjourneyV7DraftRaw => "midjourney_v7_draft_raw",
Self::MidjourneyV7Raw => "midjourney_v7_raw",
// Video models
Self::GrokVideo => "grok_video",
Self::Kling16Pro => "kling_1p6_pro",
Self::Kling21Pro => "kling_2p1_pro",
Self::Kling21Master => "kling_2p1_master",
Self::Kling2p5TurboPro => "kling_2p5_turbo_pro",
Self::Kling2p6Pro => "kling_2p6_pro",
Self::Kling3p0Standard => "kling_3p0_standard",
Self::Kling3p0Pro => "kling_3p0_pro",
Self::Seedance10Lite => "seedance_1p0_lite",
Self::Seedance10Pro => "seedance_1p0_pro",
Self::Seedance1p5Pro => "seedance_1p5_pro",
Self::Seedance2p0 => "seedance_2p0",
Self::Sora2 => "sora_2",
Self::Sora2Pro => "sora_2_pro",
Self::Veo2 => "veo_2",
Self::Veo3 => "veo_3",
Self::Veo3Fast => "veo_3_fast",
Self::Veo3p1 => "veo_3p1",
Self::Veo3p1Fast => "veo_3p1_fast",
// 3D Object generation models
Self::Hunyuan3d2_0 => "hunyuan_3d_2p0",
Self::Hunyuan3d2_1 => "hunyuan_3d_2p1",
Self::Hunyuan3d3 => "hunyuan_3d_3",
// Splat generation models (World Labs)
Self::Marble0p1Mini => "marble_0p1_mini",
Self::Marble0p1Plus => "marble_0p1_plus",
}
}
pub fn from_str(job_status: &str) -> Result<Self, String> {
match job_status {
// Image models
"flux_1_dev" => Ok(Self::Flux1Dev),
"flux_1_schnell" => Ok(Self::Flux1Schnell),
"flux_dev_juggernaut" => Ok(Self::FluxDevJuggernaut),
"flux_pro_1" => Ok(Self::FluxPro1),
"flux_pro_1p1" => Ok(Self::FluxPro11),
"flux_pro_1p1_ultra" => Ok(Self::FluxPro11Ultra),
"flux_pro_kontext_max" => Ok(Self::FluxProKontextMax),
"flux_2_lora_angles" => Ok(Self::Flux2LoraAngles),
"gpt_image_1" => Ok(Self::GptImage1),
"gpt_image_1p5" => Ok(Self::GptImage1p5),
"grok_image" => Ok(Self::GrokImage),
"recraft_3" => Ok(Self::Recraft3),
"seededit_3" => Ok(Self::SeedEdit3),
"qwen" => Ok(Self::Qwen),
"qwen_edit_2511_angles" => Ok(Self::QwenEdit2511Angles),
"gemini_25_flash" => Ok(Self::Gemini25Flash),
"nano_banana" => Ok(Self::NanoBanana),
"nano_banana_2" => Ok(Self::NanoBanana2),
"nano_banana_pro" => Ok(Self::NanoBananaPro),
"seedream_4" => Ok(Self::Seedream4),
"seedream_4p5" => Ok(Self::Seedream4p5),
"seedream_5_lite" => Ok(Self::Seedream5Lite),
"midjourney" => Ok(Self::Midjourney),
"midjourney_v6" => Ok(Self::MidjourneyV6),
"midjourney_v6p1" => Ok(Self::MidjourneyV6p1),
"midjourney_v6p1_raw" => Ok(Self::MidjourneyV6p1Raw),
"midjourney_v7" => Ok(Self::MidjourneyV7),
"midjourney_v7_draft" => Ok(Self::MidjourneyV7Draft),
"midjourney_v7_draft_raw" => Ok(Self::MidjourneyV7DraftRaw),
"midjourney_v7_raw" => Ok(Self::MidjourneyV7Raw),
// Video models
"grok_video" => Ok(Self::GrokVideo),
"kling_1p6_pro" => Ok(Self::Kling16Pro),
"kling_2p1_pro" => Ok(Self::Kling21Pro),
"kling_2p1_master" => Ok(Self::Kling21Master),
"kling_2p5_turbo_pro" => Ok(Self::Kling2p5TurboPro),
"kling_2p6_pro" => Ok(Self::Kling2p6Pro),
"kling_3p0_standard" => Ok(Self::Kling3p0Standard),
"kling_3p0_pro" => Ok(Self::Kling3p0Pro),
"seedance_1p0_lite" => Ok(Self::Seedance10Lite),
"seedance_1p0_pro" => Ok(Self::Seedance10Pro),
"seedance_1p5_pro" => Ok(Self::Seedance1p5Pro),
"seedance_2p0" => Ok(Self::Seedance2p0),
"sora_2" => Ok(Self::Sora2),
"sora_2_pro" => Ok(Self::Sora2Pro),
"veo_2" => Ok(Self::Veo2),
"veo_3" => Ok(Self::Veo3),
"veo_3_fast" => Ok(Self::Veo3Fast),
"veo_3p1" => Ok(Self::Veo3p1),
"veo_3p1_fast" => Ok(Self::Veo3p1Fast),
// 3D Object generation models
"hunyuan_3d_2p0" => Ok(Self::Hunyuan3d2_0),
"hunyuan_3d_2p1" => Ok(Self::Hunyuan3d2_1),
"hunyuan_3d_3" => Ok(Self::Hunyuan3d3),
// Splat generation models (World Labs)
"marble_0p1_mini" => Ok(Self::Marble0p1Mini),
"marble_0p1_plus" => Ok(Self::Marble0p1Plus),
_ => Err(format!("invalid model_type: {:?}", job_status)),
}
}
pub fn all_variants() -> BTreeSet<Self> {
// NB: BTreeSet is sorted
// NB: BTreeSet::from() isn't const, but not worth using LazyStatic, etc.
BTreeSet::from([
// Image models
Self::Flux1Dev,
Self::Flux1Schnell,
Self::FluxDevJuggernaut,
Self::FluxPro1,
Self::FluxPro11,
Self::FluxPro11Ultra,
Self::FluxProKontextMax,
Self::Flux2LoraAngles,
Self::GptImage1,
Self::GptImage1p5,
Self::GrokImage,
Self::Recraft3,
Self::SeedEdit3,
Self::Qwen,
Self::QwenEdit2511Angles,
Self::Gemini25Flash,
Self::NanoBanana,
Self::NanoBanana2,
Self::NanoBananaPro,
Self::Seedream4,
Self::Seedream4p5,
Self::Seedream5Lite,
Self::Midjourney,
Self::MidjourneyV6,
Self::MidjourneyV6p1,
Self::MidjourneyV6p1Raw,
Self::MidjourneyV7,
Self::MidjourneyV7Draft,
Self::MidjourneyV7DraftRaw,
Self::MidjourneyV7Raw,
// Video models
Self::GrokVideo,
Self::Kling16Pro,
Self::Kling21Pro,
Self::Kling21Master,
Self::Kling2p5TurboPro,
Self::Kling2p6Pro,
Self::Kling3p0Standard,
Self::Kling3p0Pro,
Self::Seedance10Lite,
Self::Seedance10Pro,
Self::Seedance1p5Pro,
Self::Seedance2p0,
Self::Sora2,
Self::Sora2Pro,
Self::Veo2,
Self::Veo3,
Self::Veo3Fast,
Self::Veo3p1,
Self::Veo3p1Fast,
// 3D Object generation models
Self::Hunyuan3d2_0,
Self::Hunyuan3d2_1,
Self::Hunyuan3d3,
// Splat generation models (World Labs)
Self::Marble0p1Mini,
Self::Marble0p1Plus,
])
}
/// Returns the broad model class for this model type.
pub fn get_model_class(&self) -> CommonModelClass {
match self {
// Image models
Self::Flux1Dev => CommonModelClass::Image,
Self::Flux1Schnell => CommonModelClass::Image,
Self::FluxDevJuggernaut => CommonModelClass::Image,
Self::FluxPro1 => CommonModelClass::Image,
Self::FluxPro11 => CommonModelClass::Image,
Self::FluxPro11Ultra => CommonModelClass::Image,
Self::FluxProKontextMax => CommonModelClass::Image,
Self::Flux2LoraAngles => CommonModelClass::Image,
Self::GptImage1 => CommonModelClass::Image,
Self::GptImage1p5 => CommonModelClass::Image,
Self::GrokImage => CommonModelClass::Image,
Self::Recraft3 => CommonModelClass::Image,
Self::SeedEdit3 => CommonModelClass::Image,
Self::Qwen => CommonModelClass::Image,
Self::QwenEdit2511Angles => CommonModelClass::Image,
Self::Gemini25Flash => CommonModelClass::Image,
Self::NanoBanana => CommonModelClass::Image,
Self::NanoBanana2 => CommonModelClass::Image,
Self::NanoBananaPro => CommonModelClass::Image,
Self::Seedream4 => CommonModelClass::Image,
Self::Seedream4p5 => CommonModelClass::Image,
Self::Seedream5Lite => CommonModelClass::Image,
Self::Midjourney => CommonModelClass::Image,
Self::MidjourneyV6 => CommonModelClass::Image,
Self::MidjourneyV6p1 => CommonModelClass::Image,
Self::MidjourneyV6p1Raw => CommonModelClass::Image,
Self::MidjourneyV7 => CommonModelClass::Image,
Self::MidjourneyV7Draft => CommonModelClass::Image,
Self::MidjourneyV7DraftRaw => CommonModelClass::Image,
Self::MidjourneyV7Raw => CommonModelClass::Image,
// Video models
Self::GrokVideo => CommonModelClass::Video,
Self::Kling16Pro => CommonModelClass::Video,
Self::Kling21Pro => CommonModelClass::Video,
Self::Kling21Master => CommonModelClass::Video,
Self::Kling2p5TurboPro => CommonModelClass::Video,
Self::Kling2p6Pro => CommonModelClass::Video,
Self::Kling3p0Standard => CommonModelClass::Video,
Self::Kling3p0Pro => CommonModelClass::Video,
Self::Seedance10Lite => CommonModelClass::Video,
Self::Seedance10Pro => CommonModelClass::Video,
Self::Seedance1p5Pro => CommonModelClass::Video,
Self::Seedance2p0 => CommonModelClass::Video,
Self::Sora2 => CommonModelClass::Video,
Self::Sora2Pro => CommonModelClass::Video,
Self::Veo2 => CommonModelClass::Video,
Self::Veo3 => CommonModelClass::Video,
Self::Veo3Fast => CommonModelClass::Video,
Self::Veo3p1 => CommonModelClass::Video,
Self::Veo3p1Fast => CommonModelClass::Video,
// 3D Object generation models (mesh)
Self::Hunyuan3d2_0 => CommonModelClass::DimensionalMesh,
Self::Hunyuan3d2_1 => CommonModelClass::DimensionalMesh,
Self::Hunyuan3d3 => CommonModelClass::DimensionalMesh,
// Splat generation models (World Labs)
Self::Marble0p1Mini => CommonModelClass::DimensionalSplat,
Self::Marble0p1Plus => CommonModelClass::DimensionalSplat,
}
}
}
#[cfg(test)]
mod tests {
use crate::common::generation::common_model_class::CommonModelClass;
use crate::common::generation::common_model_type::CommonModelType;
use crate::common::generation::common_model_type::MAX_LENGTH;
use crate::test_helpers::assert_serialization;
mod explicit_checks {
use super::*;
#[test]
fn test_serialization() {
// Image models
assert_serialization(CommonModelType::Flux1Dev, "flux_1_dev");
assert_serialization(CommonModelType::Flux1Schnell, "flux_1_schnell");
assert_serialization(CommonModelType::FluxDevJuggernaut, "flux_dev_juggernaut");
assert_serialization(CommonModelType::FluxPro1, "flux_pro_1");
assert_serialization(CommonModelType::FluxPro11, "flux_pro_1p1");
assert_serialization(CommonModelType::FluxPro11Ultra, "flux_pro_1p1_ultra");
assert_serialization(CommonModelType::FluxProKontextMax, "flux_pro_kontext_max");
assert_serialization(CommonModelType::Flux2LoraAngles, "flux_2_lora_angles");
assert_serialization(CommonModelType::GptImage1, "gpt_image_1");
assert_serialization(CommonModelType::GptImage1p5, "gpt_image_1p5");
assert_serialization(CommonModelType::GrokImage, "grok_image");
assert_serialization(CommonModelType::Recraft3, "recraft_3");
assert_serialization(CommonModelType::SeedEdit3, "seededit_3");
assert_serialization(CommonModelType::Qwen, "qwen");
assert_serialization(CommonModelType::QwenEdit2511Angles, "qwen_edit_2511_angles");
assert_serialization(CommonModelType::Gemini25Flash, "gemini_25_flash");
assert_serialization(CommonModelType::NanoBanana, "nano_banana");
assert_serialization(CommonModelType::NanoBanana2, "nano_banana_2");
assert_serialization(CommonModelType::NanoBananaPro, "nano_banana_pro");
assert_serialization(CommonModelType::Seedream4, "seedream_4");
assert_serialization(CommonModelType::Seedream4p5, "seedream_4p5");
assert_serialization(CommonModelType::Seedream5Lite, "seedream_5_lite");
assert_serialization(CommonModelType::Midjourney, "midjourney");
assert_serialization(CommonModelType::MidjourneyV6, "midjourney_v6");
assert_serialization(CommonModelType::MidjourneyV6p1, "midjourney_v6p1");
assert_serialization(CommonModelType::MidjourneyV6p1Raw, "midjourney_v6p1_raw");
assert_serialization(CommonModelType::MidjourneyV7, "midjourney_v7");
assert_serialization(CommonModelType::MidjourneyV7Draft, "midjourney_v7_draft");
assert_serialization(CommonModelType::MidjourneyV7DraftRaw, "midjourney_v7_draft_raw");
assert_serialization(CommonModelType::MidjourneyV7Raw, "midjourney_v7_raw");
// Video models
assert_serialization(CommonModelType::GrokVideo, "grok_video");
assert_serialization(CommonModelType::Kling16Pro, "kling_1p6_pro");
assert_serialization(CommonModelType::Kling21Pro, "kling_2p1_pro");
assert_serialization(CommonModelType::Kling21Master, "kling_2p1_master");
assert_serialization(CommonModelType::Kling2p5TurboPro, "kling_2p5_turbo_pro");
assert_serialization(CommonModelType::Kling2p6Pro, "kling_2p6_pro");
assert_serialization(CommonModelType::Kling3p0Standard, "kling_3p0_standard");
assert_serialization(CommonModelType::Kling3p0Pro, "kling_3p0_pro");
assert_serialization(CommonModelType::Seedance10Lite, "seedance_1p0_lite");
assert_serialization(CommonModelType::Seedance10Pro, "seedance_1p0_pro");
assert_serialization(CommonModelType::Seedance1p5Pro, "seedance_1p5_pro");
assert_serialization(CommonModelType::Seedance2p0, "seedance_2p0");
assert_serialization(CommonModelType::Sora2, "sora_2");
assert_serialization(CommonModelType::Sora2Pro, "sora_2_pro");
assert_serialization(CommonModelType::Veo2, "veo_2");
assert_serialization(CommonModelType::Veo3, "veo_3");
assert_serialization(CommonModelType::Veo3Fast, "veo_3_fast");
assert_serialization(CommonModelType::Veo3p1, "veo_3p1");
assert_serialization(CommonModelType::Veo3p1Fast, "veo_3p1_fast");
// 3D Object generation models
assert_serialization(CommonModelType::Hunyuan3d2_0, "hunyuan_3d_2p0");
assert_serialization(CommonModelType::Hunyuan3d2_1, "hunyuan_3d_2p1");
assert_serialization(CommonModelType::Hunyuan3d3, "hunyuan_3d_3");
// Splat generation models (World Labs)
assert_serialization(CommonModelType::Marble0p1Mini, "marble_0p1_mini");
assert_serialization(CommonModelType::Marble0p1Plus, "marble_0p1_plus");
}
#[test]
fn to_str() {
// Image models
assert_eq!(CommonModelType::Flux1Dev.to_str(), "flux_1_dev");
assert_eq!(CommonModelType::Flux1Schnell.to_str(), "flux_1_schnell");
assert_eq!(CommonModelType::FluxDevJuggernaut.to_str(), "flux_dev_juggernaut");
assert_eq!(CommonModelType::FluxPro1.to_str(), "flux_pro_1");
assert_eq!(CommonModelType::FluxPro11.to_str(), "flux_pro_1p1");
assert_eq!(CommonModelType::FluxPro11Ultra.to_str(), "flux_pro_1p1_ultra");
assert_eq!(CommonModelType::FluxProKontextMax.to_str(), "flux_pro_kontext_max");
assert_eq!(CommonModelType::Flux2LoraAngles.to_str(), "flux_2_lora_angles");
assert_eq!(CommonModelType::GptImage1.to_str(), "gpt_image_1");
assert_eq!(CommonModelType::GptImage1p5.to_str(), "gpt_image_1p5");
assert_eq!(CommonModelType::GrokImage.to_str(), "grok_image");
assert_eq!(CommonModelType::Recraft3.to_str(), "recraft_3");
assert_eq!(CommonModelType::SeedEdit3.to_str(), "seededit_3");
assert_eq!(CommonModelType::Qwen.to_str(), "qwen");
assert_eq!(CommonModelType::QwenEdit2511Angles.to_str(), "qwen_edit_2511_angles");
assert_eq!(CommonModelType::Gemini25Flash.to_str(), "gemini_25_flash");
assert_eq!(CommonModelType::NanoBanana.to_str(), "nano_banana");
assert_eq!(CommonModelType::NanoBanana2.to_str(), "nano_banana_2");
assert_eq!(CommonModelType::NanoBananaPro.to_str(), "nano_banana_pro");
assert_eq!(CommonModelType::Seedream4.to_str(), "seedream_4");
assert_eq!(CommonModelType::Seedream4p5.to_str(), "seedream_4p5");
assert_eq!(CommonModelType::Seedream5Lite.to_str(), "seedream_5_lite");
assert_eq!(CommonModelType::Midjourney.to_str(), "midjourney");
assert_eq!(CommonModelType::MidjourneyV6.to_str(), "midjourney_v6");
assert_eq!(CommonModelType::MidjourneyV6p1.to_str(), "midjourney_v6p1");
assert_eq!(CommonModelType::MidjourneyV6p1Raw.to_str(), "midjourney_v6p1_raw");
assert_eq!(CommonModelType::MidjourneyV7.to_str(), "midjourney_v7");
assert_eq!(CommonModelType::MidjourneyV7Draft.to_str(), "midjourney_v7_draft");
assert_eq!(CommonModelType::MidjourneyV7DraftRaw.to_str(), "midjourney_v7_draft_raw");
assert_eq!(CommonModelType::MidjourneyV7Raw.to_str(), "midjourney_v7_raw");
// Video models
assert_eq!(CommonModelType::GrokVideo.to_str(), "grok_video");
assert_eq!(CommonModelType::Kling16Pro.to_str(), "kling_1p6_pro");
assert_eq!(CommonModelType::Kling21Pro.to_str(), "kling_2p1_pro");
assert_eq!(CommonModelType::Kling21Master.to_str(), "kling_2p1_master");
assert_eq!(CommonModelType::Kling2p5TurboPro.to_str(), "kling_2p5_turbo_pro");
assert_eq!(CommonModelType::Kling2p6Pro.to_str(), "kling_2p6_pro");
assert_eq!(CommonModelType::Kling3p0Standard.to_str(), "kling_3p0_standard");
assert_eq!(CommonModelType::Kling3p0Pro.to_str(), "kling_3p0_pro");
assert_eq!(CommonModelType::Seedance10Lite.to_str(), "seedance_1p0_lite");
assert_eq!(CommonModelType::Seedance10Pro.to_str(), "seedance_1p0_pro");
assert_eq!(CommonModelType::Seedance1p5Pro.to_str(), "seedance_1p5_pro");
assert_eq!(CommonModelType::Seedance2p0.to_str(), "seedance_2p0");
assert_eq!(CommonModelType::Sora2.to_str(), "sora_2");
assert_eq!(CommonModelType::Sora2Pro.to_str(), "sora_2_pro");
assert_eq!(CommonModelType::Veo2.to_str(), "veo_2");
assert_eq!(CommonModelType::Veo3.to_str(), "veo_3");
assert_eq!(CommonModelType::Veo3Fast.to_str(), "veo_3_fast");
assert_eq!(CommonModelType::Veo3p1.to_str(), "veo_3p1");
assert_eq!(CommonModelType::Veo3p1Fast.to_str(), "veo_3p1_fast");
// 3D Object generation models
assert_eq!(CommonModelType::Hunyuan3d2_0.to_str(), "hunyuan_3d_2p0");
assert_eq!(CommonModelType::Hunyuan3d2_1.to_str(), "hunyuan_3d_2p1");
assert_eq!(CommonModelType::Hunyuan3d3.to_str(), "hunyuan_3d_3");
// Splat generation models (World Labs)
assert_eq!(CommonModelType::Marble0p1Mini.to_str(), "marble_0p1_mini");
assert_eq!(CommonModelType::Marble0p1Plus.to_str(), "marble_0p1_plus");
}
#[test]
fn from_str() {
// Image models
assert_eq!(CommonModelType::from_str("flux_1_dev").unwrap(), CommonModelType::Flux1Dev);
assert_eq!(CommonModelType::from_str("flux_1_schnell").unwrap(), CommonModelType::Flux1Schnell);
assert_eq!(CommonModelType::from_str("flux_dev_juggernaut").unwrap(), CommonModelType::FluxDevJuggernaut);
assert_eq!(CommonModelType::from_str("flux_pro_1").unwrap(), CommonModelType::FluxPro1);
assert_eq!(CommonModelType::from_str("flux_pro_1p1").unwrap(), CommonModelType::FluxPro11);
assert_eq!(CommonModelType::from_str("flux_pro_1p1_ultra").unwrap(), CommonModelType::FluxPro11Ultra);
assert_eq!(CommonModelType::from_str("flux_pro_kontext_max").unwrap(), CommonModelType::FluxProKontextMax);
assert_eq!(CommonModelType::from_str("flux_2_lora_angles").unwrap(), CommonModelType::Flux2LoraAngles);
assert_eq!(CommonModelType::from_str("gpt_image_1").unwrap(), CommonModelType::GptImage1);
assert_eq!(CommonModelType::from_str("gpt_image_1p5").unwrap(), CommonModelType::GptImage1p5);
assert_eq!(CommonModelType::from_str("grok_image").unwrap(), CommonModelType::GrokImage);
assert_eq!(CommonModelType::from_str("recraft_3").unwrap(), CommonModelType::Recraft3);
assert_eq!(CommonModelType::from_str("seededit_3").unwrap(), CommonModelType::SeedEdit3);
assert_eq!(CommonModelType::from_str("qwen").unwrap(), CommonModelType::Qwen);
assert_eq!(CommonModelType::from_str("qwen_edit_2511_angles").unwrap(), CommonModelType::QwenEdit2511Angles);
assert_eq!(CommonModelType::from_str("gemini_25_flash").unwrap(), CommonModelType::Gemini25Flash);
assert_eq!(CommonModelType::from_str("nano_banana").unwrap(), CommonModelType::NanoBanana);
assert_eq!(CommonModelType::from_str("nano_banana_2").unwrap(), CommonModelType::NanoBanana2);
assert_eq!(CommonModelType::from_str("nano_banana_pro").unwrap(), CommonModelType::NanoBananaPro);
assert_eq!(CommonModelType::from_str("seedream_4").unwrap(), CommonModelType::Seedream4);
assert_eq!(CommonModelType::from_str("seedream_4p5").unwrap(), CommonModelType::Seedream4p5);
assert_eq!(CommonModelType::from_str("seedream_5_lite").unwrap(), CommonModelType::Seedream5Lite);
assert_eq!(CommonModelType::from_str("midjourney").unwrap(), CommonModelType::Midjourney);
assert_eq!(CommonModelType::from_str("midjourney_v6").unwrap(), CommonModelType::MidjourneyV6);
assert_eq!(CommonModelType::from_str("midjourney_v6p1").unwrap(), CommonModelType::MidjourneyV6p1);
assert_eq!(CommonModelType::from_str("midjourney_v6p1_raw").unwrap(), CommonModelType::MidjourneyV6p1Raw);
assert_eq!(CommonModelType::from_str("midjourney_v7").unwrap(), CommonModelType::MidjourneyV7);
assert_eq!(CommonModelType::from_str("midjourney_v7_draft").unwrap(), CommonModelType::MidjourneyV7Draft);
assert_eq!(CommonModelType::from_str("midjourney_v7_draft_raw").unwrap(), CommonModelType::MidjourneyV7DraftRaw);
assert_eq!(CommonModelType::from_str("midjourney_v7_raw").unwrap(), CommonModelType::MidjourneyV7Raw);
// Video models
assert_eq!(CommonModelType::from_str("grok_video").unwrap(), CommonModelType::GrokVideo);
assert_eq!(CommonModelType::from_str("kling_1p6_pro").unwrap(), CommonModelType::Kling16Pro);
assert_eq!(CommonModelType::from_str("kling_2p1_pro").unwrap(), CommonModelType::Kling21Pro);
assert_eq!(CommonModelType::from_str("kling_2p1_master").unwrap(), CommonModelType::Kling21Master);
assert_eq!(CommonModelType::from_str("kling_2p5_turbo_pro").unwrap(), CommonModelType::Kling2p5TurboPro);
assert_eq!(CommonModelType::from_str("kling_2p6_pro").unwrap(), CommonModelType::Kling2p6Pro);
assert_eq!(CommonModelType::from_str("kling_3p0_standard").unwrap(), CommonModelType::Kling3p0Standard);
assert_eq!(CommonModelType::from_str("kling_3p0_pro").unwrap(), CommonModelType::Kling3p0Pro);
assert_eq!(CommonModelType::from_str("seedance_1p0_lite").unwrap(), CommonModelType::Seedance10Lite);
assert_eq!(CommonModelType::from_str("seedance_1p0_pro").unwrap(), CommonModelType::Seedance10Pro);
assert_eq!(CommonModelType::from_str("seedance_1p5_pro").unwrap(), CommonModelType::Seedance1p5Pro);
assert_eq!(CommonModelType::from_str("seedance_2p0").unwrap(), CommonModelType::Seedance2p0);
assert_eq!(CommonModelType::from_str("sora_2").unwrap(), CommonModelType::Sora2);
assert_eq!(CommonModelType::from_str("sora_2_pro").unwrap(), CommonModelType::Sora2Pro);
assert_eq!(CommonModelType::from_str("veo_2").unwrap(), CommonModelType::Veo2);
assert_eq!(CommonModelType::from_str("veo_3").unwrap(), CommonModelType::Veo3);
assert_eq!(CommonModelType::from_str("veo_3_fast").unwrap(), CommonModelType::Veo3Fast);
assert_eq!(CommonModelType::from_str("veo_3p1").unwrap(), CommonModelType::Veo3p1);
assert_eq!(CommonModelType::from_str("veo_3p1_fast").unwrap(), CommonModelType::Veo3p1Fast);
// 3D Object generation models
assert_eq!(CommonModelType::from_str("hunyuan_3d_2p0").unwrap(), CommonModelType::Hunyuan3d2_0);
assert_eq!(CommonModelType::from_str("hunyuan_3d_2p1").unwrap(), CommonModelType::Hunyuan3d2_1);
assert_eq!(CommonModelType::from_str("hunyuan_3d_3").unwrap(), CommonModelType::Hunyuan3d3);
// Splat generation models (World Labs)
assert_eq!(CommonModelType::from_str("marble_0p1_mini").unwrap(), CommonModelType::Marble0p1Mini);
assert_eq!(CommonModelType::from_str("marble_0p1_plus").unwrap(), CommonModelType::Marble0p1Plus);
}
#[test]
fn all_variants() {
let mut variants = CommonModelType::all_variants();
assert_eq!(variants.len(), 54);
// Image models
assert_eq!(variants.pop_first(), Some(CommonModelType::Flux1Dev));
assert_eq!(variants.pop_first(), Some(CommonModelType::Flux1Schnell));
assert_eq!(variants.pop_first(), Some(CommonModelType::FluxDevJuggernaut));
assert_eq!(variants.pop_first(), Some(CommonModelType::FluxPro1));
assert_eq!(variants.pop_first(), Some(CommonModelType::FluxPro11));
assert_eq!(variants.pop_first(), Some(CommonModelType::FluxPro11Ultra));
assert_eq!(variants.pop_first(), Some(CommonModelType::FluxProKontextMax));
assert_eq!(variants.pop_first(), Some(CommonModelType::Flux2LoraAngles));
assert_eq!(variants.pop_first(), Some(CommonModelType::GptImage1));
assert_eq!(variants.pop_first(), Some(CommonModelType::GptImage1p5));
assert_eq!(variants.pop_first(), Some(CommonModelType::GrokImage));
assert_eq!(variants.pop_first(), Some(CommonModelType::Recraft3));
assert_eq!(variants.pop_first(), Some(CommonModelType::SeedEdit3));
assert_eq!(variants.pop_first(), Some(CommonModelType::Qwen));
assert_eq!(variants.pop_first(), Some(CommonModelType::QwenEdit2511Angles));
assert_eq!(variants.pop_first(), Some(CommonModelType::Gemini25Flash));
assert_eq!(variants.pop_first(), Some(CommonModelType::NanoBanana));
assert_eq!(variants.pop_first(), Some(CommonModelType::NanoBanana2));
assert_eq!(variants.pop_first(), Some(CommonModelType::NanoBananaPro));
assert_eq!(variants.pop_first(), Some(CommonModelType::Seedream4));
assert_eq!(variants.pop_first(), Some(CommonModelType::Seedream4p5));
assert_eq!(variants.pop_first(), Some(CommonModelType::Seedream5Lite));
assert_eq!(variants.pop_first(), Some(CommonModelType::Midjourney));
assert_eq!(variants.pop_first(), Some(CommonModelType::MidjourneyV6));
assert_eq!(variants.pop_first(), Some(CommonModelType::MidjourneyV6p1));
assert_eq!(variants.pop_first(), Some(CommonModelType::MidjourneyV6p1Raw));
assert_eq!(variants.pop_first(), Some(CommonModelType::MidjourneyV7));
assert_eq!(variants.pop_first(), Some(CommonModelType::MidjourneyV7Draft));
assert_eq!(variants.pop_first(), Some(CommonModelType::MidjourneyV7DraftRaw));
assert_eq!(variants.pop_first(), Some(CommonModelType::MidjourneyV7Raw));
// Video models
assert_eq!(variants.pop_first(), Some(CommonModelType::GrokVideo));
assert_eq!(variants.pop_first(), Some(CommonModelType::Kling16Pro));
assert_eq!(variants.pop_first(), Some(CommonModelType::Kling21Pro));
assert_eq!(variants.pop_first(), Some(CommonModelType::Kling21Master));
assert_eq!(variants.pop_first(), Some(CommonModelType::Kling2p5TurboPro));
assert_eq!(variants.pop_first(), Some(CommonModelType::Kling2p6Pro));
assert_eq!(variants.pop_first(), Some(CommonModelType::Kling3p0Standard));
assert_eq!(variants.pop_first(), Some(CommonModelType::Kling3p0Pro));
assert_eq!(variants.pop_first(), Some(CommonModelType::Seedance10Lite));
assert_eq!(variants.pop_first(), Some(CommonModelType::Seedance10Pro));
assert_eq!(variants.pop_first(), Some(CommonModelType::Seedance1p5Pro));
assert_eq!(variants.pop_first(), Some(CommonModelType::Seedance2p0));
assert_eq!(variants.pop_first(), Some(CommonModelType::Sora2));
assert_eq!(variants.pop_first(), Some(CommonModelType::Sora2Pro));
assert_eq!(variants.pop_first(), Some(CommonModelType::Veo2));
assert_eq!(variants.pop_first(), Some(CommonModelType::Veo3));
assert_eq!(variants.pop_first(), Some(CommonModelType::Veo3Fast));
assert_eq!(variants.pop_first(), Some(CommonModelType::Veo3p1));
assert_eq!(variants.pop_first(), Some(CommonModelType::Veo3p1Fast));
// 3D Object generation models
assert_eq!(variants.pop_first(), Some(CommonModelType::Hunyuan3d2_0));
assert_eq!(variants.pop_first(), Some(CommonModelType::Hunyuan3d2_1));
assert_eq!(variants.pop_first(), Some(CommonModelType::Hunyuan3d3));
// Splat generation models (World Labs)
assert_eq!(variants.pop_first(), Some(CommonModelType::Marble0p1Mini));
assert_eq!(variants.pop_first(), Some(CommonModelType::Marble0p1Plus));
assert_eq!(variants.pop_first(), None);
}
}
mod mechanical_checks {
use super::*;
#[test]
fn variant_length() {
use strum::IntoEnumIterator;
assert_eq!(CommonModelType::all_variants().len(), CommonModelType::iter().len());
}
#[test]
fn round_trip() {
for variant in CommonModelType::all_variants() {
// Test to_str(), from_str(), Display, and Debug.
assert_eq!(variant, CommonModelType::from_str(variant.to_str()).unwrap());
assert_eq!(variant, CommonModelType::from_str(&format!("{}", variant)).unwrap());
assert_eq!(variant, CommonModelType::from_str(&format!("{:?}", variant)).unwrap());
}
}
#[test]
fn serialized_length_ok_for_database() {
for variant in CommonModelType::all_variants() {
let serialized = variant.to_str();
assert!(serialized.len() > 0, "variant {:?} is too short", variant);
assert!(serialized.len() <= MAX_LENGTH, "variant {:?} is too long", variant);
}
}
#[test]
fn serialized_names_must_not_contain_dots() {
for variant in CommonModelType::all_variants() {
let to_str_value = variant.to_str();
assert!(!to_str_value.contains('.'), "to_str() for {:?} contains a dot: {:?}", variant, to_str_value);
let json_value = serde_json::to_string(&variant).unwrap().replace('"', "");
assert!(!json_value.contains('.'), "JSON serialization for {:?} contains a dot: {:?}", variant, json_value);
}
}
#[test]
fn serialized_names_must_only_contain_lowercase_alphanumeric_and_underscore() {
let valid_pattern = regex::Regex::new(r"^[a-z0-9_]+$").unwrap();
for variant in CommonModelType::all_variants() {
let to_str_value = variant.to_str();
assert!(valid_pattern.is_match(to_str_value),
"to_str() for {:?} contains invalid characters: {:?} (only a-z, 0-9, _ allowed)", variant, to_str_value);
let json_value = serde_json::to_string(&variant).unwrap().replace('"', "");
assert!(valid_pattern.is_match(&json_value),
"JSON serialization for {:?} contains invalid characters: {:?} (only a-z, 0-9, _ allowed)", variant, json_value);
}
}
#[test]
fn every_variant_has_a_model_class() {
for variant in CommonModelType::all_variants() {
let class = variant.get_model_class();
// Verify the class is a known value (not panicking is the main test)
assert!(CommonModelClass::all_variants().contains(&class),
"get_model_class() for {:?} returned {:?} which is not a known CommonModelClass variant", variant, class);
}
}
#[test]
fn image_models_return_image_class() {
assert_eq!(CommonModelType::Flux1Dev.get_model_class(), CommonModelClass::Image);
assert_eq!(CommonModelType::GptImage1.get_model_class(), CommonModelClass::Image);
assert_eq!(CommonModelType::Midjourney.get_model_class(), CommonModelClass::Image);
}
#[test]
fn video_models_return_video_class() {
assert_eq!(CommonModelType::Veo3.get_model_class(), CommonModelClass::Video);
assert_eq!(CommonModelType::Sora2.get_model_class(), CommonModelClass::Video);
assert_eq!(CommonModelType::Kling3p0Pro.get_model_class(), CommonModelClass::Video);
}
#[test]
fn dimensional_models_return_correct_class() {
assert_eq!(CommonModelType::Hunyuan3d2_0.get_model_class(), CommonModelClass::DimensionalMesh);
assert_eq!(CommonModelType::Hunyuan3d3.get_model_class(), CommonModelClass::DimensionalMesh);
assert_eq!(CommonModelType::Marble0p1Mini.get_model_class(), CommonModelClass::DimensionalSplat);
assert_eq!(CommonModelType::Marble0p1Plus.get_model_class(), CommonModelClass::DimensionalSplat);
}
}
}
@@ -0,0 +1,177 @@
use std::collections::BTreeSet;
#[cfg(test)]
use strum::EnumCount;
#[cfg(test)]
use strum::EnumIter;
use utoipa::ToSchema;
use crate::error::enum_error::EnumError;
/// Maximum serialized string length for database storage.
pub const MAX_LENGTH: usize = 16;
/// Common resolutions for generation.
///
/// NB: Keep the max serialized length to 16 characters.
#[cfg_attr(test, derive(EnumIter, EnumCount))]
#[derive(Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd, Serialize, Deserialize, ToSchema)]
#[serde(rename_all = "snake_case")]
pub enum CommonResolution {
OneK,
TwoK,
ThreeK,
FourK,
// TODO: Add 480p(?), 720p(?), 1080p, etc. as there are some models that use these
}
impl_enum_display_and_debug_using_to_str!(CommonResolution);
impl_mysql_enum_coders!(CommonResolution);
impl_mysql_from_row!(CommonResolution);
impl CommonResolution {
pub fn to_str(&self) -> &'static str {
match self {
Self::OneK => "one_k",
Self::TwoK => "two_k",
Self::ThreeK => "three_k",
Self::FourK => "four_k",
}
}
pub fn from_str(value: &str) -> Result<Self, EnumError> {
match value {
"one_k" => Ok(Self::OneK),
"two_k" => Ok(Self::TwoK),
"three_k" => Ok(Self::ThreeK),
"four_k" => Ok(Self::FourK),
_ => Err(EnumError::CouldNotConvertFromString(value.to_string())),
}
}
pub fn all_variants() -> BTreeSet<Self> {
BTreeSet::from([
Self::OneK,
Self::TwoK,
Self::ThreeK,
Self::FourK,
])
}
}
#[cfg(test)]
mod tests {
use crate::common::generation::common_resolution::CommonResolution;
use crate::common::generation::common_resolution::MAX_LENGTH;
use crate::test_helpers::assert_serialization;
mod explicit_checks {
use super::*;
use crate::error::enum_error::EnumError;
#[test]
fn test_serialization() {
assert_serialization(CommonResolution::OneK, "one_k");
assert_serialization(CommonResolution::TwoK, "two_k");
assert_serialization(CommonResolution::ThreeK, "three_k");
assert_serialization(CommonResolution::FourK, "four_k");
}
#[test]
fn to_str() {
assert_eq!(CommonResolution::OneK.to_str(), "one_k");
assert_eq!(CommonResolution::TwoK.to_str(), "two_k");
assert_eq!(CommonResolution::ThreeK.to_str(), "three_k");
assert_eq!(CommonResolution::FourK.to_str(), "four_k");
}
#[test]
fn from_str() {
assert_eq!(CommonResolution::from_str("one_k").unwrap(), CommonResolution::OneK);
assert_eq!(CommonResolution::from_str("two_k").unwrap(), CommonResolution::TwoK);
assert_eq!(CommonResolution::from_str("three_k").unwrap(), CommonResolution::ThreeK);
assert_eq!(CommonResolution::from_str("four_k").unwrap(), CommonResolution::FourK);
}
#[test]
fn from_str_err() {
let result = CommonResolution::from_str("invalid");
assert!(result.is_err());
if let Err(EnumError::CouldNotConvertFromString(value)) = result {
assert_eq!(value, "invalid");
} else {
panic!("Expected EnumError::CouldNotConvertFromString");
}
}
#[test]
fn all_variants() {
let mut variants = CommonResolution::all_variants();
assert_eq!(variants.len(), 4);
assert_eq!(variants.pop_first(), Some(CommonResolution::OneK));
assert_eq!(variants.pop_first(), Some(CommonResolution::TwoK));
assert_eq!(variants.pop_first(), Some(CommonResolution::ThreeK));
assert_eq!(variants.pop_first(), Some(CommonResolution::FourK));
assert_eq!(variants.pop_first(), None);
}
}
mod mechanical_checks {
use super::*;
#[test]
fn variant_length() {
use strum::IntoEnumIterator;
assert_eq!(CommonResolution::all_variants().len(), CommonResolution::iter().len());
}
#[test]
fn round_trip() {
for variant in CommonResolution::all_variants() {
assert_eq!(variant, CommonResolution::from_str(variant.to_str()).unwrap());
assert_eq!(variant, CommonResolution::from_str(&format!("{}", variant)).unwrap());
assert_eq!(variant, CommonResolution::from_str(&format!("{:?}", variant)).unwrap());
}
}
#[test]
fn serialized_length_ok_for_database() {
for variant in CommonResolution::all_variants() {
let serialized = variant.to_str();
assert!(serialized.len() > 0, "variant {:?} is too short", variant);
assert!(serialized.len() <= MAX_LENGTH, "variant {:?} is too long via to_str()", variant);
}
for variant in CommonResolution::all_variants() {
let json = serde_json::to_string(&variant).unwrap().replace('"', "");
assert!(json.len() <= MAX_LENGTH, "variant {:?} is too long via JSON: {:?}", variant, json);
}
}
#[test]
fn serialized_names_must_not_contain_dots() {
for variant in CommonResolution::all_variants() {
let to_str_value = variant.to_str();
assert!(!to_str_value.contains('.'), "to_str() for {:?} contains a dot: {:?}", variant, to_str_value);
let json_value = serde_json::to_string(&variant).unwrap().replace('"', "");
assert!(!json_value.contains('.'), "JSON serialization for {:?} contains a dot: {:?}", variant, json_value);
}
}
#[test]
fn serialized_names_must_only_contain_lowercase_alphanumeric_and_underscore() {
let valid_pattern = regex::Regex::new(r"^[a-z0-9_]+$").unwrap();
for variant in CommonResolution::all_variants() {
let to_str_value = variant.to_str();
assert!(valid_pattern.is_match(to_str_value),
"to_str() for {:?} contains invalid characters: {:?} (only a-z, 0-9, _ allowed)", variant, to_str_value);
let json_value = serde_json::to_string(&variant).unwrap().replace('"', "");
assert!(valid_pattern.is_match(&json_value),
"JSON serialization for {:?} contains invalid characters: {:?} (only a-z, 0-9, _ allowed)", variant, json_value);
}
}
}
}
@@ -1,12 +0,0 @@
use utoipa::ToSchema;
/// Common video resolutions.
/// Mirrors artcraft_router::api::common_resolution::CommonResolution.
#[derive(Copy, Clone, Debug, Serialize, Deserialize, ToSchema)]
#[serde(rename_all = "snake_case")]
pub enum CommonVideoResolution {
OneK,
TwoK,
ThreeK,
FourK,
}
@@ -1,5 +1,8 @@
pub mod common_aspect_ratio;
pub mod common_generation_mode;
pub mod common_image_model;
pub mod common_model_class;
pub mod common_model_type;
pub mod common_resolution;
pub mod common_splat_model;
pub mod common_video_model;
pub mod common_video_resolution;
@@ -4,7 +4,6 @@ pub mod generation;
pub mod generation_provider;
pub mod job_status;
pub mod job_status_plus;
pub mod model_type;
pub mod payments_namespace;
pub mod stripe_subscription_status;
pub mod view_as;
@@ -1,645 +0,0 @@
use std::collections::BTreeSet;
#[cfg(test)]
use strum::EnumCount;
#[cfg(test)]
use strum::EnumIter;
use utoipa::ToSchema;
/// NB: This will be used by a variety of tables (MySQL and sqlite)!
/// Keep the max length to 24 characters.
#[cfg_attr(test, derive(EnumIter, EnumCount))]
#[derive(Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd, Serialize, Deserialize, ToSchema)]
#[serde(rename_all = "snake_case")]
pub enum ModelType {
// Image models
#[serde(rename = "flux_1_dev")]
Flux1Dev,
#[serde(rename = "flux_1_schnell")]
Flux1Schnell,
#[serde(rename = "flux_dev_juggernaut")]
FluxDevJuggernaut,
#[serde(rename = "flux_pro_1")]
FluxPro1,
#[serde(rename = "flux_pro_1p1")]
FluxPro11,
#[serde(rename = "flux_pro_1p1_ultra")]
FluxPro11Ultra,
#[serde(rename = "flux_pro_kontext_max")]
FluxProKontextMax,
#[serde(rename = "flux_2_lora_angles")]
Flux2LoraAngles,
#[serde(rename = "gpt_image_1")]
GptImage1,
#[serde(rename = "gpt_image_1p5")]
GptImage1p5,
// Generic grok image model without a version
#[serde(rename = "grok_image")]
GrokImage,
#[serde(rename = "recraft_3")]
Recraft3,
#[serde(rename = "seededit_3")]
SeedEdit3,
#[serde(rename = "qwen")]
Qwen,
#[serde(rename = "qwen_edit_2511_angles")]
QwenEdit2511Angles,
/// Gemini 2.5 Flash, AKA "Nano Banana"
#[serde(rename = "gemini_25_flash")]
Gemini25Flash,
#[serde(rename = "nano_banana")]
NanoBanana,
#[serde(rename = "nano_banana_2")]
NanoBanana2,
#[serde(rename = "nano_banana_pro")]
NanoBananaPro,
#[serde(rename = "seedream_4")]
Seedream4,
#[serde(rename = "seedream_4p5")]
Seedream4p5,
#[serde(rename = "seedream_5_lite")]
Seedream5Lite,
/// Midjourney without distinguishing a model type or version
#[serde(rename = "midjourney")]
Midjourney,
#[serde(rename = "midjourney_v6")]
MidjourneyV6,
#[serde(rename = "midjourney_v6p1")]
MidjourneyV6p1,
#[serde(rename = "midjourney_v6p1_raw")]
MidjourneyV6p1Raw,
#[serde(rename = "midjourney_v7")]
MidjourneyV7,
#[serde(rename = "midjourney_v7_draft")]
MidjourneyV7Draft,
#[serde(rename = "midjourney_v7_draft_raw")]
MidjourneyV7DraftRaw,
#[serde(rename = "midjourney_v7_raw")]
MidjourneyV7Raw,
//// Image Infill models
//#[serde(rename = "flux_pro_1_infill")]
//FluxPro1Infill,
// Video models
// Generic grok video model without a version
#[serde(rename = "grok_video")]
GrokVideo,
#[serde(rename = "kling_1p6_pro")]
Kling16Pro,
#[serde(rename = "kling_2p1_pro")]
Kling21Pro,
#[serde(rename = "kling_2p1_master")]
Kling21Master,
#[serde(rename = "kling_2p5_turbo_pro")]
Kling2p5TurboPro,
#[serde(rename = "kling_2p6_pro")]
Kling2p6Pro,
#[serde(rename = "kling_3p0_standard")]
Kling3p0Standard,
#[serde(rename = "kling_3p0_pro")]
Kling3p0Pro,
#[serde(rename = "seedance_1p0_lite")]
Seedance10Lite,
#[serde(rename = "seedance_1p0_pro")]
Seedance10Pro,
#[serde(rename = "seedance_1p5_pro")]
Seedance1p5Pro,
#[serde(rename = "seedance_2p0")]
Seedance2p0,
#[serde(rename = "sora_2")]
Sora2,
#[serde(rename = "sora_2_pro")]
Sora2Pro,
#[serde(rename = "veo_2")]
Veo2,
#[serde(rename = "veo_3")]
Veo3,
#[serde(rename = "veo_3_fast")]
Veo3Fast,
#[serde(rename = "veo_3p1")]
Veo3p1,
#[serde(rename = "veo_3p1_fast")]
Veo3p1Fast,
// 3D Object generation models
#[serde(rename = "hunyuan_3d_2p0")]
Hunyuan3d2_0,
#[serde(rename = "hunyuan_3d_2p1")]
Hunyuan3d2_1,
#[serde(rename = "hunyuan_3d_3")]
Hunyuan3d3,
// Splat generation models (World Labs)
#[serde(rename = "marble_0p1_mini")]
Marble0p1Mini,
#[serde(rename = "marble_0p1_plus")]
Marble0p1Plus,
}
impl_enum_display_and_debug_using_to_str!(ModelType);
impl_mysql_enum_coders!(ModelType);
impl_mysql_from_row!(ModelType);
// NB: We can derive `sqlx::Type` instead of using `impl_mysql_enum_coders`
impl ModelType {
pub fn to_str(&self) -> &'static str {
match self {
// Image models
Self::Flux1Dev => "flux_1_dev",
Self::Flux1Schnell => "flux_1_schnell",
Self::FluxDevJuggernaut => "flux_dev_juggernaut",
Self::FluxPro1 => "flux_pro_1",
Self::FluxPro11 => "flux_pro_1p1",
Self::FluxPro11Ultra => "flux_pro_1p1_ultra",
Self::FluxProKontextMax => "flux_pro_kontext_max",
Self::Flux2LoraAngles => "flux_2_lora_angles",
Self::GptImage1 => "gpt_image_1",
Self::GptImage1p5 => "gpt_image_1p5",
Self::GrokImage => "grok_image",
Self::Recraft3 => "recraft_3",
Self::SeedEdit3 => "seededit_3",
Self::Qwen => "qwen",
Self::QwenEdit2511Angles => "qwen_edit_2511_angles",
Self::Gemini25Flash => "gemini_25_flash",
Self::NanoBanana => "nano_banana",
Self::NanoBanana2 => "nano_banana_2",
Self::NanoBananaPro => "nano_banana_pro",
Self::Seedream4 => "seedream_4",
Self::Seedream4p5 => "seedream_4p5",
Self::Seedream5Lite => "seedream_5_lite",
Self::Midjourney => "midjourney",
Self::MidjourneyV6 => "midjourney_v6",
Self::MidjourneyV6p1 => "midjourney_v6p1",
Self::MidjourneyV6p1Raw => "midjourney_v6p1_raw",
Self::MidjourneyV7 => "midjourney_v7",
Self::MidjourneyV7Draft => "midjourney_v7_draft",
Self::MidjourneyV7DraftRaw => "midjourney_v7_draft_raw",
Self::MidjourneyV7Raw => "midjourney_v7_raw",
// Video models
Self::GrokVideo => "grok_video",
Self::Kling16Pro => "kling_1p6_pro",
Self::Kling21Pro => "kling_2p1_pro",
Self::Kling21Master => "kling_2p1_master",
Self::Kling2p5TurboPro => "kling_2p5_turbo_pro",
Self::Kling2p6Pro => "kling_2p6_pro",
Self::Kling3p0Standard => "kling_3p0_standard",
Self::Kling3p0Pro => "kling_3p0_pro",
Self::Seedance10Lite => "seedance_1p0_lite",
Self::Seedance10Pro => "seedance_1p0_pro",
Self::Seedance1p5Pro => "seedance_1p5_pro",
Self::Seedance2p0 => "seedance_2p0",
Self::Sora2 => "sora_2",
Self::Sora2Pro => "sora_2_pro",
Self::Veo2 => "veo_2",
Self::Veo3 => "veo_3",
Self::Veo3Fast => "veo_3_fast",
Self::Veo3p1 => "veo_3p1",
Self::Veo3p1Fast => "veo_3p1_fast",
// 3D Object generation models
Self::Hunyuan3d2_0 => "hunyuan_3d_2p0",
Self::Hunyuan3d2_1 => "hunyuan_3d_2p1",
Self::Hunyuan3d3 => "hunyuan_3d_3",
// Splat generation models (World Labs)
Self::Marble0p1Mini => "marble_0p1_mini",
Self::Marble0p1Plus => "marble_0p1_plus",
}
}
pub fn from_str(job_status: &str) -> Result<Self, String> {
match job_status {
// Image models
"flux_1_dev" => Ok(Self::Flux1Dev),
"flux_1_schnell" => Ok(Self::Flux1Schnell),
"flux_dev_juggernaut" => Ok(Self::FluxDevJuggernaut),
"flux_pro_1" => Ok(Self::FluxPro1),
"flux_pro_1p1" => Ok(Self::FluxPro11),
"flux_pro_1p1_ultra" => Ok(Self::FluxPro11Ultra),
"flux_pro_kontext_max" => Ok(Self::FluxProKontextMax),
"flux_2_lora_angles" => Ok(Self::Flux2LoraAngles),
"gpt_image_1" => Ok(Self::GptImage1),
"gpt_image_1p5" => Ok(Self::GptImage1p5),
"grok_image" => Ok(Self::GrokImage),
"recraft_3" => Ok(Self::Recraft3),
"seededit_3" => Ok(Self::SeedEdit3),
"qwen" => Ok(Self::Qwen),
"qwen_edit_2511_angles" => Ok(Self::QwenEdit2511Angles),
"gemini_25_flash" => Ok(Self::Gemini25Flash),
"nano_banana" => Ok(Self::NanoBanana),
"nano_banana_2" => Ok(Self::NanoBanana2),
"nano_banana_pro" => Ok(Self::NanoBananaPro),
"seedream_4" => Ok(Self::Seedream4),
"seedream_4p5" => Ok(Self::Seedream4p5),
"seedream_5_lite" => Ok(Self::Seedream5Lite),
"midjourney" => Ok(Self::Midjourney),
"midjourney_v6" => Ok(Self::MidjourneyV6),
"midjourney_v6p1" => Ok(Self::MidjourneyV6p1),
"midjourney_v6p1_raw" => Ok(Self::MidjourneyV6p1Raw),
"midjourney_v7" => Ok(Self::MidjourneyV7),
"midjourney_v7_draft" => Ok(Self::MidjourneyV7Draft),
"midjourney_v7_draft_raw" => Ok(Self::MidjourneyV7DraftRaw),
"midjourney_v7_raw" => Ok(Self::MidjourneyV7Raw),
// Video models
"grok_video" => Ok(Self::GrokVideo),
"kling_1p6_pro" => Ok(Self::Kling16Pro),
"kling_2p1_pro" => Ok(Self::Kling21Pro),
"kling_2p1_master" => Ok(Self::Kling21Master),
"kling_2p5_turbo_pro" => Ok(Self::Kling2p5TurboPro),
"kling_2p6_pro" => Ok(Self::Kling2p6Pro),
"kling_3p0_standard" => Ok(Self::Kling3p0Standard),
"kling_3p0_pro" => Ok(Self::Kling3p0Pro),
"seedance_1p0_lite" => Ok(Self::Seedance10Lite),
"seedance_1p0_pro" => Ok(Self::Seedance10Pro),
"seedance_1p5_pro" => Ok(Self::Seedance1p5Pro),
"seedance_2p0" => Ok(Self::Seedance2p0),
"sora_2" => Ok(Self::Sora2),
"sora_2_pro" => Ok(Self::Sora2Pro),
"veo_2" => Ok(Self::Veo2),
"veo_3" => Ok(Self::Veo3),
"veo_3_fast" => Ok(Self::Veo3Fast),
"veo_3p1" => Ok(Self::Veo3p1),
"veo_3p1_fast" => Ok(Self::Veo3p1Fast),
// 3D Object generation models
"hunyuan_3d_2p0" => Ok(Self::Hunyuan3d2_0),
"hunyuan_3d_2p1" => Ok(Self::Hunyuan3d2_1),
"hunyuan_3d_3" => Ok(Self::Hunyuan3d3),
// Splat generation models (World Labs)
"marble_0p1_mini" => Ok(Self::Marble0p1Mini),
"marble_0p1_plus" => Ok(Self::Marble0p1Plus),
_ => Err(format!("invalid model_type: {:?}", job_status)),
}
}
pub fn all_variants() -> BTreeSet<Self> {
// NB: BTreeSet is sorted
// NB: BTreeSet::from() isn't const, but not worth using LazyStatic, etc.
BTreeSet::from([
// Image models
Self::Flux1Dev,
Self::Flux1Schnell,
Self::FluxDevJuggernaut,
Self::FluxPro1,
Self::FluxPro11,
Self::FluxPro11Ultra,
Self::FluxProKontextMax,
Self::Flux2LoraAngles,
Self::GptImage1,
Self::GptImage1p5,
Self::GrokImage,
Self::Recraft3,
Self::SeedEdit3,
Self::Qwen,
Self::QwenEdit2511Angles,
Self::Gemini25Flash,
Self::NanoBanana,
Self::NanoBanana2,
Self::NanoBananaPro,
Self::Seedream4,
Self::Seedream4p5,
Self::Seedream5Lite,
Self::Midjourney,
Self::MidjourneyV6,
Self::MidjourneyV6p1,
Self::MidjourneyV6p1Raw,
Self::MidjourneyV7,
Self::MidjourneyV7Draft,
Self::MidjourneyV7DraftRaw,
Self::MidjourneyV7Raw,
// Video models
Self::GrokVideo,
Self::Kling16Pro,
Self::Kling21Pro,
Self::Kling21Master,
Self::Kling2p5TurboPro,
Self::Kling2p6Pro,
Self::Kling3p0Standard,
Self::Kling3p0Pro,
Self::Seedance10Lite,
Self::Seedance10Pro,
Self::Seedance1p5Pro,
Self::Seedance2p0,
Self::Sora2,
Self::Sora2Pro,
Self::Veo2,
Self::Veo3,
Self::Veo3Fast,
Self::Veo3p1,
Self::Veo3p1Fast,
// 3D Object generation models
Self::Hunyuan3d2_0,
Self::Hunyuan3d2_1,
Self::Hunyuan3d3,
// Splat generation models (World Labs)
Self::Marble0p1Mini,
Self::Marble0p1Plus,
])
}
}
#[cfg(test)]
mod tests {
use crate::common::model_type::ModelType;
use crate::test_helpers::assert_serialization;
mod explicit_checks {
use super::*;
#[test]
fn test_serialization() {
// Image models
assert_serialization(ModelType::Flux1Dev, "flux_1_dev");
assert_serialization(ModelType::Flux1Schnell, "flux_1_schnell");
assert_serialization(ModelType::FluxDevJuggernaut, "flux_dev_juggernaut");
assert_serialization(ModelType::FluxPro1, "flux_pro_1");
assert_serialization(ModelType::FluxPro11, "flux_pro_1p1");
assert_serialization(ModelType::FluxPro11Ultra, "flux_pro_1p1_ultra");
assert_serialization(ModelType::FluxProKontextMax, "flux_pro_kontext_max");
assert_serialization(ModelType::Flux2LoraAngles, "flux_2_lora_angles");
assert_serialization(ModelType::GptImage1, "gpt_image_1");
assert_serialization(ModelType::GptImage1p5, "gpt_image_1p5");
assert_serialization(ModelType::GrokImage, "grok_image");
assert_serialization(ModelType::Recraft3, "recraft_3");
assert_serialization(ModelType::SeedEdit3, "seededit_3");
assert_serialization(ModelType::Qwen, "qwen");
assert_serialization(ModelType::QwenEdit2511Angles, "qwen_edit_2511_angles");
assert_serialization(ModelType::Gemini25Flash, "gemini_25_flash");
assert_serialization(ModelType::NanoBanana, "nano_banana");
assert_serialization(ModelType::NanoBanana2, "nano_banana_2");
assert_serialization(ModelType::NanoBananaPro, "nano_banana_pro");
assert_serialization(ModelType::Seedream4, "seedream_4");
assert_serialization(ModelType::Seedream4p5, "seedream_4p5");
assert_serialization(ModelType::Seedream5Lite, "seedream_5_lite");
assert_serialization(ModelType::Midjourney, "midjourney");
assert_serialization(ModelType::MidjourneyV6, "midjourney_v6");
assert_serialization(ModelType::MidjourneyV6p1, "midjourney_v6p1");
assert_serialization(ModelType::MidjourneyV6p1Raw, "midjourney_v6p1_raw");
assert_serialization(ModelType::MidjourneyV7, "midjourney_v7");
assert_serialization(ModelType::MidjourneyV7Draft, "midjourney_v7_draft");
assert_serialization(ModelType::MidjourneyV7DraftRaw, "midjourney_v7_draft_raw");
assert_serialization(ModelType::MidjourneyV7Raw, "midjourney_v7_raw");
// Video models
assert_serialization(ModelType::GrokVideo, "grok_video");
assert_serialization(ModelType::Kling16Pro, "kling_1p6_pro");
assert_serialization(ModelType::Kling21Pro, "kling_2p1_pro");
assert_serialization(ModelType::Kling21Master, "kling_2p1_master");
assert_serialization(ModelType::Kling2p5TurboPro, "kling_2p5_turbo_pro");
assert_serialization(ModelType::Kling2p6Pro, "kling_2p6_pro");
assert_serialization(ModelType::Kling3p0Standard, "kling_3p0_standard");
assert_serialization(ModelType::Kling3p0Pro, "kling_3p0_pro");
assert_serialization(ModelType::Seedance10Lite, "seedance_1p0_lite");
assert_serialization(ModelType::Seedance10Pro, "seedance_1p0_pro");
assert_serialization(ModelType::Seedance1p5Pro, "seedance_1p5_pro");
assert_serialization(ModelType::Seedance2p0, "seedance_2p0");
assert_serialization(ModelType::Sora2, "sora_2");
assert_serialization(ModelType::Sora2Pro, "sora_2_pro");
assert_serialization(ModelType::Veo2, "veo_2");
assert_serialization(ModelType::Veo3, "veo_3");
assert_serialization(ModelType::Veo3Fast, "veo_3_fast");
assert_serialization(ModelType::Veo3p1, "veo_3p1");
assert_serialization(ModelType::Veo3p1Fast, "veo_3p1_fast");
// 3D Object generation models
assert_serialization(ModelType::Hunyuan3d2_0, "hunyuan_3d_2p0");
assert_serialization(ModelType::Hunyuan3d2_1, "hunyuan_3d_2p1");
assert_serialization(ModelType::Hunyuan3d3, "hunyuan_3d_3");
// Splat generation models (World Labs)
assert_serialization(ModelType::Marble0p1Mini, "marble_0p1_mini");
assert_serialization(ModelType::Marble0p1Plus, "marble_0p1_plus");
}
#[test]
fn to_str() {
// Image models
assert_eq!(ModelType::Flux1Dev.to_str(), "flux_1_dev");
assert_eq!(ModelType::Flux1Schnell.to_str(), "flux_1_schnell");
assert_eq!(ModelType::FluxDevJuggernaut.to_str(), "flux_dev_juggernaut");
assert_eq!(ModelType::FluxPro1.to_str(), "flux_pro_1");
assert_eq!(ModelType::FluxPro11.to_str(), "flux_pro_1p1");
assert_eq!(ModelType::FluxPro11Ultra.to_str(), "flux_pro_1p1_ultra");
assert_eq!(ModelType::FluxProKontextMax.to_str(), "flux_pro_kontext_max");
assert_eq!(ModelType::Flux2LoraAngles.to_str(), "flux_2_lora_angles");
assert_eq!(ModelType::GptImage1.to_str(), "gpt_image_1");
assert_eq!(ModelType::GptImage1p5.to_str(), "gpt_image_1p5");
assert_eq!(ModelType::GrokImage.to_str(), "grok_image");
assert_eq!(ModelType::Recraft3.to_str(), "recraft_3");
assert_eq!(ModelType::SeedEdit3.to_str(), "seededit_3");
assert_eq!(ModelType::Qwen.to_str(), "qwen");
assert_eq!(ModelType::QwenEdit2511Angles.to_str(), "qwen_edit_2511_angles");
assert_eq!(ModelType::Gemini25Flash.to_str(), "gemini_25_flash");
assert_eq!(ModelType::NanoBanana.to_str(), "nano_banana");
assert_eq!(ModelType::NanoBanana2.to_str(), "nano_banana_2");
assert_eq!(ModelType::NanoBananaPro.to_str(), "nano_banana_pro");
assert_eq!(ModelType::Seedream4.to_str(), "seedream_4");
assert_eq!(ModelType::Seedream4p5.to_str(), "seedream_4p5");
assert_eq!(ModelType::Seedream5Lite.to_str(), "seedream_5_lite");
assert_eq!(ModelType::Midjourney.to_str(), "midjourney");
assert_eq!(ModelType::MidjourneyV6.to_str(), "midjourney_v6");
assert_eq!(ModelType::MidjourneyV6p1.to_str(), "midjourney_v6p1");
assert_eq!(ModelType::MidjourneyV6p1Raw.to_str(), "midjourney_v6p1_raw");
assert_eq!(ModelType::MidjourneyV7.to_str(), "midjourney_v7");
assert_eq!(ModelType::MidjourneyV7Draft.to_str(), "midjourney_v7_draft");
assert_eq!(ModelType::MidjourneyV7DraftRaw.to_str(), "midjourney_v7_draft_raw");
assert_eq!(ModelType::MidjourneyV7Raw.to_str(), "midjourney_v7_raw");
// Video models
assert_eq!(ModelType::GrokVideo.to_str(), "grok_video");
assert_eq!(ModelType::Kling16Pro.to_str(), "kling_1p6_pro");
assert_eq!(ModelType::Kling21Pro.to_str(), "kling_2p1_pro");
assert_eq!(ModelType::Kling21Master.to_str(), "kling_2p1_master");
assert_eq!(ModelType::Kling2p5TurboPro.to_str(), "kling_2p5_turbo_pro");
assert_eq!(ModelType::Kling2p6Pro.to_str(), "kling_2p6_pro");
assert_eq!(ModelType::Kling3p0Standard.to_str(), "kling_3p0_standard");
assert_eq!(ModelType::Kling3p0Pro.to_str(), "kling_3p0_pro");
assert_eq!(ModelType::Seedance10Lite.to_str(), "seedance_1p0_lite");
assert_eq!(ModelType::Seedance10Pro.to_str(), "seedance_1p0_pro");
assert_eq!(ModelType::Seedance1p5Pro.to_str(), "seedance_1p5_pro");
assert_eq!(ModelType::Seedance2p0.to_str(), "seedance_2p0");
assert_eq!(ModelType::Sora2.to_str(), "sora_2");
assert_eq!(ModelType::Sora2Pro.to_str(), "sora_2_pro");
assert_eq!(ModelType::Veo2.to_str(), "veo_2");
assert_eq!(ModelType::Veo3.to_str(), "veo_3");
assert_eq!(ModelType::Veo3Fast.to_str(), "veo_3_fast");
assert_eq!(ModelType::Veo3p1.to_str(), "veo_3p1");
assert_eq!(ModelType::Veo3p1Fast.to_str(), "veo_3p1_fast");
// 3D Object generation models
assert_eq!(ModelType::Hunyuan3d2_0.to_str(), "hunyuan_3d_2p0");
assert_eq!(ModelType::Hunyuan3d2_1.to_str(), "hunyuan_3d_2p1");
assert_eq!(ModelType::Hunyuan3d3.to_str(), "hunyuan_3d_3");
// Splat generation models (World Labs)
assert_eq!(ModelType::Marble0p1Mini.to_str(), "marble_0p1_mini");
assert_eq!(ModelType::Marble0p1Plus.to_str(), "marble_0p1_plus");
}
#[test]
fn from_str() {
// Image models
assert_eq!(ModelType::from_str("flux_1_dev").unwrap(), ModelType::Flux1Dev);
assert_eq!(ModelType::from_str("flux_1_schnell").unwrap(), ModelType::Flux1Schnell);
assert_eq!(ModelType::from_str("flux_dev_juggernaut").unwrap(), ModelType::FluxDevJuggernaut);
assert_eq!(ModelType::from_str("flux_pro_1").unwrap(), ModelType::FluxPro1);
assert_eq!(ModelType::from_str("flux_pro_1p1").unwrap(), ModelType::FluxPro11);
assert_eq!(ModelType::from_str("flux_pro_1p1_ultra").unwrap(), ModelType::FluxPro11Ultra);
assert_eq!(ModelType::from_str("flux_pro_kontext_max").unwrap(), ModelType::FluxProKontextMax);
assert_eq!(ModelType::from_str("flux_2_lora_angles").unwrap(), ModelType::Flux2LoraAngles);
assert_eq!(ModelType::from_str("gpt_image_1").unwrap(), ModelType::GptImage1);
assert_eq!(ModelType::from_str("gpt_image_1p5").unwrap(), ModelType::GptImage1p5);
assert_eq!(ModelType::from_str("grok_image").unwrap(), ModelType::GrokImage);
assert_eq!(ModelType::from_str("recraft_3").unwrap(), ModelType::Recraft3);
assert_eq!(ModelType::from_str("seededit_3").unwrap(), ModelType::SeedEdit3);
assert_eq!(ModelType::from_str("qwen").unwrap(), ModelType::Qwen);
assert_eq!(ModelType::from_str("qwen_edit_2511_angles").unwrap(), ModelType::QwenEdit2511Angles);
assert_eq!(ModelType::from_str("gemini_25_flash").unwrap(), ModelType::Gemini25Flash);
assert_eq!(ModelType::from_str("nano_banana").unwrap(), ModelType::NanoBanana);
assert_eq!(ModelType::from_str("nano_banana_2").unwrap(), ModelType::NanoBanana2);
assert_eq!(ModelType::from_str("nano_banana_pro").unwrap(), ModelType::NanoBananaPro);
assert_eq!(ModelType::from_str("seedream_4").unwrap(), ModelType::Seedream4);
assert_eq!(ModelType::from_str("seedream_4p5").unwrap(), ModelType::Seedream4p5);
assert_eq!(ModelType::from_str("seedream_5_lite").unwrap(), ModelType::Seedream5Lite);
assert_eq!(ModelType::from_str("midjourney").unwrap(), ModelType::Midjourney);
assert_eq!(ModelType::from_str("midjourney_v6").unwrap(), ModelType::MidjourneyV6);
assert_eq!(ModelType::from_str("midjourney_v6p1").unwrap(), ModelType::MidjourneyV6p1);
assert_eq!(ModelType::from_str("midjourney_v6p1_raw").unwrap(), ModelType::MidjourneyV6p1Raw);
assert_eq!(ModelType::from_str("midjourney_v7").unwrap(), ModelType::MidjourneyV7);
assert_eq!(ModelType::from_str("midjourney_v7_draft").unwrap(), ModelType::MidjourneyV7Draft);
assert_eq!(ModelType::from_str("midjourney_v7_draft_raw").unwrap(), ModelType::MidjourneyV7DraftRaw);
assert_eq!(ModelType::from_str("midjourney_v7_raw").unwrap(), ModelType::MidjourneyV7Raw);
// Video models
assert_eq!(ModelType::from_str("grok_video").unwrap(), ModelType::GrokVideo);
assert_eq!(ModelType::from_str("kling_1p6_pro").unwrap(), ModelType::Kling16Pro);
assert_eq!(ModelType::from_str("kling_2p1_pro").unwrap(), ModelType::Kling21Pro);
assert_eq!(ModelType::from_str("kling_2p1_master").unwrap(), ModelType::Kling21Master);
assert_eq!(ModelType::from_str("kling_2p5_turbo_pro").unwrap(), ModelType::Kling2p5TurboPro);
assert_eq!(ModelType::from_str("kling_2p6_pro").unwrap(), ModelType::Kling2p6Pro);
assert_eq!(ModelType::from_str("kling_3p0_standard").unwrap(), ModelType::Kling3p0Standard);
assert_eq!(ModelType::from_str("kling_3p0_pro").unwrap(), ModelType::Kling3p0Pro);
assert_eq!(ModelType::from_str("seedance_1p0_lite").unwrap(), ModelType::Seedance10Lite);
assert_eq!(ModelType::from_str("seedance_1p0_pro").unwrap(), ModelType::Seedance10Pro);
assert_eq!(ModelType::from_str("seedance_1p5_pro").unwrap(), ModelType::Seedance1p5Pro);
assert_eq!(ModelType::from_str("seedance_2p0").unwrap(), ModelType::Seedance2p0);
assert_eq!(ModelType::from_str("sora_2").unwrap(), ModelType::Sora2);
assert_eq!(ModelType::from_str("sora_2_pro").unwrap(), ModelType::Sora2Pro);
assert_eq!(ModelType::from_str("veo_2").unwrap(), ModelType::Veo2);
assert_eq!(ModelType::from_str("veo_3").unwrap(), ModelType::Veo3);
assert_eq!(ModelType::from_str("veo_3_fast").unwrap(), ModelType::Veo3Fast);
assert_eq!(ModelType::from_str("veo_3p1").unwrap(), ModelType::Veo3p1);
assert_eq!(ModelType::from_str("veo_3p1_fast").unwrap(), ModelType::Veo3p1Fast);
// 3D Object generation models
assert_eq!(ModelType::from_str("hunyuan_3d_2p0").unwrap(), ModelType::Hunyuan3d2_0);
assert_eq!(ModelType::from_str("hunyuan_3d_2p1").unwrap(), ModelType::Hunyuan3d2_1);
assert_eq!(ModelType::from_str("hunyuan_3d_3").unwrap(), ModelType::Hunyuan3d3);
// Splat generation models (World Labs)
assert_eq!(ModelType::from_str("marble_0p1_mini").unwrap(), ModelType::Marble0p1Mini);
assert_eq!(ModelType::from_str("marble_0p1_plus").unwrap(), ModelType::Marble0p1Plus);
}
#[test]
fn all_variants() {
let mut variants = ModelType::all_variants();
assert_eq!(variants.len(), 54);
// Image models
assert_eq!(variants.pop_first(), Some(ModelType::Flux1Dev));
assert_eq!(variants.pop_first(), Some(ModelType::Flux1Schnell));
assert_eq!(variants.pop_first(), Some(ModelType::FluxDevJuggernaut));
assert_eq!(variants.pop_first(), Some(ModelType::FluxPro1));
assert_eq!(variants.pop_first(), Some(ModelType::FluxPro11));
assert_eq!(variants.pop_first(), Some(ModelType::FluxPro11Ultra));
assert_eq!(variants.pop_first(), Some(ModelType::FluxProKontextMax));
assert_eq!(variants.pop_first(), Some(ModelType::Flux2LoraAngles));
assert_eq!(variants.pop_first(), Some(ModelType::GptImage1));
assert_eq!(variants.pop_first(), Some(ModelType::GptImage1p5));
assert_eq!(variants.pop_first(), Some(ModelType::GrokImage));
assert_eq!(variants.pop_first(), Some(ModelType::Recraft3));
assert_eq!(variants.pop_first(), Some(ModelType::SeedEdit3));
assert_eq!(variants.pop_first(), Some(ModelType::Qwen));
assert_eq!(variants.pop_first(), Some(ModelType::QwenEdit2511Angles));
assert_eq!(variants.pop_first(), Some(ModelType::Gemini25Flash));
assert_eq!(variants.pop_first(), Some(ModelType::NanoBanana));
assert_eq!(variants.pop_first(), Some(ModelType::NanoBanana2));
assert_eq!(variants.pop_first(), Some(ModelType::NanoBananaPro));
assert_eq!(variants.pop_first(), Some(ModelType::Seedream4));
assert_eq!(variants.pop_first(), Some(ModelType::Seedream4p5));
assert_eq!(variants.pop_first(), Some(ModelType::Seedream5Lite));
assert_eq!(variants.pop_first(), Some(ModelType::Midjourney));
assert_eq!(variants.pop_first(), Some(ModelType::MidjourneyV6));
assert_eq!(variants.pop_first(), Some(ModelType::MidjourneyV6p1));
assert_eq!(variants.pop_first(), Some(ModelType::MidjourneyV6p1Raw));
assert_eq!(variants.pop_first(), Some(ModelType::MidjourneyV7));
assert_eq!(variants.pop_first(), Some(ModelType::MidjourneyV7Draft));
assert_eq!(variants.pop_first(), Some(ModelType::MidjourneyV7DraftRaw));
assert_eq!(variants.pop_first(), Some(ModelType::MidjourneyV7Raw));
// Video models
assert_eq!(variants.pop_first(), Some(ModelType::GrokVideo));
assert_eq!(variants.pop_first(), Some(ModelType::Kling16Pro));
assert_eq!(variants.pop_first(), Some(ModelType::Kling21Pro));
assert_eq!(variants.pop_first(), Some(ModelType::Kling21Master));
assert_eq!(variants.pop_first(), Some(ModelType::Kling2p5TurboPro));
assert_eq!(variants.pop_first(), Some(ModelType::Kling2p6Pro));
assert_eq!(variants.pop_first(), Some(ModelType::Kling3p0Standard));
assert_eq!(variants.pop_first(), Some(ModelType::Kling3p0Pro));
assert_eq!(variants.pop_first(), Some(ModelType::Seedance10Lite));
assert_eq!(variants.pop_first(), Some(ModelType::Seedance10Pro));
assert_eq!(variants.pop_first(), Some(ModelType::Seedance1p5Pro));
assert_eq!(variants.pop_first(), Some(ModelType::Seedance2p0));
assert_eq!(variants.pop_first(), Some(ModelType::Sora2));
assert_eq!(variants.pop_first(), Some(ModelType::Sora2Pro));
assert_eq!(variants.pop_first(), Some(ModelType::Veo2));
assert_eq!(variants.pop_first(), Some(ModelType::Veo3));
assert_eq!(variants.pop_first(), Some(ModelType::Veo3Fast));
assert_eq!(variants.pop_first(), Some(ModelType::Veo3p1));
assert_eq!(variants.pop_first(), Some(ModelType::Veo3p1Fast));
// 3D Object generation models
assert_eq!(variants.pop_first(), Some(ModelType::Hunyuan3d2_0));
assert_eq!(variants.pop_first(), Some(ModelType::Hunyuan3d2_1));
assert_eq!(variants.pop_first(), Some(ModelType::Hunyuan3d3));
// Splat generation models (World Labs)
assert_eq!(variants.pop_first(), Some(ModelType::Marble0p1Mini));
assert_eq!(variants.pop_first(), Some(ModelType::Marble0p1Plus));
assert_eq!(variants.pop_first(), None);
}
}
mod mechanical_checks {
use super::*;
#[test]
fn variant_length() {
use strum::IntoEnumIterator;
assert_eq!(ModelType::all_variants().len(), ModelType::iter().len());
}
#[test]
fn round_trip() {
for variant in ModelType::all_variants() {
// Test to_str(), from_str(), Display, and Debug.
assert_eq!(variant, ModelType::from_str(variant.to_str()).unwrap());
assert_eq!(variant, ModelType::from_str(&format!("{}", variant)).unwrap());
assert_eq!(variant, ModelType::from_str(&format!("{:?}", variant)).unwrap());
}
}
#[test]
fn serialized_length_ok_for_database() {
const MAX_LENGTH : usize = 24;
for variant in ModelType::all_variants() {
let serialized = variant.to_str();
assert!(serialized.len() > 0, "variant {:?} is too short", variant);
assert!(serialized.len() <= MAX_LENGTH, "variant {:?} is too long", variant);
}
}
}
}
@@ -332,6 +332,11 @@ pub async fn process_job_inference(
maybe_positive_prompt: Some(&positive_prompt),
maybe_negative_prompt: maybe_negative_prompt.as_deref(),
maybe_other_args: None, // TODO(bt,2024-02-22): Support other arguments
maybe_generation_mode: None,
maybe_aspect_ratio: None,
maybe_resolution: None,
maybe_batch_count: None,
maybe_generate_audio: None,
creator_ip_address: &job.creator_ip_address,
mysql_executor: &mut *transaction,
phantom: Default::default(),
@@ -87,46 +87,6 @@ use crate::http_server::endpoints::media_files::upsert_upload::write_scene_file:
use crate::http_server::endpoints::model_download::enqueue_gptsovits_model_download_handler::*;
use crate::http_server::endpoints::moderation::user_feature_flags::edit_user_feature_flags_handler::*;
use crate::http_server::endpoints::prompts::get_prompt_handler::*;
use crate::http_server::endpoints::service::status_alert_handler::*;
use crate::http_server::endpoints::stats::get_unified_queue_stats_handler::*;
use crate::http_server::endpoints::studio_gen2::enqueue_studio_gen2_handler::EnqueueStudioGen2Request;
use crate::http_server::endpoints::studio_gen2::enqueue_studio_gen2_handler::*;
use crate::http_server::endpoints::tags::list_tags_for_entity_handler::*;
use crate::http_server::endpoints::tags::set_tags_for_entity_handler::*;
use crate::http_server::endpoints::tts::enqueue_infer_tts_handler::enqueue_infer_tts_handler::*;
use crate::http_server::endpoints::user_bookmarks::batch_get_user_bookmarks_handler::*;
use crate::http_server::endpoints::user_bookmarks::create_user_bookmark_handler::*;
use crate::http_server::endpoints::user_bookmarks::delete_user_bookmark_handler::*;
use crate::http_server::endpoints::user_bookmarks::list_user_bookmarks_for_entity_handler::*;
use crate::http_server::endpoints::user_bookmarks::list_user_bookmarks_for_user_handler::*;
use crate::http_server::endpoints::user_ratings::batch_get_user_rating_handler::*;
use crate::http_server::endpoints::user_ratings::get_user_rating_handler::*;
use crate::http_server::endpoints::user_ratings::set_user_rating_handler::*;
use crate::http_server::endpoints::users::change_password_handler::*;
use crate::http_server::endpoints::users::create_account_handler::*;
use crate::http_server::endpoints::users::edit_email_handler::*;
use crate::http_server::endpoints::users::edit_username_handler::*;
use crate::http_server::endpoints::users::get_profile_handler::*;
use crate::http_server::endpoints::users::google_sso::google_sso_handler::*;
use crate::http_server::endpoints::users::login_handler::*;
use crate::http_server::endpoints::users::logout_handler::*;
use crate::http_server::endpoints::users::session_info_handler::*;
use crate::http_server::endpoints::users::session_token_info_handler::*;
use crate::http_server::endpoints::voice_conversion::enqueue_voice_conversion_inference_handler::*;
use crate::http_server::endpoints::voice_designer::inference::enqueue_tts_request::*;
use crate::http_server::endpoints::voice_designer::voice_datasets::list_datasets_by_user::*;
use crate::http_server::endpoints::weights::delete::delete_weight_handler::*;
use crate::http_server::endpoints::weights::get::get_weight_handler::*;
use crate::http_server::endpoints::weights::list::list_available_weights_handler::*;
use crate::http_server::endpoints::weights::list::list_featured_weights_handler::*;
use crate::http_server::endpoints::weights::list::list_pinned_weights_handler::*;
use crate::http_server::endpoints::weights::list::list_weights_by_user_handler::*;
use crate::http_server::endpoints::weights::search::search_model_weights_impl::*;
use crate::http_server::endpoints::weights::update::set_model_weight_cover_image_handler::*;
use crate::http_server::endpoints::weights::update::update_weight_handler::*;
use artcraft_api_defs::users::change_password::{ChangePasswordRequest, ChangePasswordResponse};
use artcraft_api_defs::users::edit_email::{EditEmailRequest, EditEmailResponse};
use artcraft_api_defs::users::edit_username::{EditUsernameRequest, EditUsernameResponse};
use artcraft_api_defs::common::responses::job_details::JobDetailsLipsyncRequest;
use artcraft_api_defs::common::responses::job_details::JobDetailsLivePortraitRequest;
use artcraft_api_defs::common::responses::media_links::*;
@@ -188,9 +148,50 @@ use artcraft_api_defs::media_file::delete_media_file::DeleteMediaFilePathInfo;
use artcraft_api_defs::media_file::delete_media_file::DeleteMediaFileRequest;
use artcraft_api_defs::prompts::create_prompt::CreatePromptRequest;
use artcraft_api_defs::prompts::create_prompt::CreatePromptResponse;
use artcraft_api_defs::prompts::get_prompt::*;
use artcraft_api_defs::users::change_password::{ChangePasswordRequest, ChangePasswordResponse};
use artcraft_api_defs::users::edit_email::{EditEmailRequest, EditEmailResponse};
use artcraft_api_defs::users::edit_username::{EditUsernameRequest, EditUsernameResponse};
use billing_component::stripe::http_endpoints::checkout::create::stripe_create_checkout_session_error::CreateCheckoutSessionError;
use billing_component::stripe::http_endpoints::checkout::create::stripe_create_checkout_session_json_handler::*;
use billing_component::users::http_endpoints::list_active_user_subscriptions_handler::*;
use crate::http_server::endpoints::service::status_alert_handler::*;
use crate::http_server::endpoints::stats::get_unified_queue_stats_handler::*;
use crate::http_server::endpoints::studio_gen2::enqueue_studio_gen2_handler::*;
use crate::http_server::endpoints::studio_gen2::enqueue_studio_gen2_handler::EnqueueStudioGen2Request;
use crate::http_server::endpoints::tags::list_tags_for_entity_handler::*;
use crate::http_server::endpoints::tags::set_tags_for_entity_handler::*;
use crate::http_server::endpoints::tts::enqueue_infer_tts_handler::enqueue_infer_tts_handler::*;
use crate::http_server::endpoints::user_bookmarks::batch_get_user_bookmarks_handler::*;
use crate::http_server::endpoints::user_bookmarks::create_user_bookmark_handler::*;
use crate::http_server::endpoints::user_bookmarks::delete_user_bookmark_handler::*;
use crate::http_server::endpoints::user_bookmarks::list_user_bookmarks_for_entity_handler::*;
use crate::http_server::endpoints::user_bookmarks::list_user_bookmarks_for_user_handler::*;
use crate::http_server::endpoints::user_ratings::batch_get_user_rating_handler::*;
use crate::http_server::endpoints::user_ratings::get_user_rating_handler::*;
use crate::http_server::endpoints::user_ratings::set_user_rating_handler::*;
use crate::http_server::endpoints::users::change_password_handler::*;
use crate::http_server::endpoints::users::create_account_handler::*;
use crate::http_server::endpoints::users::edit_email_handler::*;
use crate::http_server::endpoints::users::edit_username_handler::*;
use crate::http_server::endpoints::users::get_profile_handler::*;
use crate::http_server::endpoints::users::google_sso::google_sso_handler::*;
use crate::http_server::endpoints::users::login_handler::*;
use crate::http_server::endpoints::users::logout_handler::*;
use crate::http_server::endpoints::users::session_info_handler::*;
use crate::http_server::endpoints::users::session_token_info_handler::*;
use crate::http_server::endpoints::voice_conversion::enqueue_voice_conversion_inference_handler::*;
use crate::http_server::endpoints::voice_designer::inference::enqueue_tts_request::*;
use crate::http_server::endpoints::voice_designer::voice_datasets::list_datasets_by_user::*;
use crate::http_server::endpoints::weights::delete::delete_weight_handler::*;
use crate::http_server::endpoints::weights::get::get_weight_handler::*;
use crate::http_server::endpoints::weights::list::list_available_weights_handler::*;
use crate::http_server::endpoints::weights::list::list_featured_weights_handler::*;
use crate::http_server::endpoints::weights::list::list_pinned_weights_handler::*;
use crate::http_server::endpoints::weights::list::list_weights_by_user_handler::*;
use crate::http_server::endpoints::weights::search::search_model_weights_impl::*;
use crate::http_server::endpoints::weights::update::set_model_weight_cover_image_handler::*;
use crate::http_server::endpoints::weights::update::update_weight_handler::*;
use enums::by_table::beta_keys::beta_key_product::BetaKeyProduct;
use enums::by_table::comments::comment_entity_type::CommentEntityType;
use enums::by_table::featured_items::featured_item_entity_type::FeaturedItemEntityType;
@@ -210,9 +211,12 @@ use enums::by_table::user_bookmarks::user_bookmark_entity_type::UserBookmarkEnti
use enums::by_table::user_ratings::entity_type::UserRatingEntityType;
use enums::by_table::user_ratings::rating_value::UserRatingValue;
use enums::by_table::users::user_feature_flag::UserFeatureFlag;
use enums::common::generation::common_model_type::CommonModelType;
use enums::common::generation::common_aspect_ratio::CommonAspectRatio;
use enums::common::generation::common_generation_mode::CommonGenerationMode;
use enums::common::generation::common_resolution::CommonResolution;
use enums::common::generation_provider::GenerationProvider;
use enums::common::job_status_plus::JobStatusPlus;
use enums::common::model_type::ModelType;
use enums::common::visibility::Visibility;
use enums::no_table::style_transfer::style_transfer_name::StyleTransferName;
use enums_public::by_table::media_files::public_media_file_model_type::PublicMediaFileModelType;
@@ -511,7 +515,10 @@ use crate::http_server::endpoints::media_files::list::list_batch_generated_redux
MediaFileOriginProductCategory,
MediaFileSubtype,
MediaFileType,
ModelType,
CommonModelType,
CommonAspectRatio,
CommonGenerationMode,
CommonResolution,
PromptContextSemanticType,
PromptType,
PublicMediaFileModelType,
@@ -16,7 +16,7 @@ use artcraft_router::client::request_mismatch_mitigation_strategy::RequestMismat
use artcraft_router::generate::generate_image::generate_image_request::GenerateImageRequest;
use enums::common::generation::common_aspect_ratio::CommonAspectRatio;
use enums::common::generation::common_image_model::CommonImageModel;
use enums::common::generation::common_video_resolution::CommonVideoResolution;
use enums::common::generation::common_resolution::CommonResolution;
use enums::common::generation_provider::GenerationProvider;
/// Estimate the credit and USD cost of an image generation request.
@@ -162,11 +162,11 @@ fn map_aspect_ratio(ar: CommonAspectRatio) -> RouterAspectRatio {
}
}
fn map_resolution(res: CommonVideoResolution) -> RouterResolution {
fn map_resolution(res: CommonResolution) -> RouterResolution {
match res {
CommonVideoResolution::OneK => RouterResolution::OneK,
CommonVideoResolution::TwoK => RouterResolution::TwoK,
CommonVideoResolution::ThreeK => RouterResolution::ThreeK,
CommonVideoResolution::FourK => RouterResolution::FourK,
CommonResolution::OneK => RouterResolution::OneK,
CommonResolution::TwoK => RouterResolution::TwoK,
CommonResolution::ThreeK => RouterResolution::ThreeK,
CommonResolution::FourK => RouterResolution::FourK,
}
}
@@ -15,7 +15,7 @@ use artcraft_router::client::request_mismatch_mitigation_strategy::RequestMismat
use artcraft_router::generate::generate_video::generate_video_request::GenerateVideoRequest;
use enums::common::generation::common_aspect_ratio::CommonAspectRatio;
use enums::common::generation::common_video_model::CommonVideoModel;
use enums::common::generation::common_video_resolution::CommonVideoResolution;
use enums::common::generation::common_resolution::CommonResolution;
use enums::common::generation_provider::GenerationProvider;
@@ -172,11 +172,11 @@ fn map_aspect_ratio(ar: CommonAspectRatio) -> RouterAspectRatio {
}
}
fn map_resolution(res: CommonVideoResolution) -> RouterResolution {
fn map_resolution(res: CommonResolution) -> RouterResolution {
match res {
CommonVideoResolution::OneK => RouterResolution::OneK,
CommonVideoResolution::TwoK => RouterResolution::TwoK,
CommonVideoResolution::ThreeK => RouterResolution::ThreeK,
CommonVideoResolution::FourK => RouterResolution::FourK,
CommonResolution::OneK => RouterResolution::OneK,
CommonResolution::TwoK => RouterResolution::TwoK,
CommonResolution::ThreeK => RouterResolution::ThreeK,
CommonResolution::FourK => RouterResolution::FourK,
}
}
@@ -12,7 +12,7 @@ use artcraft_api_defs::generate::image::angle::flux_2_lora_edit_image_angle::{Fl
use enums::by_table::prompt_context_items::prompt_context_semantic_type::PromptContextSemanticType;
use enums::by_table::prompts::prompt_type::PromptType;
use enums::common::generation_provider::GenerationProvider;
use enums::common::model_type::ModelType;
use enums::common::generation::common_model_type::CommonModelType;
use enums::common::visibility::Visibility;
use fal_client::requests::traits::fal_request_cost_calculator_trait::FalRequestCostCalculator;
use fal_client::requests::webhook::image::angle::enqueue_flux_2_lora_edit_image_angle_webhook::{enqueue_flux_2_lora_edit_image_angle_webhook, EnqueueFlux2LoraEditImageAngleArgs, EnqueueFlux2LoraAngleNumImages, EnqueueFlux2LoraAngleImageSize};
@@ -168,11 +168,16 @@ pub async fn flux_2_lora_edit_image_angle_handler(
maybe_apriori_prompt_token: None,
prompt_type: PromptType::ArtcraftApp,
maybe_creator_user_token: Some(&user_token),
maybe_model_type: Some(ModelType::Flux2LoraAngles),
maybe_model_type: Some(CommonModelType::Flux2LoraAngles),
maybe_generation_provider: Some(GenerationProvider::Artcraft),
maybe_positive_prompt: None,
maybe_negative_prompt: None,
maybe_other_args: None,
maybe_generation_mode: None,
maybe_aspect_ratio: None,
maybe_resolution: None,
maybe_batch_count: None,
maybe_generate_audio: None,
creator_ip_address: &ip_address,
mysql_executor: &mut *transaction,
phantom: Default::default(),
@@ -12,7 +12,7 @@ use artcraft_api_defs::generate::image::angle::qwen_edit_2511_edit_image_angle::
use enums::by_table::prompt_context_items::prompt_context_semantic_type::PromptContextSemanticType;
use enums::by_table::prompts::prompt_type::PromptType;
use enums::common::generation_provider::GenerationProvider;
use enums::common::model_type::ModelType;
use enums::common::generation::common_model_type::CommonModelType;
use enums::common::visibility::Visibility;
use fal_client::requests::traits::fal_request_cost_calculator_trait::FalRequestCostCalculator;
use fal_client::requests::webhook::image::angle::enqueue_qwen_edit_2511_edit_image_angle_webhook::{enqueue_qwen_edit_2511_edit_image_angle_webhook, EnqueueQwenEdit2511EditImageAngleArgs, EnqueueQwenEdit2511AngleNumImages, EnqueueQwenEdit2511AngleImageSize};
@@ -169,11 +169,16 @@ pub async fn qwen_edit_2511_edit_image_angle_handler(
maybe_apriori_prompt_token: None,
prompt_type: PromptType::ArtcraftApp,
maybe_creator_user_token: Some(&user_token),
maybe_model_type: Some(ModelType::QwenEdit2511Angles),
maybe_model_type: Some(CommonModelType::QwenEdit2511Angles),
maybe_generation_provider: Some(GenerationProvider::Artcraft),
maybe_positive_prompt: request.additional_prompt.as_deref(),
maybe_negative_prompt: None,
maybe_other_args: None,
maybe_generation_mode: None,
maybe_aspect_ratio: None,
maybe_resolution: None,
maybe_batch_count: None,
maybe_generate_audio: None,
creator_ip_address: &ip_address,
mysql_executor: &mut *transaction,
phantom: Default::default(),
@@ -14,7 +14,7 @@ use bucket_paths::legacy::typified_paths::public::media_files::bucket_file_path:
use enums::by_table::prompt_context_items::prompt_context_semantic_type::PromptContextSemanticType;
use enums::by_table::prompts::prompt_type::PromptType;
use enums::common::generation_provider::GenerationProvider;
use enums::common::model_type::ModelType;
use enums::common::generation::common_model_type::CommonModelType;
use enums::common::visibility::Visibility;
use fal_client::creds::open_ai_api_key::OpenAiApiKey;
use fal_client::requests::webhook::image::edit::enqueue_flux_pro_kontext_max_edit_webhook::{enqueue_flux_pro_kontext_max_edit_webhook, FluxProKontextMaxArgs, FluxProKontextMaxNumImages};
@@ -213,11 +213,16 @@ pub async fn flux_pro_kontext_max_edit_image_handler(
maybe_creator_user_token: maybe_user_session
.as_ref()
.map(|s| &s.user_token),
maybe_model_type: Some(ModelType::FluxProKontextMax),
maybe_model_type: Some(CommonModelType::FluxProKontextMax),
maybe_generation_provider: Some(GenerationProvider::Artcraft),
maybe_positive_prompt: request.prompt.as_deref(),
maybe_negative_prompt: None,
maybe_other_args: None,
maybe_generation_mode: None,
maybe_aspect_ratio: None,
maybe_resolution: None,
maybe_batch_count: None,
maybe_generate_audio: None,
creator_ip_address: &ip_address,
mysql_executor: &mut *transaction,
phantom: Default::default(),
@@ -13,7 +13,7 @@ use bucket_paths::legacy::typified_paths::public::media_files::bucket_file_path:
use enums::by_table::prompt_context_items::prompt_context_semantic_type::PromptContextSemanticType;
use enums::by_table::prompts::prompt_type::PromptType;
use enums::common::generation_provider::GenerationProvider;
use enums::common::model_type::ModelType;
use enums::common::generation::common_model_type::CommonModelType;
use enums::common::payments_namespace::PaymentsNamespace;
use enums::common::stripe_subscription_status::StripeSubscriptionStatus;
use enums::common::visibility::Visibility;
@@ -230,11 +230,16 @@ pub async fn gemini_25_flash_edit_image_handler(
maybe_apriori_prompt_token: None,
prompt_type: PromptType::ArtcraftApp,
maybe_creator_user_token: Some(&user_token),
maybe_model_type: Some(ModelType::Gemini25Flash),
maybe_model_type: Some(CommonModelType::Gemini25Flash),
maybe_generation_provider: Some(GenerationProvider::Artcraft),
maybe_positive_prompt: request.prompt.as_deref(),
maybe_negative_prompt: None,
maybe_other_args: None,
maybe_generation_mode: None,
maybe_aspect_ratio: None,
maybe_resolution: None,
maybe_batch_count: None,
maybe_generate_audio: None,
creator_ip_address: &ip_address,
mysql_executor: &mut *transaction,
phantom: Default::default(),
@@ -14,7 +14,7 @@ use bucket_paths::legacy::typified_paths::public::media_files::bucket_file_path:
use enums::by_table::prompt_context_items::prompt_context_semantic_type::PromptContextSemanticType;
use enums::by_table::prompts::prompt_type::PromptType;
use enums::common::generation_provider::GenerationProvider;
use enums::common::model_type::ModelType;
use enums::common::generation::common_model_type::CommonModelType;
use enums::common::payments_namespace::PaymentsNamespace;
use enums::common::stripe_subscription_status::StripeSubscriptionStatus;
use enums::common::visibility::Visibility;
@@ -252,11 +252,16 @@ pub async fn gpt_image_1_edit_image_handler(
maybe_apriori_prompt_token: None,
prompt_type: PromptType::ArtcraftApp,
maybe_creator_user_token: Some(&user_token),
maybe_model_type: Some(ModelType::GptImage1),
maybe_model_type: Some(CommonModelType::GptImage1),
maybe_generation_provider: Some(GenerationProvider::Artcraft),
maybe_positive_prompt: request.prompt.as_deref(),
maybe_negative_prompt: None,
maybe_other_args: None,
maybe_generation_mode: None,
maybe_aspect_ratio: None,
maybe_resolution: None,
maybe_batch_count: None,
maybe_generate_audio: None,
creator_ip_address: &ip_address,
mysql_executor: &mut *transaction,
phantom: Default::default(),
@@ -14,7 +14,7 @@ use bucket_paths::legacy::typified_paths::public::media_files::bucket_file_path:
use enums::by_table::prompt_context_items::prompt_context_semantic_type::PromptContextSemanticType;
use enums::by_table::prompts::prompt_type::PromptType;
use enums::common::generation_provider::GenerationProvider;
use enums::common::model_type::ModelType;
use enums::common::generation::common_model_type::CommonModelType;
use enums::common::visibility::Visibility;
use fal_client::creds::open_ai_api_key::OpenAiApiKey;
use fal_client::requests::traits::fal_request_cost_calculator_trait::FalRequestCostCalculator;
@@ -204,11 +204,16 @@ pub async fn qwen_edit_image_handler(
maybe_apriori_prompt_token: None,
prompt_type: PromptType::ArtcraftApp,
maybe_creator_user_token: Some(&user_token),
maybe_model_type: Some(ModelType::Qwen),
maybe_model_type: Some(CommonModelType::Qwen),
maybe_generation_provider: Some(GenerationProvider::Artcraft),
maybe_positive_prompt: request.prompt.as_deref(),
maybe_negative_prompt: None,
maybe_other_args: None,
maybe_generation_mode: None,
maybe_aspect_ratio: None,
maybe_resolution: None,
maybe_batch_count: None,
maybe_generate_audio: None,
creator_ip_address: &ip_address,
mysql_executor: &mut *transaction,
phantom: Default::default(),
@@ -13,7 +13,7 @@ use bucket_paths::legacy::typified_paths::public::media_files::bucket_file_path:
use enums::by_table::prompt_context_items::prompt_context_semantic_type::PromptContextSemanticType;
use enums::by_table::prompts::prompt_type::PromptType;
use enums::common::generation_provider::GenerationProvider;
use enums::common::model_type::ModelType;
use enums::common::generation::common_model_type::CommonModelType;
use enums::common::visibility::Visibility;
use fal_client::creds::open_ai_api_key::OpenAiApiKey;
use fal_client::requests::webhook::image::edit::enqueue_seededit_v3_edit_webhook::{enqueue_seededit_v3_edit_webhook, SeedEditV3EditArgs};
@@ -174,11 +174,16 @@ pub async fn seededit_3_edit_image_handler(
maybe_creator_user_token: maybe_user_session
.as_ref()
.map(|s| &s.user_token),
maybe_model_type: Some(ModelType::SeedEdit3),
maybe_model_type: Some(CommonModelType::SeedEdit3),
maybe_generation_provider: Some(GenerationProvider::Artcraft),
maybe_positive_prompt: request.prompt.as_deref(),
maybe_negative_prompt: None,
maybe_other_args: None,
maybe_generation_mode: None,
maybe_aspect_ratio: None,
maybe_resolution: None,
maybe_batch_count: None,
maybe_generate_audio: None,
creator_ip_address: &ip_address,
mysql_executor: &mut *transaction,
phantom: Default::default(),
@@ -13,7 +13,7 @@ use artcraft_api_defs::generate::image::text::generate_flux_1_dev_text_to_image:
use enums::by_table::generic_inference_jobs::frontend_failure_category::FrontendFailureCategory;
use enums::by_table::prompts::prompt_type::PromptType;
use enums::common::generation_provider::GenerationProvider;
use enums::common::model_type::ModelType;
use enums::common::generation::common_model_type::CommonModelType;
use enums::common::visibility::Visibility;
use fal_client::requests::webhook::image::text::enqueue_flux_1_dev_text_to_image_webhook::enqueue_flux_1_dev_text_to_image_webhook;
use fal_client::requests::webhook::image::text::enqueue_flux_1_dev_text_to_image_webhook::{Flux1DevArgs, Flux1DevAspectRatio, Flux1DevNumImages};
@@ -193,11 +193,16 @@ pub async fn generate_flux_1_dev_text_to_image_handler(
maybe_creator_user_token: maybe_user_session
.as_ref()
.map(|s| &s.user_token),
maybe_model_type: Some(ModelType::Flux1Dev),
maybe_model_type: Some(CommonModelType::Flux1Dev),
maybe_generation_provider: Some(GenerationProvider::Artcraft),
maybe_positive_prompt: request.prompt.as_deref(),
maybe_negative_prompt: None,
maybe_other_args: None,
maybe_generation_mode: None,
maybe_aspect_ratio: None,
maybe_resolution: None,
maybe_batch_count: None,
maybe_generate_audio: None,
creator_ip_address: &ip_address,
mysql_executor: &mut *transaction,
phantom: Default::default(),
@@ -273,11 +278,16 @@ async fn insert_mock_failure_job(
maybe_apriori_prompt_token: None,
prompt_type: PromptType::ArtcraftApp,
maybe_creator_user_token,
maybe_model_type: Some(ModelType::Flux1Dev),
maybe_model_type: Some(CommonModelType::Flux1Dev),
maybe_generation_provider: Some(GenerationProvider::Artcraft),
maybe_positive_prompt: prompt,
maybe_negative_prompt: None,
maybe_other_args: None,
maybe_generation_mode: None,
maybe_aspect_ratio: None,
maybe_resolution: None,
maybe_batch_count: None,
maybe_generate_audio: None,
creator_ip_address: &ip_address,
mysql_executor: &mut *transaction,
phantom: Default::default(),
@@ -10,7 +10,7 @@ use actix_web::{web, HttpRequest};
use artcraft_api_defs::generate::image::text::generate_flux_1_schnell_text_to_image::{GenerateFlux1SchnellTextToImageAspectRatio, GenerateFlux1SchnellTextToImageNumImages, GenerateFlux1SchnellTextToImageRequest, GenerateFlux1SchnellTextToImageResponse};
use enums::by_table::prompts::prompt_type::PromptType;
use enums::common::generation_provider::GenerationProvider;
use enums::common::model_type::ModelType;
use enums::common::generation::common_model_type::CommonModelType;
use enums::common::visibility::Visibility;
use fal_client::requests::webhook::image::text::enqueue_flux_1_schnell_text_to_image_webhook::enqueue_flux_1_schnell_text_to_image_webhook;
use fal_client::requests::webhook::image::text::enqueue_flux_1_schnell_text_to_image_webhook::{Flux1SchnellArgs, Flux1SchnellAspectRatio, Flux1SchnellNumImages};
@@ -173,11 +173,16 @@ pub async fn generate_flux_1_schnell_text_to_image_handler(
maybe_creator_user_token: maybe_user_session
.as_ref()
.map(|s| &s.user_token),
maybe_model_type: Some(ModelType::Flux1Schnell),
maybe_model_type: Some(CommonModelType::Flux1Schnell),
maybe_generation_provider: Some(GenerationProvider::Artcraft),
maybe_positive_prompt: request.prompt.as_deref(),
maybe_negative_prompt: None,
maybe_other_args: None,
maybe_generation_mode: None,
maybe_aspect_ratio: None,
maybe_resolution: None,
maybe_batch_count: None,
maybe_generate_audio: None,
creator_ip_address: &ip_address,
mysql_executor: &mut *transaction,
phantom: Default::default(),
@@ -13,7 +13,7 @@ use artcraft_api_defs::generate::image::text::generate_flux_pro_11_text_to_image
use artcraft_api_defs::generate::image::text::generate_flux_pro_11_text_to_image::{GenerateFluxPro11TextToImageAspectRatio, GenerateFluxPro11TextToImageNumImages, GenerateFluxPro11TextToImageRequest};
use enums::by_table::prompts::prompt_type::PromptType;
use enums::common::generation_provider::GenerationProvider;
use enums::common::model_type::ModelType;
use enums::common::generation::common_model_type::CommonModelType;
use enums::common::visibility::Visibility;
use fal_client::requests::traits::fal_request_cost_calculator_trait::FalRequestCostCalculator;
use fal_client::requests::webhook::image::text::enqueue_flux_pro_11_text_to_image_webhook::FluxPro11Args;
@@ -157,11 +157,16 @@ pub async fn generate_flux_pro_11_text_to_image_handler(
maybe_apriori_prompt_token: None,
prompt_type: PromptType::ArtcraftApp,
maybe_creator_user_token: Some(&user_token),
maybe_model_type: Some(ModelType::FluxPro11),
maybe_model_type: Some(CommonModelType::FluxPro11),
maybe_generation_provider: Some(GenerationProvider::Artcraft),
maybe_positive_prompt: request.prompt.as_deref(),
maybe_negative_prompt: None,
maybe_other_args: None,
maybe_generation_mode: None,
maybe_aspect_ratio: None,
maybe_resolution: None,
maybe_batch_count: None,
maybe_generate_audio: None,
creator_ip_address: &ip_address,
mysql_executor: &mut *transaction,
phantom: Default::default(),
@@ -13,7 +13,7 @@ use artcraft_api_defs::generate::image::text::generate_flux_pro_11_ultra_text_to
use artcraft_api_defs::generate::image::text::generate_flux_pro_11_ultra_text_to_image::{GenerateFluxPro11UltraTextToImageAspectRatio, GenerateFluxPro11UltraTextToImageNumImages, GenerateFluxPro11UltraTextToImageRequest};
use enums::by_table::prompts::prompt_type::PromptType;
use enums::common::generation_provider::GenerationProvider;
use enums::common::model_type::ModelType;
use enums::common::generation::common_model_type::CommonModelType;
use enums::common::visibility::Visibility;
use fal_client::requests::traits::fal_request_cost_calculator_trait::FalRequestCostCalculator;
use fal_client::requests::webhook::image::text::enqueue_flux_pro_11_ultra_text_to_image_webhook::FluxPro11UltraArgs;
@@ -161,11 +161,16 @@ pub async fn generate_flux_pro_11_ultra_text_to_image_handler(
maybe_apriori_prompt_token: None,
prompt_type: PromptType::ArtcraftApp,
maybe_creator_user_token: Some(&user_token),
maybe_model_type: Some(ModelType::FluxPro11Ultra),
maybe_model_type: Some(CommonModelType::FluxPro11Ultra),
maybe_generation_provider: Some(GenerationProvider::Artcraft),
maybe_positive_prompt: request.prompt.as_deref(),
maybe_negative_prompt: None,
maybe_other_args: None,
maybe_generation_mode: None,
maybe_aspect_ratio: None,
maybe_resolution: None,
maybe_batch_count: None,
maybe_generate_audio: None,
creator_ip_address: &ip_address,
mysql_executor: &mut *transaction,
phantom: Default::default(),
@@ -13,7 +13,7 @@ use artcraft_api_defs::generate::image::text::generate_gpt_image_1_text_to_image
use bucket_paths::legacy::typified_paths::public::media_files::bucket_file_path::MediaFileBucketPath;
use enums::by_table::prompts::prompt_type::PromptType;
use enums::common::generation_provider::GenerationProvider;
use enums::common::model_type::ModelType;
use enums::common::generation::common_model_type::CommonModelType;
use enums::common::payments_namespace::PaymentsNamespace;
use enums::common::stripe_subscription_status::StripeSubscriptionStatus;
use enums::common::visibility::Visibility;
@@ -203,11 +203,16 @@ pub async fn generate_gpt_image_1_text_to_image_handler(
maybe_apriori_prompt_token: None,
prompt_type: PromptType::ArtcraftApp,
maybe_creator_user_token: Some(&user_token),
maybe_model_type: Some(ModelType::GptImage1),
maybe_model_type: Some(CommonModelType::GptImage1),
maybe_generation_provider: Some(GenerationProvider::Artcraft),
maybe_positive_prompt: request.prompt.as_deref(),
maybe_negative_prompt: None,
maybe_other_args: None,
maybe_generation_mode: None,
maybe_aspect_ratio: None,
maybe_resolution: None,
maybe_batch_count: None,
maybe_generate_audio: None,
creator_ip_address: &ip_address,
mysql_executor: &mut *transaction,
phantom: Default::default(),
@@ -13,7 +13,7 @@ use bucket_paths::legacy::typified_paths::public::media_files::bucket_file_path:
use enums::by_table::prompt_context_items::prompt_context_semantic_type::PromptContextSemanticType;
use enums::by_table::prompts::prompt_type::PromptType;
use enums::common::generation_provider::GenerationProvider;
use enums::common::model_type::ModelType;
use enums::common::generation::common_model_type::CommonModelType;
use enums::common::visibility::Visibility;
use fal_client::creds::open_ai_api_key::OpenAiApiKey;
use fal_client::requests::webhook::image::infill::enqueue_flux_dev_juggernaut_infill_webhook::{enqueue_flux_dev_juggernaut_infill_webhook, FluxDevJuggernautInfillArgs, FluxDevJuggernautInfillNumImages};
@@ -212,11 +212,16 @@ pub async fn flux_dev_juggernaut_inpaint_image_handler(
.map(|s| &s.user_token),
// TODO(bt,2025-07-31): Should we have an "inpaint" specific variant?
// Depends on how we want to model provider / feature matrices, routing, and results going forward.
maybe_model_type: Some(ModelType::FluxDevJuggernaut),
maybe_model_type: Some(CommonModelType::FluxDevJuggernaut),
maybe_generation_provider: Some(GenerationProvider::Artcraft),
maybe_positive_prompt: request.prompt.as_deref(),
maybe_negative_prompt: None,
maybe_other_args: None,
maybe_generation_mode: None,
maybe_aspect_ratio: None,
maybe_resolution: None,
maybe_batch_count: None,
maybe_generate_audio: None,
creator_ip_address: &ip_address,
mysql_executor: &mut *transaction,
phantom: Default::default(),
@@ -13,7 +13,7 @@ use bucket_paths::legacy::typified_paths::public::media_files::bucket_file_path:
use enums::by_table::prompt_context_items::prompt_context_semantic_type::PromptContextSemanticType;
use enums::by_table::prompts::prompt_type::PromptType;
use enums::common::generation_provider::GenerationProvider;
use enums::common::model_type::ModelType;
use enums::common::generation::common_model_type::CommonModelType;
use enums::common::visibility::Visibility;
use fal_client::creds::open_ai_api_key::OpenAiApiKey;
use fal_client::requests::webhook::image::infill::enqueue_flux_pro_1_infill_webhook::{enqueue_flux_pro_1_infill_webhook, FluxPro1InfillArgs, FluxPro1InfillNumImages};
@@ -212,11 +212,16 @@ pub async fn flux_pro_1_inpaint_image_handler(
.map(|s| &s.user_token),
// TODO(bt,2025-07-31): Should we have an "inpaint" specific variant?
// Depends on how we want to model provider / feature matrices, routing, and results going forward.
maybe_model_type: Some(ModelType::FluxPro1),
maybe_model_type: Some(CommonModelType::FluxPro1),
maybe_generation_provider: Some(GenerationProvider::Artcraft),
maybe_positive_prompt: request.prompt.as_deref(),
maybe_negative_prompt: None,
maybe_other_args: None,
maybe_generation_mode: None,
maybe_aspect_ratio: None,
maybe_resolution: None,
maybe_batch_count: None,
maybe_generate_audio: None,
creator_ip_address: &ip_address,
mysql_executor: &mut *transaction,
phantom: Default::default(),
@@ -12,7 +12,7 @@ use artcraft_api_defs::generate::image::multi_function::bytedance_seedream_5_lit
use enums::by_table::prompt_context_items::prompt_context_semantic_type::PromptContextSemanticType;
use enums::by_table::prompts::prompt_type::PromptType;
use enums::common::generation_provider::GenerationProvider;
use enums::common::model_type::ModelType;
use enums::common::generation::common_model_type::CommonModelType;
use enums::common::visibility::Visibility;
use fal_client::requests::traits::fal_request_cost_calculator_trait::FalRequestCostCalculator;
use fal_client::requests::webhook::image::edit::enqueue_bytedance_seedream_v5_lite_edit_image_webhook::{enqueue_bytedance_seedream_v5_lite_edit_image_webhook, EnqueueBytedanceSeedreamV5LiteEditImageArgs, EnqueueBytedanceSeedreamV5LiteEditImageNumImages, EnqueueBytedanceSeedreamV5LiteEditImageSize};
@@ -225,11 +225,16 @@ pub async fn bytedance_seedream_5_lite_multi_function_image_gen_handler(
maybe_apriori_prompt_token: None,
prompt_type: PromptType::ArtcraftApp,
maybe_creator_user_token: Some(&user_token),
maybe_model_type: Some(ModelType::Seedream5Lite),
maybe_model_type: Some(CommonModelType::Seedream5Lite),
maybe_generation_provider: Some(GenerationProvider::Artcraft),
maybe_positive_prompt: request.prompt.as_deref(),
maybe_negative_prompt: None,
maybe_other_args: None,
maybe_generation_mode: None,
maybe_aspect_ratio: None,
maybe_resolution: None,
maybe_batch_count: None,
maybe_generate_audio: None,
creator_ip_address: &ip_address,
mysql_executor: &mut *transaction,
phantom: Default::default(),
@@ -17,7 +17,7 @@ use bucket_paths::legacy::typified_paths::public::media_files::bucket_file_path:
use enums::by_table::prompt_context_items::prompt_context_semantic_type::PromptContextSemanticType;
use enums::by_table::prompts::prompt_type::PromptType;
use enums::common::generation_provider::GenerationProvider;
use enums::common::model_type::ModelType;
use enums::common::generation::common_model_type::CommonModelType;
use enums::common::visibility::Visibility;
use fal_client::creds::open_ai_api_key::OpenAiApiKey;
use fal_client::requests::traits::fal_request_cost_calculator_trait::FalRequestCostCalculator;
@@ -249,11 +249,16 @@ pub async fn bytedance_seedream_v4_multi_function_image_gen_handler(
maybe_apriori_prompt_token: None,
prompt_type: PromptType::ArtcraftApp,
maybe_creator_user_token: Some(&user_token),
maybe_model_type: Some(ModelType::Seedream4),
maybe_model_type: Some(CommonModelType::Seedream4),
maybe_generation_provider: Some(GenerationProvider::Artcraft),
maybe_positive_prompt: request.prompt.as_deref(),
maybe_negative_prompt: None,
maybe_other_args: None,
maybe_generation_mode: None,
maybe_aspect_ratio: None,
maybe_resolution: None,
maybe_batch_count: None,
maybe_generate_audio: None,
creator_ip_address: &ip_address,
mysql_executor: &mut *transaction,
phantom: Default::default(),
@@ -17,7 +17,7 @@ use bucket_paths::legacy::typified_paths::public::media_files::bucket_file_path:
use enums::by_table::prompt_context_items::prompt_context_semantic_type::PromptContextSemanticType;
use enums::by_table::prompts::prompt_type::PromptType;
use enums::common::generation_provider::GenerationProvider;
use enums::common::model_type::ModelType;
use enums::common::generation::common_model_type::CommonModelType;
use enums::common::visibility::Visibility;
use fal_client::creds::open_ai_api_key::OpenAiApiKey;
use fal_client::requests::traits::fal_request_cost_calculator_trait::FalRequestCostCalculator;
@@ -247,11 +247,16 @@ pub async fn bytedance_seedream_v4p5_multi_function_image_gen_handler(
maybe_apriori_prompt_token: None,
prompt_type: PromptType::ArtcraftApp,
maybe_creator_user_token: Some(&user_token),
maybe_model_type: Some(ModelType::Seedream4p5),
maybe_model_type: Some(CommonModelType::Seedream4p5),
maybe_generation_provider: Some(GenerationProvider::Artcraft),
maybe_positive_prompt: request.prompt.as_deref(),
maybe_negative_prompt: None,
maybe_other_args: None,
maybe_generation_mode: None,
maybe_aspect_ratio: None,
maybe_resolution: None,
maybe_batch_count: None,
maybe_generate_audio: None,
creator_ip_address: &ip_address,
mysql_executor: &mut *transaction,
phantom: Default::default(),
@@ -17,7 +17,7 @@ use bucket_paths::legacy::typified_paths::public::media_files::bucket_file_path:
use enums::by_table::prompt_context_items::prompt_context_semantic_type::PromptContextSemanticType;
use enums::by_table::prompts::prompt_type::PromptType;
use enums::common::generation_provider::GenerationProvider;
use enums::common::model_type::ModelType;
use enums::common::generation::common_model_type::CommonModelType;
use enums::common::payments_namespace::PaymentsNamespace;
use enums::common::stripe_subscription_status::StripeSubscriptionStatus;
use enums::common::visibility::Visibility;
@@ -337,11 +337,16 @@ pub async fn gpt_image_1p5_multi_function_image_gen_handler(
maybe_apriori_prompt_token: None,
prompt_type: PromptType::ArtcraftApp,
maybe_creator_user_token: Some(&user_token),
maybe_model_type: Some(ModelType::GptImage1p5),
maybe_model_type: Some(CommonModelType::GptImage1p5),
maybe_generation_provider: Some(GenerationProvider::Artcraft),
maybe_positive_prompt: request.prompt.as_deref(),
maybe_negative_prompt: None,
maybe_other_args: None,
maybe_generation_mode: None,
maybe_aspect_ratio: None,
maybe_resolution: None,
maybe_batch_count: None,
maybe_generate_audio: None,
creator_ip_address: &ip_address,
mysql_executor: &mut *transaction,
phantom: Default::default(),
@@ -12,7 +12,7 @@ use artcraft_api_defs::generate::image::multi_function::nano_banana_2_multi_func
use enums::by_table::prompt_context_items::prompt_context_semantic_type::PromptContextSemanticType;
use enums::by_table::prompts::prompt_type::PromptType;
use enums::common::generation_provider::GenerationProvider;
use enums::common::model_type::ModelType;
use enums::common::generation::common_model_type::CommonModelType;
use enums::common::visibility::Visibility;
use fal_client::requests::traits::fal_request_cost_calculator_trait::FalRequestCostCalculator;
use fal_client::requests::webhook::image::edit::enqueue_nano_banana_2_edit_image_webhook::{enqueue_nano_banana_2_edit_image_webhook, EnqueueNanoBanana2EditImageArgs, EnqueueNanoBanana2EditImageAspectRatio, EnqueueNanoBanana2EditImageNumImages, EnqueueNanoBanana2EditImageResolution};
@@ -248,11 +248,16 @@ pub async fn nano_banana_2_multi_function_image_gen_handler(
maybe_apriori_prompt_token: None,
prompt_type: PromptType::ArtcraftApp,
maybe_creator_user_token: Some(&user_token),
maybe_model_type: Some(ModelType::NanoBanana2),
maybe_model_type: Some(CommonModelType::NanoBanana2),
maybe_generation_provider: Some(GenerationProvider::Artcraft),
maybe_positive_prompt: request.prompt.as_deref(),
maybe_negative_prompt: None,
maybe_other_args: None,
maybe_generation_mode: None,
maybe_aspect_ratio: None,
maybe_resolution: None,
maybe_batch_count: None,
maybe_generate_audio: None,
creator_ip_address: &ip_address,
mysql_executor: &mut *transaction,
phantom: Default::default(),
@@ -18,7 +18,7 @@ use bucket_paths::legacy::typified_paths::public::media_files::bucket_file_path:
use enums::by_table::prompt_context_items::prompt_context_semantic_type::PromptContextSemanticType;
use enums::by_table::prompts::prompt_type::PromptType;
use enums::common::generation_provider::GenerationProvider;
use enums::common::model_type::ModelType;
use enums::common::generation::common_model_type::CommonModelType;
use enums::common::payments_namespace::PaymentsNamespace;
use enums::common::stripe_subscription_status::StripeSubscriptionStatus;
use enums::common::visibility::Visibility;
@@ -280,11 +280,16 @@ pub async fn nano_banana_multi_function_image_gen_handler(
maybe_apriori_prompt_token: None,
prompt_type: PromptType::ArtcraftApp,
maybe_creator_user_token: Some(&user_token),
maybe_model_type: Some(ModelType::NanoBanana),
maybe_model_type: Some(CommonModelType::NanoBanana),
maybe_generation_provider: Some(GenerationProvider::Artcraft),
maybe_positive_prompt: request.prompt.as_deref(),
maybe_negative_prompt: None,
maybe_other_args: None,
maybe_generation_mode: None,
maybe_aspect_ratio: None,
maybe_resolution: None,
maybe_batch_count: None,
maybe_generate_audio: None,
creator_ip_address: &ip_address,
mysql_executor: &mut *transaction,
phantom: Default::default(),
@@ -17,7 +17,7 @@ use bucket_paths::legacy::typified_paths::public::media_files::bucket_file_path:
use enums::by_table::prompt_context_items::prompt_context_semantic_type::PromptContextSemanticType;
use enums::by_table::prompts::prompt_type::PromptType;
use enums::common::generation_provider::GenerationProvider;
use enums::common::model_type::ModelType;
use enums::common::generation::common_model_type::CommonModelType;
use enums::common::payments_namespace::PaymentsNamespace;
use enums::common::stripe_subscription_status::StripeSubscriptionStatus;
use enums::common::visibility::Visibility;
@@ -295,11 +295,16 @@ pub async fn nano_banana_pro_multi_function_image_gen_handler(
maybe_apriori_prompt_token: None,
prompt_type: PromptType::ArtcraftApp,
maybe_creator_user_token: Some(&user_token),
maybe_model_type: Some(ModelType::NanoBananaPro),
maybe_model_type: Some(CommonModelType::NanoBananaPro),
maybe_generation_provider: Some(GenerationProvider::Artcraft),
maybe_positive_prompt: request.prompt.as_deref(),
maybe_negative_prompt: None,
maybe_other_args: None,
maybe_generation_mode: None,
maybe_aspect_ratio: None,
maybe_resolution: None,
maybe_batch_count: None,
maybe_generate_audio: None,
creator_ip_address: &ip_address,
mysql_executor: &mut *transaction,
phantom: Default::default(),
@@ -15,7 +15,7 @@ use artcraft_api_defs::generate::object::multi_function::hunyuan3d_v3_multi_func
use enums::by_table::prompt_context_items::prompt_context_semantic_type::PromptContextSemanticType;
use enums::by_table::prompts::prompt_type::PromptType;
use enums::common::generation_provider::GenerationProvider;
use enums::common::model_type::ModelType;
use enums::common::generation::common_model_type::CommonModelType;
use enums::common::visibility::Visibility;
use fal_client::requests::webhook::object::enqueue_hunyuan3d_v3_image_to_3d_webhook::{
enqueue_hunyuan3d_v3_image_to_3d_webhook, EnqueueHunyuan3dV3ImageTo3dArgs,
@@ -302,11 +302,16 @@ pub async fn generate_hunyuan3d_v3_multi_function_object_handler(
maybe_apriori_prompt_token: None,
prompt_type: PromptType::ArtcraftApp,
maybe_creator_user_token: maybe_user_session.as_ref().map(|s| &s.user_token),
maybe_model_type: Some(ModelType::Hunyuan3d3),
maybe_model_type: Some(CommonModelType::Hunyuan3d3),
maybe_generation_provider: Some(GenerationProvider::Artcraft),
maybe_positive_prompt: request.prompt.as_deref(),
maybe_negative_prompt: None,
maybe_other_args: None,
maybe_generation_mode: None,
maybe_aspect_ratio: None,
maybe_resolution: None,
maybe_batch_count: None,
maybe_generate_audio: None,
creator_ip_address: &ip_address,
mysql_executor: &mut *transaction,
phantom: Default::default(),
@@ -13,7 +13,7 @@ use artcraft_api_defs::generate::splat::generate_worldlabs_marble_0p1_mini_splat
use enums::by_table::prompt_context_items::prompt_context_semantic_type::PromptContextSemanticType;
use enums::by_table::prompts::prompt_type::PromptType;
use enums::common::generation_provider::GenerationProvider;
use enums::common::model_type::ModelType;
use enums::common::generation::common_model_type::CommonModelType;
use enums::common::visibility::Visibility;
use http_server_common::request::get_request_ip::get_request_ip;
use log::{error, info, warn};
@@ -174,11 +174,16 @@ pub async fn generate_worldlabs_marble_0p1_mini_splat_handler(
maybe_apriori_prompt_token: None,
prompt_type: PromptType::ArtcraftApp,
maybe_creator_user_token: Some(user_token),
maybe_model_type: Some(ModelType::Marble0p1Mini),
maybe_model_type: Some(CommonModelType::Marble0p1Mini),
maybe_generation_provider: Some(GenerationProvider::Artcraft),
maybe_positive_prompt: request.prompt.as_deref(),
maybe_negative_prompt: None,
maybe_other_args: None,
maybe_generation_mode: None,
maybe_aspect_ratio: None,
maybe_resolution: None,
maybe_batch_count: None,
maybe_generate_audio: None,
creator_ip_address: &ip_address,
mysql_executor: &mut *transaction,
phantom: Default::default(),
@@ -13,7 +13,7 @@ use artcraft_api_defs::generate::splat::generate_worldlabs_marble_0p1_plus_splat
use enums::by_table::prompt_context_items::prompt_context_semantic_type::PromptContextSemanticType;
use enums::by_table::prompts::prompt_type::PromptType;
use enums::common::generation_provider::GenerationProvider;
use enums::common::model_type::ModelType;
use enums::common::generation::common_model_type::CommonModelType;
use enums::common::visibility::Visibility;
use http_server_common::request::get_request_ip::get_request_ip;
use log::{error, info, warn};
@@ -174,11 +174,16 @@ pub async fn generate_worldlabs_marble_0p1_plus_splat_handler(
maybe_apriori_prompt_token: None,
prompt_type: PromptType::ArtcraftApp,
maybe_creator_user_token: Some(user_token),
maybe_model_type: Some(ModelType::Marble0p1Plus),
maybe_model_type: Some(CommonModelType::Marble0p1Plus),
maybe_generation_provider: Some(GenerationProvider::Artcraft),
maybe_positive_prompt: request.prompt.as_deref(),
maybe_negative_prompt: None,
maybe_other_args: None,
maybe_generation_mode: None,
maybe_aspect_ratio: None,
maybe_resolution: None,
maybe_batch_count: None,
maybe_generate_audio: None,
creator_ip_address: &ip_address,
mysql_executor: &mut *transaction,
phantom: Default::default(),
@@ -16,7 +16,7 @@ use artcraft_api_defs::generate::video::generate_kling_1_6_pro_image_to_video::{
use bucket_paths::legacy::typified_paths::public::media_files::bucket_file_path::MediaFileBucketPath;
use enums::by_table::prompts::prompt_type::PromptType;
use enums::common::generation_provider::GenerationProvider;
use enums::common::model_type::ModelType;
use enums::common::generation::common_model_type::CommonModelType;
use enums::common::visibility::Visibility;
use fal_client::requests::traits::fal_request_cost_calculator_trait::FalRequestCostCalculator;
use fal_client::requests::webhook::video::image::enqueue_kling_v1p6_pro_image_to_video_webhook::{enqueue_kling_v1p6_pro_image_to_video_webhook, Kling1p6ProArgs, Kling1p6ProAspectRatio, Kling1p6ProDuration};
@@ -216,11 +216,16 @@ pub async fn generate_kling_1_6_pro_video_handler(
maybe_creator_user_token: maybe_user_session
.as_ref()
.map(|s| &s.user_token),
maybe_model_type: Some(ModelType::Kling16Pro),
maybe_model_type: Some(CommonModelType::Kling16Pro),
maybe_generation_provider: Some(GenerationProvider::Artcraft),
maybe_positive_prompt: Some(prompt),
maybe_negative_prompt: None,
maybe_other_args: None,
maybe_generation_mode: None,
maybe_aspect_ratio: None,
maybe_resolution: None,
maybe_batch_count: None,
maybe_generate_audio: None,
creator_ip_address: &ip_address,
mysql_executor: &mut *transaction,
phantom: Default::default(),
@@ -14,7 +14,7 @@ use artcraft_api_defs::generate::video::generate_kling_2_1_master_image_to_video
use bucket_paths::legacy::typified_paths::public::media_files::bucket_file_path::MediaFileBucketPath;
use enums::by_table::prompts::prompt_type::PromptType;
use enums::common::generation_provider::GenerationProvider;
use enums::common::model_type::ModelType;
use enums::common::generation::common_model_type::CommonModelType;
use enums::common::visibility::Visibility;
use fal_client::requests::traits::fal_request_cost_calculator_trait::FalRequestCostCalculator;
use fal_client::requests::webhook::video::image::enqueue_kling_v2p1_master_image_to_video_webhook::{enqueue_kling_v2p1_master_image_to_video_webhook, Kling2p1MasterArgs, Kling2p1MasterAspectRatio, Kling2p1MasterDuration};
@@ -203,11 +203,16 @@ pub async fn generate_kling_2_1_master_video_handler(
maybe_creator_user_token: maybe_user_session
.as_ref()
.map(|s| &s.user_token),
maybe_model_type: Some(ModelType::Kling21Master),
maybe_model_type: Some(CommonModelType::Kling21Master),
maybe_generation_provider: Some(GenerationProvider::Artcraft),
maybe_positive_prompt: Some(prompt),
maybe_negative_prompt: None,
maybe_other_args: None,
maybe_generation_mode: None,
maybe_aspect_ratio: None,
maybe_resolution: None,
maybe_batch_count: None,
maybe_generate_audio: None,
creator_ip_address: &ip_address,
mysql_executor: &mut *transaction,
phantom: Default::default(),
@@ -16,7 +16,7 @@ use artcraft_api_defs::generate::video::generate_kling_2_1_pro_image_to_video::{
use bucket_paths::legacy::typified_paths::public::media_files::bucket_file_path::MediaFileBucketPath;
use enums::by_table::prompts::prompt_type::PromptType;
use enums::common::generation_provider::GenerationProvider;
use enums::common::model_type::ModelType;
use enums::common::generation::common_model_type::CommonModelType;
use enums::common::visibility::Visibility;
use fal_client::requests::traits::fal_request_cost_calculator_trait::FalRequestCostCalculator;
use fal_client::requests::webhook::video::image::enqueue_kling_v2p1_pro_image_to_video_webhook::enqueue_kling_v2p1_pro_image_to_video_webhook;
@@ -218,11 +218,16 @@ pub async fn generate_kling_2_1_pro_video_handler(
maybe_creator_user_token: maybe_user_session
.as_ref()
.map(|s| &s.user_token),
maybe_model_type: Some(ModelType::Kling21Pro),
maybe_model_type: Some(CommonModelType::Kling21Pro),
maybe_generation_provider: Some(GenerationProvider::Artcraft),
maybe_positive_prompt: Some(prompt),
maybe_negative_prompt: None,
maybe_other_args: None,
maybe_generation_mode: None,
maybe_aspect_ratio: None,
maybe_resolution: None,
maybe_batch_count: None,
maybe_generate_audio: None,
creator_ip_address: &ip_address,
mysql_executor: &mut *transaction,
phantom: Default::default(),
@@ -18,7 +18,7 @@ use artcraft_api_defs::generate::video::generate_seedance_1_0_lite_image_to_vide
use bucket_paths::legacy::typified_paths::public::media_files::bucket_file_path::MediaFileBucketPath;
use enums::by_table::prompts::prompt_type::PromptType;
use enums::common::generation_provider::GenerationProvider;
use enums::common::model_type::ModelType;
use enums::common::generation::common_model_type::CommonModelType;
use enums::common::visibility::Visibility;
use fal_client::requests::traits::fal_request_cost_calculator_trait::FalRequestCostCalculator;
use fal_client::requests::webhook::video::image::enqueue_seedance_1_lite_image_to_video_webhook::Seedance1LiteDuration;
@@ -233,11 +233,16 @@ pub async fn generate_seedance_1_0_lite_image_to_video_handler(
maybe_creator_user_token: maybe_user_session
.as_ref()
.map(|s| &s.user_token),
maybe_model_type: Some(ModelType::Seedance10Lite),
maybe_model_type: Some(CommonModelType::Seedance10Lite),
maybe_generation_provider: Some(GenerationProvider::Artcraft),
maybe_positive_prompt: Some(prompt),
maybe_negative_prompt: None,
maybe_other_args: None,
maybe_generation_mode: None,
maybe_aspect_ratio: None,
maybe_resolution: None,
maybe_batch_count: None,
maybe_generate_audio: None,
creator_ip_address: &ip_address,
mysql_executor: &mut *transaction,
phantom: Default::default(),
@@ -18,7 +18,7 @@ use artcraft_api_defs::generate::video::generate_seedance_1_0_pro_image_to_video
use bucket_paths::legacy::typified_paths::public::media_files::bucket_file_path::MediaFileBucketPath;
use enums::by_table::prompts::prompt_type::PromptType;
use enums::common::generation_provider::GenerationProvider;
use enums::common::model_type::ModelType;
use enums::common::generation::common_model_type::CommonModelType;
use enums::common::visibility::Visibility;
use fal_client::requests::traits::fal_request_cost_calculator_trait::FalRequestCostCalculator;
use fal_client::requests::webhook::video::image::enqueue_seedance_1_pro_image_to_video_webhook::enqueue_seedance_1_pro_image_to_video_webhook;
@@ -210,11 +210,16 @@ pub async fn generate_seedance_1_0_pro_image_to_video_handler(
maybe_creator_user_token: maybe_user_session
.as_ref()
.map(|s| &s.user_token),
maybe_model_type: Some(ModelType::Seedance10Pro),
maybe_model_type: Some(CommonModelType::Seedance10Pro),
maybe_generation_provider: Some(GenerationProvider::Artcraft),
maybe_positive_prompt: Some(prompt),
maybe_negative_prompt: None,
maybe_other_args: None,
maybe_generation_mode: None,
maybe_aspect_ratio: None,
maybe_resolution: None,
maybe_batch_count: None,
maybe_generate_audio: None,
creator_ip_address: &ip_address,
mysql_executor: &mut *transaction,
phantom: Default::default(),
@@ -18,7 +18,7 @@ use artcraft_api_defs::generate::video::generate_veo_2_image_to_video::GenerateV
use bucket_paths::legacy::typified_paths::public::media_files::bucket_file_path::MediaFileBucketPath;
use enums::by_table::prompts::prompt_type::PromptType;
use enums::common::generation_provider::GenerationProvider;
use enums::common::model_type::ModelType;
use enums::common::generation::common_model_type::CommonModelType;
use enums::common::visibility::Visibility;
use fal_client::requests::traits::fal_request_cost_calculator_trait::FalRequestCostCalculator;
use fal_client::requests::webhook::video::image::enqueue_veo_2_image_to_video_webhook::enqueue_veo_2_image_to_video_webhook;
@@ -214,11 +214,16 @@ pub async fn generate_veo_2_image_to_video_handler(
maybe_creator_user_token: maybe_user_session
.as_ref()
.map(|s| &s.user_token),
maybe_model_type: Some(ModelType::Veo2),
maybe_model_type: Some(CommonModelType::Veo2),
maybe_generation_provider: Some(GenerationProvider::Artcraft),
maybe_positive_prompt: Some(prompt),
maybe_negative_prompt: None,
maybe_other_args: None,
maybe_generation_mode: None,
maybe_aspect_ratio: None,
maybe_resolution: None,
maybe_batch_count: None,
maybe_generate_audio: None,
creator_ip_address: &ip_address,
mysql_executor: &mut *transaction,
phantom: Default::default(),
@@ -15,7 +15,7 @@ use artcraft_api_defs::generate::video::generate_veo_3_fast_image_to_video::{Gen
use bucket_paths::legacy::typified_paths::public::media_files::bucket_file_path::MediaFileBucketPath;
use enums::by_table::prompts::prompt_type::PromptType;
use enums::common::generation_provider::GenerationProvider;
use enums::common::model_type::ModelType;
use enums::common::generation::common_model_type::CommonModelType;
use enums::common::visibility::Visibility;
use fal_client::requests::traits::fal_request_cost_calculator_trait::FalRequestCostCalculator;
use fal_client::requests::webhook::video::image::enqueue_veo_3_fast_image_to_video_webhook::{enqueue_veo_3_fast_image_to_video_webhook, Veo3FastArgs, Veo3FastDuration, Veo3FastResolution};
@@ -207,11 +207,16 @@ pub async fn generate_veo_3_fast_image_to_video_handler(
maybe_creator_user_token: maybe_user_session
.as_ref()
.map(|s| &s.user_token),
maybe_model_type: Some(ModelType::Veo3Fast),
maybe_model_type: Some(CommonModelType::Veo3Fast),
maybe_generation_provider: Some(GenerationProvider::Artcraft),
maybe_positive_prompt: Some(prompt),
maybe_negative_prompt: None,
maybe_other_args: None,
maybe_generation_mode: None,
maybe_aspect_ratio: None,
maybe_resolution: None,
maybe_batch_count: None,
maybe_generate_audio: None,
creator_ip_address: &ip_address,
mysql_executor: &mut *transaction,
phantom: Default::default(),
@@ -15,7 +15,7 @@ use artcraft_api_defs::generate::video::generate_veo_3_image_to_video::{Generate
use bucket_paths::legacy::typified_paths::public::media_files::bucket_file_path::MediaFileBucketPath;
use enums::by_table::prompts::prompt_type::PromptType;
use enums::common::generation_provider::GenerationProvider;
use enums::common::model_type::ModelType;
use enums::common::generation::common_model_type::CommonModelType;
use enums::common::visibility::Visibility;
use fal_client::requests::traits::fal_request_cost_calculator_trait::FalRequestCostCalculator;
use fal_client::requests::webhook::video::image::enqueue_veo_3_image_to_video_webhook::{enqueue_veo_3_image_to_video_webhook, Veo3Args, Veo3AspectRatio, Veo3Duration, Veo3Resolution};
@@ -215,11 +215,16 @@ pub async fn generate_veo_3_image_to_video_handler(
maybe_creator_user_token: maybe_user_session
.as_ref()
.map(|s| &s.user_token),
maybe_model_type: Some(ModelType::Veo3),
maybe_model_type: Some(CommonModelType::Veo3),
maybe_generation_provider: Some(GenerationProvider::Artcraft),
maybe_positive_prompt: Some(prompt),
maybe_negative_prompt: None,
maybe_other_args: None,
maybe_generation_mode: None,
maybe_aspect_ratio: None,
maybe_resolution: None,
maybe_batch_count: None,
maybe_generate_audio: None,
creator_ip_address: &ip_address,
mysql_executor: &mut *transaction,
phantom: Default::default(),
@@ -17,7 +17,7 @@ use bucket_paths::legacy::typified_paths::public::media_files::bucket_file_path:
use enums::by_table::prompt_context_items::prompt_context_semantic_type::PromptContextSemanticType;
use enums::by_table::prompts::prompt_type::PromptType;
use enums::common::generation_provider::GenerationProvider;
use enums::common::model_type::ModelType;
use enums::common::generation::common_model_type::CommonModelType;
use enums::common::visibility::Visibility;
use fal_client::creds::open_ai_api_key::OpenAiApiKey;
use fal_client::requests::traits::fal_request_cost_calculator_trait::FalRequestCostCalculator;
@@ -256,11 +256,16 @@ pub async fn kling_2p5_turbo_pro_multi_function_video_gen_handler(
maybe_creator_user_token: maybe_user_session
.as_ref()
.map(|s| &s.user_token),
maybe_model_type: Some(ModelType::Kling2p5TurboPro),
maybe_model_type: Some(CommonModelType::Kling2p5TurboPro),
maybe_generation_provider: Some(GenerationProvider::Artcraft),
maybe_positive_prompt: request.prompt.as_deref(),
maybe_negative_prompt: None,
maybe_other_args: None,
maybe_generation_mode: None,
maybe_aspect_ratio: None,
maybe_resolution: None,
maybe_batch_count: None,
maybe_generate_audio: None,
creator_ip_address: &ip_address,
mysql_executor: &mut *transaction,
phantom: Default::default(),
@@ -17,7 +17,7 @@ use bucket_paths::legacy::typified_paths::public::media_files::bucket_file_path:
use enums::by_table::prompt_context_items::prompt_context_semantic_type::PromptContextSemanticType;
use enums::by_table::prompts::prompt_type::PromptType;
use enums::common::generation_provider::GenerationProvider;
use enums::common::model_type::ModelType;
use enums::common::generation::common_model_type::CommonModelType;
use enums::common::visibility::Visibility;
use fal_client::creds::open_ai_api_key::OpenAiApiKey;
use fal_client::requests::traits::fal_request_cost_calculator_trait::FalRequestCostCalculator;
@@ -255,11 +255,16 @@ pub async fn kling_2p6_pro_multi_function_video_gen_handler(
maybe_creator_user_token: maybe_user_session
.as_ref()
.map(|s| &s.user_token),
maybe_model_type: Some(ModelType::Kling2p6Pro),
maybe_model_type: Some(CommonModelType::Kling2p6Pro),
maybe_generation_provider: Some(GenerationProvider::Artcraft),
maybe_positive_prompt: request.prompt.as_deref(),
maybe_negative_prompt: None,
maybe_other_args: None,
maybe_generation_mode: None,
maybe_aspect_ratio: None,
maybe_resolution: None,
maybe_batch_count: None,
maybe_generate_audio: None,
creator_ip_address: &ip_address,
mysql_executor: &mut *transaction,
phantom: Default::default(),
@@ -17,7 +17,7 @@ use bucket_paths::legacy::typified_paths::public::media_files::bucket_file_path:
use enums::by_table::prompt_context_items::prompt_context_semantic_type::PromptContextSemanticType;
use enums::by_table::prompts::prompt_type::PromptType;
use enums::common::generation_provider::GenerationProvider;
use enums::common::model_type::ModelType;
use enums::common::generation::common_model_type::CommonModelType;
use enums::common::visibility::Visibility;
use fal_client::creds::open_ai_api_key::OpenAiApiKey;
use fal_client::requests::traits::fal_request_cost_calculator_trait::FalRequestCostCalculator;
@@ -304,11 +304,16 @@ pub async fn kling_3p0_pro_multi_function_video_gen_handler(
maybe_creator_user_token: maybe_user_session
.as_ref()
.map(|s| &s.user_token),
maybe_model_type: Some(ModelType::Kling3p0Pro),
maybe_model_type: Some(CommonModelType::Kling3p0Pro),
maybe_generation_provider: Some(GenerationProvider::Artcraft),
maybe_positive_prompt: request.prompt.as_deref(),
maybe_negative_prompt: None,
maybe_other_args: None,
maybe_generation_mode: None,
maybe_aspect_ratio: None,
maybe_resolution: None,
maybe_batch_count: None,
maybe_generate_audio: None,
creator_ip_address: &ip_address,
mysql_executor: &mut *transaction,
phantom: Default::default(),
@@ -17,7 +17,7 @@ use bucket_paths::legacy::typified_paths::public::media_files::bucket_file_path:
use enums::by_table::prompt_context_items::prompt_context_semantic_type::PromptContextSemanticType;
use enums::by_table::prompts::prompt_type::PromptType;
use enums::common::generation_provider::GenerationProvider;
use enums::common::model_type::ModelType;
use enums::common::generation::common_model_type::CommonModelType;
use enums::common::visibility::Visibility;
use fal_client::creds::open_ai_api_key::OpenAiApiKey;
use fal_client::requests::traits::fal_request_cost_calculator_trait::FalRequestCostCalculator;
@@ -304,11 +304,16 @@ pub async fn kling_3p0_standard_multi_function_video_gen_handler(
maybe_creator_user_token: maybe_user_session
.as_ref()
.map(|s| &s.user_token),
maybe_model_type: Some(ModelType::Kling3p0Standard),
maybe_model_type: Some(CommonModelType::Kling3p0Standard),
maybe_generation_provider: Some(GenerationProvider::Artcraft),
maybe_positive_prompt: request.prompt.as_deref(),
maybe_negative_prompt: None,
maybe_other_args: None,
maybe_generation_mode: None,
maybe_aspect_ratio: None,
maybe_resolution: None,
maybe_batch_count: None,
maybe_generate_audio: None,
creator_ip_address: &ip_address,
mysql_executor: &mut *transaction,
phantom: Default::default(),
@@ -13,7 +13,7 @@ use artcraft_api_defs::generate::video::multi_function::seedance_1p5_pro_multi_f
use enums::by_table::prompt_context_items::prompt_context_semantic_type::PromptContextSemanticType;
use enums::by_table::prompts::prompt_type::PromptType;
use enums::common::generation_provider::GenerationProvider;
use enums::common::model_type::ModelType;
use enums::common::generation::common_model_type::CommonModelType;
use enums::common::visibility::Visibility;
use fal_client::requests::traits::fal_request_cost_calculator_trait::FalRequestCostCalculator;
use fal_client::requests::webhook::video::image::enqueue_seedance_1p5_pro_image_to_video_webhook::{enqueue_seedance_1p5_pro_image_to_video_webhook, EnqueueSeedance1p5ProImageToVideoArgs, EnqueueSeedance1p5ProImageToVideoAspectRatio, EnqueueSeedance1p5ProImageToVideoDuration, EnqueueSeedance1p5ProImageToVideoResolution};
@@ -309,11 +309,16 @@ pub async fn seedance_1p5_pro_multi_function_video_gen_handler(
maybe_creator_user_token: maybe_user_session
.as_ref()
.map(|s| &s.user_token),
maybe_model_type: Some(ModelType::Seedance1p5Pro),
maybe_model_type: Some(CommonModelType::Seedance1p5Pro),
maybe_generation_provider: Some(GenerationProvider::Artcraft),
maybe_positive_prompt: request.prompt.as_deref(),
maybe_negative_prompt: None,
maybe_other_args: None,
maybe_generation_mode: None,
maybe_aspect_ratio: None,
maybe_resolution: None,
maybe_batch_count: None,
maybe_generate_audio: None,
creator_ip_address: &ip_address,
mysql_executor: &mut *transaction,
phantom: Default::default(),
@@ -18,7 +18,7 @@ use artcraft_api_defs::generate::video::multi_function::seedance_2p0_multi_funct
use enums::by_table::prompt_context_items::prompt_context_semantic_type::PromptContextSemanticType;
use enums::by_table::prompts::prompt_type::PromptType;
use enums::common::generation_provider::GenerationProvider;
use enums::common::model_type::ModelType;
use enums::common::generation::common_model_type::CommonModelType;
use enums::common::visibility::Visibility;
use http_server_common::request::get_request_ip::get_request_ip;
use log::{error, info, warn};
@@ -268,11 +268,16 @@ pub async fn seedance_2p0_multi_function_video_gen_handler(
maybe_apriori_prompt_token: None,
prompt_type: PromptType::ArtcraftApp,
maybe_creator_user_token: maybe_user_session.as_ref().map(|s| &s.user_token),
maybe_model_type: Some(ModelType::Seedance2p0),
maybe_model_type: Some(CommonModelType::Seedance2p0),
maybe_generation_provider: Some(GenerationProvider::Artcraft),
maybe_positive_prompt: request.prompt.as_deref(),
maybe_negative_prompt: None,
maybe_other_args: None,
maybe_generation_mode: None,
maybe_aspect_ratio: None,
maybe_resolution: None,
maybe_batch_count: None,
maybe_generate_audio: None,
creator_ip_address: &ip_address,
mysql_executor: &mut *transaction,
phantom: Default::default(),
@@ -17,7 +17,7 @@ use bucket_paths::legacy::typified_paths::public::media_files::bucket_file_path:
use enums::by_table::prompt_context_items::prompt_context_semantic_type::PromptContextSemanticType;
use enums::by_table::prompts::prompt_type::PromptType;
use enums::common::generation_provider::GenerationProvider;
use enums::common::model_type::ModelType;
use enums::common::generation::common_model_type::CommonModelType;
use enums::common::visibility::Visibility;
use fal_client::creds::open_ai_api_key::OpenAiApiKey;
use fal_client::requests::traits::fal_request_cost_calculator_trait::FalRequestCostCalculator;
@@ -275,11 +275,16 @@ pub async fn sora_2_multi_function_video_gen_handler(
maybe_creator_user_token: maybe_user_session
.as_ref()
.map(|s| &s.user_token),
maybe_model_type: Some(ModelType::Sora2),
maybe_model_type: Some(CommonModelType::Sora2),
maybe_generation_provider: Some(GenerationProvider::Artcraft),
maybe_positive_prompt: request.prompt.as_deref(),
maybe_negative_prompt: None,
maybe_other_args: None,
maybe_generation_mode: None,
maybe_aspect_ratio: None,
maybe_resolution: None,
maybe_batch_count: None,
maybe_generate_audio: None,
creator_ip_address: &ip_address,
mysql_executor: &mut *transaction,
phantom: Default::default(),
@@ -17,7 +17,7 @@ use bucket_paths::legacy::typified_paths::public::media_files::bucket_file_path:
use enums::by_table::prompt_context_items::prompt_context_semantic_type::PromptContextSemanticType;
use enums::by_table::prompts::prompt_type::PromptType;
use enums::common::generation_provider::GenerationProvider;
use enums::common::model_type::ModelType;
use enums::common::generation::common_model_type::CommonModelType;
use enums::common::visibility::Visibility;
use fal_client::creds::open_ai_api_key::OpenAiApiKey;
use fal_client::requests::traits::fal_request_cost_calculator_trait::FalRequestCostCalculator;
@@ -277,11 +277,16 @@ pub async fn sora_2_pro_multi_function_video_gen_handler(
maybe_creator_user_token: maybe_user_session
.as_ref()
.map(|s| &s.user_token),
maybe_model_type: Some(ModelType::Sora2Pro),
maybe_model_type: Some(CommonModelType::Sora2Pro),
maybe_generation_provider: Some(GenerationProvider::Artcraft),
maybe_positive_prompt: request.prompt.as_deref(),
maybe_negative_prompt: None,
maybe_other_args: None,
maybe_generation_mode: None,
maybe_aspect_ratio: None,
maybe_resolution: None,
maybe_batch_count: None,
maybe_generate_audio: None,
creator_ip_address: &ip_address,
mysql_executor: &mut *transaction,
phantom: Default::default(),
@@ -17,7 +17,7 @@ use bucket_paths::legacy::typified_paths::public::media_files::bucket_file_path:
use enums::by_table::prompt_context_items::prompt_context_semantic_type::PromptContextSemanticType;
use enums::by_table::prompts::prompt_type::PromptType;
use enums::common::generation_provider::GenerationProvider;
use enums::common::model_type::ModelType;
use enums::common::generation::common_model_type::CommonModelType;
use enums::common::visibility::Visibility;
use fal_client::creds::open_ai_api_key::OpenAiApiKey;
use fal_client::requests::traits::fal_request_cost_calculator_trait::FalRequestCostCalculator;
@@ -340,11 +340,16 @@ pub async fn veo_3p1_fast_multi_function_video_gen_handler(
maybe_creator_user_token: maybe_user_session
.as_ref()
.map(|s| &s.user_token),
maybe_model_type: Some(ModelType::Veo3p1Fast),
maybe_model_type: Some(CommonModelType::Veo3p1Fast),
maybe_generation_provider: Some(GenerationProvider::Artcraft),
maybe_positive_prompt: request.prompt.as_deref(),
maybe_negative_prompt: None,
maybe_other_args: None,
maybe_generation_mode: None,
maybe_aspect_ratio: None,
maybe_resolution: None,
maybe_batch_count: None,
maybe_generate_audio: None,
creator_ip_address: &ip_address,
mysql_executor: &mut *transaction,
phantom: Default::default(),
@@ -17,7 +17,7 @@ use bucket_paths::legacy::typified_paths::public::media_files::bucket_file_path:
use enums::by_table::prompt_context_items::prompt_context_semantic_type::PromptContextSemanticType;
use enums::by_table::prompts::prompt_type::PromptType;
use enums::common::generation_provider::GenerationProvider;
use enums::common::model_type::ModelType;
use enums::common::generation::common_model_type::CommonModelType;
use enums::common::visibility::Visibility;
use fal_client::creds::open_ai_api_key::OpenAiApiKey;
use fal_client::requests::traits::fal_request_cost_calculator_trait::FalRequestCostCalculator;
@@ -340,11 +340,16 @@ pub async fn veo_3p1_multi_function_video_gen_handler(
maybe_creator_user_token: maybe_user_session
.as_ref()
.map(|s| &s.user_token),
maybe_model_type: Some(ModelType::Veo3p1),
maybe_model_type: Some(CommonModelType::Veo3p1),
maybe_generation_provider: Some(GenerationProvider::Artcraft),
maybe_positive_prompt: request.prompt.as_deref(),
maybe_negative_prompt: None,
maybe_other_args: None,
maybe_generation_mode: None,
maybe_aspect_ratio: None,
maybe_resolution: None,
maybe_batch_count: None,
maybe_generate_audio: None,
creator_ip_address: &ip_address,
mysql_executor: &mut *transaction,
phantom: Default::default(),
@@ -17,7 +17,7 @@ use mysql_queries::queries::idepotency_tokens::insert_idempotency_token::insert_
use mysql_queries::queries::prompts::insert_prompt::{insert_prompt, InsertPromptArgs};
/// Create a new prompt record
/// Create a new prompt record (note: most inference endpoints do this internally for us automatically).
#[utoipa::path(
post,
tag = "Prompts",
@@ -80,6 +80,11 @@ pub async fn create_prompt_handler(
maybe_positive_prompt,
maybe_negative_prompt,
maybe_other_args: None,
maybe_generation_mode: request.maybe_generation_mode,
maybe_aspect_ratio: request.maybe_aspect_ratio,
maybe_resolution: request.maybe_resolution,
maybe_batch_count: request.maybe_batch_count,
maybe_generate_audio: request.maybe_generate_audio,
creator_ip_address: &ip_address,
mysql_executor: &mut *mysql_connection,
phantom: Default::default(),
@@ -9,135 +9,17 @@ use actix_web::error::ResponseError;
use actix_web::http::StatusCode;
use actix_web::web::Path;
use actix_web::{web, HttpMessage, HttpRequest, HttpResponse};
use artcraft_api_defs::common::responses::media_links::MediaLinks;
use artcraft_api_defs::prompts::get_prompt::{
GetPromptImageContextItem, GetPromptPathInfo, GetPromptSuccessResponse,
PromptInfo, PromptInfoModeratorFields,
};
use bucket_paths::legacy::typified_paths::public::media_files::bucket_file_path::MediaFileBucketPath;
use chrono::{DateTime, Utc};
use enums::by_table::prompt_context_items::prompt_context_semantic_type::PromptContextSemanticType;
use enums::by_table::prompts::prompt_type::PromptType;
use enums::common::generation_provider::GenerationProvider;
use enums::common::model_type::ModelType;
use enums::no_table::style_transfer::style_transfer_name::StyleTransferName;
use log::{error, warn};
use mysql_queries::queries::prompt_context_items::list_prompt_context_items::list_prompt_context_items;
use mysql_queries::queries::prompts::get_prompt::{get_prompt, get_prompt_from_connection};
use tokens::tokens::media_files::MediaFileToken;
use tokens::tokens::prompts::PromptToken;
use mysql_queries::queries::prompts::get_prompt::get_prompt_from_connection;
use utoipa::ToSchema;
/// For the URL PathInfo
#[derive(Deserialize, ToSchema)]
pub struct GetPromptPathInfo {
token: PromptToken,
}
#[derive(Serialize, ToSchema)]
pub struct GetPromptSuccessResponse {
pub success: bool,
pub prompt: PromptInfo,
}
#[derive(Serialize, ToSchema)]
pub struct GetPromptImageContextItem {
pub media_token: MediaFileToken,
pub semantic: PromptContextSemanticType,
pub media_links: MediaLinks,
}
#[derive(Serialize, ToSchema)]
pub struct PromptInfo {
pub token: PromptToken,
/// The type of prompt.
/// Note: Prompts may or may not be compatible across systems.
pub prompt_type: PromptType,
/// The type of model used
pub maybe_model_type: Option<ModelType>,
/// The service provider used
pub maybe_generation_provider: Option<GenerationProvider>,
/// Positive prompt (technically optional, but usually present)
pub maybe_positive_prompt: Option<String>,
/// Negative prompt (optional)
pub maybe_negative_prompt: Option<String>,
/// Context images (optional)
pub maybe_context_images: Option<Vec<GetPromptImageContextItem>>,
/// Scheduled / travel prompt (optional)
pub maybe_travel_prompt: Option<String>,
/// If a "style" was used, this is the name of it.
/// This might not be present for all types of inference
/// and typically only applies to video style transfer.
pub maybe_style_name: Option<StyleTransferName>,
/// How many milliseconds it took to run generation.
pub maybe_inference_duration_millis: Option<u64>,
/// If a "strength" was used.
/// Typically only for video style transfer.
pub maybe_strength: Option<f32>,
/// If a frame skip setting was used.
pub maybe_frame_skip: Option<u8>,
/// If a face detailer was used.
/// This might not be present for all types of inference
/// and typically only applies to video style transfer.
pub used_face_detailer: bool,
/// If an upscaling pass was used.
/// This might not be present for all types of inference
/// and typically only applies to video style transfer.
pub used_upscaler: bool,
/// If lipsync was enabled.
/// This might not be present for all types of inference
/// and typically only applies to video style transfer.
pub lipsync_enabled: bool,
/// If LCM was disabled.
/// Only staff can do this for now.
pub lcm_disabled: bool,
/// If the cinematic workflow was used.
/// Only staff can do this for now.
pub use_cinematic: bool,
/// If a global IP Adapter Image was used, this is the details.
/// NB: We can't easily query for this without another DB round trip,
/// so the frontend should query for it instead.
pub maybe_global_ipa_image_token: Option<MediaFileToken>,
// TODO: Author of prompt info
/// Fields that only moderators should see.
pub maybe_moderator_fields: Option<PromptInfoModeratorFields>,
pub created_at: DateTime<Utc>,
}
#[derive(Serialize, ToSchema)]
pub struct PromptInfoModeratorFields {
/// How many milliseconds it took to run generation.
pub maybe_inference_duration_millis: Option<u64>,
/// Version of Yae's workflow
/// Used for logging and debugging by the art team.
pub main_ipa_workflow: Option<String>,
/// Version of Yae's face detailer workflow
/// Used for logging and debugging by the art team.
pub face_detailer_workflow: Option<String>,
/// Version of Yae's upscaler workflow
/// Used for logging and debugging by the art team.
pub upscaler_workflow: Option<String>,
}
#[derive(Debug, ToSchema)]
pub enum GetPromptError {
ServerError,
@@ -169,6 +51,7 @@ impl fmt::Display for GetPromptError {
}
}
/// Get details on a prompt.
#[utoipa::path(
get,
tag = "Prompts",
@@ -334,6 +217,11 @@ pub async fn get_prompt_handler(
maybe_generation_provider: result.maybe_generation_provider,
maybe_positive_prompt: result.maybe_positive_prompt,
maybe_negative_prompt: result.maybe_negative_prompt,
maybe_generation_mode: result.maybe_generation_mode,
maybe_aspect_ratio: result.maybe_aspect_ratio,
maybe_resolution: result.maybe_resolution,
maybe_batch_count: result.maybe_batch_count,
maybe_generate_audio: result.maybe_generate_audio,
maybe_context_images,
maybe_travel_prompt,
maybe_style_name,