From bf5a4feae4e67df393db7b62f6081fea04059a65 Mon Sep 17 00:00:00 2001 From: Brandon Thomas Date: Thu, 28 Aug 2025 21:17:46 -0400 Subject: [PATCH] placeholder for gemini --- .../enqueue_text_to_image_command.rs | 18 +++- .../gemini_25_flash/handle_gemini_25_flash.rs | 66 +++++++++++++ .../handle_gemini_25_flash_artcraft.rs | 98 +++++++++++++++++++ .../text_to_image/gemini_25_flash/mod.rs | 2 + .../generic/handle_image_artcraft.rs | 1 + .../text_to_image/generic/handle_image_fal.rs | 3 +- .../commands/enqueue/text_to_image/mod.rs | 1 + 7 files changed, 187 insertions(+), 2 deletions(-) create mode 100644 crates/desktop/artcraft/src/core/commands/enqueue/text_to_image/gemini_25_flash/handle_gemini_25_flash.rs create mode 100644 crates/desktop/artcraft/src/core/commands/enqueue/text_to_image/gemini_25_flash/handle_gemini_25_flash_artcraft.rs create mode 100644 crates/desktop/artcraft/src/core/commands/enqueue/text_to_image/gemini_25_flash/mod.rs 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 9f18513463..d081b1ff56 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,4 +1,5 @@ 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; @@ -32,7 +33,7 @@ use serde::{Deserialize, Serialize}; use sqlite_tasks::queries::create_task::{create_task, CreateTaskArgs}; use tauri::{AppHandle, State}; -/// This is used in the Tauri command bridge. +/// This is used in the Tauri command bridge. /// Don't change the serializations without coordinating with the frontend. #[derive(Deserialize, Debug, Copy, Clone)] #[serde(rename_all = "snake_case")] @@ -50,6 +51,9 @@ pub enum ImageModel { #[serde(rename = "recraft_3")] Recraft3, + #[serde(rename = "gemini_25_flash")] + Gemini25Flash, + // Generic Midjourney model, version unknown. #[serde(rename = "midjourney")] Midjourney, @@ -277,6 +281,18 @@ pub async fn dispatch_request( None => { return Err(InternalImageError::NoModelSpecified); } + Some(ImageModel::Gemini25Flash) => { + return handle_gemini_25_flash( + request, + app, + app_data_root, + app_env_configs, + provider_priority_store, + storyteller_creds_manager, + sora_creds_manager, + sora_task_queue, + ).await; + } Some(ImageModel::GptImage1) => { return handle_gpt_image_1( request, 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/gemini_25_flash/handle_gemini_25_flash.rs new file mode 100644 index 0000000000..8f64d0ce72 --- /dev/null +++ b/crates/desktop/artcraft/src/core/commands/enqueue/text_to_image/gemini_25_flash/handle_gemini_25_flash.rs @@ -0,0 +1,66 @@ +use crate::core::commands::enqueue::image_edit::errors::InternalContextualEditImageError; +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; +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::internal_image_error::InternalImageError; +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 fal_client::requests::queue::image_gen::enqueue_flux_pro_11_ultra_text_to_image::{enqueue_flux_pro_11_ultra_text_to_image, FluxPro11UltraTextToImageArgs}; +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 tauri::AppHandle; + +pub async fn handle_gemini_25_flash( + 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 gemini 2.5 flash via Artcraft..."); + return handle_gemini_25_flash_artcraft( + request, + app_env_configs, + storyteller_creds_manager + ).await; + } + } + } + + Err(InternalImageError::NoProviderAvailable) +} 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/gemini_25_flash/handle_gemini_25_flash_artcraft.rs new file mode 100644 index 0000000000..4018891f34 --- /dev/null +++ b/crates/desktop/artcraft/src/core/commands/enqueue/text_to_image/gemini_25_flash/handle_gemini_25_flash_artcraft.rs @@ -0,0 +1,98 @@ +use crate::core::commands::enqueue::image_edit::enqueue_contextual_edit_image_command::{EditImageQuality, EditImageSize}; +use crate::core::commands::enqueue::image_edit::errors::InternalContextualEditImageError; +use crate::core::commands::enqueue::image_inpaint::errors::InternalImageInpaintError; +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::internal_image_error::InternalImageError; +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 enums::common::generation_provider::GenerationProvider; +use enums::tauri::tasks::task_type::TaskType; +use fal_client::requests::queue::image_gen::enqueue_flux_pro_11_ultra_text_to_image::{enqueue_flux_pro_11_ultra_text_to_image, FluxPro11UltraTextToImageArgs}; +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::generate::image::edit::gemini_25_flash_edit_image::gemini_25_flash_edit_image; +use storyteller_client::generate::image::edit::gpt_image_1_edit_image::gpt_image_1_edit_image; +use storyteller_client::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( + 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(InternalImageError::NeedsStorytellerCredentials); + }, + }; + + info!("Calling Artcraft gemini 2.5 flash ..."); + + let uuid_idempotency_token = generate_random_uuid(); + + // let image_media_token = match &request.image_media_token { + // Some(token) => token.clone(), + // None => { + // return Err(InternalImageInpaintError::NoSourceImageSpecified); + // }, + // }; + + let num_images = match request.number_images{ + None => None, + Some(1) => Some(Gemini25FlashEditImageNumImages::One), + Some(2) => Some(Gemini25FlashEditImageNumImages::Two), + Some(3) => Some(Gemini25FlashEditImageNumImages::Three), + Some(4) => Some(Gemini25FlashEditImageNumImages::Four), + _ => Some(Gemini25FlashEditImageNumImages::One), // Default to one image if invalid number + }; + + let request = Gemini25FlashEditImageRequest { + uuid_idempotency_token, + prompt: request.prompt.clone(), + image_media_tokens: Some(vec![]), // TODO: Needs image media tokens. + num_images, + image_quality: None, + }; + + let result = gemini_25_flash_edit_image( + &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 gemini 2.5 flash. Job token: {}", + enqueued.inference_job_token); + enqueued.inference_job_token + } + Err(err) => { + error!("Failed to use Artcraft gemini 2.5 flash: {:?}", err); + return Err(InternalImageError::StorytellerError(err)); + } + }; + + Ok(TaskEnqueueSuccess { + provider: GenerationProvider::Artcraft, + model: Some(GenerationModel::GptImage1), + 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/gemini_25_flash/mod.rs b/crates/desktop/artcraft/src/core/commands/enqueue/text_to_image/gemini_25_flash/mod.rs new file mode 100644 index 0000000000..0a64fffd65 --- /dev/null +++ b/crates/desktop/artcraft/src/core/commands/enqueue/text_to_image/gemini_25_flash/mod.rs @@ -0,0 +1,2 @@ +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 80de227e3b..e86482a615 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,7 @@ pub async fn handle_image_artcraft( return Err(InternalImageError::NoModelSpecified); } Some( + ImageModel::Gemini25Flash | ImageModel::GptImage1 | ImageModel::Midjourney ) => { diff --git a/crates/desktop/artcraft/src/core/commands/enqueue/text_to_image/generic/handle_image_fal.rs b/crates/desktop/artcraft/src/core/commands/enqueue/text_to_image/generic/handle_image_fal.rs index 6bcbc5d1e0..2c7ba26c03 100644 --- a/crates/desktop/artcraft/src/core/commands/enqueue/text_to_image/generic/handle_image_fal.rs +++ b/crates/desktop/artcraft/src/core/commands/enqueue/text_to_image/generic/handle_image_fal.rs @@ -57,7 +57,8 @@ pub async fn handle_image_fal( return Err(InternalImageError::AnyhowError(anyhow!("wrong logic: another branch should handle this: {:?}", request.model))); } Some( - ImageModel::Flux1Dev | + ImageModel::Gemini25Flash | + ImageModel::Flux1Dev | ImageModel::Flux1Schnell | ImageModel::FluxPro11 ) => { 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 db3e2e58f8..839f6ca98b 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,5 +1,6 @@ pub mod enqueue_text_to_image_command; pub mod generic; pub mod internal_image_error; +mod gemini_25_flash; mod gpt_image_1; mod midjourney; \ No newline at end of file