refactor, move handlers

This commit is contained in:
Brandon Thomas committed 2026-05-07 23:13:27 -04:00
1 parent fbb65df591
commit 04ffb75de9
12 files changed
+46 -548

No files matched your search

@@ -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<TaskEnqueueSuccess, GenerateError> {
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<CommonImageModel> {
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
}
}
@@ -1 +0,0 @@
pub(super) mod handle_artcraft_via_omni_endpoint;
@@ -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<TaskEnqueueSuccess, GenerateError> {
//// 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<ClientMessageAspectRatio> {
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
}
@@ -1 +0,0 @@
pub (super) mod handle_grok;
@@ -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<TaskEnqueueSuccess, GenerateError> {
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<Vec<TextToImageError>>
) -> Result<TaskEnqueueSuccess, GenerateError> {
if let Some(errors) = maybe_errors {
if !errors.is_empty() {
let messages: Vec<String> = 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))
}
@@ -1 +0,0 @@
pub (super) mod handle_midjourney;
@@ -1,4 +1,3 @@
mod artcraft;
pub mod generate_image_command;
pub mod providers;
pub mod tauri_generate_image_request;
@@ -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<TaskEnqueueSuccess, GenerateError> {
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<Vec<MediaFileToken>> {
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)
}
}
@@ -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;
@@ -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<TaskEnqueueSuccess, GenerateError> {
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<ImageSize> {
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
}
@@ -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<TaskEnqueueSuccess, GenerateError> {
handle_gpt_image_1_sora_text_to_image(
request,
app,
sora_creds_manager,
sora_task_queue,
).await
}
@@ -1,2 +0,0 @@
pub (super) mod handle_text_to_image_sora;
mod handle_gpt_image_1_sora_text_to_image;