diff --git a/crates/desktop/artcraft/src/core/commands/generate/generate_image/artcraft/handle_artcraft_via_omni_endpoint.rs b/crates/desktop/artcraft/src/core/commands/generate/generate_image/artcraft/handle_artcraft_via_omni_endpoint.rs deleted file mode 100644 index e32264a07c..0000000000 --- a/crates/desktop/artcraft/src/core/commands/generate/generate_image/artcraft/handle_artcraft_via_omni_endpoint.rs +++ /dev/null @@ -1,129 +0,0 @@ -use artcraft_api_defs::omni_gen::cost_and_generate_requests::omni_gen_image_cost_and_generate_request::OmniGenImageCostAndGenerateRequest; -use artcraft_client::endpoints::omni_gen::generate::image::omni_gen_image::omni_gen_image_generate; -use enums::common::generation::common_image_model::CommonImageModel; -use enums::common::generation_provider::GenerationProvider; -use enums::tauri::tasks::task_type::TaskType; -use log::{error, info}; - -use crate::core::commands::enqueue::generate_error::{GenerateError, MissingCredentialsReason}; -use crate::core::commands::enqueue::task_enqueue_success::TaskEnqueueSuccess; -use crate::core::commands::generate::generate_image::tauri_generate_image_request::TauriGenerateImageRequest; -use crate::core::commands::generate::generate_image::tauri_image_model::TauriImageModel; -use crate::core::events::generation_events::common::GenerationModel; -use crate::core::state::app_env_configs::app_env_configs::AppEnvConfigs; -use crate::services::storyteller::state::storyteller_credential_manager::StorytellerCredentialManager; - -pub async fn handle_artcraft_via_omni_endpoint( - request: &TauriGenerateImageRequest, - app_env_configs: &AppEnvConfigs, - storyteller_creds_manager: &StorytellerCredentialManager, -) -> Result { - let model = request.model.ok_or(GenerateError::no_model_specified())?; - - let omni_model = map_to_common_image_model(model) - .ok_or(GenerateError::NotYetImplemented( - format!("Model {:?} is not supported via the omni endpoint", model), - ))?; - - let generation_model = map_to_generation_model(model); - - let creds = match storyteller_creds_manager.get_credentials()? { - Some(creds) => creds, - None => return Err(GenerateError::MissingCredentials(MissingCredentialsReason::NeedsStorytellerCredentials)), - }; - - // Build the omni gen request. - let mut image_media_tokens = request.image_media_tokens.clone().unwrap_or_default(); - - // Prepend canvas image if present. - if let Some(canvas_token) = &request.canvas_image_media_token { - image_media_tokens.insert(0, canvas_token.clone()); - } - - let image_media_tokens = if image_media_tokens.is_empty() { None } else { Some(image_media_tokens) }; - - let omni_request = OmniGenImageCostAndGenerateRequest { - idempotency_token: None, - model: Some(omni_model), - prompt: request.prompt.clone(), - image_media_tokens, - resolution: None, // TODO: Convert from request.resolution - aspect_ratio: None, // TODO: Convert from request.aspect_ratio - quality: request.quality, - image_batch_count: request.batch_size.map(|n| n as u16), - adjust_horizontal_angle: request.adjust_horizontal_angle, - adjust_vertical_angle: request.adjust_vertical_angle, - adjust_zoom: request.adjust_zoom, - }; - - info!("Sending image generation via omni endpoint: model={:?}", omni_model); - - let response = omni_gen_image_generate( - &app_env_configs.storyteller_host, - Some(&creds), - omni_request, - ).await.map_err(|err| { - error!("Omni image generation failed: {:?}", err); - GenerateError::from(err) - })?; - - info!("Omni image generation succeeded: job_token={}", response.inference_job_token.as_str()); - - Ok(TaskEnqueueSuccess { - task_type: TaskType::ImageGeneration, - model: Some(generation_model), - provider: GenerationProvider::Artcraft, - provider_job_id: Some(response.inference_job_token.to_string()), - }) -} - -// ── Model mapping helpers ── - -fn map_to_common_image_model(model: TauriImageModel) -> Option { - match model { - TauriImageModel::Flux1Dev => Some(CommonImageModel::Flux1Dev), - TauriImageModel::Flux1Schnell => Some(CommonImageModel::Flux1Schnell), - TauriImageModel::FluxPro11 => Some(CommonImageModel::FluxPro11), - TauriImageModel::FluxPro11Ultra => Some(CommonImageModel::FluxPro11Ultra), - TauriImageModel::Gemini25Flash => Some(CommonImageModel::NanoBanana), // NB: Alias for Nano Banana - TauriImageModel::GptImage1 => Some(CommonImageModel::GptImage1), - TauriImageModel::GptImage1p5 => Some(CommonImageModel::GptImage1p5), - TauriImageModel::GptImage2 => Some(CommonImageModel::GptImage2), - TauriImageModel::NanoBanana => Some(CommonImageModel::NanoBanana), - TauriImageModel::NanoBanana2 => Some(CommonImageModel::NanoBanana2), - TauriImageModel::NanoBananaPro => Some(CommonImageModel::NanoBananaPro), - TauriImageModel::Seedream4 => Some(CommonImageModel::Seedream4), - TauriImageModel::Seedream4p5 => Some(CommonImageModel::Seedream4p5), - TauriImageModel::Seedream5Lite => Some(CommonImageModel::Seedream5Lite), - // Models not in CommonImageModel (Grok, Recraft, Midjourney, edit-only, inpaint-only) - _ => None, - } -} - -fn map_to_generation_model(model: TauriImageModel) -> GenerationModel { - match model { - TauriImageModel::Flux1Dev => GenerationModel::Flux1Dev, - TauriImageModel::Flux1Schnell => GenerationModel::Flux1Schnell, - TauriImageModel::Flux2LoraAngles => GenerationModel::Flux2LoraAngles, - TauriImageModel::FluxDevJuggernaut => GenerationModel::FluxDevJuggernaut, - TauriImageModel::FluxPro1 => GenerationModel::FluxPro1, - TauriImageModel::FluxPro11 => GenerationModel::FluxPro11, - TauriImageModel::FluxPro11Ultra => GenerationModel::FluxPro11Ultra, - TauriImageModel::FluxProKontextMax => GenerationModel::FluxProKontextMax, - TauriImageModel::Gemini25Flash => GenerationModel::NanoBanana, // NB: Alias for Nano Banana - TauriImageModel::GptImage1 => GenerationModel::GptImage1, - TauriImageModel::GptImage1p5 => GenerationModel::GptImage1p5, - TauriImageModel::GptImage2 => GenerationModel::GptImage2, - TauriImageModel::GrokImage => GenerationModel::GrokImage, - TauriImageModel::NanoBanana => GenerationModel::NanoBanana, - TauriImageModel::NanoBanana2 => GenerationModel::NanoBanana2, - TauriImageModel::NanoBananaPro => GenerationModel::NanoBananaPro, - TauriImageModel::QwenEdit2511Angles => GenerationModel::QwenEdit2511Angles, - TauriImageModel::Seedream4 => GenerationModel::Seedream4, - TauriImageModel::Seedream4p5 => GenerationModel::Seedream4p5, - TauriImageModel::Seedream5Lite => GenerationModel::Seedream5Lite, - // TODO: - TauriImageModel::Midjourney => GenerationModel::Flux1Dev, // Fallback - TauriImageModel::Recraft3 => GenerationModel::Flux1Dev, // Fallback - } -} diff --git a/crates/desktop/artcraft/src/core/commands/generate/generate_image/artcraft/mod.rs b/crates/desktop/artcraft/src/core/commands/generate/generate_image/artcraft/mod.rs deleted file mode 100644 index 4738550231..0000000000 --- a/crates/desktop/artcraft/src/core/commands/generate/generate_image/artcraft/mod.rs +++ /dev/null @@ -1 +0,0 @@ -pub(super) mod handle_artcraft_via_omni_endpoint; diff --git a/crates/desktop/artcraft/src/core/commands/generate/generate_image/grok/handle_grok.rs b/crates/desktop/artcraft/src/core/commands/generate/generate_image/grok/handle_grok.rs deleted file mode 100644 index 8318f5f6e6..0000000000 --- a/crates/desktop/artcraft/src/core/commands/generate/generate_image/grok/handle_grok.rs +++ /dev/null @@ -1,92 +0,0 @@ -use crate::core::api_adapters::aspect_ratio::convert::aspect_ratio_to_grok_image::aspect_ratio_to_grok_image; -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::events::generation_events::common::GenerationModel; -use crate::core::state::app_env_configs::app_env_configs::AppEnvConfigs; -use crate::services::grok::state::grok_credential_manager::GrokCredentialManager; -use crate::services::grok::state::grok_image_prompt_queue::{GrokImagePromptQueue, PromptItem}; -use enums::common::generation_provider::GenerationProvider; -use enums::tauri::tasks::task_type::TaskType; -use grok_client::requests::image_websocket::messages::websocket_client_message::ClientMessageAspectRatio; -use uuid_utils::uuid::generate_random_uuid; -use tauri::AppHandle; - -pub async fn handle_grok( - app: &AppHandle, - request: &EnqueueTextToImageRequest, - app_env_configs: &AppEnvConfigs, - creds_manager: &GrokCredentialManager, - grok_image_prompt_queue: &GrokImagePromptQueue, -) -> Result { - - - //// TODO: We can request population of the user info if absent or expired. - //let websocket = get_websocket( - // app, - // creds_manager, - // websocket_manager, - //).await?; - // - //let _result = prompt_websocket_image(PromptWebsocketImageArgs { - // websocket_wrapped: &websocket, - // prompt: request.prompt.as_deref().unwrap_or_else(||""), - //}).await?; - // - //let request_id = listen_for_websocket_request_id(ListenForWebsocketRequestIdArgs { - // websocket: &websocket, - // timeout: Duration::from_millis(10_000), - //}).await?; - // - //let request_id = match request_id.request_id { - // Some(request_id) => request_id.to_string(), - // None => { - // warn!("No request id from Grok image generation (websocket)"); - // return Err(GenerateError::ProviderFailure(ProviderFailureReason::GrokJobEnqueueFailed)); - // } - //}; - // - //info!("Successfully enqueued MidJourney. Job token: {}", request_id); - - let job_id = generate_random_uuid(); - - let prompt = request.prompt - .as_deref() - .map(|prompt| prompt.trim().to_string()) - .unwrap_or_else(|| "".to_string()); - - let aspect_ratio = get_aspect_ratio(request); - - grok_image_prompt_queue.enqueue(PromptItem { - task_id: job_id.clone(), - prompt, - aspect_ratio: aspect_ratio.unwrap_or(ClientMessageAspectRatio::Square), - })?; - - Ok(TaskEnqueueSuccess { - provider: GenerationProvider::Grok, - model: Some(GenerationModel::GrokImage), - provider_job_id: Some(job_id), - task_type: TaskType::ImageGeneration, - }) -} - -fn get_aspect_ratio(request: &EnqueueTextToImageRequest) -> Option { - if let Some(common_aspect_ratio) = request.common_aspect_ratio { - // Handle modern aspect ratio - let aspect = aspect_ratio_to_grok_image(common_aspect_ratio); - return Some(aspect); - } - - if let Some(aspect_ratio) = request.aspect_ratio { - // Handle deprecated aspect ratio - return match aspect_ratio { - TextToImageSize::Auto => Some(ClientMessageAspectRatio::Square), - TextToImageSize::Square => Some(ClientMessageAspectRatio::Square), - TextToImageSize::Wide => Some(ClientMessageAspectRatio::WideThreeByTwo), - TextToImageSize::Tall => Some(ClientMessageAspectRatio::TallTwoByThree), - } - } - - None -} diff --git a/crates/desktop/artcraft/src/core/commands/generate/generate_image/grok/mod.rs b/crates/desktop/artcraft/src/core/commands/generate/generate_image/grok/mod.rs deleted file mode 100644 index 181e756f1a..0000000000 --- a/crates/desktop/artcraft/src/core/commands/generate/generate_image/grok/mod.rs +++ /dev/null @@ -1 +0,0 @@ -pub (super) mod handle_grok; \ No newline at end of file diff --git a/crates/desktop/artcraft/src/core/commands/generate/generate_image/midjourney/handle_midjourney.rs b/crates/desktop/artcraft/src/core/commands/generate/generate_image/midjourney/handle_midjourney.rs deleted file mode 100644 index 848ebe8105..0000000000 --- a/crates/desktop/artcraft/src/core/commands/generate/generate_image/midjourney/handle_midjourney.rs +++ /dev/null @@ -1,140 +0,0 @@ -use crate::core::commands::enqueue::generate_error::{GenerateError, MissingCredentialsReason, ProviderFailureReason}; -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; -use crate::core::events::functional_events::canvas_background_removal_complete_event::CanvasBackgroundRemovalCompleteEvent; -use crate::core::events::functional_events::show_provider_login_modal_event::ShowProviderLoginModalEvent; -use crate::core::events::generation_events::common::{GenerationAction, GenerationModel, GenerationServiceProvider}; -use crate::core::events::generation_events::generation_enqueue_failure_event::GenerationEnqueueFailureEvent; -use crate::core::events::warning_events::flash_user_input_error_event::FlashUserInputErrorEvent; -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::midjourney::state::midjourney_credential_manager::MidjourneyCredentialManager; -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 uuid_utils::uuid::generate_random_uuid; -use log::{error, info}; -use midjourney_client::client::midjourney_hostname::MidjourneyHostname; -use midjourney_client::endpoints::submit_job::{submit_job, SubmitJobRequest}; -use midjourney_client::error::midjourney_api_error::MidjourneyApiError; -use midjourney_client::recipes::channel_id::ChannelId; -use midjourney_client::recipes::text_to_image::{text_to_image, TextToImageError, TextToImageRequest}; -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; -use tokens::tokens::media_files::MediaFileToken; - -pub async fn handle_midjourney( - app: &AppHandle, - request: &EnqueueTextToImageRequest, - app_env_configs: &AppEnvConfigs, - mj_creds_manager: &MidjourneyCredentialManager, -) -> Result { - - let creds = match mj_creds_manager.maybe_copy_cookie_store() { - Ok(Some(creds)) => creds, - Ok(None) => { - error!("Midjourney credentials not found."); - ShowProviderLoginModalEvent::send_for_provider(GenerationProvider::Midjourney, &app); - return Err(GenerateError::needs_midjourney_credentials()); - } - Err(err) => { - error!("Error reading Midjourney credentials: {:?}", err); - ShowProviderLoginModalEvent::send_for_provider(GenerationProvider::Midjourney, &app); - return Err(GenerateError::needs_midjourney_credentials()); - }, - }; - - // TODO: We can request population of the user info if absent or expired. - - let user_info = match mj_creds_manager.maybe_copy_user_info() { - Ok(Some(user_info)) => user_info, - Ok(None) => { - return Err(GenerateError::MissingCredentials(MissingCredentialsReason::NeedsMidjourneyUserInfo)); - } - Err(err) => { - error!("Error reading Midjourney user info: {:?}", err); - return Err(GenerateError::MissingCredentials(MissingCredentialsReason::NeedsMidjourneyUserInfo)); - }, - }; - - let channel_id = match user_info.user_id { - Some(user_id) => ChannelId::UserId(user_id), - None => { - error!("Midjourney user info does not contain a user ID."); - return Err(GenerateError::MissingCredentials(MissingCredentialsReason::NeedsMidjourneyUserId)); - } - }; - - info!("Calling midjourney ..."); - - let cookie_header = creds.to_cookie_string(); - - let prompt = request.prompt - .as_deref() - .unwrap_or(""); - - let result = text_to_image(TextToImageRequest { - prompt, - channel_id: &channel_id, - hostname: MidjourneyHostname::Standard, - cookie_header, - }).await; - - let result = match result { - Ok(result) => result, - Err(err) => { - error!("Failed to use MidJourney: {:?}", err); - return Err(GenerateError::from(err)); - } - }; - - let job_id = match result.maybe_job_id { - Some(job_id) => job_id, - None => { - error!("Failed to enqueue MidJourney: No job ID returned."); - return handle_midjourney_errors(app, result.maybe_errors); - } - }; - - info!("Successfully enqueued MidJourney. Job token: {}", job_id); - - Ok(TaskEnqueueSuccess { - provider: GenerationProvider::Midjourney, - model: Some(GenerationModel::Midjourney), - provider_job_id: Some(job_id), - task_type: TaskType::ImageGeneration, - }) -} - -fn handle_midjourney_errors( - app: &AppHandle, - maybe_errors: Option> -) -> Result { - if let Some(errors) = maybe_errors { - if !errors.is_empty() { - let messages: Vec = errors.iter() - .map(|e| format!("{:?}", e)) - .collect(); - - let combined_message = messages.join("; "); - - let event = FlashUserInputErrorEvent { - message: format!("Midjourney Error: {}", combined_message), - }; - - if let Err(err) = event.send(&app) { - error!("Failed to send FlashUserInputErrorEvent: {:?}", err); // Fail open - } - } - } - - Err(GenerateError::ProviderFailure(ProviderFailureReason::MidjourneyJobEnqueueFailed)) -} diff --git a/crates/desktop/artcraft/src/core/commands/generate/generate_image/midjourney/mod.rs b/crates/desktop/artcraft/src/core/commands/generate/generate_image/midjourney/mod.rs deleted file mode 100644 index 18048875b6..0000000000 --- a/crates/desktop/artcraft/src/core/commands/generate/generate_image/midjourney/mod.rs +++ /dev/null @@ -1 +0,0 @@ -pub (super) mod handle_midjourney; \ No newline at end of file diff --git a/crates/desktop/artcraft/src/core/commands/generate/generate_image/mod.rs b/crates/desktop/artcraft/src/core/commands/generate/generate_image/mod.rs index 09127f7e9c..568b848fe2 100644 --- a/crates/desktop/artcraft/src/core/commands/generate/generate_image/mod.rs +++ b/crates/desktop/artcraft/src/core/commands/generate/generate_image/mod.rs @@ -1,4 +1,3 @@ -mod artcraft; pub mod generate_image_command; pub mod providers; pub mod tauri_generate_image_request; diff --git a/crates/desktop/artcraft/src/core/commands/generate/generate_image/providers/artcraft/handle_artcraft_via_omni_endpoint.rs b/crates/desktop/artcraft/src/core/commands/generate/generate_image/providers/artcraft/handle_artcraft_via_omni_endpoint.rs index 1071bbf99d..abcb05a5dc 100644 --- a/crates/desktop/artcraft/src/core/commands/generate/generate_image/providers/artcraft/handle_artcraft_via_omni_endpoint.rs +++ b/crates/desktop/artcraft/src/core/commands/generate/generate_image/providers/artcraft/handle_artcraft_via_omni_endpoint.rs @@ -3,47 +3,37 @@ use artcraft_client::endpoints::omni_gen::generate::image::omni_gen_image::omni_ use enums::common::generation_provider::GenerationProvider; use enums::tauri::tasks::task_type::TaskType; use log::{error, info}; +use tokens::tokens::media_files::MediaFileToken; use uuid_utils::uuid::generate_random_uuid; -use crate::core::commands::enqueue::generate_error::{GenerateError, MissingCredentialsReason}; +use artcraft_client::credentials::storyteller_credential_set::StorytellerCredentialSet; +use crate::core::commands::enqueue::generate_error::GenerateError; use crate::core::commands::enqueue::task_enqueue_success::TaskEnqueueSuccess; -use crate::core::commands::generate::generate_image::providers::artcraft::model_mapping::{ - map_to_generation_model, map_to_omni_aspect_ratio, map_to_omni_image_model, map_to_omni_resolution, -}; +use crate::core::api_adapters::models::image::tauri_image_model_to_enums_model::tauri_image_model_to_enums_model; +use crate::core::api_adapters::models::image::tauri_image_model_to_generation_model::tauri_image_model_to_generation_model; use crate::core::commands::generate::generate_image::tauri_generate_image_request::TauriGenerateImageRequest; +use crate::core::commands::generate::generate_image::utils::parse_semantic_media_files::SemanticMediaFiles; use crate::core::state::app_env_configs::app_env_configs::AppEnvConfigs; -use crate::services::storyteller::state::storyteller_credential_manager::StorytellerCredentialManager; pub async fn handle_artcraft_via_omni_endpoint( request: &TauriGenerateImageRequest, + semantic_media_files: &SemanticMediaFiles, + creds: &StorytellerCredentialSet, app_env_configs: &AppEnvConfigs, - storyteller_creds_manager: &StorytellerCredentialManager, ) -> Result { let tauri_model = request.model.ok_or(GenerateError::no_model_specified())?; - let omni_model = map_to_omni_image_model(tauri_model) + let omni_api_model = tauri_image_model_to_enums_model(tauri_model) .ok_or(GenerateError::NotYetImplemented( format!("Model {:?} is not supported via the omni endpoint", tauri_model), ))?; - let generation_model = map_to_generation_model(tauri_model); - - let creds = match storyteller_creds_manager.get_credentials()? { - Some(creds) => creds, - None => return Err(GenerateError::MissingCredentials(MissingCredentialsReason::NeedsStorytellerCredentials)), - }; - let uuid_idempotency_token = generate_random_uuid(); - - // Build image_media_tokens: prepend canvas image if present. - let mut image_media_tokens = request.image_media_tokens.clone().unwrap_or_default(); - if let Some(canvas_token) = &request.canvas_image_media_token { - image_media_tokens.insert(0, canvas_token.clone()); - } - let image_media_tokens = if image_media_tokens.is_empty() { None } else { Some(image_media_tokens) }; + + let image_media_tokens = get_image_media_tokens(request, semantic_media_files); let omni_request = OmniGenImageCostAndGenerateRequest { idempotency_token: Some(uuid_idempotency_token), - model: Some(omni_model), + model: Some(omni_api_model), prompt: request.prompt.clone(), image_media_tokens, resolution: request.resolution, @@ -55,11 +45,11 @@ pub async fn handle_artcraft_via_omni_endpoint( adjust_zoom: request.adjust_zoom, }; - info!("Sending image generation via omni endpoint: model={:?}", omni_model); + info!("Sending image generation via omni endpoint: model={:?}", omni_api_model); let response = omni_gen_image_generate( &app_env_configs.storyteller_host, - Some(&creds), + Some(creds), omni_request, ).await.map_err(|err| { error!("Omni image generation failed: {:?}", err); @@ -68,6 +58,8 @@ pub async fn handle_artcraft_via_omni_endpoint( info!("Omni image generation succeeded: job_token={}", response.inference_job_token.as_str()); + let generation_model = tauri_image_model_to_generation_model(tauri_model); + Ok(TaskEnqueueSuccess { task_type: TaskType::ImageGeneration, model: Some(generation_model), @@ -75,3 +67,32 @@ pub async fn handle_artcraft_via_omni_endpoint( provider_job_id: Some(response.inference_job_token.to_string()), }) } + +fn get_image_media_tokens( + request: &TauriGenerateImageRequest, + semantic_media_files: &SemanticMediaFiles, +) -> Option> { + let num_images = request.image_media_tokens.as_deref() + .map(|t| t.len()) + .unwrap_or(0); + + let mut image_media_tokens = Vec::with_capacity(num_images + 2); + + if let Some(canvas_token) = &semantic_media_files.canvas_image_media_token { + image_media_tokens.push(canvas_token.clone()); + } + + if let Some(scene_token) = &semantic_media_files.scene_image_media_token { + image_media_tokens.push(scene_token.clone()); + } + + if let Some(media_tokens) = &request.image_media_tokens { + image_media_tokens.extend(media_tokens.clone()); + } + + if image_media_tokens.is_empty() { + None + } else { + Some(image_media_tokens) + } +} diff --git a/crates/desktop/artcraft/src/core/commands/generate/generate_image/providers/artcraft/mod.rs b/crates/desktop/artcraft/src/core/commands/generate/generate_image/providers/artcraft/mod.rs index 657bbe3d3f..af34f55def 100644 --- a/crates/desktop/artcraft/src/core/commands/generate/generate_image/providers/artcraft/mod.rs +++ b/crates/desktop/artcraft/src/core/commands/generate/generate_image/providers/artcraft/mod.rs @@ -1,6 +1,6 @@ mod handle_artcraft; mod handle_artcraft_via_legacy; mod handle_artcraft_via_omni_endpoint; -mod model_mapping; +mod legacy; pub use handle_artcraft::handle_artcraft; diff --git a/crates/desktop/artcraft/src/core/commands/generate/generate_image/sora/handle_gpt_image_1_sora_text_to_image.rs b/crates/desktop/artcraft/src/core/commands/generate/generate_image/sora/handle_gpt_image_1_sora_text_to_image.rs deleted file mode 100644 index 82a1abedbf..0000000000 --- a/crates/desktop/artcraft/src/core/commands/generate/generate_image/sora/handle_gpt_image_1_sora_text_to_image.rs +++ /dev/null @@ -1,135 +0,0 @@ -use crate::core::api_adapters::aspect_ratio::convert::aspect_ratio_to_grok_image::aspect_ratio_to_grok_image; -use crate::core::api_adapters::aspect_ratio::convert::aspect_ratio_to_sora_native_gpt_image_1::aspect_ratio_to_sora_native_gpt_image_1; -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::events::basic_sendable_event_trait::BasicSendableEvent; -use crate::core::events::functional_events::show_provider_login_modal_event::ShowProviderLoginModalEvent; -use crate::core::events::generation_events::common::{GenerationAction, GenerationModel, GenerationServiceProvider}; -use crate::core::events::generation_events::generation_enqueue_failure_event::GenerationEnqueueFailureEvent; -use crate::services::sora::state::sora_credential_manager::SoraCredentialManager; -use crate::services::sora::state::sora_task_queue::SoraTaskQueue; -use enums::common::generation_provider::GenerationProvider; -use enums::tauri::tasks::task_type::TaskType; -use errors::AnyhowResult; -use grok_client::requests::image_websocket::messages::websocket_client_message::ClientMessageAspectRatio; -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; - -const SORA_SIMPLE_IMAGE_GEN_TIMEOUT : Duration = Duration::from_millis(1000 * 30); // 30 seconds - -pub async fn handle_gpt_image_1_sora_text_to_image( - request: &EnqueueTextToImageRequest, - app: &AppHandle, - sora_creds_manager: &SoraCredentialManager, - sora_task_queue: &SoraTaskQueue, -) -> Result { - - let mut creds = match sora_creds_manager.get_credentials() { - Ok(Some(creds)) => creds, - Ok(None) => { - error!("Sora credentials not found."); - ShowProviderLoginModalEvent::send_for_provider(GenerationProvider::Sora, &app); - return Err(GenerateError::needs_sora_credentials()); - } - Err(err) => { - error!("Error reading Sora credentials: {:?}", err); - ShowProviderLoginModalEvent::send_for_provider(GenerationProvider::Sora, &app); - return Err(GenerateError::needs_sora_credentials()); - } - }; - - let credential_updated = maybe_upgrade_or_renew_session(&mut creds) - .await - .map_err(|err| { - error!("Failed to upgrade or renew session: {:?}", err); - err - })?; - - if credential_updated { - info!("Storing updated credentials"); - sora_creds_manager.set_credentials(&creds)?; - } - - let prompt = request.prompt.as_deref().unwrap_or(""); - - let aspect_ratio = get_aspect_ratio(request) - .unwrap_or(ImageSize::Square); - - let num_images = match request.number_images { - None => NumImages::One, - Some(1) => NumImages::One, - Some(2) => NumImages::Two, - Some(3) => NumImages::Two, // NB: There is no option for three images! - Some(4) => NumImages::Four, - _ => NumImages::One, - }; - - let result = - simple_image_gen_with_session_auto_renew(SimpleImageGenAutoRenewRequest { - prompt: prompt.to_string(), - num_images, - image_size: aspect_ratio, - credentials: &creds, - request_timeout: Some(SORA_SIMPLE_IMAGE_GEN_TIMEOUT), - }).await; - - let (response, maybe_new_creds) = - match result { - Ok((response, maybe_new_creds)) => (response, maybe_new_creds), - Err(err) => { - let event = GenerationEnqueueFailureEvent { - action: GenerationAction::GenerateImage, - service: GenerationServiceProvider::Sora, - model: None, - reason: None, - }; - - if let Err(err) = event.send(app) { - error!("Failed to emit event: {:?}", err); // Fail open. - } - - return Err(GenerateError::from(err)); - } - }; - - if let Some(new_creds) = maybe_new_creds { - info!("Storing updated credentials."); - sora_creds_manager.set_credentials(&new_creds)?; - } - - info!("New Sora Task ID: {:?} ", response.task_id); - - sora_task_queue.insert(&response.task_id)?; - - Ok(TaskEnqueueSuccess { - provider: GenerationProvider::Sora, - task_type: TaskType::ImageGeneration, - model: Some(GenerationModel::GptImage1), - provider_job_id: Some(response.task_id.to_string()), - }) -} - -fn get_aspect_ratio(request: &EnqueueTextToImageRequest) -> Option { - if let Some(common_aspect_ratio) = request.common_aspect_ratio { - // Handle modern aspect ratio - let aspect = aspect_ratio_to_sora_native_gpt_image_1(common_aspect_ratio); - return Some(aspect); - } - - if let Some(aspect_ratio) = request.aspect_ratio { - // Handle deprecated aspect ratio - return match aspect_ratio { - TextToImageSize::Auto => Some(ImageSize::Square), - TextToImageSize::Square => Some(ImageSize::Square), - TextToImageSize::Wide => Some(ImageSize::Wide), - TextToImageSize::Tall => Some(ImageSize::Tall), - } - } - - None -} diff --git a/crates/desktop/artcraft/src/core/commands/generate/generate_image/sora/handle_text_to_image_sora.rs b/crates/desktop/artcraft/src/core/commands/generate/generate_image/sora/handle_text_to_image_sora.rs deleted file mode 100644 index a632ed8eca..0000000000 --- a/crates/desktop/artcraft/src/core/commands/generate/generate_image/sora/handle_text_to_image_sora.rs +++ /dev/null @@ -1,21 +0,0 @@ -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; -use crate::core::commands::generate::generate_image::sora::handle_gpt_image_1_sora_text_to_image::handle_gpt_image_1_sora_text_to_image; -use crate::services::sora::state::sora_credential_manager::SoraCredentialManager; -use crate::services::sora::state::sora_task_queue::SoraTaskQueue; -use tauri::AppHandle; - -pub async fn handle_text_to_image_sora( - request: &EnqueueTextToImageRequest, - app: &AppHandle, - sora_creds_manager: &SoraCredentialManager, - sora_task_queue: &SoraTaskQueue, -) -> Result { - handle_gpt_image_1_sora_text_to_image( - request, - app, - sora_creds_manager, - sora_task_queue, - ).await -} diff --git a/crates/desktop/artcraft/src/core/commands/generate/generate_image/sora/mod.rs b/crates/desktop/artcraft/src/core/commands/generate/generate_image/sora/mod.rs deleted file mode 100644 index 9a9529d300..0000000000 --- a/crates/desktop/artcraft/src/core/commands/generate/generate_image/sora/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub (super) mod handle_text_to_image_sora; -mod handle_gpt_image_1_sora_text_to_image; \ No newline at end of file