mirror of
https://github.com/storytold/artcraft.git
synced 2026-11-04 09:28:11 +00:00
work in progress multiple image batches, having to add new gpt-image-1
This commit is contained in:
1 parent
c36e88d8ce
commit
b1735eb38c
23 files changed
+587
-30
No files matched your search
+19
@@ -0,0 +1,19 @@
|
||||
use crate::credentials::storyteller_credential_set::StorytellerCredentialSet;
|
||||
use crate::error::storyteller_error::StorytellerError;
|
||||
use crate::utils::api_host::ApiHost;
|
||||
use crate::utils::basic_json_post_request::basic_json_post_request;
|
||||
use artcraft_api_defs::generate::image::generate_gpt_image_1_text_to_image::{GenerateGptImage1TextToImageRequest, GenerateGptImage1TextToImageResponse, GPT_IMAGE_1_TEXT_TO_IMAGE_PATH};
|
||||
|
||||
|
||||
pub async fn generate_gpt_image_1_text_to_image(
|
||||
api_host: &ApiHost,
|
||||
maybe_creds: Option<&StorytellerCredentialSet>,
|
||||
request: GenerateGptImage1TextToImageRequest,
|
||||
) -> Result<GenerateGptImage1TextToImageResponse, StorytellerError> {
|
||||
Ok(basic_json_post_request(
|
||||
api_host,
|
||||
GPT_IMAGE_1_TEXT_TO_IMAGE_PATH,
|
||||
maybe_creds,
|
||||
request,
|
||||
).await?)
|
||||
}
|
||||
@@ -3,4 +3,5 @@ pub mod generate_flux_1_dev_text_to_image;
|
||||
pub mod generate_flux_1_schnell_text_to_image;
|
||||
pub mod generate_flux_pro_11_text_to_image;
|
||||
pub mod generate_flux_pro_11_ultra_text_to_image;
|
||||
pub mod generate_gpt_image_1_text_to_image;
|
||||
pub mod remove_image_background;
|
||||
+15
-2
@@ -1,6 +1,7 @@
|
||||
use crate::core::commands::enqueue::image::generic::handle_image_artcraft::handle_image_artcraft;
|
||||
use crate::core::commands::enqueue::image::generic::handle_image_fal::handle_image_fal;
|
||||
use crate::core::commands::enqueue::image::gpt_image_1::handle_image_sora::handle_image_sora;
|
||||
use crate::core::commands::enqueue::image::gpt_image_1::handle_gpt_image_1::handle_gpt_image_1;
|
||||
use crate::core::commands::enqueue::image::gpt_image_1::handle_gpt_image_1_sora::handle_gpt_image_1_sora;
|
||||
use crate::core::commands::enqueue::image::internal_image_error::InternalImageError;
|
||||
use crate::core::commands::enqueue::task_enqueue_success::TaskEnqueueSuccess;
|
||||
use crate::core::commands::response::failure_response_wrapper::{CommandErrorResponseWrapper, CommandErrorStatus};
|
||||
@@ -39,6 +40,9 @@ pub struct EnqueueTextToImageRequest {
|
||||
|
||||
/// Aspect ratio.
|
||||
pub aspect_ratio: Option<TextToImageSize>,
|
||||
|
||||
/// The number of images to generate.
|
||||
pub number_images: Option<u32>,
|
||||
}
|
||||
|
||||
// TODO(bt,2025-07-14): Support other aspect ratios / resolutions -
|
||||
@@ -224,7 +228,16 @@ pub async fn dispatch_request(
|
||||
return Err(InternalImageError::NoModelSpecified);
|
||||
}
|
||||
Some(ImageModel::GptImage1) => {
|
||||
return handle_image_sora(&app, request, sora_creds_manager, sora_task_queue).await;
|
||||
return handle_gpt_image_1(
|
||||
request,
|
||||
app,
|
||||
app_data_root,
|
||||
app_env_configs,
|
||||
provider_priority_store,
|
||||
storyteller_creds_manager,
|
||||
sora_creds_manager,
|
||||
sora_task_queue,
|
||||
).await;
|
||||
}
|
||||
_ => {
|
||||
// Fall-through
|
||||
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
use crate::core::commands::enqueue::image::enqueue_text_to_image_command::{EnqueueTextToImageRequest, TextToImageSize};
|
||||
use crate::core::commands::enqueue::image::gpt_image_1::handle_gpt_image_1_artcraft::handle_gpt_image_1_artcraft;
|
||||
use crate::core::commands::enqueue::image::gpt_image_1::handle_gpt_image_1_sora::handle_gpt_image_1_sora;
|
||||
use crate::core::commands::enqueue::image::internal_image_error::InternalImageError;
|
||||
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::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::model::image_models::ImageModel;
|
||||
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_gpt_image_1(
|
||||
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<TaskEnqueueSuccess, InternalImageError> {
|
||||
|
||||
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
|
||||
// If in the future we support OpenAI API keys, it's worth considering whether we
|
||||
// send those requests ourselves, or if we use FAL as an intermediary. FAL makes
|
||||
// the API nicer to deal with, but the user needs an additional key.
|
||||
}
|
||||
Provider::Artcraft => {
|
||||
info!("Dispatching gpt-image-1 via Artcraft...");
|
||||
return handle_gpt_image_1_artcraft(
|
||||
request,
|
||||
app_env_configs,
|
||||
storyteller_creds_manager
|
||||
).await;
|
||||
}
|
||||
Provider::Sora => {
|
||||
info!("Dispatching gpt-image-1 via Sora...");
|
||||
return handle_gpt_image_1_sora(
|
||||
request,
|
||||
app,
|
||||
sora_creds_manager,
|
||||
sora_task_queue,
|
||||
).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Err(InternalImageError::NoProviderAvailable)
|
||||
}
|
||||
+115
@@ -0,0 +1,115 @@
|
||||
use crate::core::commands::enqueue::image::enqueue_text_to_image_command::{EnqueueTextToImageRequest, TextToImageSize};
|
||||
use crate::core::commands::enqueue::image::internal_image_error::InternalImageError;
|
||||
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::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::model::image_models::ImageModel;
|
||||
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::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::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_gpt_image_1_artcraft(
|
||||
request: EnqueueTextToImageRequest,
|
||||
app_env_configs: &AppEnvConfigs,
|
||||
storyteller_creds_manager: &StorytellerCredentialManager,
|
||||
) -> Result<TaskEnqueueSuccess, InternalImageError> {
|
||||
|
||||
let creds = match storyteller_creds_manager.get_credentials()? {
|
||||
Some(creds) => creds,
|
||||
None => {
|
||||
return Err(InternalImageError::NeedsStorytellerCredentials);
|
||||
},
|
||||
};
|
||||
|
||||
info!("Calling Artcraft gpt-image-1 ...");
|
||||
|
||||
let uuid_idempotency_token = generate_random_uuid();
|
||||
|
||||
let image_quality = Some(GenerateGptImage1TextToImageImageQuality::High);
|
||||
|
||||
//let image_quality = request.image_quality
|
||||
// .map(|quality| match quality {
|
||||
// EditImageQuality::Auto => GenerateGptImage1TextToImageImageQuality::Auto,
|
||||
// EditImageQuality::High => GenerateGptImage1TextToImageImageQuality::High,
|
||||
// EditImageQuality::Medium => GenerateGptImage1TextToImageImageQuality::Medium,
|
||||
// EditImageQuality::Low => GenerateGptImage1TextToImageImageQuality::Low,
|
||||
// });
|
||||
|
||||
let image_size = request.aspect_ratio
|
||||
.map(|size| match size {
|
||||
TextToImageSize::Auto => GenerateGptImage1TextToImageImageSize::Square,
|
||||
TextToImageSize::Square => GenerateGptImage1TextToImageImageSize::Square,
|
||||
TextToImageSize::Tall => GenerateGptImage1TextToImageImageSize::Vertical,
|
||||
TextToImageSize::Wide => GenerateGptImage1TextToImageImageSize::Horizontal,
|
||||
});
|
||||
|
||||
let num_images = match request.number_images {
|
||||
None => None,
|
||||
Some(1) => Some(GenerateGptImage1TextToImageNumImages::One),
|
||||
Some(2) => Some(GenerateGptImage1TextToImageNumImages::Two),
|
||||
Some(3) => Some(GenerateGptImage1TextToImageNumImages::Three),
|
||||
Some(4) => Some(GenerateGptImage1TextToImageNumImages::Four),
|
||||
// TODO: Error
|
||||
//Some(other) => {
|
||||
// return Err(InternalImageError::InvalidNumberOfRequestedImages {
|
||||
// min: 1,
|
||||
// max: 4,
|
||||
// requested: other,
|
||||
// });
|
||||
//},
|
||||
_ => Some(GenerateGptImage1TextToImageNumImages::One), // Default to one image if invalid number
|
||||
};
|
||||
|
||||
let request = GenerateGptImage1TextToImageRequest {
|
||||
uuid_idempotency_token,
|
||||
prompt: request.prompt.clone(),
|
||||
image_size,
|
||||
num_images,
|
||||
image_quality,
|
||||
};
|
||||
|
||||
let result = generate_gpt_image_1_text_to_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 gpt-image-1. Job token: {}",
|
||||
enqueued.inference_job_token);
|
||||
enqueued.inference_job_token
|
||||
}
|
||||
Err(err) => {
|
||||
error!("Failed to use Artcraft gpt-image-1: {:?}", 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,
|
||||
})
|
||||
}
|
||||
+12
-3
@@ -19,9 +19,9 @@ use tauri::AppHandle;
|
||||
|
||||
const SORA_SIMPLE_IMAGE_GEN_TIMEOUT : Duration = Duration::from_millis(1000 * 30); // 30 seconds
|
||||
|
||||
pub async fn handle_image_sora(
|
||||
app: &AppHandle,
|
||||
pub async fn handle_gpt_image_1_sora(
|
||||
request: EnqueueTextToImageRequest,
|
||||
app: &AppHandle,
|
||||
sora_creds_manager: &SoraCredentialManager,
|
||||
sora_task_queue: &SoraTaskQueue,
|
||||
) -> Result<TaskEnqueueSuccess, InternalImageError> {
|
||||
@@ -55,10 +55,19 @@ pub async fn handle_image_sora(
|
||||
Some(TextToImageSize::Wide) => ImageSize::Wide,
|
||||
};
|
||||
|
||||
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: NumImages::One,
|
||||
num_images,
|
||||
image_size: size,
|
||||
credentials: &creds,
|
||||
request_timeout: Some(SORA_SIMPLE_IMAGE_GEN_TIMEOUT),
|
||||
@@ -1 +1,3 @@
|
||||
pub mod handle_image_sora;
|
||||
pub (crate) mod handle_gpt_image_1_artcraft;
|
||||
pub (crate) mod handle_gpt_image_1_sora;
|
||||
pub (super) mod handle_gpt_image_1;
|
||||
+1
-1
@@ -3,7 +3,7 @@ use crate::core::commands::enqueue::image::internal_image_error::InternalImageEr
|
||||
use crate::core::commands::enqueue::image_bg_removal::errors::InternalBgRemovalError;
|
||||
use crate::core::commands::enqueue::image_bg_removal::generic::handle_generic_bg_removal::handle_generic_bg_removal;
|
||||
use crate::core::commands::enqueue::image_edit::errors::InternalContextualEditImageError;
|
||||
use crate::core::commands::enqueue::image_edit::gpt_image_1::handle_gpt_image_1::handle_gpt_image_1;
|
||||
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::task_enqueue_success::TaskEnqueueSuccess;
|
||||
use crate::core::commands::response::failure_response_wrapper::{CommandErrorResponseWrapper, CommandErrorStatus};
|
||||
use crate::core::commands::response::shorthand::{Response, ResponseOrErrorType};
|
||||
|
||||
+2
-2
@@ -4,8 +4,8 @@ use crate::core::commands::enqueue::image_bg_removal::generic::handle_generic_bg
|
||||
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::errors::InternalContextualEditImageError;
|
||||
use crate::core::commands::enqueue::image_edit::gpt_image_1::handle_gpt_image_1_artcraft::handle_gpt_image_1_artcraft;
|
||||
use crate::core::commands::enqueue::image_edit::gpt_image_1::handle_gpt_image_1_sora::handle_gpt_image_1_sora;
|
||||
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::state::app_env_configs::app_env_configs::AppEnvConfigs;
|
||||
use crate::core::state::data_dir::app_data_root::AppDataRoot;
|
||||
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
use crate::core::commands::enqueue::image::enqueue_text_to_image_command::{EnqueueTextToImageRequest, EnqueueTextToImageSuccessResponse};
|
||||
use crate::core::commands::enqueue::image::internal_image_error::InternalImageError;
|
||||
use crate::core::commands::enqueue::image_edit::errors::InternalContextualEditImageError;
|
||||
use crate::core::commands::enqueue::image_edit::gpt_image_1::handle_gpt_image_1::handle_gpt_image_1;
|
||||
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::task_enqueue_success::TaskEnqueueSuccess;
|
||||
use crate::core::commands::response::failure_response_wrapper::{CommandErrorResponseWrapper, CommandErrorStatus};
|
||||
use crate::core::commands::response::shorthand::{Response, ResponseOrErrorType};
|
||||
@@ -208,7 +208,7 @@ pub async fn handle_request(
|
||||
return Err(InternalContextualEditImageError::NoModelSpecified)
|
||||
}
|
||||
Some(ContextualImageEditModel::GptImage1) => {
|
||||
handle_gpt_image_1(
|
||||
handle_gpt_image_1_edit(
|
||||
request,
|
||||
app,
|
||||
app_data_root,
|
||||
|
||||
+7
-7
@@ -1,7 +1,7 @@
|
||||
use crate::core::commands::enqueue::image_edit::enqueue_contextual_edit_image_command::EnqueueContextualEditImageCommand;
|
||||
use crate::core::commands::enqueue::image_edit::errors::InternalContextualEditImageError;
|
||||
use crate::core::commands::enqueue::image_edit::gpt_image_1::handle_gpt_image_1_artcraft::handle_gpt_image_1_artcraft;
|
||||
use crate::core::commands::enqueue::image_edit::gpt_image_1::handle_gpt_image_1_sora::handle_gpt_image_1_sora;
|
||||
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::state::app_env_configs::app_env_configs::AppEnvConfigs;
|
||||
use crate::core::state::data_dir::app_data_root::AppDataRoot;
|
||||
@@ -16,7 +16,7 @@ use tauri::AppHandle;
|
||||
|
||||
pub(super) const MAX_IMAGES: usize = 10;
|
||||
|
||||
pub async fn handle_gpt_image_1(
|
||||
pub async fn handle_gpt_image_1_edit(
|
||||
request: &EnqueueContextualEditImageCommand,
|
||||
app: &AppHandle,
|
||||
app_data_root: &AppDataRoot,
|
||||
@@ -44,8 +44,8 @@ pub async fn handle_gpt_image_1(
|
||||
// the API nicer to deal with, but the user needs an additional key.
|
||||
}
|
||||
Provider::Artcraft => {
|
||||
info!("Dispatching gpt-image-1 via Artcraft...");
|
||||
return handle_gpt_image_1_artcraft(
|
||||
info!("Dispatching gpt-image-1 (edit) via Artcraft...");
|
||||
return handle_gpt_image_1_edit_artcraft(
|
||||
request,
|
||||
app,
|
||||
app_data_root,
|
||||
@@ -54,8 +54,8 @@ pub async fn handle_gpt_image_1(
|
||||
).await;
|
||||
}
|
||||
Provider::Sora => {
|
||||
info!("Dispatching gpt-image-1 via Sora...");
|
||||
return handle_gpt_image_1_sora(
|
||||
info!("Dispatching gpt-image-1 (edit) via Sora...");
|
||||
return handle_gpt_image_1_edit_sora(
|
||||
request,
|
||||
app,
|
||||
app_data_root,
|
||||
+3
-3
@@ -2,7 +2,7 @@ use crate::core::commands::enqueue::image::enqueue_text_to_image_command::Enqueu
|
||||
use crate::core::commands::enqueue::image::internal_image_error::InternalImageError;
|
||||
use crate::core::commands::enqueue::image_edit::enqueue_contextual_edit_image_command::{EditImageQuality, EditImageSize, EnqueueContextualEditImageCommand};
|
||||
use crate::core::commands::enqueue::image_edit::errors::InternalContextualEditImageError;
|
||||
use crate::core::commands::enqueue::image_edit::gpt_image_1::handle_gpt_image_1::MAX_IMAGES;
|
||||
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;
|
||||
use crate::core::events::generation_events::common::{GenerationAction, GenerationModel, GenerationServiceProvider};
|
||||
@@ -36,7 +36,7 @@ use tauri::AppHandle;
|
||||
use enums::common::generation_provider::GenerationProvider;
|
||||
use enums::tauri::tasks::task_type::TaskType;
|
||||
|
||||
pub async fn handle_gpt_image_1_artcraft(
|
||||
pub async fn handle_gpt_image_1_edit_artcraft(
|
||||
request: &EnqueueContextualEditImageCommand,
|
||||
app: &AppHandle,
|
||||
app_data_root: &AppDataRoot,
|
||||
@@ -51,7 +51,7 @@ pub async fn handle_gpt_image_1_artcraft(
|
||||
},
|
||||
};
|
||||
|
||||
info!("Calling Artcraft gpt-image-1 ...");
|
||||
info!("Calling Artcraft gpt-image-1 (edit) ...");
|
||||
|
||||
let uuid_idempotency_token = generate_random_uuid();
|
||||
|
||||
+2
-2
@@ -2,7 +2,7 @@ use crate::core::commands::enqueue::image::enqueue_text_to_image_command::Enqueu
|
||||
use crate::core::commands::enqueue::image::internal_image_error::InternalImageError;
|
||||
use crate::core::commands::enqueue::image_edit::enqueue_contextual_edit_image_command::{EditImageSize, EnqueueContextualEditImageCommand, EnqueueContextualEditImageErrorType};
|
||||
use crate::core::commands::enqueue::image_edit::errors::InternalContextualEditImageError;
|
||||
use crate::core::commands::enqueue::image_edit::gpt_image_1::handle_gpt_image_1::MAX_IMAGES;
|
||||
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};
|
||||
use crate::core::events::basic_sendable_event_trait::BasicSendableEvent;
|
||||
@@ -48,7 +48,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_sora(
|
||||
pub async fn handle_gpt_image_1_edit_sora(
|
||||
request: &EnqueueContextualEditImageCommand,
|
||||
app: &AppHandle,
|
||||
app_data_root: &AppDataRoot,
|
||||
@@ -1,3 +1,3 @@
|
||||
pub mod handle_gpt_image_1;
|
||||
pub mod handle_gpt_image_1_artcraft;
|
||||
pub mod handle_gpt_image_1_sora;
|
||||
pub mod handle_gpt_image_1_edit;
|
||||
pub mod handle_gpt_image_1_edit_artcraft;
|
||||
pub mod handle_gpt_image_1_edit_sora;
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use tokens::tokens::generic_inference_jobs::InferenceJobToken;
|
||||
use tokens::tokens::media_files::MediaFileToken;
|
||||
use utoipa::ToSchema;
|
||||
|
||||
pub const GPT_IMAGE_1_TEXT_TO_IMAGE_PATH: &str = "/v1/generate/image/gpt_image_1_text_to_image";
|
||||
|
||||
#[derive(Serialize, Deserialize, ToSchema)]
|
||||
pub struct GenerateGptImage1TextToImageRequest {
|
||||
/// Idempotency token to prevent duplicate requests.
|
||||
pub uuid_idempotency_token: String,
|
||||
|
||||
/// Text prompt to generate the image from.
|
||||
pub prompt: Option<String>,
|
||||
|
||||
/// Size of the image to generate. Default is Square.
|
||||
pub image_size: Option<GenerateGptImage1TextToImageImageSize>,
|
||||
|
||||
/// Number of images to generate. Default is one.
|
||||
pub num_images: Option<GenerateGptImage1TextToImageNumImages>,
|
||||
|
||||
/// Quality of the image to generate. Default is High.
|
||||
pub image_quality: Option<GenerateGptImage1TextToImageImageQuality>,
|
||||
}
|
||||
|
||||
|
||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize, ToSchema)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum GenerateGptImage1TextToImageImageSize {
|
||||
Square, // 1:1, Default
|
||||
Horizontal,
|
||||
Vertical,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize, ToSchema)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum GenerateGptImage1TextToImageNumImages {
|
||||
One, // Default
|
||||
Two,
|
||||
Three,
|
||||
Four,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize, ToSchema)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum GenerateGptImage1TextToImageImageQuality {
|
||||
Auto,
|
||||
Low,
|
||||
Medium,
|
||||
High, // Default
|
||||
}
|
||||
|
||||
|
||||
#[derive(Serialize, Deserialize, ToSchema)]
|
||||
pub struct GenerateGptImage1TextToImageResponse {
|
||||
pub success: bool,
|
||||
pub inference_job_token: InferenceJobToken,
|
||||
}
|
||||
@@ -3,4 +3,5 @@ pub mod generate_flux_1_dev_text_to_image;
|
||||
pub mod generate_flux_1_schnell_text_to_image;
|
||||
pub mod generate_flux_pro_11_text_to_image;
|
||||
pub mod generate_flux_pro_11_ultra_text_to_image;
|
||||
pub mod generate_gpt_image_1_text_to_image;
|
||||
pub mod remove_image_background;
|
||||
@@ -141,6 +141,11 @@ use artcraft_api_defs::generate::image::generate_flux_pro_11_ultra_text_to_image
|
||||
use artcraft_api_defs::generate::image::generate_flux_pro_11_ultra_text_to_image::GenerateFluxPro11UltraTextToImageNumImages;
|
||||
use artcraft_api_defs::generate::image::generate_flux_pro_11_ultra_text_to_image::GenerateFluxPro11UltraTextToImageRequest;
|
||||
use artcraft_api_defs::generate::image::generate_flux_pro_11_ultra_text_to_image::GenerateFluxPro11UltraTextToImageResponse;
|
||||
use artcraft_api_defs::generate::image::generate_gpt_image_1_text_to_image::GenerateGptImage1TextToImageImageQuality;
|
||||
use artcraft_api_defs::generate::image::generate_gpt_image_1_text_to_image::GenerateGptImage1TextToImageImageSize;
|
||||
use artcraft_api_defs::generate::image::generate_gpt_image_1_text_to_image::GenerateGptImage1TextToImageNumImages;
|
||||
use artcraft_api_defs::generate::image::generate_gpt_image_1_text_to_image::GenerateGptImage1TextToImageRequest;
|
||||
use artcraft_api_defs::generate::image::generate_gpt_image_1_text_to_image::GenerateGptImage1TextToImageResponse;
|
||||
use artcraft_api_defs::generate::image::remove_image_background::RemoveImageBackgroundRequest;
|
||||
use artcraft_api_defs::generate::image::remove_image_background::RemoveImageBackgroundResponse;
|
||||
use artcraft_api_defs::generate::object::generate_hunyuan_2_0_image_to_3d::GenerateHunyuan20ImageTo3dRequest;
|
||||
@@ -237,6 +242,7 @@ use tokens::tokens::zs_voice_datasets::*;
|
||||
crate::http_server::endpoints::generate::image::generate_flux_1_dev_text_to_image_handler::generate_flux_1_dev_text_to_image_handler,
|
||||
crate::http_server::endpoints::generate::image::generate_flux_1_schnell_text_to_image_handler::generate_flux_1_schnell_text_to_image_handler,
|
||||
crate::http_server::endpoints::generate::image::generate_flux_pro_11_text_to_image_handler::generate_flux_pro_11_text_to_image_handler,
|
||||
crate::http_server::endpoints::generate::image::generate_gpt_image_1_text_to_image_handler::generate_gpt_image_1_text_to_image_handler,
|
||||
crate::http_server::endpoints::generate::image::generate_flux_pro_11_ultra_text_to_image_handler::generate_flux_pro_11_ultra_text_to_image_handler,
|
||||
crate::http_server::endpoints::generate::image::remove_image_background_handler::remove_image_background_handler,
|
||||
crate::http_server::endpoints::generate::image::edit::gpt_image_1_edit_image_handler::gpt_image_1_edit_image_handler,
|
||||
@@ -420,6 +426,11 @@ use tokens::tokens::zs_voice_datasets::*;
|
||||
BatchGetUserRatingError,
|
||||
BatchGetUserRatingQueryParams,
|
||||
BatchGetUserRatingResponse,
|
||||
GenerateGptImage1TextToImageImageQuality,
|
||||
GenerateGptImage1TextToImageImageSize,
|
||||
GenerateGptImage1TextToImageNumImages,
|
||||
GenerateGptImage1TextToImageRequest,
|
||||
GenerateGptImage1TextToImageResponse,
|
||||
BatchInferenceJobStatusResponsePayload,
|
||||
BatchMediaFileInfo,
|
||||
BatchRequestDetailsResponse,
|
||||
|
||||
+216
@@ -0,0 +1,216 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::http_server::common_responses::common_web_error::CommonWebError;
|
||||
use crate::http_server::common_responses::media::media_links_builder::MediaLinksBuilder;
|
||||
use crate::http_server::endpoints::media_files::helpers::get_media_domain::get_media_domain;
|
||||
use crate::http_server::validations::validate_idempotency_token_format::validate_idempotency_token_format;
|
||||
use crate::state::server_state::ServerState;
|
||||
use actix_web::web::Json;
|
||||
use actix_web::{web, HttpRequest};
|
||||
use artcraft_api_defs::generate::image::generate_gpt_image_1_text_to_image::{GenerateGptImage1TextToImageImageQuality, GenerateGptImage1TextToImageImageSize, GenerateGptImage1TextToImageNumImages, GenerateGptImage1TextToImageRequest, GenerateGptImage1TextToImageResponse};
|
||||
use bucket_paths::legacy::typified_paths::public::media_files::bucket_file_path::MediaFileBucketPath;
|
||||
use enums::by_table::prompt_context_items::prompt_context_semantic_type::PromptContextSemanticType;
|
||||
use enums::by_table::prompts::prompt_type::PromptType;
|
||||
use enums::common::generation_provider::GenerationProvider;
|
||||
use enums::common::model_type::ModelType;
|
||||
use enums::common::visibility::Visibility;
|
||||
use fal_client::creds::open_ai_api_key::OpenAiApiKey;
|
||||
use fal_client::requests::webhook::image::enqueue_gpt_image_1_edit_image_webhook::enqueue_gpt_image_1_edit_image_webhook;
|
||||
use fal_client::requests::webhook::image::enqueue_gpt_image_1_edit_image_webhook::GptEditImageNumImages;
|
||||
use fal_client::requests::webhook::image::enqueue_gpt_image_1_edit_image_webhook::{GptEditImageByokArgs, GptEditImageQuality, GptEditImageSize};
|
||||
use http_server_common::request::get_request_ip::get_request_ip;
|
||||
use log::{error, info, warn};
|
||||
use mysql_queries::queries::generic_inference::fal::insert_generic_inference_job_for_fal_queue::insert_generic_inference_job_for_fal_queue;
|
||||
use mysql_queries::queries::generic_inference::fal::insert_generic_inference_job_for_fal_queue::FalCategory;
|
||||
use mysql_queries::queries::generic_inference::fal::insert_generic_inference_job_for_fal_queue::InsertGenericInferenceForFalArgs;
|
||||
use mysql_queries::queries::idepotency_tokens::insert_idempotency_token::insert_idempotency_token;
|
||||
use mysql_queries::queries::media_files::get::batch_get_media_files_by_tokens::{batch_get_media_files_by_tokens, batch_get_media_files_by_tokens_with_connection};
|
||||
use mysql_queries::queries::prompt_context_items::insert_batch_prompt_context_items::{insert_batch_prompt_context_items, InsertBatchArgs, PromptContextItem};
|
||||
use mysql_queries::queries::prompts::insert_prompt::{insert_prompt, InsertPromptArgs};
|
||||
use sqlx::Acquire;
|
||||
use utoipa::ToSchema;
|
||||
|
||||
/// Gpt Image 1
|
||||
#[utoipa::path(
|
||||
post,
|
||||
tag = "Generate Images",
|
||||
path = "/v1/generate/image/gpt_image_1_text_to_image",
|
||||
responses(
|
||||
(status = 200, description = "Success", body = GenerateGptImage1TextToImageResponse),
|
||||
),
|
||||
params(
|
||||
("request" = GenerateGptImage1TextToImageRequest, description = "Payload for Request"),
|
||||
)
|
||||
)]
|
||||
pub async fn generate_gpt_image_1_text_to_image_handler(
|
||||
http_request: HttpRequest,
|
||||
request: Json<GenerateGptImage1TextToImageRequest>,
|
||||
server_state: web::Data<Arc<ServerState>>
|
||||
) -> Result<Json<GenerateGptImage1TextToImageResponse>, CommonWebError> {
|
||||
let mut mysql_connection = server_state.mysql_pool
|
||||
.acquire()
|
||||
.await?;
|
||||
|
||||
let maybe_user_session = server_state
|
||||
.session_checker
|
||||
.maybe_get_user_session_from_connection(&http_request, &mut mysql_connection)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
warn!("Session checker error: {:?}", e);
|
||||
CommonWebError::ServerError
|
||||
})?;
|
||||
|
||||
let maybe_avt_token = server_state
|
||||
.avt_cookie_manager
|
||||
.get_avt_token_from_request(&http_request);
|
||||
|
||||
// TODO: Limit usage for new accounts. Billing, free credits metering, etc.
|
||||
|
||||
//let user_session = match maybe_user_session {
|
||||
// Some(session) => session,
|
||||
// None => {
|
||||
// warn!("not logged in");
|
||||
// return Err(CommonWebError::NotAuthorized);
|
||||
// }
|
||||
//};
|
||||
|
||||
if let Err(reason) = validate_idempotency_token_format(&request.uuid_idempotency_token) {
|
||||
return Err(CommonWebError::BadInputWithSimpleMessage(reason));
|
||||
}
|
||||
|
||||
const CAN_SEE_DELETED: bool = false;
|
||||
|
||||
let media_domain = get_media_domain(&http_request);
|
||||
|
||||
insert_idempotency_token(&request.uuid_idempotency_token, &mut *mysql_connection)
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!("Error inserting idempotency token: {:?}", err);
|
||||
CommonWebError::BadInputWithSimpleMessage("repeated idempotency token".to_string())
|
||||
})?;
|
||||
|
||||
info!("Fal webhook URL: {}", server_state.fal.webhook_url);
|
||||
|
||||
let image_size = match request.image_size {
|
||||
Some(GenerateGptImage1TextToImageImageSize::Square) => GptEditImageSize::Square,
|
||||
Some(GenerateGptImage1TextToImageImageSize::Horizontal) => GptEditImageSize::Horizontal,
|
||||
Some(GenerateGptImage1TextToImageImageSize::Vertical) => GptEditImageSize::Vertical,
|
||||
None => GptEditImageSize::Square, // Default to Square
|
||||
};
|
||||
|
||||
let num_images = match request.num_images {
|
||||
Some(GenerateGptImage1TextToImageNumImages::One) => GptEditImageNumImages::One,
|
||||
Some(GenerateGptImage1TextToImageNumImages::Two) => GptEditImageNumImages::Two,
|
||||
Some(GenerateGptImage1TextToImageNumImages::Three) => GptEditImageNumImages::Three,
|
||||
Some(GenerateGptImage1TextToImageNumImages::Four) => GptEditImageNumImages::Four,
|
||||
None => GptEditImageNumImages::One, // Default to One
|
||||
};
|
||||
|
||||
let quality = match request.image_quality {
|
||||
Some(quality) => match quality {
|
||||
GenerateGptImage1TextToImageImageQuality::Auto => GptEditImageQuality::Auto,
|
||||
GenerateGptImage1TextToImageImageQuality::Low => GptEditImageQuality::Low,
|
||||
GenerateGptImage1TextToImageImageQuality::Medium => GptEditImageQuality::Medium,
|
||||
GenerateGptImage1TextToImageImageQuality::High => GptEditImageQuality::High,
|
||||
},
|
||||
None => GptEditImageQuality::High, // Default to High
|
||||
};
|
||||
|
||||
let openai_api_key = OpenAiApiKey::from_str(&server_state.openai.api_key);
|
||||
|
||||
let args = GptEditImageByokArgs {
|
||||
image_urls: vec![], // TODO(bt,2025-07-29): Don't call non-edit endpoint
|
||||
prompt: request.prompt.as_deref().unwrap_or(""),
|
||||
webhook_url: &server_state.fal.webhook_url,
|
||||
api_key: &server_state.fal.api_key,
|
||||
num_images,
|
||||
image_size,
|
||||
quality,
|
||||
openai_api_key: &openai_api_key,
|
||||
};
|
||||
|
||||
let fal_result = enqueue_gpt_image_1_edit_image_webhook(args)
|
||||
.await
|
||||
.map_err(|err| {
|
||||
warn!("Error calling enqueue_gpt_image_1_edit_image_webhook: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
})?;
|
||||
|
||||
let external_job_id = fal_result.request_id
|
||||
.ok_or_else(|| {
|
||||
warn!("Fal request_id is None");
|
||||
CommonWebError::ServerError
|
||||
})?;
|
||||
|
||||
info!("Fal request_id: {}", external_job_id);
|
||||
|
||||
let ip_address = get_request_ip(&http_request);
|
||||
|
||||
let mut transaction = mysql_connection
|
||||
.begin()
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!("Error starting MySQL transaction: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
})?;
|
||||
|
||||
// NB: Don't fail the job if the query fails.
|
||||
let prompt_result = insert_prompt(InsertPromptArgs {
|
||||
maybe_apriori_prompt_token: None,
|
||||
prompt_type: PromptType::ArtcraftApp,
|
||||
maybe_creator_user_token: maybe_user_session
|
||||
.as_ref()
|
||||
.map(|s| &s.user_token),
|
||||
maybe_model_type: Some(ModelType::GptImage1),
|
||||
maybe_generation_provider: Some(GenerationProvider::Artcraft),
|
||||
maybe_positive_prompt: request.prompt.as_deref(),
|
||||
maybe_negative_prompt: None,
|
||||
maybe_other_args: None,
|
||||
creator_ip_address: &ip_address,
|
||||
mysql_executor: &mut *transaction,
|
||||
phantom: Default::default(),
|
||||
}).await;
|
||||
|
||||
let prompt_token = match prompt_result {
|
||||
Ok(token) => Some(token),
|
||||
Err(err) => {
|
||||
warn!("Error inserting prompt: {:?}", err);
|
||||
None // Don't fail the job if the prompt insertion fails.
|
||||
}
|
||||
};
|
||||
|
||||
let db_result = insert_generic_inference_job_for_fal_queue(InsertGenericInferenceForFalArgs {
|
||||
uuid_idempotency_token: &request.uuid_idempotency_token,
|
||||
maybe_external_third_party_id: &external_job_id,
|
||||
fal_category: FalCategory::ImageGeneration,
|
||||
maybe_inference_args: None,
|
||||
maybe_prompt_token: prompt_token.as_ref(),
|
||||
maybe_creator_user_token: maybe_user_session.as_ref().map(|s| &s.user_token),
|
||||
maybe_avt_token: maybe_avt_token.as_ref(),
|
||||
creator_ip_address: &ip_address,
|
||||
creator_set_visibility: Visibility::Public,
|
||||
mysql_executor: &mut *transaction,
|
||||
phantom: Default::default(),
|
||||
}).await;
|
||||
|
||||
let job_token = match db_result {
|
||||
Ok(token) => token,
|
||||
Err(err) => {
|
||||
warn!("Error inserting generic inference job for FAL queue: {:?}", err);
|
||||
return Err(CommonWebError::ServerError);
|
||||
}
|
||||
};
|
||||
|
||||
let _r = transaction
|
||||
.commit()
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!("Error committing MySQL transaction: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
})?;
|
||||
|
||||
Ok(Json(GenerateGptImage1TextToImageResponse {
|
||||
success: true,
|
||||
inference_job_token: job_token,
|
||||
}))
|
||||
}
|
||||
@@ -3,4 +3,5 @@ pub mod generate_flux_1_dev_text_to_image_handler;
|
||||
pub mod generate_flux_1_schnell_text_to_image_handler;
|
||||
pub mod generate_flux_pro_11_text_to_image_handler;
|
||||
pub mod generate_flux_pro_11_ultra_text_to_image_handler;
|
||||
pub mod generate_gpt_image_1_text_to_image_handler;
|
||||
pub mod remove_image_background_handler;
|
||||
@@ -1,7 +1,9 @@
|
||||
use crate::http_server::endpoints::generate::image::edit::gpt_image_1_edit_image_handler::gpt_image_1_edit_image_handler;
|
||||
use crate::http_server::endpoints::generate::image::generate_flux_1_dev_text_to_image_handler::generate_flux_1_dev_text_to_image_handler;
|
||||
use crate::http_server::endpoints::generate::image::generate_flux_1_schnell_text_to_image_handler::generate_flux_1_schnell_text_to_image_handler;
|
||||
use crate::http_server::endpoints::generate::image::generate_flux_pro_11_text_to_image_handler::generate_flux_pro_11_text_to_image_handler;
|
||||
use crate::http_server::endpoints::generate::image::generate_flux_pro_11_ultra_text_to_image_handler::generate_flux_pro_11_ultra_text_to_image_handler;
|
||||
use crate::http_server::endpoints::generate::image::generate_gpt_image_1_text_to_image_handler::generate_gpt_image_1_text_to_image_handler;
|
||||
use crate::http_server::endpoints::generate::image::remove_image_background_handler::remove_image_background_handler;
|
||||
use crate::http_server::endpoints::generate::object::generate_hunyuan_2_0_image_to_3d_handler::generate_hunyuan_2_0_image_to_3d_handler;
|
||||
use crate::http_server::endpoints::generate::object::generate_hunyuan_2_1_image_to_3d_handler::generate_hunyuan_2_1_image_to_3d_handler;
|
||||
@@ -14,7 +16,6 @@ use actix_http::body::MessageBody;
|
||||
use actix_service::ServiceFactory;
|
||||
use actix_web::dev::{ServiceRequest, ServiceResponse};
|
||||
use actix_web::{web, App, Error, HttpResponse};
|
||||
use crate::http_server::endpoints::generate::image::edit::gpt_image_1_edit_image_handler::gpt_image_1_edit_image_handler;
|
||||
|
||||
pub fn add_generate_routes<T, B> (app: App<T>) -> App<T>
|
||||
where
|
||||
@@ -51,6 +52,10 @@ where
|
||||
.route(web::post().to(generate_flux_pro_11_ultra_text_to_image_handler))
|
||||
.route(web::head().to(|| HttpResponse::Ok()))
|
||||
)
|
||||
.service(web::resource("/gpt_image_1_text_to_image")
|
||||
.route(web::post().to(generate_gpt_image_1_text_to_image_handler))
|
||||
.route(web::head().to(|| HttpResponse::Ok()))
|
||||
)
|
||||
.service(web::resource("/remove_background")
|
||||
.route(web::post().to(remove_image_background_handler))
|
||||
.route(web::head().to(|| HttpResponse::Ok()))
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useRef } from "react";
|
||||
import { useRef, useState } from "react";
|
||||
import { JobContextType } from "@storyteller/common";
|
||||
import { PromptBoxImage } from "@storyteller/ui-promptbox";
|
||||
import BackgroundGallery from "./BackgroundGallery";
|
||||
@@ -27,6 +27,13 @@ const TextToImage = ({ imageMediaId, imageUrl }: TextToImageProps) => {
|
||||
const selectedModelInfo: ModelInfo | undefined =
|
||||
IMAGE_MODELS_BY_LABEL[selectedModel];
|
||||
|
||||
const [numberImages, setNumberImages] = useState(1);
|
||||
|
||||
const handleNumberImagesChange = (event: React.ChangeEvent<HTMLSelectElement>) => {
|
||||
const value = event.target.value;
|
||||
setNumberImages(parseInt(value));
|
||||
};
|
||||
|
||||
const jobContext: JobContextType = {
|
||||
jobTokens: [],
|
||||
addJobToken: () => {},
|
||||
@@ -46,6 +53,14 @@ const TextToImage = ({ imageMediaId, imageUrl }: TextToImageProps) => {
|
||||
<span className="pt-2 text-xl opacity-80">
|
||||
Add a prompt, then generate
|
||||
</span>
|
||||
|
||||
|
||||
<select onChange={handleNumberImagesChange} value={numberImages}>
|
||||
<option value="1">1</option>
|
||||
<option value="2">2</option>
|
||||
<option value="3">3</option>
|
||||
<option value="4">4</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<PromptBoxImage
|
||||
@@ -53,6 +68,7 @@ const TextToImage = ({ imageMediaId, imageUrl }: TextToImageProps) => {
|
||||
console.log("Using job context");
|
||||
return jobContext;
|
||||
}}
|
||||
numberImages={numberImages}
|
||||
model={selectedModel}
|
||||
modelInfo={selectedModelInfo}
|
||||
imageMediaId={imageMediaId}
|
||||
|
||||
@@ -8,6 +8,7 @@ import { Button, ToggleButton } from "@storyteller/ui-button";
|
||||
import { Modal } from "@storyteller/ui-modal";
|
||||
import {
|
||||
EnqueueTextToImage,
|
||||
EnqueueTextToImageRequest,
|
||||
EnqueueTextToImageSize,
|
||||
} from "@storyteller/tauri-api";
|
||||
import {
|
||||
@@ -37,6 +38,7 @@ interface PromptBoxImageProps {
|
||||
model: string;
|
||||
modelInfo?: ModelInfo;
|
||||
imageMediaId?: string;
|
||||
numberImages: number;
|
||||
url?: string;
|
||||
}
|
||||
|
||||
@@ -46,6 +48,7 @@ export const PromptBoxImage = ({
|
||||
model,
|
||||
modelInfo,
|
||||
imageMediaId,
|
||||
numberImages,
|
||||
url,
|
||||
}: PromptBoxImageProps) => {
|
||||
useSignals();
|
||||
@@ -210,11 +213,17 @@ export const PromptBoxImage = ({
|
||||
|
||||
const aspectRatio = getCurrentAspectRatio();
|
||||
|
||||
const generateResponse = await EnqueueTextToImage({
|
||||
let request : EnqueueTextToImageRequest = {
|
||||
prompt: prompt,
|
||||
model: modelInfo,
|
||||
aspect_ratio: aspectRatio,
|
||||
});
|
||||
};
|
||||
|
||||
if (numberImages >= 1 && numberImages <= 4) {
|
||||
request.number_images = numberImages;
|
||||
}
|
||||
|
||||
const generateResponse = await EnqueueTextToImage(request);
|
||||
|
||||
console.log("PromptBoxImage - generateResponse", generateResponse);
|
||||
|
||||
|
||||
@@ -17,12 +17,14 @@ export interface EnqueueTextToImageRequest {
|
||||
prompt?: string;
|
||||
model?: ModelInfo | EnqueueTextToImageModel;
|
||||
aspect_ratio?: EnqueueTextToImageSize;
|
||||
number_images?: number;
|
||||
}
|
||||
|
||||
interface EnqueueTextToImageRawRequest {
|
||||
prompt?: string;
|
||||
model?: EnqueueTextToImageModel | string; // TODO: Shouldn't allow string
|
||||
aspect_ratio?: EnqueueTextToImageSize;
|
||||
number_images?: number;
|
||||
}
|
||||
|
||||
export enum EnqueueTextToImageModel {
|
||||
@@ -75,6 +77,10 @@ export const EnqueueTextToImage = async (request: EnqueueTextToImageRequest) : P
|
||||
mutableRequest.aspect_ratio = request.aspect_ratio;
|
||||
}
|
||||
|
||||
if (!!request.number_images) {
|
||||
mutableRequest.number_images = request.number_images;
|
||||
}
|
||||
|
||||
const result = await invoke("enqueue_text_to_image_command", {
|
||||
request: mutableRequest,
|
||||
});
|
||||
|
||||
Reference in new issue
Block a user