diff --git a/crates/desktop/artcraft/src/core/commands/generate/generate_image/generate_image_command.rs b/crates/desktop/artcraft/src/core/commands/generate/generate_image/generate_image_command.rs new file mode 100644 index 0000000000..e88253aeec --- /dev/null +++ b/crates/desktop/artcraft/src/core/commands/generate/generate_image/generate_image_command.rs @@ -0,0 +1,17 @@ +use crate::core::commands::generate::generate_image::request::{TauriGenerateImageErrorType, TauriGenerateImageRequest, TauriGenerateImageResponse}; +use crate::core::commands::response::shorthand::Response; +use log::info; +use tauri::AppHandle; + +#[tauri::command] +pub async fn generate_image_command( + request: TauriGenerateImageRequest, + app: AppHandle, +) -> Response { + + info!("generate_image_command called, request: {:?}", request); + + // TODO: Handle request. + + Ok(TauriGenerateImageResponse {}.into()) +} diff --git a/crates/desktop/artcraft/src/core/commands/generate/generate_image/mod.rs b/crates/desktop/artcraft/src/core/commands/generate/generate_image/mod.rs new file mode 100644 index 0000000000..c4ab767b7f --- /dev/null +++ b/crates/desktop/artcraft/src/core/commands/generate/generate_image/mod.rs @@ -0,0 +1,2 @@ +pub mod generate_image_command; +pub mod request; diff --git a/crates/desktop/artcraft/src/core/commands/generate/generate_image/request.rs b/crates/desktop/artcraft/src/core/commands/generate/generate_image/request.rs new file mode 100644 index 0000000000..f30a4cd400 --- /dev/null +++ b/crates/desktop/artcraft/src/core/commands/generate/generate_image/request.rs @@ -0,0 +1,215 @@ +use artcraft_router::api::common_aspect_ratio::CommonAspectRatio; +use artcraft_router::api::common_resolution::CommonResolution; +use enums::common::generation::common_model_type::CommonModelType; +use enums::common::generation::common_quality::CommonQuality; +use enums::common::generation_provider::GenerationProvider; +use enums::tauri::ux::tauri_command_caller::TauriCommandCaller; +use serde_derive::{Deserialize, Serialize}; +use tokens::tokens::media_files::MediaFileToken; + +use crate::core::commands::response::success_response_wrapper::SerializeMarker; + +// ── Model ── + +/// Unified image model enum covering text-to-image, image edit, and inpainting. +/// +/// This is used in the Tauri command bridge. +/// Don't change the serializations without coordinating with the frontend. +#[derive(Deserialize, Debug, Copy, Clone)] +#[serde(rename_all = "snake_case")] +pub enum TauriImageModel { + // Text-to-image models + + #[serde(rename = "flux_1_dev")] + Flux1Dev, + #[serde(rename = "flux_1_schnell")] + Flux1Schnell, + #[serde(rename = "flux_pro_11")] + FluxPro11, + #[serde(rename = "flux_pro_11_ultra")] + FluxPro11Ultra, + #[serde(rename = "grok_image")] + GrokImage, + #[serde(rename = "recraft_3")] + Recraft3, + #[serde(rename = "gpt_image_1")] + GptImage1, + #[serde(rename = "gpt_image_1p5")] + GptImage1p5, + #[serde(rename = "gpt_image_2")] + GptImage2, + #[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, + #[serde(rename = "midjourney")] + Midjourney, + + // Image edit models + + #[serde(rename = "flux_pro_kontext_max")] + FluxProKontextMax, + #[serde(rename = "qwen_edit_2511_angles")] + QwenEdit2511Angles, + #[serde(rename = "flux_2_lora_angles")] + Flux2LoraAngles, + + // Inpainting models + + #[serde(rename = "flux_dev_juggernaut")] + FluxDevJuggernaut, + #[serde(rename = "flux_pro_1")] + FluxPro1, +} + +impl TauriImageModel { + pub fn to_common_model_type(&self) -> CommonModelType { + match self { + Self::Flux1Dev => CommonModelType::Flux1Dev, + Self::Flux1Schnell => CommonModelType::Flux1Schnell, + Self::FluxPro11 => CommonModelType::FluxPro11, + Self::FluxPro11Ultra => CommonModelType::FluxPro11Ultra, + Self::GrokImage => CommonModelType::GrokImage, + Self::Recraft3 => CommonModelType::Recraft3, + Self::GptImage1 => CommonModelType::GptImage1, + Self::GptImage1p5 => CommonModelType::GptImage1p5, + Self::GptImage2 => CommonModelType::GptImage2, + Self::Gemini25Flash => CommonModelType::NanoBanana, + Self::NanoBanana => CommonModelType::NanoBanana, + Self::NanoBanana2 => CommonModelType::NanoBanana2, + Self::NanoBananaPro => CommonModelType::NanoBananaPro, + Self::Seedream4 => CommonModelType::Seedream4, + Self::Seedream4p5 => CommonModelType::Seedream4p5, + Self::Seedream5Lite => CommonModelType::Seedream5Lite, + Self::Midjourney => CommonModelType::Midjourney, + Self::FluxProKontextMax => CommonModelType::FluxProKontextMax, + Self::QwenEdit2511Angles => CommonModelType::QwenEdit2511Angles, + Self::Flux2LoraAngles => CommonModelType::Flux2LoraAngles, + Self::FluxDevJuggernaut => CommonModelType::FluxDevJuggernaut, + Self::FluxPro1 => CommonModelType::FluxPro1, + } + } +} + +// ── Request ── + +#[derive(Deserialize, Debug)] +pub struct TauriGenerateImageRequest { + /// The provider to use (defaults to Artcraft/Storyteller). + /// Not all (provider, model) combinations are valid. + pub provider: Option, + + /// The model to use. + pub model: Option, + + /// Text prompt for the image generation. + pub prompt: Option, + + /// Aspect ratio. + pub aspect_ratio: Option, + + /// Resolution. + pub resolution: Option, + + /// Quality (used by OpenAI models). + pub quality: Option, + + /// The number of images to generate. + pub batch_size: Option, + + /// Reference images (without semantics). + /// The purpose varies on a model-by-model basis. + pub image_media_tokens: Option>, + + // ── Canvas / scene images ── + + /// Supply this *XOR* `canvas_image_raw_bytes`. + /// Becomes the first image reference (pushing back `image_media_tokens` by one). + pub canvas_image_media_token: Option, + + /// Supply this *XOR* `canvas_image_media_token`. + /// Raw bytes of a canvas image. + pub canvas_image_raw_bytes: Option>, + + /// Supply this *XOR* `scene_image_raw_bytes`. + /// Becomes a scene reference image. + pub scene_image_media_token: Option, + + /// Supply this *XOR* `scene_image_media_token`. + /// Raw bytes of a scene image. + pub scene_image_raw_bytes: Option>, + + // ── Inpainting ── + + /// Supply this *XOR* `inpainting_mask_image_raw_bytes`. + /// The mask to focus the edit (already uploaded). + pub inpainting_mask_image_media_token: Option, + + /// Supply this *XOR* `inpainting_mask_image_media_token`. + /// The mask to focus the edit (raw bytes). + pub inpainting_mask_image_raw_bytes: Option>, + + // ── Angle adjustment (for edit models like QwenEdit, Flux2LoraAngles) ── + + /// Horizontal angle adjustment. + pub adjust_horizontal_angle: Option, + + /// Vertical angle adjustment. + pub adjust_vertical_angle: Option, + + /// Zoom adjustment. + pub adjust_zoom: Option, + + /// Turn on the system prompt. + pub enable_system_prompt: Option, + + // ── Frontend metadata ── + + /// Name of the frontend caller. + pub frontend_caller: Option, + + /// A frontend-defined identifier sent back as a Tauri event on task completion. + pub frontend_subscriber_id: Option, + + /// A frontend-defined payload sent back as a Tauri event on task completion. + pub frontend_subscriber_payload: Option, +} + +// ── Response ── + +#[derive(Serialize)] +pub struct TauriGenerateImageResponse { +} + +impl SerializeMarker for TauriGenerateImageResponse {} + +// ── Error ── + +#[derive(Serialize, Debug)] +#[serde(rename_all = "snake_case")] +pub enum TauriGenerateImageErrorType { + /// Caller didn't specify a model + ModelNotSpecified, + /// Bad input + BadInput, + /// No provider available + NoProviderAvailable, + /// Generic server error + ServerError, + /// Needs to be logged into Artcraft + NeedsStorytellerCredentials, + /// Needs Grok credentials + NeedsGrokCredentials, + /// Billing issue + BillingIssue, +} diff --git a/crates/desktop/artcraft/src/core/commands/generate/mod.rs b/crates/desktop/artcraft/src/core/commands/generate/mod.rs index 2492e000e2..295de01e41 100644 --- a/crates/desktop/artcraft/src/core/commands/generate/mod.rs +++ b/crates/desktop/artcraft/src/core/commands/generate/mod.rs @@ -1 +1,2 @@ +pub mod generate_image; pub mod generate_video; diff --git a/crates/desktop/artcraft/src/lib.rs b/crates/desktop/artcraft/src/lib.rs index e29856dc90..94c84411e5 100644 --- a/crates/desktop/artcraft/src/lib.rs +++ b/crates/desktop/artcraft/src/lib.rs @@ -18,6 +18,7 @@ use crate::core::commands::enqueue::image_inpaint::enqueue_image_inpaint_command use crate::core::commands::enqueue::image_to_gaussian::enqueue_image_to_gaussian_command::enqueue_image_to_gaussian_command; use crate::core::commands::enqueue::image_to_object::enqueue_image_to_3d_object_command::enqueue_image_to_3d_object_command; use crate::core::commands::enqueue::text_to_image::enqueue_text_to_image_command::enqueue_text_to_image_command; +use crate::core::commands::generate::generate_image::generate_image_command::generate_image_command; use crate::core::commands::generate::generate_video::generate_video_command::generate_video_command; use crate::core::commands::flip_image::flip_image; use crate::core::commands::get_app_info_command::get_app_info_command; @@ -217,6 +218,7 @@ pub fn run() { estimate_splat_cost_command, estimate_video_cost_command, flip_image, + generate_image_command, generate_video_command, get_app_info_command, get_app_preferences_command,