From 0c6021e95c4403b2d31ec677cbbb37dce0190ebf Mon Sep 17 00:00:00 2001 From: Brandon Thomas Date: Sat, 6 Dec 2025 04:50:30 -0500 Subject: [PATCH] nano banana pro in tauri (#923) --- .../app_state/reset_artcraft_command.rs | 2 +- .../generic/handle_generic_bg_removal.rs | 2 +- .../handle_generic_bg_removal_artcraft.rs | 2 +- ...mmand.rs => enqueue_edit_image_command.rs} | 96 ++++++++---- .../flux_kontext/handle_flux_kontext_edit.rs | 4 +- .../handle_flux_kontext_edit_artcraft.rs | 4 +- .../enqueue/image_edit/gemini_25_flash/mod.rs | 2 - .../gpt_image_1/handle_gpt_image_1_edit.rs | 4 +- .../handle_gpt_image_1_edit_artcraft.rs | 4 +- .../handle_gpt_image_1_edit_sora.rs | 4 +- .../core/commands/enqueue/image_edit/mod.rs | 5 +- .../handle_nano_banana_edit.rs} | 12 +- .../handle_nano_banana_edit_artcraft.rs} | 12 +- .../enqueue/image_edit/nano_banana/mod.rs | 2 + .../handle_nano_banana_pro_edit.rs | 53 +++++++ .../handle_nano_banana_pro_edit_artcraft.rs | 137 ++++++++++++++++++ .../enqueue/image_edit/nano_banana_pro/mod.rs | 2 + .../handle_flux_dev_juggernaut_inpaint.rs | 2 +- ...le_flux_dev_juggernaut_inpaint_artcraft.rs | 2 +- .../handle_flux_pro_1_inpaint.rs | 2 +- .../handle_flux_pro_1_inpaint_artcraft.rs | 2 +- .../handle_flux_pro_kontext_inpaint.rs | 2 +- ...andle_flux_pro_kontext_inpaint_artcraft.rs | 2 +- .../handle_gemini_25_flash_inpaint.rs | 2 +- ...handle_gemini_25_flash_inpaint_artcraft.rs | 2 +- .../image_to_video/grok/handle_grok_video.rs | 2 +- .../sora2/handle_sora2_video.rs | 2 +- .../commands/enqueue/task_enqueue_success.rs | 2 + .../enqueue_text_to_image_command.rs | 37 ++++- .../text_to_image/gemini_25_flash/mod.rs | 2 - .../generic/handle_image_artcraft.rs | 2 + .../handle_gpt_image_1_artcraft.rs | 2 +- .../midjourney/handle_midjourney.rs | 2 +- .../commands/enqueue/text_to_image/mod.rs | 3 +- .../handle_nano_banana.rs} | 8 +- .../handle_nano_banana_artcraft.rs} | 10 +- .../enqueue/text_to_image/nano_banana/mod.rs | 2 + .../nano_banana_pro/handle_nano_banana_pro.rs | 59 ++++++++ .../handle_nano_banana_pro_artcraft.rs | 111 ++++++++++++++ .../text_to_image/nano_banana_pro/mod.rs | 2 + .../core/events/generation_events/common.rs | 6 +- crates/desktop/artcraft/src/lib.rs | 4 +- .../storyteller_get_credits_command.rs | 2 +- .../storyteller_get_subscription_command.rs | 2 +- .../storyteller_purge_credentials_command.rs | 2 +- ...oryteller_open_credits_purchase_command.rs | 2 +- ...ller_open_subscription_purchase_command.rs | 2 +- ...pen_customer_portal_cancel_plan_command.rs | 2 +- ...pen_customer_portal_manage_plan_command.rs | 2 +- ...pen_customer_portal_switch_plan_command.rs | 2 +- ...er_portal_update_payment_method_command.rs | 2 +- .../enums/src/tauri/tasks/task_model_type.rs | 20 ++- .../app/src/pages/PageEdit/PageEdit.tsx | 40 +++-- .../promptbox/src/lib/PromptBox2D.tsx | 14 +- .../promptbox/src/lib/PromptBox3D.tsx | 14 +- .../model-list/src/lib/classes/ImageModel.ts | 22 +++ .../libs/model-list/src/lib/legacy/Models.ts | 15 ++ .../model-list/src/lib/lists/ImageModels.ts | 26 +++- frontend/libs/tauri-api/src/index.ts | 2 +- ...extualEditImage.ts => EnqueueEditImage.ts} | 69 ++++++--- script/artcraft/unix_rust_dev.sh | 5 + 61 files changed, 719 insertions(+), 145 deletions(-) rename crates/desktop/artcraft/src/core/commands/enqueue/image_edit/{enqueue_contextual_edit_image_command.rs => enqueue_edit_image_command.rs} (80%) delete mode 100644 crates/desktop/artcraft/src/core/commands/enqueue/image_edit/gemini_25_flash/mod.rs rename crates/desktop/artcraft/src/core/commands/enqueue/image_edit/{gemini_25_flash/handle_gemini_25_flash_edit.rs => nano_banana/handle_nano_banana_edit.rs} (79%) rename crates/desktop/artcraft/src/core/commands/enqueue/image_edit/{gemini_25_flash/handle_gemini_25_flash_edit_artcraft.rs => nano_banana/handle_nano_banana_edit_artcraft.rs} (91%) create mode 100644 crates/desktop/artcraft/src/core/commands/enqueue/image_edit/nano_banana/mod.rs create mode 100644 crates/desktop/artcraft/src/core/commands/enqueue/image_edit/nano_banana_pro/handle_nano_banana_pro_edit.rs create mode 100644 crates/desktop/artcraft/src/core/commands/enqueue/image_edit/nano_banana_pro/handle_nano_banana_pro_edit_artcraft.rs create mode 100644 crates/desktop/artcraft/src/core/commands/enqueue/image_edit/nano_banana_pro/mod.rs delete mode 100644 crates/desktop/artcraft/src/core/commands/enqueue/text_to_image/gemini_25_flash/mod.rs rename crates/desktop/artcraft/src/core/commands/enqueue/text_to_image/{gemini_25_flash/handle_gemini_25_flash.rs => nano_banana/handle_nano_banana.rs} (91%) rename crates/desktop/artcraft/src/core/commands/enqueue/text_to_image/{gemini_25_flash/handle_gemini_25_flash_artcraft.rs => nano_banana/handle_nano_banana_artcraft.rs} (91%) create mode 100644 crates/desktop/artcraft/src/core/commands/enqueue/text_to_image/nano_banana/mod.rs create mode 100644 crates/desktop/artcraft/src/core/commands/enqueue/text_to_image/nano_banana_pro/handle_nano_banana_pro.rs create mode 100644 crates/desktop/artcraft/src/core/commands/enqueue/text_to_image/nano_banana_pro/handle_nano_banana_pro_artcraft.rs create mode 100644 crates/desktop/artcraft/src/core/commands/enqueue/text_to_image/nano_banana_pro/mod.rs rename frontend/libs/tauri-api/src/lib/enqueue/{EnqueueContextualEditImage.ts => EnqueueEditImage.ts} (62%) diff --git a/crates/desktop/artcraft/src/core/commands/app_state/reset_artcraft_command.rs b/crates/desktop/artcraft/src/core/commands/app_state/reset_artcraft_command.rs index 10eb57bb00..a6b7830273 100644 --- a/crates/desktop/artcraft/src/core/commands/app_state/reset_artcraft_command.rs +++ b/crates/desktop/artcraft/src/core/commands/app_state/reset_artcraft_command.rs @@ -1,4 +1,4 @@ -use crate::core::commands::enqueue::image_edit::enqueue_contextual_edit_image_command::EnqueueContextualEditImageSuccessResponse; +use crate::core::commands::enqueue::image_edit::enqueue_edit_image_command::EnqueueEditImageSuccessResponse; use crate::core::commands::response::shorthand::ResponseOrErrorMessage; use crate::core::commands::response::success_response_wrapper::SerializeMarker; use crate::core::state::app_preferences::app_preferences_manager::AppPreferencesManager; diff --git a/crates/desktop/artcraft/src/core/commands/enqueue/image_bg_removal/generic/handle_generic_bg_removal.rs b/crates/desktop/artcraft/src/core/commands/enqueue/image_bg_removal/generic/handle_generic_bg_removal.rs index 8d91ef500c..d72c4c0d37 100644 --- a/crates/desktop/artcraft/src/core/commands/enqueue/image_bg_removal/generic/handle_generic_bg_removal.rs +++ b/crates/desktop/artcraft/src/core/commands/enqueue/image_bg_removal/generic/handle_generic_bg_removal.rs @@ -2,7 +2,7 @@ use crate::core::commands::enqueue::generate_error::GenerateError; use crate::core::commands::enqueue::image_bg_removal::enqueue_image_bg_removal_command::EnqueueImageBgRemovalCommand; use crate::core::commands::enqueue::image_bg_removal::generic::handle_generic_bg_removal_artcraft::handle_generic_bg_removal_artcraft; use crate::core::commands::enqueue::image_bg_removal::generic::handle_generic_bg_removal_fal::handle_generic_bg_removal_fal; -use crate::core::commands::enqueue::image_edit::enqueue_contextual_edit_image_command::EnqueueContextualEditImageCommand; +use crate::core::commands::enqueue::image_edit::enqueue_edit_image_command::EnqueueEditImageCommand; use crate::core::commands::enqueue::image_edit::gpt_image_1::handle_gpt_image_1_edit_artcraft::handle_gpt_image_1_edit_artcraft; use crate::core::commands::enqueue::image_edit::gpt_image_1::handle_gpt_image_1_edit_sora::handle_gpt_image_1_edit_sora; use crate::core::commands::enqueue::task_enqueue_success::TaskEnqueueSuccess; diff --git a/crates/desktop/artcraft/src/core/commands/enqueue/image_bg_removal/generic/handle_generic_bg_removal_artcraft.rs b/crates/desktop/artcraft/src/core/commands/enqueue/image_bg_removal/generic/handle_generic_bg_removal_artcraft.rs index 844bc9c286..433120340b 100644 --- a/crates/desktop/artcraft/src/core/commands/enqueue/image_bg_removal/generic/handle_generic_bg_removal_artcraft.rs +++ b/crates/desktop/artcraft/src/core/commands/enqueue/image_bg_removal/generic/handle_generic_bg_removal_artcraft.rs @@ -1,6 +1,6 @@ use crate::core::commands::enqueue::generate_error::{BadInputReason, GenerateError, MissingCredentialsReason, ProviderFailureReason}; use crate::core::commands::enqueue::image_bg_removal::enqueue_image_bg_removal_command::EnqueueImageBgRemovalCommand; -use crate::core::commands::enqueue::image_edit::enqueue_contextual_edit_image_command::{EditImageQuality, EditImageSize, EnqueueContextualEditImageCommand}; +use crate::core::commands::enqueue::image_edit::enqueue_edit_image_command::{EditImageQuality, EditImageSize, EnqueueEditImageCommand}; use crate::core::commands::enqueue::task_enqueue_success::TaskEnqueueSuccess; use crate::core::events::basic_sendable_event_trait::BasicSendableEvent; use crate::core::events::generation_events::common::{GenerationAction, GenerationServiceProvider}; diff --git a/crates/desktop/artcraft/src/core/commands/enqueue/image_edit/enqueue_contextual_edit_image_command.rs b/crates/desktop/artcraft/src/core/commands/enqueue/image_edit/enqueue_edit_image_command.rs similarity index 80% rename from crates/desktop/artcraft/src/core/commands/enqueue/image_edit/enqueue_contextual_edit_image_command.rs rename to crates/desktop/artcraft/src/core/commands/enqueue/image_edit/enqueue_edit_image_command.rs index ca1c67fcfc..7d9f2c8b57 100644 --- a/crates/desktop/artcraft/src/core/commands/enqueue/image_edit/enqueue_contextual_edit_image_command.rs +++ b/crates/desktop/artcraft/src/core/commands/enqueue/image_edit/enqueue_edit_image_command.rs @@ -1,8 +1,9 @@ use crate::core::commands::enqueue::common::notify_frontend_of_errors::notify_frontend_of_errors; use crate::core::commands::enqueue::generate_error::{BadInputReason, GenerateError}; use crate::core::commands::enqueue::image_edit::flux_kontext::handle_flux_kontext_edit::handle_flux_kontext_edit; -use crate::core::commands::enqueue::image_edit::gemini_25_flash::handle_gemini_25_flash_edit::handle_gemini_25_flash_edit; use crate::core::commands::enqueue::image_edit::gpt_image_1::handle_gpt_image_1_edit::handle_gpt_image_1_edit; +use crate::core::commands::enqueue::image_edit::nano_banana::handle_nano_banana_edit::handle_nano_banana_edit; +use crate::core::commands::enqueue::image_edit::nano_banana_pro::handle_nano_banana_pro_edit::handle_nano_banana_pro_edit; use crate::core::commands::enqueue::task_enqueue_success::TaskEnqueueSuccess; use crate::core::commands::response::failure_response_wrapper::{CommandErrorResponseWrapper, CommandErrorStatus}; use crate::core::commands::response::shorthand::{Response, ResponseOrErrorType}; @@ -36,8 +37,8 @@ use openai_sora_client::requests::image_gen::common::{ImageSize, NumImages}; use serde_derive::{Deserialize, Serialize}; use sqlite_tasks::queries::create_task::{create_task, CreateTaskArgs}; use std::time::Duration; -use storyteller_client::error::storyteller_error::StorytellerError; use storyteller_client::endpoints::media_files::get_media_file::get_media_file; +use storyteller_client::error::storyteller_error::StorytellerError; use storyteller_client::utils::api_host::ApiHost; use tauri::{AppHandle, Manager, State}; use tokens::tokens::media_files::MediaFileToken; @@ -46,13 +47,19 @@ use tokens::tokens::media_files::MediaFileToken; /// Don't change the serializations without coordinating with the frontend. #[derive(Deserialize, Debug, Copy, Clone)] #[serde(rename_all = "snake_case")] -pub enum ContextualImageEditModel { +pub enum ImageEditModel { #[serde(rename = "flux_pro_kontext_max")] FluxProKontextMax, #[serde(rename = "gemini_25_flash")] Gemini25Flash, + #[serde(rename = "nano_banana")] + NanoBanana, + + #[serde(rename = "nano_banana_pro")] + NanoBananaPro, + #[serde(rename = "gpt_image_1")] GptImage1, @@ -64,9 +71,9 @@ pub enum ContextualImageEditModel { } #[derive(Deserialize, Debug)] -pub struct EnqueueContextualEditImageCommand { +pub struct EnqueueEditImageCommand { /// The model to use. - pub model: Option, + pub model: Option, /// Images to use for the image edit. /// The first image is typically a 2D canvas or 3D stage, but doesn't have to be. @@ -93,6 +100,9 @@ pub struct EnqueueContextualEditImageCommand { /// Image quality. pub image_quality: Option, + /// Image resolution + pub image_resolution: Option, + /// OPTIONAL. /// Name of the frontend caller. /// We'll use this to selectively trigger events. @@ -127,9 +137,20 @@ pub enum EditImageQuality { Low, } +#[derive(Deserialize, Debug, Copy, Clone)] +#[serde(rename_all = "snake_case")] +pub enum EditImageResolution { + /// 1K - nano banana pro + OneK, + /// 2K - nano banana pro + TwoK, + /// 4K - nano banana pro + FourK, +} + #[derive(Serialize, Debug)] #[serde(rename_all = "snake_case")] -pub enum EnqueueContextualEditImageErrorType { +pub enum EnqueueEditImageErrorType { /// Caller didn't specify a model ModelNotSpecified, @@ -153,15 +174,15 @@ pub enum EnqueueContextualEditImageErrorType { } #[derive(Serialize)] -pub struct EnqueueContextualEditImageSuccessResponse { +pub struct EnqueueEditImageSuccessResponse { } -impl SerializeMarker for EnqueueContextualEditImageSuccessResponse {} +impl SerializeMarker for EnqueueEditImageSuccessResponse {} #[tauri::command] -pub async fn enqueue_contextual_edit_image_command( +pub async fn enqueue_edit_image_command( app: AppHandle, - request: EnqueueContextualEditImageCommand, + request: EnqueueEditImageCommand, app_data_root: State<'_, AppDataRoot>, app_env_configs: State<'_, AppEnvConfigs>, provider_priority_store: State<'_, ProviderPriorityStore>, @@ -169,9 +190,9 @@ pub async fn enqueue_contextual_edit_image_command( storyteller_creds_manager: State<'_, StorytellerCredentialManager>, sora_creds_manager: State<'_, SoraCredentialManager>, sora_task_queue: State<'_, SoraTaskQueue>, -) -> ResponseOrErrorType { +) -> ResponseOrErrorType { - info!("enqueue_contextual_edit_image_command called; image media tokens : {:?}, full request: {:?}", + info!("enqueue_edit_image_command called; image media tokens : {:?}, full request: {:?}", &request.image_media_tokens, &request); let result = handle_request( @@ -219,13 +240,13 @@ pub async fn enqueue_contextual_edit_image_command( CreditsBalanceChangedEvent{}.send_infallible(&app); - Ok(EnqueueContextualEditImageSuccessResponse {}.into()) + Ok(EnqueueEditImageSuccessResponse {}.into()) } } } pub async fn handle_request( - request: &EnqueueContextualEditImageCommand, + request: &EnqueueEditImageCommand, app: &AppHandle, app_data_root: &AppDataRoot, app_env_configs: &AppEnvConfigs, @@ -235,11 +256,11 @@ pub async fn handle_request( sora_creds_manager: &SoraCredentialManager, sora_task_queue: &SoraTaskQueue, ) -> Result { - let success_event= match request.model { + let mut success_event= match request.model { None => { return Err(GenerateError::no_model_specified()) } - Some(ContextualImageEditModel::FluxProKontextMax) => { + Some(ImageEditModel::FluxProKontextMax) => { handle_flux_kontext_edit( request, app, @@ -251,8 +272,8 @@ pub async fn handle_request( sora_task_queue, ).await? } - Some(ContextualImageEditModel::Gemini25Flash) => { - handle_gemini_25_flash_edit( + Some(ImageEditModel::Gemini25Flash | ImageEditModel::NanoBanana) => { + handle_nano_banana_edit( request, app, app_data_root, @@ -263,7 +284,19 @@ pub async fn handle_request( sora_task_queue, ).await? } - Some(ContextualImageEditModel::GptImage1) => { + Some(ImageEditModel::NanoBananaPro) => { + handle_nano_banana_pro_edit( + request, + app, + app_data_root, + app_env_configs, + provider_priority_store, + storyteller_creds_manager, + sora_creds_manager, + sora_task_queue, + ).await? + } + Some(ImageEditModel::GptImage1) => { handle_gpt_image_1_edit( request, app, @@ -276,7 +309,18 @@ pub async fn handle_request( ).await? } }; - + + match request.frontend_caller { + Some(TauriCommandCaller::ImageEditor) => { + // NB: Certain image generations enqueued in the inpainting editor are classed + // as "image_generation" and won't update the inpainting UI. This is a hack to + // temporarily fix the UI event binding. + // TODO: The frontend shouldn't require us to change the type to inpainting. + success_event.task_type = TaskType::ImageInpaintEdit; + } + _ => {} // fall-through + } + let result = success_event .insert_into_task_database_with_frontend_payload( task_database, @@ -294,31 +338,31 @@ pub async fn handle_request( Ok(success_event) } -fn error_to_tauri_response(error: GenerateError) -> CommandErrorResponseWrapper { +fn error_to_tauri_response(error: GenerateError) -> CommandErrorResponseWrapper { let mut status = CommandErrorStatus::ServerError; - let mut error_type = EnqueueContextualEditImageErrorType::ServerError; + let mut error_type = EnqueueEditImageErrorType::ServerError; let mut error_message = "A server error occurred. Please try again. If it continues, please tell our staff about the problem.".to_string(); match error { GenerateError::BadInput(BadInputReason::NoModelSpecified) => { status = CommandErrorStatus::BadRequest; - error_type = EnqueueContextualEditImageErrorType::ModelNotSpecified; + error_type = EnqueueEditImageErrorType::ModelNotSpecified; error_message = "No model specified for image generation".to_string(); } GenerateError::NoProviderAvailable => { status = CommandErrorStatus::ServerError; - error_type = EnqueueContextualEditImageErrorType::NoProviderAvailable; + error_type = EnqueueEditImageErrorType::NoProviderAvailable; error_message = "No configured provider available for image generation".to_string(); } GenerateError::BadInput(BadInputReason::InvalidNumberOfRequestedImages { min, max, requested }) => { status = CommandErrorStatus::BadRequest; - error_type = EnqueueContextualEditImageErrorType::BadRequest; + error_type = EnqueueEditImageErrorType::BadRequest; error_message = format!("Invalid number of images requested ({}). Must be between {} and {}", requested, min, max); } GenerateError::BadInput(BadInputReason::InvalidNumberOfInputImages{ min, max, provided }) => { status = CommandErrorStatus::BadRequest; - error_type = EnqueueContextualEditImageErrorType::BadRequest; + error_type = EnqueueEditImageErrorType::BadRequest; error_message = format!("Invalid number of input images ({}). Must be between {} and {}", provided, min, max); } _ => {} // Fall-through for now diff --git a/crates/desktop/artcraft/src/core/commands/enqueue/image_edit/flux_kontext/handle_flux_kontext_edit.rs b/crates/desktop/artcraft/src/core/commands/enqueue/image_edit/flux_kontext/handle_flux_kontext_edit.rs index 0ac31bc151..a692dbe298 100644 --- a/crates/desktop/artcraft/src/core/commands/enqueue/image_edit/flux_kontext/handle_flux_kontext_edit.rs +++ b/crates/desktop/artcraft/src/core/commands/enqueue/image_edit/flux_kontext/handle_flux_kontext_edit.rs @@ -1,5 +1,5 @@ use crate::core::commands::enqueue::generate_error::GenerateError; -use crate::core::commands::enqueue::image_edit::enqueue_contextual_edit_image_command::EnqueueContextualEditImageCommand; +use crate::core::commands::enqueue::image_edit::enqueue_edit_image_command::EnqueueEditImageCommand; use crate::core::commands::enqueue::image_edit::flux_kontext::handle_flux_kontext_edit_artcraft::handle_flux_kontext_edit_artcraft; use crate::core::commands::enqueue::image_edit::gpt_image_1::handle_gpt_image_1_edit_artcraft::handle_gpt_image_1_edit_artcraft; use crate::core::commands::enqueue::image_edit::gpt_image_1::handle_gpt_image_1_edit_sora::handle_gpt_image_1_edit_sora; @@ -14,7 +14,7 @@ use log::info; use tauri::AppHandle; pub async fn handle_flux_kontext_edit( - request: &EnqueueContextualEditImageCommand, + request: &EnqueueEditImageCommand, app: &AppHandle, app_data_root: &AppDataRoot, app_env_configs: &AppEnvConfigs, diff --git a/crates/desktop/artcraft/src/core/commands/enqueue/image_edit/flux_kontext/handle_flux_kontext_edit_artcraft.rs b/crates/desktop/artcraft/src/core/commands/enqueue/image_edit/flux_kontext/handle_flux_kontext_edit_artcraft.rs index 70a4fe3c52..b42ccd9070 100644 --- a/crates/desktop/artcraft/src/core/commands/enqueue/image_edit/flux_kontext/handle_flux_kontext_edit_artcraft.rs +++ b/crates/desktop/artcraft/src/core/commands/enqueue/image_edit/flux_kontext/handle_flux_kontext_edit_artcraft.rs @@ -1,5 +1,5 @@ use crate::core::commands::enqueue::generate_error::{BadInputReason, GenerateError}; -use crate::core::commands::enqueue::image_edit::enqueue_contextual_edit_image_command::{EditImageQuality, EditImageSize, EnqueueContextualEditImageCommand}; +use crate::core::commands::enqueue::image_edit::enqueue_edit_image_command::{EditImageQuality, EditImageSize, EnqueueEditImageCommand}; use crate::core::commands::enqueue::task_enqueue_success::TaskEnqueueSuccess; use crate::core::events::basic_sendable_event_trait::BasicSendableEvent; use crate::core::events::generation_events::common::{GenerationAction, GenerationModel, GenerationServiceProvider}; @@ -30,7 +30,7 @@ use storyteller_client::endpoints::generate::image::generate_flux_pro_11_ultra_t use tauri::AppHandle; pub async fn handle_flux_kontext_edit_artcraft( - request: &EnqueueContextualEditImageCommand, + request: &EnqueueEditImageCommand, app: &AppHandle, app_data_root: &AppDataRoot, app_env_configs: &AppEnvConfigs, diff --git a/crates/desktop/artcraft/src/core/commands/enqueue/image_edit/gemini_25_flash/mod.rs b/crates/desktop/artcraft/src/core/commands/enqueue/image_edit/gemini_25_flash/mod.rs deleted file mode 100644 index 092a12d1f2..0000000000 --- a/crates/desktop/artcraft/src/core/commands/enqueue/image_edit/gemini_25_flash/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub (super) mod handle_gemini_25_flash_edit; -pub (super) mod handle_gemini_25_flash_edit_artcraft; \ No newline at end of file diff --git a/crates/desktop/artcraft/src/core/commands/enqueue/image_edit/gpt_image_1/handle_gpt_image_1_edit.rs b/crates/desktop/artcraft/src/core/commands/enqueue/image_edit/gpt_image_1/handle_gpt_image_1_edit.rs index 4ea8bb4021..473228d22e 100644 --- a/crates/desktop/artcraft/src/core/commands/enqueue/image_edit/gpt_image_1/handle_gpt_image_1_edit.rs +++ b/crates/desktop/artcraft/src/core/commands/enqueue/image_edit/gpt_image_1/handle_gpt_image_1_edit.rs @@ -1,5 +1,5 @@ use crate::core::commands::enqueue::generate_error::GenerateError; -use crate::core::commands::enqueue::image_edit::enqueue_contextual_edit_image_command::EnqueueContextualEditImageCommand; +use crate::core::commands::enqueue::image_edit::enqueue_edit_image_command::EnqueueEditImageCommand; use crate::core::commands::enqueue::image_edit::gpt_image_1::handle_gpt_image_1_edit_artcraft::handle_gpt_image_1_edit_artcraft; use crate::core::commands::enqueue::image_edit::gpt_image_1::handle_gpt_image_1_edit_sora::handle_gpt_image_1_edit_sora; use crate::core::commands::enqueue::task_enqueue_success::TaskEnqueueSuccess; @@ -15,7 +15,7 @@ use tauri::AppHandle; pub(super) const MAX_IMAGES: usize = 10; pub async fn handle_gpt_image_1_edit( - request: &EnqueueContextualEditImageCommand, + request: &EnqueueEditImageCommand, app: &AppHandle, app_data_root: &AppDataRoot, app_env_configs: &AppEnvConfigs, diff --git a/crates/desktop/artcraft/src/core/commands/enqueue/image_edit/gpt_image_1/handle_gpt_image_1_edit_artcraft.rs b/crates/desktop/artcraft/src/core/commands/enqueue/image_edit/gpt_image_1/handle_gpt_image_1_edit_artcraft.rs index 58ad147293..e15df84bb2 100644 --- a/crates/desktop/artcraft/src/core/commands/enqueue/image_edit/gpt_image_1/handle_gpt_image_1_edit_artcraft.rs +++ b/crates/desktop/artcraft/src/core/commands/enqueue/image_edit/gpt_image_1/handle_gpt_image_1_edit_artcraft.rs @@ -1,5 +1,5 @@ use crate::core::commands::enqueue::generate_error::{BadInputReason, GenerateError}; -use crate::core::commands::enqueue::image_edit::enqueue_contextual_edit_image_command::{EditImageQuality, EditImageSize, EnqueueContextualEditImageCommand}; +use crate::core::commands::enqueue::image_edit::enqueue_edit_image_command::{EditImageQuality, EditImageSize, EnqueueEditImageCommand}; use crate::core::commands::enqueue::image_edit::gpt_image_1::handle_gpt_image_1_edit::MAX_IMAGES; use crate::core::commands::enqueue::task_enqueue_success::TaskEnqueueSuccess; use crate::core::events::basic_sendable_event_trait::BasicSendableEvent; @@ -29,7 +29,7 @@ use storyteller_client::endpoints::generate::image::generate_flux_pro_11_ultra_t use tauri::AppHandle; pub async fn handle_gpt_image_1_edit_artcraft( - request: &EnqueueContextualEditImageCommand, + request: &EnqueueEditImageCommand, app: &AppHandle, app_data_root: &AppDataRoot, app_env_configs: &AppEnvConfigs, diff --git a/crates/desktop/artcraft/src/core/commands/enqueue/image_edit/gpt_image_1/handle_gpt_image_1_edit_sora.rs b/crates/desktop/artcraft/src/core/commands/enqueue/image_edit/gpt_image_1/handle_gpt_image_1_edit_sora.rs index 5f37ca52d8..32d21efaad 100644 --- a/crates/desktop/artcraft/src/core/commands/enqueue/image_edit/gpt_image_1/handle_gpt_image_1_edit_sora.rs +++ b/crates/desktop/artcraft/src/core/commands/enqueue/image_edit/gpt_image_1/handle_gpt_image_1_edit_sora.rs @@ -1,5 +1,5 @@ use crate::core::commands::enqueue::generate_error::{BadInputReason, GenerateError}; -use crate::core::commands::enqueue::image_edit::enqueue_contextual_edit_image_command::{EditImageSize, EnqueueContextualEditImageCommand, EnqueueContextualEditImageErrorType}; +use crate::core::commands::enqueue::image_edit::enqueue_edit_image_command::{EditImageSize, EnqueueEditImageCommand, EnqueueEditImageErrorType}; use crate::core::commands::enqueue::image_edit::gpt_image_1::handle_gpt_image_1_edit::MAX_IMAGES; use crate::core::commands::enqueue::task_enqueue_success::TaskEnqueueSuccess; use crate::core::commands::response::failure_response_wrapper::{CommandErrorResponseWrapper, CommandErrorStatus}; @@ -42,7 +42,7 @@ const SORA_IMAGE_REMIX_TIMEOUT: Duration = Duration::from_millis(1000 * 30); // const DEFAULT_ASPECT_RATIO : EditImageSize = EditImageSize::Square; pub async fn handle_gpt_image_1_edit_sora( - request: &EnqueueContextualEditImageCommand, + request: &EnqueueEditImageCommand, app: &AppHandle, app_data_root: &AppDataRoot, app_env_configs: &AppEnvConfigs, diff --git a/crates/desktop/artcraft/src/core/commands/enqueue/image_edit/mod.rs b/crates/desktop/artcraft/src/core/commands/enqueue/image_edit/mod.rs index 9829ae1a64..821cbcec93 100644 --- a/crates/desktop/artcraft/src/core/commands/enqueue/image_edit/mod.rs +++ b/crates/desktop/artcraft/src/core/commands/enqueue/image_edit/mod.rs @@ -1,4 +1,5 @@ -pub mod enqueue_contextual_edit_image_command; +pub mod enqueue_edit_image_command; pub mod gpt_image_1; mod flux_kontext; -mod gemini_25_flash; \ No newline at end of file +mod nano_banana; +mod nano_banana_pro; diff --git a/crates/desktop/artcraft/src/core/commands/enqueue/image_edit/gemini_25_flash/handle_gemini_25_flash_edit.rs b/crates/desktop/artcraft/src/core/commands/enqueue/image_edit/nano_banana/handle_nano_banana_edit.rs similarity index 79% rename from crates/desktop/artcraft/src/core/commands/enqueue/image_edit/gemini_25_flash/handle_gemini_25_flash_edit.rs rename to crates/desktop/artcraft/src/core/commands/enqueue/image_edit/nano_banana/handle_nano_banana_edit.rs index 4b585f5db8..af815ebdbd 100644 --- a/crates/desktop/artcraft/src/core/commands/enqueue/image_edit/gemini_25_flash/handle_gemini_25_flash_edit.rs +++ b/crates/desktop/artcraft/src/core/commands/enqueue/image_edit/nano_banana/handle_nano_banana_edit.rs @@ -1,6 +1,5 @@ use crate::core::commands::enqueue::generate_error::GenerateError; -use crate::core::commands::enqueue::image_edit::enqueue_contextual_edit_image_command::EnqueueContextualEditImageCommand; -use crate::core::commands::enqueue::image_edit::gemini_25_flash::handle_gemini_25_flash_edit_artcraft::handle_gemini_25_flash_edit_artcraft; +use crate::core::commands::enqueue::image_edit::enqueue_edit_image_command::EnqueueEditImageCommand; use crate::core::commands::enqueue::image_edit::gpt_image_1::handle_gpt_image_1_edit_artcraft::handle_gpt_image_1_edit_artcraft; use crate::core::commands::enqueue::image_edit::gpt_image_1::handle_gpt_image_1_edit_sora::handle_gpt_image_1_edit_sora; use crate::core::commands::enqueue::task_enqueue_success::TaskEnqueueSuccess; @@ -12,9 +11,10 @@ use crate::services::sora::state::sora_task_queue::SoraTaskQueue; use crate::services::storyteller::state::storyteller_credential_manager::StorytellerCredentialManager; use log::info; use tauri::AppHandle; +use crate::core::commands::enqueue::image_edit::nano_banana::handle_nano_banana_edit_artcraft::handle_nano_banana_edit_artcraft; -pub async fn handle_gemini_25_flash_edit( - request: &EnqueueContextualEditImageCommand, +pub async fn handle_nano_banana_edit( + request: &EnqueueEditImageCommand, app: &AppHandle, app_data_root: &AppDataRoot, app_env_configs: &AppEnvConfigs, @@ -39,8 +39,8 @@ pub async fn handle_gemini_25_flash_edit( // Fallthrough } Provider::Artcraft => { - info!("Dispatching gemini 2.5 flash (edit) via Artcraft..."); - return handle_gemini_25_flash_edit_artcraft( + info!("Dispatching Nano Banana (edit) via Artcraft..."); + return handle_nano_banana_edit_artcraft( request, app, app_data_root, diff --git a/crates/desktop/artcraft/src/core/commands/enqueue/image_edit/gemini_25_flash/handle_gemini_25_flash_edit_artcraft.rs b/crates/desktop/artcraft/src/core/commands/enqueue/image_edit/nano_banana/handle_nano_banana_edit_artcraft.rs similarity index 91% rename from crates/desktop/artcraft/src/core/commands/enqueue/image_edit/gemini_25_flash/handle_gemini_25_flash_edit_artcraft.rs rename to crates/desktop/artcraft/src/core/commands/enqueue/image_edit/nano_banana/handle_nano_banana_edit_artcraft.rs index 6ab9d96be4..d4ba0f5fd0 100644 --- a/crates/desktop/artcraft/src/core/commands/enqueue/image_edit/gemini_25_flash/handle_gemini_25_flash_edit_artcraft.rs +++ b/crates/desktop/artcraft/src/core/commands/enqueue/image_edit/nano_banana/handle_nano_banana_edit_artcraft.rs @@ -1,5 +1,5 @@ use crate::core::commands::enqueue::generate_error::{BadInputReason, GenerateError}; -use crate::core::commands::enqueue::image_edit::enqueue_contextual_edit_image_command::{EditImageQuality, EditImageSize, EnqueueContextualEditImageCommand}; +use crate::core::commands::enqueue::image_edit::enqueue_edit_image_command::{EditImageQuality, EditImageSize, EnqueueEditImageCommand}; use crate::core::commands::enqueue::task_enqueue_success::TaskEnqueueSuccess; use crate::core::events::basic_sendable_event_trait::BasicSendableEvent; use crate::core::events::generation_events::common::{GenerationAction, GenerationModel, GenerationServiceProvider}; @@ -29,8 +29,8 @@ use tauri::AppHandle; pub(super) const MAX_IMAGES: usize = 10; -pub async fn handle_gemini_25_flash_edit_artcraft( - request: &EnqueueContextualEditImageCommand, +pub async fn handle_nano_banana_edit_artcraft( + request: &EnqueueEditImageCommand, app: &AppHandle, app_data_root: &AppDataRoot, app_env_configs: &AppEnvConfigs, @@ -44,7 +44,7 @@ pub async fn handle_gemini_25_flash_edit_artcraft( }, }; - info!("Calling Artcraft gemini 2.5 flash (edit) ..."); + info!("Calling Artcraft Nano Banana (edit) ..."); let uuid_idempotency_token = generate_random_uuid(); @@ -98,12 +98,12 @@ pub async fn handle_gemini_25_flash_edit_artcraft( let job_id = match result { Ok(enqueued) => { // TODO(bt,2025-07-05): Enqueue job token? - info!("Successfully enqueued Artcraft gpt-image-1. Job token: {}", + info!("Successfully enqueued Artcraft Nano Banana. Job token: {}", enqueued.inference_job_token); enqueued.inference_job_token } Err(err) => { - error!("Failed to use Artcraft gpt-image-1: {:?}", err); + error!("Failed to use Artcraft Nano Banana: {:?}", err); return Err(GenerateError::from(err)); } }; diff --git a/crates/desktop/artcraft/src/core/commands/enqueue/image_edit/nano_banana/mod.rs b/crates/desktop/artcraft/src/core/commands/enqueue/image_edit/nano_banana/mod.rs new file mode 100644 index 0000000000..33cfb238f3 --- /dev/null +++ b/crates/desktop/artcraft/src/core/commands/enqueue/image_edit/nano_banana/mod.rs @@ -0,0 +1,2 @@ +pub (super) mod handle_nano_banana_edit; +pub (super) mod handle_nano_banana_edit_artcraft; \ No newline at end of file diff --git a/crates/desktop/artcraft/src/core/commands/enqueue/image_edit/nano_banana_pro/handle_nano_banana_pro_edit.rs b/crates/desktop/artcraft/src/core/commands/enqueue/image_edit/nano_banana_pro/handle_nano_banana_pro_edit.rs new file mode 100644 index 0000000000..32e0736d2f --- /dev/null +++ b/crates/desktop/artcraft/src/core/commands/enqueue/image_edit/nano_banana_pro/handle_nano_banana_pro_edit.rs @@ -0,0 +1,53 @@ +use crate::core::commands::enqueue::generate_error::GenerateError; +use crate::core::commands::enqueue::image_edit::enqueue_edit_image_command::EnqueueEditImageCommand; +use crate::core::commands::enqueue::image_edit::nano_banana_pro::handle_nano_banana_pro_edit_artcraft::handle_nano_banana_pro_edit_artcraft; +use crate::core::commands::enqueue::task_enqueue_success::TaskEnqueueSuccess; +use crate::core::state::app_env_configs::app_env_configs::AppEnvConfigs; +use crate::core::state::data_dir::app_data_root::AppDataRoot; +use crate::core::state::provider_priority::{Provider, ProviderPriorityStore}; +use crate::services::sora::state::sora_credential_manager::SoraCredentialManager; +use crate::services::sora::state::sora_task_queue::SoraTaskQueue; +use crate::services::storyteller::state::storyteller_credential_manager::StorytellerCredentialManager; +use log::info; +use tauri::AppHandle; + +pub async fn handle_nano_banana_pro_edit( + request: &EnqueueEditImageCommand, + app: &AppHandle, + app_data_root: &AppDataRoot, + app_env_configs: &AppEnvConfigs, + provider_priority_store: &ProviderPriorityStore, + storyteller_creds_manager: &StorytellerCredentialManager, + sora_creds_manager: &SoraCredentialManager, + sora_task_queue: &SoraTaskQueue, +) -> Result { + + let priority = provider_priority_store.get_priority()?; + + // TODO: Check if providers are available before proceeding. + + info!("Providers by priority: {:?}", priority); + + for provider in priority.iter() { + match provider { + Provider::Fal => { + // Fallthrough (TODO) + } + Provider::Sora => { + // Fallthrough + } + Provider::Artcraft => { + info!("Dispatching Nano Banana Pro (edit) via Artcraft..."); + return handle_nano_banana_pro_edit_artcraft( + request, + app, + app_data_root, + app_env_configs, + storyteller_creds_manager + ).await; + } + } + } + + Err(GenerateError::NoProviderAvailable) +} diff --git a/crates/desktop/artcraft/src/core/commands/enqueue/image_edit/nano_banana_pro/handle_nano_banana_pro_edit_artcraft.rs b/crates/desktop/artcraft/src/core/commands/enqueue/image_edit/nano_banana_pro/handle_nano_banana_pro_edit_artcraft.rs new file mode 100644 index 0000000000..5c6b518413 --- /dev/null +++ b/crates/desktop/artcraft/src/core/commands/enqueue/image_edit/nano_banana_pro/handle_nano_banana_pro_edit_artcraft.rs @@ -0,0 +1,137 @@ +use crate::core::commands::enqueue::generate_error::{BadInputReason, GenerateError}; +use crate::core::commands::enqueue::image_edit::enqueue_edit_image_command::{EditImageQuality, EditImageResolution, EditImageSize, EnqueueEditImageCommand}; +use crate::core::commands::enqueue::task_enqueue_success::TaskEnqueueSuccess; +use crate::core::events::basic_sendable_event_trait::BasicSendableEvent; +use crate::core::events::generation_events::common::{GenerationAction, GenerationModel, GenerationServiceProvider}; +use crate::core::events::generation_events::generation_enqueue_failure_event::GenerationEnqueueFailureEvent; +use crate::core::state::app_env_configs::app_env_configs::AppEnvConfigs; +use crate::core::state::data_dir::app_data_root::AppDataRoot; +use crate::core::state::provider_priority::ProviderPriorityStore; +use crate::services::sora::state::sora_credential_manager::SoraCredentialManager; +use crate::services::sora::state::sora_task_queue::SoraTaskQueue; +use crate::services::storyteller::state::storyteller_credential_manager::StorytellerCredentialManager; +use anyhow::anyhow; +use artcraft_api_defs::generate::image::edit::flux_pro_kontext_max_edit_image::{FluxProKontextMaxEditImageNumImages, FluxProKontextMaxEditImageRequest}; +use artcraft_api_defs::generate::image::edit::gemini_25_flash_edit_image::{Gemini25FlashEditImageNumImages, Gemini25FlashEditImageRequest}; +use artcraft_api_defs::generate::image::edit::gpt_image_1_edit_image::{GptImage1EditImageImageQuality, GptImage1EditImageImageSize, GptImage1EditImageNumImages, GptImage1EditImageRequest}; +use artcraft_api_defs::generate::image::generate_flux_1_dev_text_to_image::GenerateFlux1DevTextToImageRequest; +use artcraft_api_defs::generate::image::generate_flux_1_schnell_text_to_image::GenerateFlux1SchnellTextToImageRequest; +use artcraft_api_defs::generate::image::generate_flux_pro_11_text_to_image::GenerateFluxPro11TextToImageRequest; +use artcraft_api_defs::generate::image::generate_flux_pro_11_ultra_text_to_image::GenerateFluxPro11UltraTextToImageRequest; +use enums::common::generation_provider::GenerationProvider; +use enums::tauri::tasks::task_type::TaskType; +use idempotency::uuid::generate_random_uuid; +use log::{error, info}; +use storyteller_client::endpoints::generate::image::edit::flux_pro_kontext_max_edit_image::flux_pro_kontext_max_edit_image; +use storyteller_client::endpoints::generate::image::edit::gemini_25_flash_edit_image::gemini_25_flash_edit_image; +use storyteller_client::endpoints::generate::image::edit::gpt_image_1_edit_image::gpt_image_1_edit_image; +use tauri::AppHandle; +use artcraft_api_defs::generate::image::multi_function::nano_banana_pro_multi_function_image_gen::{NanoBananaProMultiFunctionImageGenAspectRatio, NanoBananaProMultiFunctionImageGenImageResolution, NanoBananaProMultiFunctionImageGenNumImages, NanoBananaProMultiFunctionImageGenRequest}; +use storyteller_client::endpoints::generate::image::multi_function::nano_banana_pro_multi_function_image_gen_image::nano_banana_pro_multi_function_image_gen; +use crate::core::commands::enqueue::image_edit::enqueue_edit_image_command::ImageEditModel::NanoBanana; + +pub(super) const MAX_IMAGES: usize = 10; + +pub async fn handle_nano_banana_pro_edit_artcraft( + request: &EnqueueEditImageCommand, + app: &AppHandle, + app_data_root: &AppDataRoot, + app_env_configs: &AppEnvConfigs, + storyteller_creds_manager: &StorytellerCredentialManager, +) -> Result { + + let creds = match storyteller_creds_manager.get_credentials()? { + Some(creds) => creds, + None => { + return Err(GenerateError::needs_storyteller_credentials()); + }, + }; + + info!("Calling Artcraft Nano Banana (edit) ..."); + + let uuid_idempotency_token = generate_random_uuid(); + + let num_images = match request.image_count { + None => None, + Some(1) => Some(NanoBananaProMultiFunctionImageGenNumImages::One), + Some(2) => Some(NanoBananaProMultiFunctionImageGenNumImages::Two), + Some(3) => Some(NanoBananaProMultiFunctionImageGenNumImages::Three), + Some(4) => Some(NanoBananaProMultiFunctionImageGenNumImages::Four), + Some(other) => { + return Err(GenerateError::BadInput(BadInputReason::InvalidNumberOfRequestedImages { + min: 1, + max: 4, + requested: other, + })); + }, + }; + + let aspect_ratio = request.aspect_ratio + .map(|aspect_ratio| match aspect_ratio { + EditImageSize::Auto => None, + EditImageSize::Square => Some(NanoBananaProMultiFunctionImageGenAspectRatio::OneByOne), + EditImageSize::Wide => Some(NanoBananaProMultiFunctionImageGenAspectRatio::SixteenByNine), + EditImageSize::Tall => Some(NanoBananaProMultiFunctionImageGenAspectRatio::NineBySixteen), + }) + .flatten(); + + let resolution = request.image_resolution + .map(|image_resolution| match image_resolution { + EditImageResolution::OneK => NanoBananaProMultiFunctionImageGenImageResolution::OneK, + EditImageResolution::TwoK => NanoBananaProMultiFunctionImageGenImageResolution::TwoK, + EditImageResolution::FourK => NanoBananaProMultiFunctionImageGenImageResolution::FourK, + }); + + let mut media_tokens = Vec::with_capacity(10); + + if let Some(scene_image_media_token) = request.scene_image_media_token.clone() { + media_tokens.push(scene_image_media_token); + } + + if let Some(image_media_tokens) = request.image_media_tokens.as_ref() { + media_tokens.extend_from_slice(image_media_tokens); + } + + if media_tokens.len() > MAX_IMAGES { + return Err(GenerateError::BadInput(BadInputReason::InvalidNumberOfInputImages { + min: 1, + max: MAX_IMAGES as u32, + provided: media_tokens.len() as u32, + })); + } + + let request = NanoBananaProMultiFunctionImageGenRequest { + uuid_idempotency_token, + prompt: Some(request.prompt.clone()), + image_media_tokens: Some(media_tokens), + num_images, + resolution, + aspect_ratio, + }; + + let result = nano_banana_pro_multi_function_image_gen( + &app_env_configs.storyteller_host, + Some(&creds), + request, + ).await; + + let job_id = match result { + Ok(enqueued) => { + // TODO(bt,2025-07-05): Enqueue job token? + info!("Successfully enqueued Artcraft Nano Banana. Job token: {}", + enqueued.inference_job_token); + enqueued.inference_job_token + } + Err(err) => { + error!("Failed to use Artcraft Nano Banana: {:?}", err); + return Err(GenerateError::from(err)); + } + }; + + Ok(TaskEnqueueSuccess { + provider: GenerationProvider::Artcraft, + model: Some(GenerationModel::NanoBananaPro), + provider_job_id: Some(job_id.to_string()), + task_type: TaskType::ImageGeneration, + }) +} diff --git a/crates/desktop/artcraft/src/core/commands/enqueue/image_edit/nano_banana_pro/mod.rs b/crates/desktop/artcraft/src/core/commands/enqueue/image_edit/nano_banana_pro/mod.rs new file mode 100644 index 0000000000..359d44ae7f --- /dev/null +++ b/crates/desktop/artcraft/src/core/commands/enqueue/image_edit/nano_banana_pro/mod.rs @@ -0,0 +1,2 @@ +pub (super) mod handle_nano_banana_pro_edit; +pub (super) mod handle_nano_banana_pro_edit_artcraft; \ No newline at end of file diff --git a/crates/desktop/artcraft/src/core/commands/enqueue/image_inpaint/flux_dev_juggernaut_inpaint/handle_flux_dev_juggernaut_inpaint.rs b/crates/desktop/artcraft/src/core/commands/enqueue/image_inpaint/flux_dev_juggernaut_inpaint/handle_flux_dev_juggernaut_inpaint.rs index 01914d8117..ff5da1ae64 100644 --- a/crates/desktop/artcraft/src/core/commands/enqueue/image_inpaint/flux_dev_juggernaut_inpaint/handle_flux_dev_juggernaut_inpaint.rs +++ b/crates/desktop/artcraft/src/core/commands/enqueue/image_inpaint/flux_dev_juggernaut_inpaint/handle_flux_dev_juggernaut_inpaint.rs @@ -1,5 +1,5 @@ use crate::core::commands::enqueue::generate_error::GenerateError; -use crate::core::commands::enqueue::image_edit::enqueue_contextual_edit_image_command::EnqueueContextualEditImageCommand; +use crate::core::commands::enqueue::image_edit::enqueue_edit_image_command::EnqueueEditImageCommand; use crate::core::commands::enqueue::image_edit::gpt_image_1::handle_gpt_image_1_edit_artcraft::handle_gpt_image_1_edit_artcraft; use crate::core::commands::enqueue::image_edit::gpt_image_1::handle_gpt_image_1_edit_sora::handle_gpt_image_1_edit_sora; use crate::core::commands::enqueue::image_inpaint::enqueue_image_inpaint_command::EnqueueInpaintImageCommand; diff --git a/crates/desktop/artcraft/src/core/commands/enqueue/image_inpaint/flux_dev_juggernaut_inpaint/handle_flux_dev_juggernaut_inpaint_artcraft.rs b/crates/desktop/artcraft/src/core/commands/enqueue/image_inpaint/flux_dev_juggernaut_inpaint/handle_flux_dev_juggernaut_inpaint_artcraft.rs index 2409772933..23a3e698d2 100644 --- a/crates/desktop/artcraft/src/core/commands/enqueue/image_inpaint/flux_dev_juggernaut_inpaint/handle_flux_dev_juggernaut_inpaint_artcraft.rs +++ b/crates/desktop/artcraft/src/core/commands/enqueue/image_inpaint/flux_dev_juggernaut_inpaint/handle_flux_dev_juggernaut_inpaint_artcraft.rs @@ -1,6 +1,6 @@ use crate::core::commands::enqueue::generate_error::{BadInputReason, GenerateError}; use crate::core::commands::enqueue::image_bg_removal::enqueue_image_bg_removal_command::EnqueueImageBgRemovalCommand; -use crate::core::commands::enqueue::image_edit::enqueue_contextual_edit_image_command::{EditImageQuality, EditImageSize, EnqueueContextualEditImageCommand}; +use crate::core::commands::enqueue::image_edit::enqueue_edit_image_command::{EditImageQuality, EditImageSize, EnqueueEditImageCommand}; use crate::core::commands::enqueue::image_inpaint::enqueue_image_inpaint_command::EnqueueInpaintImageCommand; use crate::core::commands::enqueue::task_enqueue_success::TaskEnqueueSuccess; use crate::core::events::basic_sendable_event_trait::BasicSendableEvent; diff --git a/crates/desktop/artcraft/src/core/commands/enqueue/image_inpaint/flux_pro_1_inpaint/handle_flux_pro_1_inpaint.rs b/crates/desktop/artcraft/src/core/commands/enqueue/image_inpaint/flux_pro_1_inpaint/handle_flux_pro_1_inpaint.rs index b8a2b4511b..fa0359a6c2 100644 --- a/crates/desktop/artcraft/src/core/commands/enqueue/image_inpaint/flux_pro_1_inpaint/handle_flux_pro_1_inpaint.rs +++ b/crates/desktop/artcraft/src/core/commands/enqueue/image_inpaint/flux_pro_1_inpaint/handle_flux_pro_1_inpaint.rs @@ -1,5 +1,5 @@ use crate::core::commands::enqueue::generate_error::GenerateError; -use crate::core::commands::enqueue::image_edit::enqueue_contextual_edit_image_command::EnqueueContextualEditImageCommand; +use crate::core::commands::enqueue::image_edit::enqueue_edit_image_command::EnqueueEditImageCommand; use crate::core::commands::enqueue::image_edit::gpt_image_1::handle_gpt_image_1_edit_artcraft::handle_gpt_image_1_edit_artcraft; use crate::core::commands::enqueue::image_edit::gpt_image_1::handle_gpt_image_1_edit_sora::handle_gpt_image_1_edit_sora; use crate::core::commands::enqueue::image_inpaint::enqueue_image_inpaint_command::EnqueueInpaintImageCommand; diff --git a/crates/desktop/artcraft/src/core/commands/enqueue/image_inpaint/flux_pro_1_inpaint/handle_flux_pro_1_inpaint_artcraft.rs b/crates/desktop/artcraft/src/core/commands/enqueue/image_inpaint/flux_pro_1_inpaint/handle_flux_pro_1_inpaint_artcraft.rs index fa8b3eea78..8600ae7d2e 100644 --- a/crates/desktop/artcraft/src/core/commands/enqueue/image_inpaint/flux_pro_1_inpaint/handle_flux_pro_1_inpaint_artcraft.rs +++ b/crates/desktop/artcraft/src/core/commands/enqueue/image_inpaint/flux_pro_1_inpaint/handle_flux_pro_1_inpaint_artcraft.rs @@ -1,6 +1,6 @@ use crate::core::commands::enqueue::generate_error::{BadInputReason, GenerateError}; use crate::core::commands::enqueue::image_bg_removal::enqueue_image_bg_removal_command::EnqueueImageBgRemovalCommand; -use crate::core::commands::enqueue::image_edit::enqueue_contextual_edit_image_command::{EditImageQuality, EditImageSize, EnqueueContextualEditImageCommand}; +use crate::core::commands::enqueue::image_edit::enqueue_edit_image_command::{EditImageQuality, EditImageSize, EnqueueEditImageCommand}; use crate::core::commands::enqueue::image_inpaint::enqueue_image_inpaint_command::EnqueueInpaintImageCommand; use crate::core::commands::enqueue::task_enqueue_success::TaskEnqueueSuccess; use crate::core::events::basic_sendable_event_trait::BasicSendableEvent; diff --git a/crates/desktop/artcraft/src/core/commands/enqueue/image_inpaint/flux_pro_kontext_inpaint/handle_flux_pro_kontext_inpaint.rs b/crates/desktop/artcraft/src/core/commands/enqueue/image_inpaint/flux_pro_kontext_inpaint/handle_flux_pro_kontext_inpaint.rs index b86ee5f396..5abbe62141 100644 --- a/crates/desktop/artcraft/src/core/commands/enqueue/image_inpaint/flux_pro_kontext_inpaint/handle_flux_pro_kontext_inpaint.rs +++ b/crates/desktop/artcraft/src/core/commands/enqueue/image_inpaint/flux_pro_kontext_inpaint/handle_flux_pro_kontext_inpaint.rs @@ -1,5 +1,5 @@ use crate::core::commands::enqueue::generate_error::GenerateError; -use crate::core::commands::enqueue::image_edit::enqueue_contextual_edit_image_command::EnqueueContextualEditImageCommand; +use crate::core::commands::enqueue::image_edit::enqueue_edit_image_command::EnqueueEditImageCommand; use crate::core::commands::enqueue::image_inpaint::enqueue_image_inpaint_command::EnqueueInpaintImageCommand; use crate::core::commands::enqueue::image_inpaint::flux_pro_kontext_inpaint::handle_flux_pro_kontext_inpaint_artcraft::handle_flux_pro_kontext_inpaint_artcraft; use crate::core::commands::enqueue::task_enqueue_success::TaskEnqueueSuccess; diff --git a/crates/desktop/artcraft/src/core/commands/enqueue/image_inpaint/flux_pro_kontext_inpaint/handle_flux_pro_kontext_inpaint_artcraft.rs b/crates/desktop/artcraft/src/core/commands/enqueue/image_inpaint/flux_pro_kontext_inpaint/handle_flux_pro_kontext_inpaint_artcraft.rs index 80980b646a..77ee6b6951 100644 --- a/crates/desktop/artcraft/src/core/commands/enqueue/image_inpaint/flux_pro_kontext_inpaint/handle_flux_pro_kontext_inpaint_artcraft.rs +++ b/crates/desktop/artcraft/src/core/commands/enqueue/image_inpaint/flux_pro_kontext_inpaint/handle_flux_pro_kontext_inpaint_artcraft.rs @@ -1,6 +1,6 @@ use crate::core::commands::enqueue::generate_error::{BadInputReason, GenerateError}; use crate::core::commands::enqueue::image_bg_removal::enqueue_image_bg_removal_command::EnqueueImageBgRemovalCommand; -use crate::core::commands::enqueue::image_edit::enqueue_contextual_edit_image_command::{EditImageQuality, EditImageSize, EnqueueContextualEditImageCommand}; +use crate::core::commands::enqueue::image_edit::enqueue_edit_image_command::{EditImageQuality, EditImageSize, EnqueueEditImageCommand}; use crate::core::commands::enqueue::image_inpaint::enqueue_image_inpaint_command::EnqueueInpaintImageCommand; use crate::core::commands::enqueue::task_enqueue_success::TaskEnqueueSuccess; use crate::core::events::basic_sendable_event_trait::BasicSendableEvent; diff --git a/crates/desktop/artcraft/src/core/commands/enqueue/image_inpaint/gemini_25_flash_inpaint/handle_gemini_25_flash_inpaint.rs b/crates/desktop/artcraft/src/core/commands/enqueue/image_inpaint/gemini_25_flash_inpaint/handle_gemini_25_flash_inpaint.rs index 207bbadb85..62f57cc6d7 100644 --- a/crates/desktop/artcraft/src/core/commands/enqueue/image_inpaint/gemini_25_flash_inpaint/handle_gemini_25_flash_inpaint.rs +++ b/crates/desktop/artcraft/src/core/commands/enqueue/image_inpaint/gemini_25_flash_inpaint/handle_gemini_25_flash_inpaint.rs @@ -1,5 +1,5 @@ use crate::core::commands::enqueue::generate_error::GenerateError; -use crate::core::commands::enqueue::image_edit::enqueue_contextual_edit_image_command::EnqueueContextualEditImageCommand; +use crate::core::commands::enqueue::image_edit::enqueue_edit_image_command::EnqueueEditImageCommand; use crate::core::commands::enqueue::image_inpaint::enqueue_image_inpaint_command::EnqueueInpaintImageCommand; use crate::core::commands::enqueue::image_inpaint::flux_pro_kontext_inpaint::handle_flux_pro_kontext_inpaint_artcraft::handle_flux_pro_kontext_inpaint_artcraft; use crate::core::commands::enqueue::image_inpaint::gemini_25_flash_inpaint::handle_gemini_25_flash_inpaint_artcraft::handle_gemini_25_flash_inpaint_artcraft; diff --git a/crates/desktop/artcraft/src/core/commands/enqueue/image_inpaint/gemini_25_flash_inpaint/handle_gemini_25_flash_inpaint_artcraft.rs b/crates/desktop/artcraft/src/core/commands/enqueue/image_inpaint/gemini_25_flash_inpaint/handle_gemini_25_flash_inpaint_artcraft.rs index 8c884cfc8e..8ec60a7140 100644 --- a/crates/desktop/artcraft/src/core/commands/enqueue/image_inpaint/gemini_25_flash_inpaint/handle_gemini_25_flash_inpaint_artcraft.rs +++ b/crates/desktop/artcraft/src/core/commands/enqueue/image_inpaint/gemini_25_flash_inpaint/handle_gemini_25_flash_inpaint_artcraft.rs @@ -1,6 +1,6 @@ use crate::core::commands::enqueue::generate_error::{BadInputReason, GenerateError}; use crate::core::commands::enqueue::image_bg_removal::enqueue_image_bg_removal_command::EnqueueImageBgRemovalCommand; -use crate::core::commands::enqueue::image_edit::enqueue_contextual_edit_image_command::{EditImageQuality, EditImageSize, EnqueueContextualEditImageCommand}; +use crate::core::commands::enqueue::image_edit::enqueue_edit_image_command::{EditImageQuality, EditImageSize, EnqueueEditImageCommand}; use crate::core::commands::enqueue::image_inpaint::enqueue_image_inpaint_command::EnqueueInpaintImageCommand; use crate::core::commands::enqueue::task_enqueue_success::TaskEnqueueSuccess; use crate::core::events::basic_sendable_event_trait::BasicSendableEvent; diff --git a/crates/desktop/artcraft/src/core/commands/enqueue/image_to_video/grok/handle_grok_video.rs b/crates/desktop/artcraft/src/core/commands/enqueue/image_to_video/grok/handle_grok_video.rs index 5fc40c5fd6..bd6d1e0cb9 100644 --- a/crates/desktop/artcraft/src/core/commands/enqueue/image_to_video/grok/handle_grok_video.rs +++ b/crates/desktop/artcraft/src/core/commands/enqueue/image_to_video/grok/handle_grok_video.rs @@ -1,5 +1,5 @@ use crate::core::commands::enqueue::generate_error::{BadInputReason, GenerateError}; -use crate::core::commands::enqueue::image_edit::enqueue_contextual_edit_image_command::EnqueueContextualEditImageCommand; +use crate::core::commands::enqueue::image_edit::enqueue_edit_image_command::EnqueueEditImageCommand; use crate::core::commands::enqueue::image_inpaint::enqueue_image_inpaint_command::EnqueueInpaintImageCommand; use crate::core::commands::enqueue::image_to_video::enqueue_image_to_video_command::{EnqueueImageToVideoRequest, GrokAspectRatio, SoraOrientation}; use crate::core::commands::enqueue::task_enqueue_success::TaskEnqueueSuccess; diff --git a/crates/desktop/artcraft/src/core/commands/enqueue/image_to_video/sora2/handle_sora2_video.rs b/crates/desktop/artcraft/src/core/commands/enqueue/image_to_video/sora2/handle_sora2_video.rs index f19af807a1..8502029fdb 100644 --- a/crates/desktop/artcraft/src/core/commands/enqueue/image_to_video/sora2/handle_sora2_video.rs +++ b/crates/desktop/artcraft/src/core/commands/enqueue/image_to_video/sora2/handle_sora2_video.rs @@ -1,5 +1,5 @@ use crate::core::commands::enqueue::generate_error::GenerateError; -use crate::core::commands::enqueue::image_edit::enqueue_contextual_edit_image_command::EnqueueContextualEditImageCommand; +use crate::core::commands::enqueue::image_edit::enqueue_edit_image_command::EnqueueEditImageCommand; use crate::core::commands::enqueue::image_inpaint::enqueue_image_inpaint_command::EnqueueInpaintImageCommand; use crate::core::commands::enqueue::image_to_video::enqueue_image_to_video_command::EnqueueImageToVideoRequest; use crate::core::commands::enqueue::image_to_video::sora2::handle_sora2_video_sora::handle_sora2_video_sora; diff --git a/crates/desktop/artcraft/src/core/commands/enqueue/task_enqueue_success.rs b/crates/desktop/artcraft/src/core/commands/enqueue/task_enqueue_success.rs index 848dc484e6..ac76434588 100644 --- a/crates/desktop/artcraft/src/core/commands/enqueue/task_enqueue_success.rs +++ b/crates/desktop/artcraft/src/core/commands/enqueue/task_enqueue_success.rs @@ -66,6 +66,8 @@ impl TaskEnqueueSuccess{ Some(GenerationModel::FluxPro11Ultra) => Some(TaskModelType::FluxPro11Ultra), Some(GenerationModel::FluxProKontextMax) => Some(TaskModelType::FluxProKontextMax), Some(GenerationModel::Gemini25Flash) => Some(TaskModelType::Gemini25Flash), + Some(GenerationModel::NanoBanana) => Some(TaskModelType::NanoBanana), + Some(GenerationModel::NanoBananaPro) => Some(TaskModelType::NanoBananaPro), Some(GenerationModel::GptImage1) => Some(TaskModelType::GptImage1), Some(GenerationModel::GrokImage) => Some(TaskModelType::GrokImage), Some(GenerationModel::Recraft3) => Some(TaskModelType::Recraft3), diff --git a/crates/desktop/artcraft/src/core/commands/enqueue/text_to_image/enqueue_text_to_image_command.rs b/crates/desktop/artcraft/src/core/commands/enqueue/text_to_image/enqueue_text_to_image_command.rs index 87a3915294..5a01fa649d 100644 --- a/crates/desktop/artcraft/src/core/commands/enqueue/text_to_image/enqueue_text_to_image_command.rs +++ b/crates/desktop/artcraft/src/core/commands/enqueue/text_to_image/enqueue_text_to_image_command.rs @@ -1,13 +1,14 @@ use crate::core::commands::enqueue::common::notify_frontend_of_errors::notify_frontend_of_errors; use crate::core::commands::enqueue::generate_error::{BadInputReason, GenerateError, MissingCredentialsReason}; use crate::core::commands::enqueue::task_enqueue_success::TaskEnqueueSuccess; -use crate::core::commands::enqueue::text_to_image::gemini_25_flash::handle_gemini_25_flash::handle_gemini_25_flash; use crate::core::commands::enqueue::text_to_image::generic::handle_image_artcraft::handle_image_artcraft; use crate::core::commands::enqueue::text_to_image::generic::handle_image_fal::handle_image_fal; use crate::core::commands::enqueue::text_to_image::gpt_image_1::handle_gpt_image_1::handle_gpt_image_1; use crate::core::commands::enqueue::text_to_image::gpt_image_1::handle_gpt_image_1_sora::handle_gpt_image_1_sora; use crate::core::commands::enqueue::text_to_image::grok::handle_grok::handle_grok; use crate::core::commands::enqueue::text_to_image::midjourney::handle_midjourney::handle_midjourney; +use crate::core::commands::enqueue::text_to_image::nano_banana::handle_nano_banana::handle_nano_banana; +use crate::core::commands::enqueue::text_to_image::nano_banana_pro::handle_nano_banana_pro::handle_nano_banana_pro; use crate::core::commands::response::failure_response_wrapper::{CommandErrorResponseWrapper, CommandErrorStatus}; use crate::core::commands::response::shorthand::Response; use crate::core::commands::response::success_response_wrapper::SerializeMarker; @@ -58,6 +59,10 @@ pub enum ImageModel { #[serde(rename = "gemini_25_flash")] Gemini25Flash, + #[serde(rename = "nano_banana")] + NanoBanana, + #[serde(rename = "nano_banana_pro")] + NanoBananaPro, // Generic Midjourney model, version unknown. #[serde(rename = "midjourney")] @@ -75,6 +80,9 @@ pub struct EnqueueTextToImageRequest { /// Aspect ratio. pub aspect_ratio: Option, + /// Aspect ratio. + pub image_resolution: Option, + /// The number of images to generate. pub number_images: Option, @@ -116,6 +124,17 @@ pub enum TextToImageSize { Tall, } +#[derive(Deserialize, Debug, Copy, Clone)] +#[serde(rename_all = "snake_case")] +pub enum TextToImageResolution { + /// 1K - nano banana pro + OneK, + /// 2K - nano banana pro + TwoK, + /// 4K - nano banana pro + FourK, +} + #[derive(Serialize)] pub struct EnqueueTextToImageSuccessResponse { } @@ -297,8 +316,20 @@ pub async fn dispatch_request( None => { return Err(GenerateError::BadInput(BadInputReason::NoModelSpecified)); } - Some(ImageModel::Gemini25Flash) => { - return handle_gemini_25_flash( + Some(ImageModel::NanoBanana | ImageModel::Gemini25Flash) => { + return handle_nano_banana( + request, + app, + app_data_root, + app_env_configs, + provider_priority_store, + storyteller_creds_manager, + sora_creds_manager, + sora_task_queue, + ).await; + } + Some(ImageModel::NanoBananaPro) => { + return handle_nano_banana_pro( request, app, app_data_root, diff --git a/crates/desktop/artcraft/src/core/commands/enqueue/text_to_image/gemini_25_flash/mod.rs b/crates/desktop/artcraft/src/core/commands/enqueue/text_to_image/gemini_25_flash/mod.rs deleted file mode 100644 index 0a64fffd65..0000000000 --- a/crates/desktop/artcraft/src/core/commands/enqueue/text_to_image/gemini_25_flash/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub (crate) mod handle_gemini_25_flash_artcraft; -pub (super) mod handle_gemini_25_flash; diff --git a/crates/desktop/artcraft/src/core/commands/enqueue/text_to_image/generic/handle_image_artcraft.rs b/crates/desktop/artcraft/src/core/commands/enqueue/text_to_image/generic/handle_image_artcraft.rs index aa175a3e4a..38ef711190 100644 --- a/crates/desktop/artcraft/src/core/commands/enqueue/text_to_image/generic/handle_image_artcraft.rs +++ b/crates/desktop/artcraft/src/core/commands/enqueue/text_to_image/generic/handle_image_artcraft.rs @@ -57,6 +57,8 @@ pub async fn handle_image_artcraft( } Some( ImageModel::Gemini25Flash | + ImageModel::NanoBanana | + ImageModel::NanoBananaPro | ImageModel::GptImage1 | ImageModel::GrokImage | ImageModel::Midjourney diff --git a/crates/desktop/artcraft/src/core/commands/enqueue/text_to_image/gpt_image_1/handle_gpt_image_1_artcraft.rs b/crates/desktop/artcraft/src/core/commands/enqueue/text_to_image/gpt_image_1/handle_gpt_image_1_artcraft.rs index 24a14421ad..f31db72c19 100644 --- a/crates/desktop/artcraft/src/core/commands/enqueue/text_to_image/gpt_image_1/handle_gpt_image_1_artcraft.rs +++ b/crates/desktop/artcraft/src/core/commands/enqueue/text_to_image/gpt_image_1/handle_gpt_image_1_artcraft.rs @@ -1,5 +1,5 @@ use crate::core::commands::enqueue::generate_error::GenerateError; -use crate::core::commands::enqueue::image_edit::enqueue_contextual_edit_image_command::{EditImageQuality, EditImageSize}; +use crate::core::commands::enqueue::image_edit::enqueue_edit_image_command::{EditImageQuality, EditImageSize}; use crate::core::commands::enqueue::task_enqueue_success::TaskEnqueueSuccess; use crate::core::commands::enqueue::text_to_image::enqueue_text_to_image_command::{EnqueueTextToImageRequest, TextToImageSize}; use crate::core::events::basic_sendable_event_trait::BasicSendableEvent; diff --git a/crates/desktop/artcraft/src/core/commands/enqueue/text_to_image/midjourney/handle_midjourney.rs b/crates/desktop/artcraft/src/core/commands/enqueue/text_to_image/midjourney/handle_midjourney.rs index a5bbc85571..eb6826ab8a 100644 --- a/crates/desktop/artcraft/src/core/commands/enqueue/text_to_image/midjourney/handle_midjourney.rs +++ b/crates/desktop/artcraft/src/core/commands/enqueue/text_to_image/midjourney/handle_midjourney.rs @@ -1,5 +1,5 @@ use crate::core::commands::enqueue::generate_error::{GenerateError, MissingCredentialsReason, ProviderFailureReason}; -use crate::core::commands::enqueue::image_edit::enqueue_contextual_edit_image_command::{EditImageQuality, EditImageSize}; +use crate::core::commands::enqueue::image_edit::enqueue_edit_image_command::{EditImageQuality, EditImageSize}; use crate::core::commands::enqueue::task_enqueue_success::TaskEnqueueSuccess; use crate::core::commands::enqueue::text_to_image::enqueue_text_to_image_command::{EnqueueTextToImageRequest, TextToImageSize}; use crate::core::events::basic_sendable_event_trait::BasicSendableEvent; diff --git a/crates/desktop/artcraft/src/core/commands/enqueue/text_to_image/mod.rs b/crates/desktop/artcraft/src/core/commands/enqueue/text_to_image/mod.rs index 7f981744f8..2fd6e6cebc 100644 --- a/crates/desktop/artcraft/src/core/commands/enqueue/text_to_image/mod.rs +++ b/crates/desktop/artcraft/src/core/commands/enqueue/text_to_image/mod.rs @@ -1,6 +1,7 @@ pub mod enqueue_text_to_image_command; pub mod generic; -mod gemini_25_flash; +mod nano_banana; +mod nano_banana_pro; mod gpt_image_1; mod midjourney; mod grok; \ No newline at end of file diff --git a/crates/desktop/artcraft/src/core/commands/enqueue/text_to_image/gemini_25_flash/handle_gemini_25_flash.rs b/crates/desktop/artcraft/src/core/commands/enqueue/text_to_image/nano_banana/handle_nano_banana.rs similarity index 91% rename from crates/desktop/artcraft/src/core/commands/enqueue/text_to_image/gemini_25_flash/handle_gemini_25_flash.rs rename to crates/desktop/artcraft/src/core/commands/enqueue/text_to_image/nano_banana/handle_nano_banana.rs index 336fff626d..8997272a2b 100644 --- a/crates/desktop/artcraft/src/core/commands/enqueue/text_to_image/gemini_25_flash/handle_gemini_25_flash.rs +++ b/crates/desktop/artcraft/src/core/commands/enqueue/text_to_image/nano_banana/handle_nano_banana.rs @@ -3,9 +3,9 @@ use crate::core::commands::enqueue::image_edit::gpt_image_1::handle_gpt_image_1_ use crate::core::commands::enqueue::image_edit::gpt_image_1::handle_gpt_image_1_edit_sora::handle_gpt_image_1_edit_sora; use crate::core::commands::enqueue::task_enqueue_success::TaskEnqueueSuccess; use crate::core::commands::enqueue::text_to_image::enqueue_text_to_image_command::{EnqueueTextToImageRequest, TextToImageSize}; -use crate::core::commands::enqueue::text_to_image::gemini_25_flash::handle_gemini_25_flash_artcraft::handle_gemini_25_flash_artcraft; use crate::core::commands::enqueue::text_to_image::gpt_image_1::handle_gpt_image_1_artcraft::handle_gpt_image_1_artcraft; use crate::core::commands::enqueue::text_to_image::gpt_image_1::handle_gpt_image_1_sora::handle_gpt_image_1_sora; +use crate::core::commands::enqueue::text_to_image::nano_banana::handle_nano_banana_artcraft::handle_nano_banana_artcraft; use crate::core::events::basic_sendable_event_trait::BasicSendableEvent; use crate::core::events::generation_events::common::{GenerationAction, GenerationModel, GenerationServiceProvider}; use crate::core::events::generation_events::generation_enqueue_failure_event::GenerationEnqueueFailureEvent; @@ -24,7 +24,7 @@ use openai_sora_client::requests::image_gen::common::{ImageSize, NumImages}; use std::time::Duration; use tauri::AppHandle; -pub async fn handle_gemini_25_flash( +pub async fn handle_nano_banana( request: &EnqueueTextToImageRequest, app: &AppHandle, app_data_root: &AppDataRoot, @@ -50,8 +50,8 @@ pub async fn handle_gemini_25_flash( // Fallthrough } Provider::Artcraft => { - info!("Dispatching gemini 2.5 flash via Artcraft..."); - return handle_gemini_25_flash_artcraft( + info!("Dispatching Nano Banana via Artcraft..."); + return handle_nano_banana_artcraft( request, app_env_configs, storyteller_creds_manager diff --git a/crates/desktop/artcraft/src/core/commands/enqueue/text_to_image/gemini_25_flash/handle_gemini_25_flash_artcraft.rs b/crates/desktop/artcraft/src/core/commands/enqueue/text_to_image/nano_banana/handle_nano_banana_artcraft.rs similarity index 91% rename from crates/desktop/artcraft/src/core/commands/enqueue/text_to_image/gemini_25_flash/handle_gemini_25_flash_artcraft.rs rename to crates/desktop/artcraft/src/core/commands/enqueue/text_to_image/nano_banana/handle_nano_banana_artcraft.rs index 523066f477..1a95679bf6 100644 --- a/crates/desktop/artcraft/src/core/commands/enqueue/text_to_image/gemini_25_flash/handle_gemini_25_flash_artcraft.rs +++ b/crates/desktop/artcraft/src/core/commands/enqueue/text_to_image/nano_banana/handle_nano_banana_artcraft.rs @@ -1,5 +1,5 @@ use crate::core::commands::enqueue::generate_error::{GenerateError, MissingCredentialsReason, ProviderFailureReason}; -use crate::core::commands::enqueue::image_edit::enqueue_contextual_edit_image_command::{EditImageQuality, EditImageSize}; +use crate::core::commands::enqueue::image_edit::enqueue_edit_image_command::{EditImageQuality, EditImageSize}; use crate::core::commands::enqueue::task_enqueue_success::TaskEnqueueSuccess; use crate::core::commands::enqueue::text_to_image::enqueue_text_to_image_command::{EnqueueTextToImageRequest, TextToImageSize}; use crate::core::events::basic_sendable_event_trait::BasicSendableEvent; @@ -26,7 +26,7 @@ use storyteller_client::endpoints::generate::image::edit::gpt_image_1_edit_image use storyteller_client::endpoints::generate::image::generate_gpt_image_1_text_to_image::generate_gpt_image_1_text_to_image; use tauri::AppHandle; -pub async fn handle_gemini_25_flash_artcraft( +pub async fn handle_nano_banana_artcraft( request: &EnqueueTextToImageRequest, app_env_configs: &AppEnvConfigs, storyteller_creds_manager: &StorytellerCredentialManager, @@ -39,7 +39,7 @@ pub async fn handle_gemini_25_flash_artcraft( }, }; - info!("Calling Artcraft gemini 2.5 flash ..."); + info!("Calling Artcraft Nano Banana..."); let uuid_idempotency_token = generate_random_uuid(); @@ -69,12 +69,12 @@ pub async fn handle_gemini_25_flash_artcraft( let job_id = match result { Ok(enqueued) => { // TODO(bt,2025-07-05): Enqueue job token? - info!("Successfully enqueued Artcraft gemini 2.5 flash. Job token: {}", + info!("Successfully enqueued Artcraft Nano Banana. Job token: {}", enqueued.inference_job_token); enqueued.inference_job_token } Err(err) => { - error!("Failed to use Artcraft gemini 2.5 flash: {:?}", err); + error!("Failed to use Artcraft Nano Banana: {:?}", err); return Err(GenerateError::ProviderFailure(ProviderFailureReason::StorytellerError(err))); } }; diff --git a/crates/desktop/artcraft/src/core/commands/enqueue/text_to_image/nano_banana/mod.rs b/crates/desktop/artcraft/src/core/commands/enqueue/text_to_image/nano_banana/mod.rs new file mode 100644 index 0000000000..d671e177bb --- /dev/null +++ b/crates/desktop/artcraft/src/core/commands/enqueue/text_to_image/nano_banana/mod.rs @@ -0,0 +1,2 @@ +pub (crate) mod handle_nano_banana_artcraft; +pub (super) mod handle_nano_banana; diff --git a/crates/desktop/artcraft/src/core/commands/enqueue/text_to_image/nano_banana_pro/handle_nano_banana_pro.rs b/crates/desktop/artcraft/src/core/commands/enqueue/text_to_image/nano_banana_pro/handle_nano_banana_pro.rs new file mode 100644 index 0000000000..4d7d31ca3d --- /dev/null +++ b/crates/desktop/artcraft/src/core/commands/enqueue/text_to_image/nano_banana_pro/handle_nano_banana_pro.rs @@ -0,0 +1,59 @@ +use crate::core::commands::enqueue::generate_error::GenerateError; +use crate::core::commands::enqueue::task_enqueue_success::TaskEnqueueSuccess; +use crate::core::commands::enqueue::text_to_image::enqueue_text_to_image_command::{EnqueueTextToImageRequest, TextToImageSize}; +use crate::core::commands::enqueue::text_to_image::nano_banana_pro::handle_nano_banana_pro_artcraft::handle_nano_banana_pro_artcraft; +use crate::core::events::basic_sendable_event_trait::BasicSendableEvent; +use crate::core::events::generation_events::common::{GenerationAction, GenerationModel, GenerationServiceProvider}; +use crate::core::events::generation_events::generation_enqueue_failure_event::GenerationEnqueueFailureEvent; +use crate::core::state::app_env_configs::app_env_configs::AppEnvConfigs; +use crate::core::state::data_dir::app_data_root::AppDataRoot; +use crate::core::state::provider_priority::{Provider, ProviderPriorityStore}; +use crate::services::sora::state::sora_credential_manager::SoraCredentialManager; +use crate::services::sora::state::sora_task_queue::SoraTaskQueue; +use crate::services::storyteller::state::storyteller_credential_manager::StorytellerCredentialManager; +use enums::common::generation_provider::GenerationProvider; +use enums::tauri::tasks::task_type::TaskType; +use log::{error, info}; +use openai_sora_client::recipes::simple_image_gen_with_session_auto_renew::{simple_image_gen_with_session_auto_renew, SimpleImageGenAutoRenewRequest}; +use openai_sora_client::requests::image_gen::common::{ImageSize, NumImages}; +use std::time::Duration; +use tauri::AppHandle; + +pub async fn handle_nano_banana_pro( + request: &EnqueueTextToImageRequest, + app: &AppHandle, + app_data_root: &AppDataRoot, + app_env_configs: &AppEnvConfigs, + provider_priority_store: &ProviderPriorityStore, + storyteller_creds_manager: &StorytellerCredentialManager, + sora_creds_manager: &SoraCredentialManager, + sora_task_queue: &SoraTaskQueue, +) -> Result { + + let priority = provider_priority_store.get_priority()?; + + // TODO: Check if providers are available before proceeding. + + info!("Providers by priority: {:?}", priority); + + for provider in priority.iter() { + match provider { + Provider::Fal => { + // Fallthrough (for now) + } + Provider::Sora => { + // Fallthrough + } + Provider::Artcraft => { + info!("Dispatching Nano Banana Pro via Artcraft..."); + return handle_nano_banana_pro_artcraft( + request, + app_env_configs, + storyteller_creds_manager + ).await; + } + } + } + + Err(GenerateError::NoProviderAvailable) +} diff --git a/crates/desktop/artcraft/src/core/commands/enqueue/text_to_image/nano_banana_pro/handle_nano_banana_pro_artcraft.rs b/crates/desktop/artcraft/src/core/commands/enqueue/text_to_image/nano_banana_pro/handle_nano_banana_pro_artcraft.rs new file mode 100644 index 0000000000..52633c5768 --- /dev/null +++ b/crates/desktop/artcraft/src/core/commands/enqueue/text_to_image/nano_banana_pro/handle_nano_banana_pro_artcraft.rs @@ -0,0 +1,111 @@ +use crate::core::commands::enqueue::generate_error::{BadInputReason, GenerateError, MissingCredentialsReason, ProviderFailureReason}; +use crate::core::commands::enqueue::image_edit::enqueue_edit_image_command::{EditImageQuality, EditImageResolution, EditImageSize}; +use crate::core::commands::enqueue::task_enqueue_success::TaskEnqueueSuccess; +use crate::core::commands::enqueue::text_to_image::enqueue_text_to_image_command::{EnqueueTextToImageRequest, TextToImageResolution, TextToImageSize}; +use crate::core::events::basic_sendable_event_trait::BasicSendableEvent; +use crate::core::events::generation_events::common::{GenerationAction, GenerationModel, GenerationServiceProvider}; +use crate::core::events::generation_events::generation_enqueue_failure_event::GenerationEnqueueFailureEvent; +use crate::core::state::app_env_configs::app_env_configs::AppEnvConfigs; +use crate::core::state::data_dir::app_data_root::AppDataRoot; +use crate::core::state::provider_priority::ProviderPriorityStore; +use crate::services::sora::state::sora_credential_manager::SoraCredentialManager; +use crate::services::sora::state::sora_task_queue::SoraTaskQueue; +use crate::services::storyteller::state::storyteller_credential_manager::StorytellerCredentialManager; +use artcraft_api_defs::generate::image::edit::gemini_25_flash_edit_image::{Gemini25FlashEditImageNumImages, Gemini25FlashEditImageRequest}; +use artcraft_api_defs::generate::image::generate_gpt_image_1_text_to_image::{GenerateGptImage1TextToImageImageQuality, GenerateGptImage1TextToImageImageSize, GenerateGptImage1TextToImageNumImages, GenerateGptImage1TextToImageRequest}; +use artcraft_api_defs::generate::image::multi_function::nano_banana_pro_multi_function_image_gen::{NanoBananaProMultiFunctionImageGenAspectRatio, NanoBananaProMultiFunctionImageGenImageResolution, NanoBananaProMultiFunctionImageGenNumImages, NanoBananaProMultiFunctionImageGenRequest}; +use enums::common::generation_provider::GenerationProvider; +use enums::tauri::tasks::task_type::TaskType; +use idempotency::uuid::generate_random_uuid; +use log::{error, info}; +use openai_sora_client::recipes::maybe_upgrade_or_renew_session::maybe_upgrade_or_renew_session; +use openai_sora_client::recipes::simple_image_gen_with_session_auto_renew::{simple_image_gen_with_session_auto_renew, SimpleImageGenAutoRenewRequest}; +use openai_sora_client::requests::image_gen::common::{ImageSize, NumImages}; +use std::time::Duration; +use storyteller_client::endpoints::generate::image::edit::gemini_25_flash_edit_image::gemini_25_flash_edit_image; +use storyteller_client::endpoints::generate::image::multi_function::nano_banana_pro_multi_function_image_gen_image::nano_banana_pro_multi_function_image_gen; +use tauri::AppHandle; + +pub async fn handle_nano_banana_pro_artcraft( + request: &EnqueueTextToImageRequest, + app_env_configs: &AppEnvConfigs, + storyteller_creds_manager: &StorytellerCredentialManager, +) -> Result { + + let creds = match storyteller_creds_manager.get_credentials()? { + Some(creds) => creds, + None => { + return Err(GenerateError::MissingCredentials(MissingCredentialsReason::NeedsStorytellerCredentials)); + }, + }; + + info!("Calling Artcraft Nano Banana Pro..."); + + let uuid_idempotency_token = generate_random_uuid(); + + let num_images = match request.number_images { + None => None, + Some(1) => Some(NanoBananaProMultiFunctionImageGenNumImages::One), + Some(2) => Some(NanoBananaProMultiFunctionImageGenNumImages::Two), + Some(3) => Some(NanoBananaProMultiFunctionImageGenNumImages::Three), + Some(4) => Some(NanoBananaProMultiFunctionImageGenNumImages::Four), + Some(other) => { + return Err(GenerateError::BadInput(BadInputReason::InvalidNumberOfRequestedImages { + min: 1, + max: 4, + requested: other, + })); + }, + }; + + let aspect_ratio = request.aspect_ratio + .map(|aspect_ratio| match aspect_ratio { + TextToImageSize::Auto => None, + TextToImageSize::Square => Some(NanoBananaProMultiFunctionImageGenAspectRatio::OneByOne), + TextToImageSize::Wide => Some(NanoBananaProMultiFunctionImageGenAspectRatio::SixteenByNine), + TextToImageSize::Tall => Some(NanoBananaProMultiFunctionImageGenAspectRatio::NineBySixteen), + }) + .flatten(); + + let resolution = request.image_resolution + .map(|image_resolution| match image_resolution { + TextToImageResolution::OneK => NanoBananaProMultiFunctionImageGenImageResolution::OneK, + TextToImageResolution::TwoK => NanoBananaProMultiFunctionImageGenImageResolution::TwoK, + TextToImageResolution::FourK => NanoBananaProMultiFunctionImageGenImageResolution::FourK, + }); + + let request = NanoBananaProMultiFunctionImageGenRequest { + uuid_idempotency_token, + prompt: request.prompt.clone(), + image_media_tokens: None, + num_images, + resolution, + aspect_ratio, + }; + + let result = nano_banana_pro_multi_function_image_gen( + &app_env_configs.storyteller_host, + Some(&creds), + request, + ).await; + + let job_id = match result { + Ok(enqueued) => { + // TODO(bt,2025-07-05): Enqueue job token? + info!("Successfully enqueued Artcraft Nano Banana. Job token: {}", + enqueued.inference_job_token); + enqueued.inference_job_token + } + Err(err) => { + error!("Failed to use Artcraft Nano Banana: {:?}", err); + return Err(GenerateError::ProviderFailure(ProviderFailureReason::StorytellerError(err))); + } + }; + + Ok(TaskEnqueueSuccess { + provider: GenerationProvider::Artcraft, + model: Some(GenerationModel::NanoBananaPro), + provider_job_id: Some(job_id.to_string()), + task_type: TaskType::ImageGeneration, + }) +} diff --git a/crates/desktop/artcraft/src/core/commands/enqueue/text_to_image/nano_banana_pro/mod.rs b/crates/desktop/artcraft/src/core/commands/enqueue/text_to_image/nano_banana_pro/mod.rs new file mode 100644 index 0000000000..5df46835f7 --- /dev/null +++ b/crates/desktop/artcraft/src/core/commands/enqueue/text_to_image/nano_banana_pro/mod.rs @@ -0,0 +1,2 @@ +pub (crate) mod handle_nano_banana_pro_artcraft; +pub (super) mod handle_nano_banana_pro; diff --git a/crates/desktop/artcraft/src/core/events/generation_events/common.rs b/crates/desktop/artcraft/src/core/events/generation_events/common.rs index 1ee08c3772..eb6ec67c53 100644 --- a/crates/desktop/artcraft/src/core/events/generation_events/common.rs +++ b/crates/desktop/artcraft/src/core/events/generation_events/common.rs @@ -34,7 +34,11 @@ pub enum GenerationModel { GrokImage, #[serde(rename = "gemini_25_flash")] Gemini25Flash, - + #[serde(rename = "nano_banana")] + NanoBanana, + #[serde(rename = "nano_banana_pro")] + NanoBananaPro, + #[serde(rename = "hunyuan_3d_2_0")] Hunyuan3d2_0, #[serde(rename = "hunyuan_3d_2_1")] diff --git a/crates/desktop/artcraft/src/lib.rs b/crates/desktop/artcraft/src/lib.rs index 511c694205..ccf8ef10d0 100644 --- a/crates/desktop/artcraft/src/lib.rs +++ b/crates/desktop/artcraft/src/lib.rs @@ -6,7 +6,7 @@ use tauri::Manager; use crate::core::commands::app_preferences::get_app_preferences_command::get_app_preferences_command; use crate::core::commands::app_preferences::update_app_preference_command::update_app_preferences_command; use crate::core::commands::enqueue::image_bg_removal::enqueue_image_bg_removal_command::enqueue_image_bg_removal_command; -use crate::core::commands::enqueue::image_edit::enqueue_contextual_edit_image_command::enqueue_contextual_edit_image_command; +use crate::core::commands::enqueue::image_edit::enqueue_edit_image_command::enqueue_edit_image_command; use crate::core::commands::enqueue::image_inpaint::enqueue_image_inpaint_command::enqueue_image_inpaint_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::image_to_video::enqueue_image_to_video_command::enqueue_image_to_video_command; @@ -172,7 +172,7 @@ pub fn run() { // My first attempt at naively doing this didn't work because the macros can't find their codegen'd targets. let builder = builder.invoke_handler(tauri::generate_handler![ check_sora_session_command, - enqueue_contextual_edit_image_command, + enqueue_edit_image_command, enqueue_image_bg_removal_command, enqueue_image_inpaint_command, enqueue_image_to_3d_object_command, diff --git a/crates/desktop/artcraft/src/services/storyteller/commands/storyteller_get_credits_command.rs b/crates/desktop/artcraft/src/services/storyteller/commands/storyteller_get_credits_command.rs index 2442df7dbf..3be0e35a77 100644 --- a/crates/desktop/artcraft/src/services/storyteller/commands/storyteller_get_credits_command.rs +++ b/crates/desktop/artcraft/src/services/storyteller/commands/storyteller_get_credits_command.rs @@ -1,4 +1,4 @@ -use crate::core::commands::enqueue::image_edit::enqueue_contextual_edit_image_command::{ContextualImageEditModel, EditImageQuality, EditImageSize}; +use crate::core::commands::enqueue::image_edit::enqueue_edit_image_command::{ImageEditModel, EditImageQuality, EditImageSize}; use crate::core::commands::response::shorthand::ResponseOrErrorMessage; use crate::core::state::app_env_configs::app_env_configs::AppEnvConfigs; use crate::core::state::data_dir::app_data_root::AppDataRoot; diff --git a/crates/desktop/artcraft/src/services/storyteller/commands/storyteller_get_subscription_command.rs b/crates/desktop/artcraft/src/services/storyteller/commands/storyteller_get_subscription_command.rs index 44f4d66d75..bb61f26ced 100644 --- a/crates/desktop/artcraft/src/services/storyteller/commands/storyteller_get_subscription_command.rs +++ b/crates/desktop/artcraft/src/services/storyteller/commands/storyteller_get_subscription_command.rs @@ -1,5 +1,5 @@ use chrono::{DateTime, Utc}; -use crate::core::commands::enqueue::image_edit::enqueue_contextual_edit_image_command::{ContextualImageEditModel, EditImageQuality, EditImageSize}; +use crate::core::commands::enqueue::image_edit::enqueue_edit_image_command::{ImageEditModel, EditImageQuality, EditImageSize}; use crate::core::commands::providers::get_provider_order_command::GetProviderOrderResponse; use crate::core::commands::response::shorthand::ResponseOrErrorMessage; use crate::core::commands::response::success_response_wrapper::SerializeMarker; diff --git a/crates/desktop/artcraft/src/services/storyteller/commands/storyteller_purge_credentials_command.rs b/crates/desktop/artcraft/src/services/storyteller/commands/storyteller_purge_credentials_command.rs index 65aed23986..5a3c2028b1 100644 --- a/crates/desktop/artcraft/src/services/storyteller/commands/storyteller_purge_credentials_command.rs +++ b/crates/desktop/artcraft/src/services/storyteller/commands/storyteller_purge_credentials_command.rs @@ -1,4 +1,4 @@ -use crate::core::commands::enqueue::image_edit::enqueue_contextual_edit_image_command::EnqueueContextualEditImageSuccessResponse; +use crate::core::commands::enqueue::image_edit::enqueue_edit_image_command::EnqueueEditImageSuccessResponse; use crate::core::commands::response::shorthand::ResponseOrErrorMessage; use crate::core::commands::response::success_response_wrapper::SerializeMarker; use crate::core::state::app_preferences::app_preferences_manager::AppPreferencesManager; diff --git a/crates/desktop/artcraft/src/services/storyteller/commands/stripe_checkout/storyteller_open_credits_purchase_command.rs b/crates/desktop/artcraft/src/services/storyteller/commands/stripe_checkout/storyteller_open_credits_purchase_command.rs index 99016654da..6e34588193 100644 --- a/crates/desktop/artcraft/src/services/storyteller/commands/stripe_checkout/storyteller_open_credits_purchase_command.rs +++ b/crates/desktop/artcraft/src/services/storyteller/commands/stripe_checkout/storyteller_open_credits_purchase_command.rs @@ -1,4 +1,4 @@ -use crate::core::commands::enqueue::image_edit::enqueue_contextual_edit_image_command::{ContextualImageEditModel, EditImageQuality, EditImageSize}; +use crate::core::commands::enqueue::image_edit::enqueue_edit_image_command::{ImageEditModel, EditImageQuality, EditImageSize}; use crate::core::state::app_env_configs::app_env_configs::AppEnvConfigs; use crate::core::state::data_dir::app_data_root::AppDataRoot; use crate::services::midjourney::state::midjourney_credential_manager::MidjourneyCredentialManager; diff --git a/crates/desktop/artcraft/src/services/storyteller/commands/stripe_checkout/storyteller_open_subscription_purchase_command.rs b/crates/desktop/artcraft/src/services/storyteller/commands/stripe_checkout/storyteller_open_subscription_purchase_command.rs index a5809b4e49..3c7b39c517 100644 --- a/crates/desktop/artcraft/src/services/storyteller/commands/stripe_checkout/storyteller_open_subscription_purchase_command.rs +++ b/crates/desktop/artcraft/src/services/storyteller/commands/stripe_checkout/storyteller_open_subscription_purchase_command.rs @@ -1,4 +1,4 @@ -use crate::core::commands::enqueue::image_edit::enqueue_contextual_edit_image_command::{ContextualImageEditModel, EditImageQuality, EditImageSize}; +use crate::core::commands::enqueue::image_edit::enqueue_edit_image_command::{ImageEditModel, EditImageQuality, EditImageSize}; use crate::core::state::app_env_configs::app_env_configs::AppEnvConfigs; use crate::core::state::data_dir::app_data_root::AppDataRoot; use crate::services::midjourney::state::midjourney_credential_manager::MidjourneyCredentialManager; diff --git a/crates/desktop/artcraft/src/services/storyteller/commands/stripe_customer_portal/storyteller_open_customer_portal_cancel_plan_command.rs b/crates/desktop/artcraft/src/services/storyteller/commands/stripe_customer_portal/storyteller_open_customer_portal_cancel_plan_command.rs index 4bd814bce0..057d4dbf92 100644 --- a/crates/desktop/artcraft/src/services/storyteller/commands/stripe_customer_portal/storyteller_open_customer_portal_cancel_plan_command.rs +++ b/crates/desktop/artcraft/src/services/storyteller/commands/stripe_customer_portal/storyteller_open_customer_portal_cancel_plan_command.rs @@ -1,4 +1,4 @@ -use crate::core::commands::enqueue::image_edit::enqueue_contextual_edit_image_command::{ContextualImageEditModel, EditImageQuality, EditImageSize}; +use crate::core::commands::enqueue::image_edit::enqueue_edit_image_command::{ImageEditModel, EditImageQuality, EditImageSize}; use crate::core::state::app_env_configs::app_env_configs::AppEnvConfigs; use crate::core::state::data_dir::app_data_root::AppDataRoot; use crate::services::midjourney::state::midjourney_credential_manager::MidjourneyCredentialManager; diff --git a/crates/desktop/artcraft/src/services/storyteller/commands/stripe_customer_portal/storyteller_open_customer_portal_manage_plan_command.rs b/crates/desktop/artcraft/src/services/storyteller/commands/stripe_customer_portal/storyteller_open_customer_portal_manage_plan_command.rs index 7a5719aee1..6b217c11ae 100644 --- a/crates/desktop/artcraft/src/services/storyteller/commands/stripe_customer_portal/storyteller_open_customer_portal_manage_plan_command.rs +++ b/crates/desktop/artcraft/src/services/storyteller/commands/stripe_customer_portal/storyteller_open_customer_portal_manage_plan_command.rs @@ -1,4 +1,4 @@ -use crate::core::commands::enqueue::image_edit::enqueue_contextual_edit_image_command::{ContextualImageEditModel, EditImageQuality, EditImageSize}; +use crate::core::commands::enqueue::image_edit::enqueue_edit_image_command::{ImageEditModel, EditImageQuality, EditImageSize}; use crate::core::state::app_env_configs::app_env_configs::AppEnvConfigs; use crate::core::state::data_dir::app_data_root::AppDataRoot; use crate::services::midjourney::state::midjourney_credential_manager::MidjourneyCredentialManager; diff --git a/crates/desktop/artcraft/src/services/storyteller/commands/stripe_customer_portal/storyteller_open_customer_portal_switch_plan_command.rs b/crates/desktop/artcraft/src/services/storyteller/commands/stripe_customer_portal/storyteller_open_customer_portal_switch_plan_command.rs index ddcc700fee..d4ef00b883 100644 --- a/crates/desktop/artcraft/src/services/storyteller/commands/stripe_customer_portal/storyteller_open_customer_portal_switch_plan_command.rs +++ b/crates/desktop/artcraft/src/services/storyteller/commands/stripe_customer_portal/storyteller_open_customer_portal_switch_plan_command.rs @@ -1,4 +1,4 @@ -use crate::core::commands::enqueue::image_edit::enqueue_contextual_edit_image_command::{ContextualImageEditModel, EditImageQuality, EditImageSize}; +use crate::core::commands::enqueue::image_edit::enqueue_edit_image_command::{ImageEditModel, EditImageQuality, EditImageSize}; use crate::core::state::app_env_configs::app_env_configs::AppEnvConfigs; use crate::core::state::data_dir::app_data_root::AppDataRoot; use crate::services::midjourney::state::midjourney_credential_manager::MidjourneyCredentialManager; diff --git a/crates/desktop/artcraft/src/services/storyteller/commands/stripe_customer_portal/storyteller_open_customer_portal_update_payment_method_command.rs b/crates/desktop/artcraft/src/services/storyteller/commands/stripe_customer_portal/storyteller_open_customer_portal_update_payment_method_command.rs index 762b4ae839..d04fac37f1 100644 --- a/crates/desktop/artcraft/src/services/storyteller/commands/stripe_customer_portal/storyteller_open_customer_portal_update_payment_method_command.rs +++ b/crates/desktop/artcraft/src/services/storyteller/commands/stripe_customer_portal/storyteller_open_customer_portal_update_payment_method_command.rs @@ -1,4 +1,4 @@ -use crate::core::commands::enqueue::image_edit::enqueue_contextual_edit_image_command::{ContextualImageEditModel, EditImageQuality, EditImageSize}; +use crate::core::commands::enqueue::image_edit::enqueue_edit_image_command::{ImageEditModel, EditImageQuality, EditImageSize}; use crate::core::state::app_env_configs::app_env_configs::AppEnvConfigs; use crate::core::state::data_dir::app_data_root::AppDataRoot; use crate::services::midjourney::state::midjourney_credential_manager::MidjourneyCredentialManager; diff --git a/crates/schema/public/enums/src/tauri/tasks/task_model_type.rs b/crates/schema/public/enums/src/tauri/tasks/task_model_type.rs index 99f08a361f..b26825be94 100644 --- a/crates/schema/public/enums/src/tauri/tasks/task_model_type.rs +++ b/crates/schema/public/enums/src/tauri/tasks/task_model_type.rs @@ -29,6 +29,10 @@ pub enum TaskModelType { FluxProKontextMax, #[serde(rename = "gemini_25_flash")] Gemini25Flash, + #[serde(rename = "nano_banana")] + NanoBanana, + #[serde(rename = "nano_banana_pro")] + NanoBananaPro, #[serde(rename = "gpt_image_1")] GptImage1, #[serde(rename = "grok_image")] @@ -85,6 +89,8 @@ impl TaskModelType { Self::FluxPro11Ultra => "flux_pro_1.1_ultra", Self::FluxProKontextMax => "flux_pro_kontext_max", Self::Gemini25Flash => "gemini_25_flash", + Self::NanoBanana => "nano_banana", + Self::NanoBananaPro => "nano_banana_pro", Self::GptImage1 => "gpt_image_1", Self::GrokImage => "grok_image", Self::Recraft3 => "recraft_3", @@ -116,6 +122,8 @@ impl TaskModelType { "flux_pro_1.1_ultra" => Ok(Self::FluxPro11Ultra), "flux_pro_kontext_max" => Ok(Self::FluxProKontextMax), "gemini_25_flash" => Ok(Self::Gemini25Flash), + "nano_banana" => Ok(Self::NanoBanana), + "nano_banana_pro" => Ok(Self::NanoBananaPro), "gpt_image_1" => Ok(Self::GptImage1), "grok_image" => Ok(Self::GrokImage), "recraft_3" => Ok(Self::Recraft3), @@ -150,6 +158,8 @@ impl TaskModelType { Self::FluxPro11Ultra, Self::FluxProKontextMax, Self::Gemini25Flash, + Self::NanoBanana, + Self::NanoBananaPro, Self::GptImage1, Self::GrokImage, Self::Recraft3, @@ -191,6 +201,8 @@ mod tests { assert_serialization(TaskModelType::FluxPro11Ultra, "flux_pro_1.1_ultra"); assert_serialization(TaskModelType::FluxProKontextMax, "flux_pro_kontext_max"); assert_serialization(TaskModelType::Gemini25Flash, "gemini_25_flash"); + assert_serialization(TaskModelType::NanoBanana, "nano_banana"); + assert_serialization(TaskModelType::NanoBananaPro, "nano_banana_pro"); assert_serialization(TaskModelType::GptImage1, "gpt_image_1"); assert_serialization(TaskModelType::GrokImage, "grok_image"); assert_serialization(TaskModelType::Recraft3, "recraft_3"); @@ -221,6 +233,8 @@ mod tests { assert_eq!(TaskModelType::FluxPro11Ultra.to_str(), "flux_pro_1.1_ultra"); assert_eq!(TaskModelType::FluxProKontextMax.to_str(), "flux_pro_kontext_max"); assert_eq!(TaskModelType::Gemini25Flash.to_str(), "gemini_25_flash"); + assert_eq!(TaskModelType::NanoBanana.to_str(), "nano_banana"); + assert_eq!(TaskModelType::NanoBananaPro.to_str(), "nano_banana_pro"); assert_eq!(TaskModelType::GptImage1.to_str(), "gpt_image_1"); assert_eq!(TaskModelType::GrokImage.to_str(), "grok_image"); assert_eq!(TaskModelType::Recraft3.to_str(), "recraft_3"); @@ -251,6 +265,8 @@ mod tests { assert_eq!(TaskModelType::from_str("flux_pro_1.1_ultra").unwrap(), TaskModelType::FluxPro11Ultra); assert_eq!(TaskModelType::from_str("flux_pro_kontext_max").unwrap(), TaskModelType::FluxProKontextMax); assert_eq!(TaskModelType::from_str("gemini_25_flash").unwrap(), TaskModelType::Gemini25Flash); + assert_eq!(TaskModelType::from_str("nano_banana").unwrap(), TaskModelType::NanoBanana); + assert_eq!(TaskModelType::from_str("nano_banana_pro").unwrap(), TaskModelType::NanoBananaPro); assert_eq!(TaskModelType::from_str("gpt_image_1").unwrap(), TaskModelType::GptImage1); assert_eq!(TaskModelType::from_str("grok_image").unwrap(), TaskModelType::GrokImage); assert_eq!(TaskModelType::from_str("recraft_3").unwrap(), TaskModelType::Recraft3); @@ -284,7 +300,7 @@ mod tests { #[test] fn all_variants() { let mut variants = TaskModelType::all_variants(); - assert_eq!(variants.len(), 23); + assert_eq!(variants.len(), 25); // Image models assert_eq!(variants.pop_first(), Some(TaskModelType::Flux1Dev)); assert_eq!(variants.pop_first(), Some(TaskModelType::Flux1Schnell)); @@ -294,6 +310,8 @@ mod tests { assert_eq!(variants.pop_first(), Some(TaskModelType::FluxPro11Ultra)); assert_eq!(variants.pop_first(), Some(TaskModelType::FluxProKontextMax)); assert_eq!(variants.pop_first(), Some(TaskModelType::Gemini25Flash)); + assert_eq!(variants.pop_first(), Some(TaskModelType::NanoBanana)); + assert_eq!(variants.pop_first(), Some(TaskModelType::NanoBananaPro)); assert_eq!(variants.pop_first(), Some(TaskModelType::GptImage1)); assert_eq!(variants.pop_first(), Some(TaskModelType::GrokImage)); assert_eq!(variants.pop_first(), Some(TaskModelType::Recraft3)); diff --git a/frontend/apps/editor3d/app/src/pages/PageEdit/PageEdit.tsx b/frontend/apps/editor3d/app/src/pages/PageEdit/PageEdit.tsx index 5aab907f16..97c3a28d24 100644 --- a/frontend/apps/editor3d/app/src/pages/PageEdit/PageEdit.tsx +++ b/frontend/apps/editor3d/app/src/pages/PageEdit/PageEdit.tsx @@ -1,6 +1,6 @@ import { useState, useRef, useEffect, useCallback } from "react"; import Konva from "konva"; // just for types -import { EnqueueImageInpaint } from "@storyteller/tauri-api"; +import { EnqueueEditImage, EnqueueImageInpaint } from "@storyteller/tauri-api"; import { ContextMenuContainer } from "../PageDraw/components/ui/ContextMenu"; import { useCopyPasteHotkeys } from "../PageDraw/hooks/useCopyPasteHotkeys"; import { useDeleteHotkeys } from "../PageDraw/hooks/useDeleteHotkeys"; @@ -285,15 +285,32 @@ const PageEdit = () => { `inpaint-${Date.now()}-${Math.random().toString(36).slice(2)}`; try { - const result = await EnqueueImageInpaint({ - model: selectedImageModel, - image_media_token: editedImageToken, - mask_image_raw_bytes: arrayBuffer, - prompt: prompt, - image_count: generationCount, - frontend_caller: "image_editor", - frontend_subscriber_id: subscriberId, - }); + let result; + + if (selectedImageModel?.editingIsInpainting) { + result = await EnqueueImageInpaint({ + model: selectedImageModel, + image_media_token: editedImageToken, + mask_image_raw_bytes: arrayBuffer, + prompt: prompt, + image_count: generationCount, + frontend_caller: "image_editor", + frontend_subscriber_id: subscriberId, + }); + } else { + result = await EnqueueEditImage({ + model: selectedImageModel, + image_media_tokens: [editedImageToken], + disable_system_prompt: true, // No meaning yet + prompt: prompt, + image_count: generationCount, + frontend_caller: "image_editor", + frontend_subscriber_id: subscriberId, + //scene_image_media_token, + //aspect_ratio: aspectRatio, + }); + } + if (result.status === "success") { setPendingGenerations((prev) => [ ...prev, @@ -310,7 +327,8 @@ const PageEdit = () => { [generationCount, selectedImageModel, store.baseImageInfo?.mediaToken], ); - const isNanoBananaModel = selectedImageModel?.id === "gemini_25_flash"; + const isNanoBananaModel = selectedImageModel?.isNanoBananaModel(); + const supportsMaskedInpainting = selectedImageModel?.usesInpaintingMask ?? false; diff --git a/frontend/libs/components/promptbox/src/lib/PromptBox2D.tsx b/frontend/libs/components/promptbox/src/lib/PromptBox2D.tsx index 00b6a3b591..0bc28b6e95 100644 --- a/frontend/libs/components/promptbox/src/lib/PromptBox2D.tsx +++ b/frontend/libs/components/promptbox/src/lib/PromptBox2D.tsx @@ -28,8 +28,8 @@ import { IconProp } from "@fortawesome/fontawesome-svg-core"; import { IsDesktopApp } from "@storyteller/tauri-utils"; import { PromptsApi } from "@storyteller/api"; import { - EnqueueContextualEditImage, - EnqueueContextualEditImageSize, + EnqueueEditImage, + EnqueueEditImageSize, } from "@storyteller/tauri-api"; import { usePrompt2DStore } from "./promptStore"; import { gtagEvent } from "@storyteller/google-analytics"; @@ -235,7 +235,7 @@ export const PromptBox2D = ({ const aspectRatio = getCurrentAspectRatio(); - const generateResponse = await EnqueueContextualEditImage({ + const generateResponse = await EnqueueEditImage({ model: selectedImageModel, scene_image_media_token: snapshotMediaToken.data!, image_media_tokens: referenceImages.map((image) => image.mediaToken), @@ -354,16 +354,16 @@ export const PromptBox2D = ({ return iconElement.props.icon; }; - const getCurrentAspectRatio = (): EnqueueContextualEditImageSize => { + const getCurrentAspectRatio = (): EnqueueEditImageSize => { const selected = aspectRatioList.find((item) => item.selected); switch (selected?.label) { case "3:2": - return EnqueueContextualEditImageSize.Wide; + return EnqueueEditImageSize.Wide; case "2:3": - return EnqueueContextualEditImageSize.Tall; + return EnqueueEditImageSize.Tall; case "1:1": default: - return EnqueueContextualEditImageSize.Square; + return EnqueueEditImageSize.Square; } }; diff --git a/frontend/libs/components/promptbox/src/lib/PromptBox3D.tsx b/frontend/libs/components/promptbox/src/lib/PromptBox3D.tsx index 7a2d43071a..bbe6676f7f 100644 --- a/frontend/libs/components/promptbox/src/lib/PromptBox3D.tsx +++ b/frontend/libs/components/promptbox/src/lib/PromptBox3D.tsx @@ -38,8 +38,8 @@ import { Modal } from "@storyteller/ui-modal"; import { Signal } from "@preact/signals-react"; import { CommandSuccessStatus, - EnqueueContextualEditImage, - EnqueueContextualEditImageSize, + EnqueueEditImage, + EnqueueEditImageSize, } from "@storyteller/tauri-api"; import { usePrompt3DStore } from "./promptStore"; import { gtagEvent } from "@storyteller/google-analytics"; @@ -336,7 +336,7 @@ export const PromptBox3D = ({ const aspectRatio = getCurrentAspectRatio(); - const generateResponse = await EnqueueContextualEditImage({ + const generateResponse = await EnqueueEditImage({ model: selectedImageModel, scene_image_media_token: snapshotResult.data!, image_media_tokens: referenceImages.map((image) => image.mediaToken), @@ -400,16 +400,16 @@ export const PromptBox3D = ({ } }; - const getCurrentAspectRatio = (): EnqueueContextualEditImageSize => { + const getCurrentAspectRatio = (): EnqueueEditImageSize => { switch (cameraAspectRatio.value) { case CameraAspectRatio.HORIZONTAL_3_2: - return EnqueueContextualEditImageSize.Wide; + return EnqueueEditImageSize.Wide; case CameraAspectRatio.VERTICAL_2_3: case CameraAspectRatio.VERTICAL_9_16: - return EnqueueContextualEditImageSize.Tall; + return EnqueueEditImageSize.Tall; case CameraAspectRatio.SQUARE_1_1: default: - return EnqueueContextualEditImageSize.Square; + return EnqueueEditImageSize.Square; } }; diff --git a/frontend/libs/model-list/src/lib/classes/ImageModel.ts b/frontend/libs/model-list/src/lib/classes/ImageModel.ts index c6239b4432..3a8733fb5a 100644 --- a/frontend/libs/model-list/src/lib/classes/ImageModel.ts +++ b/frontend/libs/model-list/src/lib/classes/ImageModel.ts @@ -21,6 +21,11 @@ export class ImageModel extends Model { // For inpainting models, does it require sending a mask? readonly usesInpaintingMask: boolean; + // For editing models, is "editing" == "inpainting"? + // Examples of "true" are "flux dev juggernaut" + // Examples of "false" are "nano_banana_pro", which is just "editing". + readonly editingIsInpainting: boolean; + // Whether this model supports additional image prompts (reference images) readonly canUseImagePrompt: boolean; @@ -44,6 +49,7 @@ export class ImageModel extends Model { defaultGenerationCount: number; canEditImages?: boolean; usesInpaintingMask?: boolean; + editingIsInpainting?: boolean; canUseImagePrompt?: boolean; maxImagePromptCount?: number; canTextToImage?: boolean; @@ -66,8 +72,24 @@ export class ImageModel extends Model { this.defaultGenerationCount = args.defaultGenerationCount; this.canEditImages = args.canEditImages ?? false; this.usesInpaintingMask = args.usesInpaintingMask ?? false; + this.editingIsInpainting = args.editingIsInpainting ?? false; this.canUseImagePrompt = args.canUseImagePrompt ?? false; this.maxImagePromptCount = Math.max(0, args.maxImagePromptCount ?? 1); this.canTextToImage = args.canTextToImage === false ? false : true; // Default to true ! } + + // If the model is a "Nano Banana"-type model, we may want to enable certain features. + // For example, in the editor, we may want to use the marker. + // TODO: Rather than "isNanoBananaModel()", we should have: "enableEditorMarker" as it's + // more semantic. + isNanoBananaModel(): boolean { + switch(this.id) { + case "gemini_25_flash": + case "nano_banana": + case "nano_banana_pro": + return true; + default: + return false; + } + } } diff --git a/frontend/libs/model-list/src/lib/legacy/Models.ts b/frontend/libs/model-list/src/lib/legacy/Models.ts index 47913e7e92..e1cff5d57b 100644 --- a/frontend/libs/model-list/src/lib/legacy/Models.ts +++ b/frontend/libs/model-list/src/lib/legacy/Models.ts @@ -134,6 +134,21 @@ const ALL_MODELS: ModelConfig[] = [ }, tags: [ModelTag.InstructiveEdit, ModelTag.NonMaskedInpainting], }), + cfg({ + id: "nano_banana_pro", + category: "image", + info: { + name: "Nano Banana Pro", + tauri_id: "nano_banana_pro", + creator: mc.Google, + }, + description: "Extremely powerful", + badges: [{ label: "35 sec." }], + capabilities: { + maxGenerationCount: 4, + }, + tags: [ModelTag.InstructiveEdit, ModelTag.NonMaskedInpainting], + }), cfg({ id: "flux_pro_inpaint", category: "image", diff --git a/frontend/libs/model-list/src/lib/lists/ImageModels.ts b/frontend/libs/model-list/src/lib/lists/ImageModels.ts index 34349a7b3d..d18a976c8e 100644 --- a/frontend/libs/model-list/src/lib/lists/ImageModels.ts +++ b/frontend/libs/model-list/src/lib/lists/ImageModels.ts @@ -2,6 +2,8 @@ import { ImageModel } from "../classes/ImageModel.js"; import { ModelCreator } from "../classes/metadata/ModelCreator.js"; import { ModelTag } from "../classes/metadata/ModelTag.js"; +// TODO: Some of the model configs, such as generation counts, are authoritatively controlled in `legacy/Models.ts` + export const IMAGE_MODELS: ImageModel[] = [ new ImageModel({ id: "grok_image", @@ -111,6 +113,24 @@ export const IMAGE_MODELS: ImageModel[] = [ tags: [ModelTag.InstructiveEdit], progressBarTime: 25000, }), + new ImageModel({ + id: "nano_banana_pro", + tauriId: "nano_banana_pro", + fullName: "Nano Banana Pro", + category: "image", + creator: ModelCreator.Google, + selectorName: "Nano Banana Pro", + selectorDescription: "Extremely powerful", + selectorBadges: ["30 sec."], + maxGenerationCount: 4, + defaultGenerationCount: 1, + canEditImages: true, + canUseImagePrompt: true, + maxImagePromptCount: 4, + canTextToImage: true, + tags: [ModelTag.InstructiveEdit], + progressBarTime: 25000, + }), new ImageModel({ id: "gpt_image_1", tauriId: "gpt_image_1", @@ -120,7 +140,7 @@ export const IMAGE_MODELS: ImageModel[] = [ selectorName: "GPT Image 1 (GPT-4o)", selectorDescription: "Slow, but super smart", selectorBadges: ["60 sec."], - maxGenerationCount: 1, + maxGenerationCount: 4, defaultGenerationCount: 1, tags: [ModelTag.InstructiveEdit], canEditImages: true, @@ -150,13 +170,14 @@ export const IMAGE_MODELS: ImageModel[] = [ fullName: "Flux Pro Inpaint", category: "image", creator: ModelCreator.BlackForestLabs, - selectorName: "Flux Pro", + selectorName: "Flux Pro (Inpainting)", selectorDescription: "Fast inpainting", selectorBadges: ["30 sec."], maxGenerationCount: 1, // NB: Fal only allows one image for some reason! defaultGenerationCount: 1, // NB: Fal only allows one image for some reason! canEditImages: true, usesInpaintingMask: true, + editingIsInpainting: true, canTextToImage: false, progressBarTime: 30000, }), @@ -173,6 +194,7 @@ export const IMAGE_MODELS: ImageModel[] = [ defaultGenerationCount: 4, canEditImages: true, usesInpaintingMask: true, + editingIsInpainting: true, canTextToImage: false, progressBarTime: 10000, }), diff --git a/frontend/libs/tauri-api/src/index.ts b/frontend/libs/tauri-api/src/index.ts index 63eeb562eb..4218a3ef6b 100644 --- a/frontend/libs/tauri-api/src/index.ts +++ b/frontend/libs/tauri-api/src/index.ts @@ -2,7 +2,7 @@ export * from "./lib/artcraft/ArtcraftGetCredits.js"; export * from "./lib/artcraft/ArtcraftGetSubscription.js"; export * from "./lib/artcraft/media_files/MediaFileDelete.js"; export * from "./lib/common/CommandStatus.js"; -export * from "./lib/enqueue/EnqueueContextualEditImage.js"; +export * from "./lib/enqueue/EnqueueEditImage.js"; export * from "./lib/enqueue/EnqueueImageBgRemovalCommand.js"; export * from "./lib/enqueue/EnqueueImageInpaint.js"; export * from "./lib/enqueue/EnqueueImageTo3dObject.js"; diff --git a/frontend/libs/tauri-api/src/lib/enqueue/EnqueueContextualEditImage.ts b/frontend/libs/tauri-api/src/lib/enqueue/EnqueueEditImage.ts similarity index 62% rename from frontend/libs/tauri-api/src/lib/enqueue/EnqueueContextualEditImage.ts rename to frontend/libs/tauri-api/src/lib/enqueue/EnqueueEditImage.ts index 50c15b9fa8..f870fee7e6 100644 --- a/frontend/libs/tauri-api/src/lib/enqueue/EnqueueContextualEditImage.ts +++ b/frontend/libs/tauri-api/src/lib/enqueue/EnqueueEditImage.ts @@ -2,10 +2,10 @@ import { invoke } from "@tauri-apps/api/core"; import { CommandResult } from "../common/CommandStatus"; import { ImageModel } from "@storyteller/model-list"; -export interface EnqueueContextualEditImageRequest { +export interface EnqueueEditImageRequest { /// The model to use. - model?: ImageModel | EnqueueContextualEditImageModel; + model?: ImageModel | EnqueueEditImageModel; /// If set, this is the first image in the contextual image set. /// This gets submitted along with `image_media_tokens` and will @@ -26,26 +26,37 @@ export interface EnqueueContextualEditImageRequest { /// Number of images to generate. image_count?: number; - aspect_ratio?: EnqueueContextualEditImageSize; + aspect_ratio?: EnqueueEditImageSize; - image_quality?: EnqueueContextualEditImageQuality; + image_quality?: EnqueueEditImageQuality; + + /// Image resolution. Support: Nano Banana Pro + image_resolution?: EnqueueEditImageResolution; + + // TODO: Actual enum. + frontend_caller?: string; + + // Optional frontend state to return later. + frontend_subscriber_id?: string; } // Shape of request sent to Tauri // (We do some transformations from the public-facing request object.) -interface RawEnqueueContextualEditImageRequest { - model?: EnqueueContextualEditImageModel | string; +interface RawEnqueueEditImageRequest { + model?: EnqueueEditImageModel | string; scene_image_media_token?: string; image_media_tokens?: string[]; prompt?: string; disable_system_prompt?: boolean; image_count?: number; - aspect_ratio?: EnqueueContextualEditImageSize; - image_quality?: EnqueueContextualEditImageQuality; + aspect_ratio?: EnqueueEditImageSize; + image_quality?: EnqueueEditImageQuality; + frontend_caller?: string; + frontend_subscriber_id?: string; } -export enum EnqueueContextualEditImageErrorType { +export enum EnqueueEditImageErrorType { ModelNotSpecified = "model_not_specified", NoProviderAvailable = "no_provider_available", BadRequest = "bad_request", @@ -56,40 +67,46 @@ export enum EnqueueContextualEditImageErrorType { SoraIsHavingProblems = "sora_is_having_problems", } -export enum EnqueueContextualEditImageModel { +export enum EnqueueEditImageModel { GptImage1 = "gpt_image_1", FluxProKontextMax = "flux_pro_kontext_max", } -export enum EnqueueContextualEditImageSize { +export enum EnqueueEditImageSize { Auto = "auto", Square = "square", Wide = "wide", Tall = "tall", } -export enum EnqueueContextualEditImageQuality { +export enum EnqueueEditImageQuality { Auto = "auto", High = "high", Medium = "medium", Low = "low", } -export interface EnqueueContextualEditImageError extends CommandResult { - error_type: EnqueueContextualEditImageErrorType; +export enum EnqueueEditImageResolution { + OneK = "one_k", + TwoK = "two_k", + FourK = "four_k", +} + +export interface EnqueueEditImageError extends CommandResult { + error_type: EnqueueEditImageErrorType; error_message?: string; } -export interface EnqueueContextualEditImagePayload { +export interface EnqueueEditImagePayload { } -export interface EnqueueContextualEditImageSuccess extends CommandResult { - payload: EnqueueContextualEditImagePayload; +export interface EnqueueEditImageSuccess extends CommandResult { + payload: EnqueueEditImagePayload; } -export type EnqueueContextualEditImageResult = EnqueueContextualEditImageSuccess | EnqueueContextualEditImageError; +export type EnqueueEditImageResult = EnqueueEditImageSuccess | EnqueueEditImageError; -export const EnqueueContextualEditImage = async (request: EnqueueContextualEditImageRequest) : Promise => { +export const EnqueueEditImage = async (request: EnqueueEditImageRequest) : Promise => { let modelName = undefined; if (!!request.model) { @@ -105,7 +122,7 @@ export const EnqueueContextualEditImage = async (request: EnqueueContextualEditI throw new Error("No model specified in request: " + JSON.stringify(request)); } - let mutableRequest : RawEnqueueContextualEditImageRequest = { + let mutableRequest : RawEnqueueEditImageRequest = { model: modelName, prompt: request.prompt, }; @@ -134,9 +151,17 @@ export const EnqueueContextualEditImage = async (request: EnqueueContextualEditI mutableRequest.disable_system_prompt = request.disable_system_prompt; } - const result = await invoke("enqueue_contextual_edit_image_command", { + if (!!request.frontend_caller) { + mutableRequest.frontend_caller = request.frontend_caller; + } + + if (!!request.frontend_subscriber_id) { + mutableRequest.frontend_subscriber_id = request.frontend_subscriber_id; + } + + const result = await invoke("enqueue_edit_image_command", { request: mutableRequest, }); - return (result as EnqueueContextualEditImageResult); + return (result as EnqueueEditImageResult); } diff --git a/script/artcraft/unix_rust_dev.sh b/script/artcraft/unix_rust_dev.sh index c2f5457c24..85710fca1a 100755 --- a/script/artcraft/unix_rust_dev.sh +++ b/script/artcraft/unix_rust_dev.sh @@ -45,6 +45,11 @@ sqlx_offline() { #sqlx_migrate sqlx_offline +# Issue with Linux performance +# https://github.com/tauri-apps/tauri/issues/13498 +export WEBKIT_DISABLE_DMABUF_RENDERER=1 +export WEBKIT_DISABLE_COMPOSITING_MODE=1 + echo "Build and run..." RUSTFLAGS="-Awarnings" cargo tauri dev \ --no-dev-server \