mirror of
https://github.com/storytold/artcraft.git
synced 2026-11-03 21:10:58 -05:00
Compare commits
10
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
62947529b1 | ||
|
|
3b5a2d5873 | ||
|
|
6e8d2a6f58 | ||
|
|
518438248f | ||
|
|
5d7835a629 | ||
|
|
a632682fe9 | ||
|
|
7b9854431a | ||
|
|
3e6323e91b | ||
|
|
7edbd788df | ||
|
|
dbe492f197 |
@@ -1,4 +1,4 @@
|
||||
use crate::core::commands::enqueue::image_edit::enqueue_contextual_edit_image_command::EnqueueContextualEditImageSuccessResponse;
|
||||
use crate::core::commands::enqueue::image_edit::enqueue_edit_image_command::EnqueueEditImageSuccessResponse;
|
||||
use crate::core::commands::response::shorthand::ResponseOrErrorMessage;
|
||||
use crate::core::commands::response::success_response_wrapper::SerializeMarker;
|
||||
use crate::core::state::app_preferences::app_preferences_manager::AppPreferencesManager;
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@ use crate::core::commands::enqueue::generate_error::GenerateError;
|
||||
use crate::core::commands::enqueue::image_bg_removal::enqueue_image_bg_removal_command::EnqueueImageBgRemovalCommand;
|
||||
use crate::core::commands::enqueue::image_bg_removal::generic::handle_generic_bg_removal_artcraft::handle_generic_bg_removal_artcraft;
|
||||
use crate::core::commands::enqueue::image_bg_removal::generic::handle_generic_bg_removal_fal::handle_generic_bg_removal_fal;
|
||||
use crate::core::commands::enqueue::image_edit::enqueue_contextual_edit_image_command::EnqueueContextualEditImageCommand;
|
||||
use crate::core::commands::enqueue::image_edit::enqueue_edit_image_command::EnqueueEditImageCommand;
|
||||
use crate::core::commands::enqueue::image_edit::gpt_image_1::handle_gpt_image_1_edit_artcraft::handle_gpt_image_1_edit_artcraft;
|
||||
use crate::core::commands::enqueue::image_edit::gpt_image_1::handle_gpt_image_1_edit_sora::handle_gpt_image_1_edit_sora;
|
||||
use crate::core::commands::enqueue::task_enqueue_success::TaskEnqueueSuccess;
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
use crate::core::commands::enqueue::generate_error::{BadInputReason, GenerateError, MissingCredentialsReason, ProviderFailureReason};
|
||||
use crate::core::commands::enqueue::image_bg_removal::enqueue_image_bg_removal_command::EnqueueImageBgRemovalCommand;
|
||||
use crate::core::commands::enqueue::image_edit::enqueue_contextual_edit_image_command::{EditImageQuality, EditImageSize, EnqueueContextualEditImageCommand};
|
||||
use crate::core::commands::enqueue::image_edit::enqueue_edit_image_command::{EditImageQuality, EditImageSize, EnqueueEditImageCommand};
|
||||
use crate::core::commands::enqueue::task_enqueue_success::TaskEnqueueSuccess;
|
||||
use crate::core::events::basic_sendable_event_trait::BasicSendableEvent;
|
||||
use crate::core::events::generation_events::common::{GenerationAction, GenerationServiceProvider};
|
||||
|
||||
+70
-26
@@ -1,8 +1,9 @@
|
||||
use crate::core::commands::enqueue::common::notify_frontend_of_errors::notify_frontend_of_errors;
|
||||
use crate::core::commands::enqueue::generate_error::{BadInputReason, GenerateError};
|
||||
use crate::core::commands::enqueue::image_edit::flux_kontext::handle_flux_kontext_edit::handle_flux_kontext_edit;
|
||||
use crate::core::commands::enqueue::image_edit::gemini_25_flash::handle_gemini_25_flash_edit::handle_gemini_25_flash_edit;
|
||||
use crate::core::commands::enqueue::image_edit::gpt_image_1::handle_gpt_image_1_edit::handle_gpt_image_1_edit;
|
||||
use crate::core::commands::enqueue::image_edit::nano_banana::handle_nano_banana_edit::handle_nano_banana_edit;
|
||||
use crate::core::commands::enqueue::image_edit::nano_banana_pro::handle_nano_banana_pro_edit::handle_nano_banana_pro_edit;
|
||||
use crate::core::commands::enqueue::task_enqueue_success::TaskEnqueueSuccess;
|
||||
use crate::core::commands::response::failure_response_wrapper::{CommandErrorResponseWrapper, CommandErrorStatus};
|
||||
use crate::core::commands::response::shorthand::{Response, ResponseOrErrorType};
|
||||
@@ -36,8 +37,8 @@ use openai_sora_client::requests::image_gen::common::{ImageSize, NumImages};
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use sqlite_tasks::queries::create_task::{create_task, CreateTaskArgs};
|
||||
use std::time::Duration;
|
||||
use storyteller_client::error::storyteller_error::StorytellerError;
|
||||
use storyteller_client::endpoints::media_files::get_media_file::get_media_file;
|
||||
use storyteller_client::error::storyteller_error::StorytellerError;
|
||||
use storyteller_client::utils::api_host::ApiHost;
|
||||
use tauri::{AppHandle, Manager, State};
|
||||
use tokens::tokens::media_files::MediaFileToken;
|
||||
@@ -46,13 +47,19 @@ use tokens::tokens::media_files::MediaFileToken;
|
||||
/// Don't change the serializations without coordinating with the frontend.
|
||||
#[derive(Deserialize, Debug, Copy, Clone)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum ContextualImageEditModel {
|
||||
pub enum ImageEditModel {
|
||||
#[serde(rename = "flux_pro_kontext_max")]
|
||||
FluxProKontextMax,
|
||||
|
||||
#[serde(rename = "gemini_25_flash")]
|
||||
Gemini25Flash,
|
||||
|
||||
#[serde(rename = "nano_banana")]
|
||||
NanoBanana,
|
||||
|
||||
#[serde(rename = "nano_banana_pro")]
|
||||
NanoBananaPro,
|
||||
|
||||
#[serde(rename = "gpt_image_1")]
|
||||
GptImage1,
|
||||
|
||||
@@ -64,9 +71,9 @@ pub enum ContextualImageEditModel {
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
pub struct EnqueueContextualEditImageCommand {
|
||||
pub struct EnqueueEditImageCommand {
|
||||
/// The model to use.
|
||||
pub model: Option<ContextualImageEditModel>,
|
||||
pub model: Option<ImageEditModel>,
|
||||
|
||||
/// Images to use for the image edit.
|
||||
/// The first image is typically a 2D canvas or 3D stage, but doesn't have to be.
|
||||
@@ -93,6 +100,9 @@ pub struct EnqueueContextualEditImageCommand {
|
||||
/// Image quality.
|
||||
pub image_quality: Option<EditImageQuality>,
|
||||
|
||||
/// Image resolution
|
||||
pub image_resolution: Option<EditImageResolution>,
|
||||
|
||||
/// OPTIONAL.
|
||||
/// Name of the frontend caller.
|
||||
/// We'll use this to selectively trigger events.
|
||||
@@ -127,9 +137,20 @@ pub enum EditImageQuality {
|
||||
Low,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug, Copy, Clone)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum EditImageResolution {
|
||||
/// 1K - nano banana pro
|
||||
OneK,
|
||||
/// 2K - nano banana pro
|
||||
TwoK,
|
||||
/// 4K - nano banana pro
|
||||
FourK,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Debug)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum EnqueueContextualEditImageErrorType {
|
||||
pub enum EnqueueEditImageErrorType {
|
||||
/// Caller didn't specify a model
|
||||
ModelNotSpecified,
|
||||
|
||||
@@ -153,15 +174,15 @@ pub enum EnqueueContextualEditImageErrorType {
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct EnqueueContextualEditImageSuccessResponse {
|
||||
pub struct EnqueueEditImageSuccessResponse {
|
||||
}
|
||||
|
||||
impl SerializeMarker for EnqueueContextualEditImageSuccessResponse {}
|
||||
impl SerializeMarker for EnqueueEditImageSuccessResponse {}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn enqueue_contextual_edit_image_command(
|
||||
pub async fn enqueue_edit_image_command(
|
||||
app: AppHandle,
|
||||
request: EnqueueContextualEditImageCommand,
|
||||
request: EnqueueEditImageCommand,
|
||||
app_data_root: State<'_, AppDataRoot>,
|
||||
app_env_configs: State<'_, AppEnvConfigs>,
|
||||
provider_priority_store: State<'_, ProviderPriorityStore>,
|
||||
@@ -169,9 +190,9 @@ pub async fn enqueue_contextual_edit_image_command(
|
||||
storyteller_creds_manager: State<'_, StorytellerCredentialManager>,
|
||||
sora_creds_manager: State<'_, SoraCredentialManager>,
|
||||
sora_task_queue: State<'_, SoraTaskQueue>,
|
||||
) -> ResponseOrErrorType<EnqueueContextualEditImageSuccessResponse, EnqueueContextualEditImageErrorType> {
|
||||
) -> ResponseOrErrorType<EnqueueEditImageSuccessResponse, EnqueueEditImageErrorType> {
|
||||
|
||||
info!("enqueue_contextual_edit_image_command called; image media tokens : {:?}, full request: {:?}",
|
||||
info!("enqueue_edit_image_command called; image media tokens : {:?}, full request: {:?}",
|
||||
&request.image_media_tokens, &request);
|
||||
|
||||
let result = handle_request(
|
||||
@@ -219,13 +240,13 @@ pub async fn enqueue_contextual_edit_image_command(
|
||||
|
||||
CreditsBalanceChangedEvent{}.send_infallible(&app);
|
||||
|
||||
Ok(EnqueueContextualEditImageSuccessResponse {}.into())
|
||||
Ok(EnqueueEditImageSuccessResponse {}.into())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn handle_request(
|
||||
request: &EnqueueContextualEditImageCommand,
|
||||
request: &EnqueueEditImageCommand,
|
||||
app: &AppHandle,
|
||||
app_data_root: &AppDataRoot,
|
||||
app_env_configs: &AppEnvConfigs,
|
||||
@@ -235,11 +256,11 @@ pub async fn handle_request(
|
||||
sora_creds_manager: &SoraCredentialManager,
|
||||
sora_task_queue: &SoraTaskQueue,
|
||||
) -> Result<TaskEnqueueSuccess, GenerateError> {
|
||||
let success_event= match request.model {
|
||||
let mut success_event= match request.model {
|
||||
None => {
|
||||
return Err(GenerateError::no_model_specified())
|
||||
}
|
||||
Some(ContextualImageEditModel::FluxProKontextMax) => {
|
||||
Some(ImageEditModel::FluxProKontextMax) => {
|
||||
handle_flux_kontext_edit(
|
||||
request,
|
||||
app,
|
||||
@@ -251,8 +272,8 @@ pub async fn handle_request(
|
||||
sora_task_queue,
|
||||
).await?
|
||||
}
|
||||
Some(ContextualImageEditModel::Gemini25Flash) => {
|
||||
handle_gemini_25_flash_edit(
|
||||
Some(ImageEditModel::Gemini25Flash | ImageEditModel::NanoBanana) => {
|
||||
handle_nano_banana_edit(
|
||||
request,
|
||||
app,
|
||||
app_data_root,
|
||||
@@ -263,7 +284,19 @@ pub async fn handle_request(
|
||||
sora_task_queue,
|
||||
).await?
|
||||
}
|
||||
Some(ContextualImageEditModel::GptImage1) => {
|
||||
Some(ImageEditModel::NanoBananaPro) => {
|
||||
handle_nano_banana_pro_edit(
|
||||
request,
|
||||
app,
|
||||
app_data_root,
|
||||
app_env_configs,
|
||||
provider_priority_store,
|
||||
storyteller_creds_manager,
|
||||
sora_creds_manager,
|
||||
sora_task_queue,
|
||||
).await?
|
||||
}
|
||||
Some(ImageEditModel::GptImage1) => {
|
||||
handle_gpt_image_1_edit(
|
||||
request,
|
||||
app,
|
||||
@@ -276,7 +309,18 @@ pub async fn handle_request(
|
||||
).await?
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
match request.frontend_caller {
|
||||
Some(TauriCommandCaller::ImageEditor) => {
|
||||
// NB: Certain image generations enqueued in the inpainting editor are classed
|
||||
// as "image_generation" and won't update the inpainting UI. This is a hack to
|
||||
// temporarily fix the UI event binding.
|
||||
// TODO: The frontend shouldn't require us to change the type to inpainting.
|
||||
success_event.task_type = TaskType::ImageInpaintEdit;
|
||||
}
|
||||
_ => {} // fall-through
|
||||
}
|
||||
|
||||
let result = success_event
|
||||
.insert_into_task_database_with_frontend_payload(
|
||||
task_database,
|
||||
@@ -294,31 +338,31 @@ pub async fn handle_request(
|
||||
Ok(success_event)
|
||||
}
|
||||
|
||||
fn error_to_tauri_response(error: GenerateError) -> CommandErrorResponseWrapper<EnqueueContextualEditImageErrorType, ()> {
|
||||
fn error_to_tauri_response(error: GenerateError) -> CommandErrorResponseWrapper<EnqueueEditImageErrorType, ()> {
|
||||
let mut status = CommandErrorStatus::ServerError;
|
||||
let mut error_type = EnqueueContextualEditImageErrorType::ServerError;
|
||||
let mut error_type = EnqueueEditImageErrorType::ServerError;
|
||||
let mut error_message = "A server error occurred. Please try again. If it continues, please tell our staff about the problem.".to_string();
|
||||
|
||||
match error {
|
||||
GenerateError::BadInput(BadInputReason::NoModelSpecified) => {
|
||||
status = CommandErrorStatus::BadRequest;
|
||||
error_type = EnqueueContextualEditImageErrorType::ModelNotSpecified;
|
||||
error_type = EnqueueEditImageErrorType::ModelNotSpecified;
|
||||
error_message = "No model specified for image generation".to_string();
|
||||
}
|
||||
GenerateError::NoProviderAvailable => {
|
||||
status = CommandErrorStatus::ServerError;
|
||||
error_type = EnqueueContextualEditImageErrorType::NoProviderAvailable;
|
||||
error_type = EnqueueEditImageErrorType::NoProviderAvailable;
|
||||
error_message = "No configured provider available for image generation".to_string();
|
||||
}
|
||||
GenerateError::BadInput(BadInputReason::InvalidNumberOfRequestedImages { min, max, requested }) => {
|
||||
status = CommandErrorStatus::BadRequest;
|
||||
error_type = EnqueueContextualEditImageErrorType::BadRequest;
|
||||
error_type = EnqueueEditImageErrorType::BadRequest;
|
||||
error_message = format!("Invalid number of images requested ({}). Must be between {} and {}", requested, min, max);
|
||||
|
||||
}
|
||||
GenerateError::BadInput(BadInputReason::InvalidNumberOfInputImages{ min, max, provided }) => {
|
||||
status = CommandErrorStatus::BadRequest;
|
||||
error_type = EnqueueContextualEditImageErrorType::BadRequest;
|
||||
error_type = EnqueueEditImageErrorType::BadRequest;
|
||||
error_message = format!("Invalid number of input images ({}). Must be between {} and {}", provided, min, max);
|
||||
}
|
||||
_ => {} // Fall-through for now
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
use crate::core::commands::enqueue::generate_error::GenerateError;
|
||||
use crate::core::commands::enqueue::image_edit::enqueue_contextual_edit_image_command::EnqueueContextualEditImageCommand;
|
||||
use crate::core::commands::enqueue::image_edit::enqueue_edit_image_command::EnqueueEditImageCommand;
|
||||
use crate::core::commands::enqueue::image_edit::flux_kontext::handle_flux_kontext_edit_artcraft::handle_flux_kontext_edit_artcraft;
|
||||
use crate::core::commands::enqueue::image_edit::gpt_image_1::handle_gpt_image_1_edit_artcraft::handle_gpt_image_1_edit_artcraft;
|
||||
use crate::core::commands::enqueue::image_edit::gpt_image_1::handle_gpt_image_1_edit_sora::handle_gpt_image_1_edit_sora;
|
||||
@@ -14,7 +14,7 @@ use log::info;
|
||||
use tauri::AppHandle;
|
||||
|
||||
pub async fn handle_flux_kontext_edit(
|
||||
request: &EnqueueContextualEditImageCommand,
|
||||
request: &EnqueueEditImageCommand,
|
||||
app: &AppHandle,
|
||||
app_data_root: &AppDataRoot,
|
||||
app_env_configs: &AppEnvConfigs,
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
use crate::core::commands::enqueue::generate_error::{BadInputReason, GenerateError};
|
||||
use crate::core::commands::enqueue::image_edit::enqueue_contextual_edit_image_command::{EditImageQuality, EditImageSize, EnqueueContextualEditImageCommand};
|
||||
use crate::core::commands::enqueue::image_edit::enqueue_edit_image_command::{EditImageQuality, EditImageSize, EnqueueEditImageCommand};
|
||||
use crate::core::commands::enqueue::task_enqueue_success::TaskEnqueueSuccess;
|
||||
use crate::core::events::basic_sendable_event_trait::BasicSendableEvent;
|
||||
use crate::core::events::generation_events::common::{GenerationAction, GenerationModel, GenerationServiceProvider};
|
||||
@@ -30,7 +30,7 @@ use storyteller_client::endpoints::generate::image::generate_flux_pro_11_ultra_t
|
||||
use tauri::AppHandle;
|
||||
|
||||
pub async fn handle_flux_kontext_edit_artcraft(
|
||||
request: &EnqueueContextualEditImageCommand,
|
||||
request: &EnqueueEditImageCommand,
|
||||
app: &AppHandle,
|
||||
app_data_root: &AppDataRoot,
|
||||
app_env_configs: &AppEnvConfigs,
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
pub (super) mod handle_gemini_25_flash_edit;
|
||||
pub (super) mod handle_gemini_25_flash_edit_artcraft;
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
use crate::core::commands::enqueue::generate_error::GenerateError;
|
||||
use crate::core::commands::enqueue::image_edit::enqueue_contextual_edit_image_command::EnqueueContextualEditImageCommand;
|
||||
use crate::core::commands::enqueue::image_edit::enqueue_edit_image_command::EnqueueEditImageCommand;
|
||||
use crate::core::commands::enqueue::image_edit::gpt_image_1::handle_gpt_image_1_edit_artcraft::handle_gpt_image_1_edit_artcraft;
|
||||
use crate::core::commands::enqueue::image_edit::gpt_image_1::handle_gpt_image_1_edit_sora::handle_gpt_image_1_edit_sora;
|
||||
use crate::core::commands::enqueue::task_enqueue_success::TaskEnqueueSuccess;
|
||||
@@ -15,7 +15,7 @@ use tauri::AppHandle;
|
||||
pub(super) const MAX_IMAGES: usize = 10;
|
||||
|
||||
pub async fn handle_gpt_image_1_edit(
|
||||
request: &EnqueueContextualEditImageCommand,
|
||||
request: &EnqueueEditImageCommand,
|
||||
app: &AppHandle,
|
||||
app_data_root: &AppDataRoot,
|
||||
app_env_configs: &AppEnvConfigs,
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
use crate::core::commands::enqueue::generate_error::{BadInputReason, GenerateError};
|
||||
use crate::core::commands::enqueue::image_edit::enqueue_contextual_edit_image_command::{EditImageQuality, EditImageSize, EnqueueContextualEditImageCommand};
|
||||
use crate::core::commands::enqueue::image_edit::enqueue_edit_image_command::{EditImageQuality, EditImageSize, EnqueueEditImageCommand};
|
||||
use crate::core::commands::enqueue::image_edit::gpt_image_1::handle_gpt_image_1_edit::MAX_IMAGES;
|
||||
use crate::core::commands::enqueue::task_enqueue_success::TaskEnqueueSuccess;
|
||||
use crate::core::events::basic_sendable_event_trait::BasicSendableEvent;
|
||||
@@ -29,7 +29,7 @@ use storyteller_client::endpoints::generate::image::generate_flux_pro_11_ultra_t
|
||||
use tauri::AppHandle;
|
||||
|
||||
pub async fn handle_gpt_image_1_edit_artcraft(
|
||||
request: &EnqueueContextualEditImageCommand,
|
||||
request: &EnqueueEditImageCommand,
|
||||
app: &AppHandle,
|
||||
app_data_root: &AppDataRoot,
|
||||
app_env_configs: &AppEnvConfigs,
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
use crate::core::commands::enqueue::generate_error::{BadInputReason, GenerateError};
|
||||
use crate::core::commands::enqueue::image_edit::enqueue_contextual_edit_image_command::{EditImageSize, EnqueueContextualEditImageCommand, EnqueueContextualEditImageErrorType};
|
||||
use crate::core::commands::enqueue::image_edit::enqueue_edit_image_command::{EditImageSize, EnqueueEditImageCommand, EnqueueEditImageErrorType};
|
||||
use crate::core::commands::enqueue::image_edit::gpt_image_1::handle_gpt_image_1_edit::MAX_IMAGES;
|
||||
use crate::core::commands::enqueue::task_enqueue_success::TaskEnqueueSuccess;
|
||||
use crate::core::commands::response::failure_response_wrapper::{CommandErrorResponseWrapper, CommandErrorStatus};
|
||||
@@ -42,7 +42,7 @@ const SORA_IMAGE_REMIX_TIMEOUT: Duration = Duration::from_millis(1000 * 30); //
|
||||
const DEFAULT_ASPECT_RATIO : EditImageSize = EditImageSize::Square;
|
||||
|
||||
pub async fn handle_gpt_image_1_edit_sora(
|
||||
request: &EnqueueContextualEditImageCommand,
|
||||
request: &EnqueueEditImageCommand,
|
||||
app: &AppHandle,
|
||||
app_data_root: &AppDataRoot,
|
||||
app_env_configs: &AppEnvConfigs,
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
pub mod enqueue_contextual_edit_image_command;
|
||||
pub mod enqueue_edit_image_command;
|
||||
pub mod gpt_image_1;
|
||||
mod flux_kontext;
|
||||
mod gemini_25_flash;
|
||||
mod nano_banana;
|
||||
mod nano_banana_pro;
|
||||
|
||||
+6
-6
@@ -1,6 +1,5 @@
|
||||
use crate::core::commands::enqueue::generate_error::GenerateError;
|
||||
use crate::core::commands::enqueue::image_edit::enqueue_contextual_edit_image_command::EnqueueContextualEditImageCommand;
|
||||
use crate::core::commands::enqueue::image_edit::gemini_25_flash::handle_gemini_25_flash_edit_artcraft::handle_gemini_25_flash_edit_artcraft;
|
||||
use crate::core::commands::enqueue::image_edit::enqueue_edit_image_command::EnqueueEditImageCommand;
|
||||
use crate::core::commands::enqueue::image_edit::gpt_image_1::handle_gpt_image_1_edit_artcraft::handle_gpt_image_1_edit_artcraft;
|
||||
use crate::core::commands::enqueue::image_edit::gpt_image_1::handle_gpt_image_1_edit_sora::handle_gpt_image_1_edit_sora;
|
||||
use crate::core::commands::enqueue::task_enqueue_success::TaskEnqueueSuccess;
|
||||
@@ -12,9 +11,10 @@ use crate::services::sora::state::sora_task_queue::SoraTaskQueue;
|
||||
use crate::services::storyteller::state::storyteller_credential_manager::StorytellerCredentialManager;
|
||||
use log::info;
|
||||
use tauri::AppHandle;
|
||||
use crate::core::commands::enqueue::image_edit::nano_banana::handle_nano_banana_edit_artcraft::handle_nano_banana_edit_artcraft;
|
||||
|
||||
pub async fn handle_gemini_25_flash_edit(
|
||||
request: &EnqueueContextualEditImageCommand,
|
||||
pub async fn handle_nano_banana_edit(
|
||||
request: &EnqueueEditImageCommand,
|
||||
app: &AppHandle,
|
||||
app_data_root: &AppDataRoot,
|
||||
app_env_configs: &AppEnvConfigs,
|
||||
@@ -39,8 +39,8 @@ pub async fn handle_gemini_25_flash_edit(
|
||||
// Fallthrough
|
||||
}
|
||||
Provider::Artcraft => {
|
||||
info!("Dispatching gemini 2.5 flash (edit) via Artcraft...");
|
||||
return handle_gemini_25_flash_edit_artcraft(
|
||||
info!("Dispatching Nano Banana (edit) via Artcraft...");
|
||||
return handle_nano_banana_edit_artcraft(
|
||||
request,
|
||||
app,
|
||||
app_data_root,
|
||||
+6
-6
@@ -1,5 +1,5 @@
|
||||
use crate::core::commands::enqueue::generate_error::{BadInputReason, GenerateError};
|
||||
use crate::core::commands::enqueue::image_edit::enqueue_contextual_edit_image_command::{EditImageQuality, EditImageSize, EnqueueContextualEditImageCommand};
|
||||
use crate::core::commands::enqueue::image_edit::enqueue_edit_image_command::{EditImageQuality, EditImageSize, EnqueueEditImageCommand};
|
||||
use crate::core::commands::enqueue::task_enqueue_success::TaskEnqueueSuccess;
|
||||
use crate::core::events::basic_sendable_event_trait::BasicSendableEvent;
|
||||
use crate::core::events::generation_events::common::{GenerationAction, GenerationModel, GenerationServiceProvider};
|
||||
@@ -29,8 +29,8 @@ use tauri::AppHandle;
|
||||
|
||||
pub(super) const MAX_IMAGES: usize = 10;
|
||||
|
||||
pub async fn handle_gemini_25_flash_edit_artcraft(
|
||||
request: &EnqueueContextualEditImageCommand,
|
||||
pub async fn handle_nano_banana_edit_artcraft(
|
||||
request: &EnqueueEditImageCommand,
|
||||
app: &AppHandle,
|
||||
app_data_root: &AppDataRoot,
|
||||
app_env_configs: &AppEnvConfigs,
|
||||
@@ -44,7 +44,7 @@ pub async fn handle_gemini_25_flash_edit_artcraft(
|
||||
},
|
||||
};
|
||||
|
||||
info!("Calling Artcraft gemini 2.5 flash (edit) ...");
|
||||
info!("Calling Artcraft Nano Banana (edit) ...");
|
||||
|
||||
let uuid_idempotency_token = generate_random_uuid();
|
||||
|
||||
@@ -98,12 +98,12 @@ pub async fn handle_gemini_25_flash_edit_artcraft(
|
||||
let job_id = match result {
|
||||
Ok(enqueued) => {
|
||||
// TODO(bt,2025-07-05): Enqueue job token?
|
||||
info!("Successfully enqueued Artcraft gpt-image-1. Job token: {}",
|
||||
info!("Successfully enqueued Artcraft Nano Banana. Job token: {}",
|
||||
enqueued.inference_job_token);
|
||||
enqueued.inference_job_token
|
||||
}
|
||||
Err(err) => {
|
||||
error!("Failed to use Artcraft gpt-image-1: {:?}", err);
|
||||
error!("Failed to use Artcraft Nano Banana: {:?}", err);
|
||||
return Err(GenerateError::from(err));
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,2 @@
|
||||
pub (super) mod handle_nano_banana_edit;
|
||||
pub (super) mod handle_nano_banana_edit_artcraft;
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
use crate::core::commands::enqueue::generate_error::GenerateError;
|
||||
use crate::core::commands::enqueue::image_edit::enqueue_edit_image_command::EnqueueEditImageCommand;
|
||||
use crate::core::commands::enqueue::image_edit::nano_banana_pro::handle_nano_banana_pro_edit_artcraft::handle_nano_banana_pro_edit_artcraft;
|
||||
use crate::core::commands::enqueue::task_enqueue_success::TaskEnqueueSuccess;
|
||||
use crate::core::state::app_env_configs::app_env_configs::AppEnvConfigs;
|
||||
use crate::core::state::data_dir::app_data_root::AppDataRoot;
|
||||
use crate::core::state::provider_priority::{Provider, ProviderPriorityStore};
|
||||
use crate::services::sora::state::sora_credential_manager::SoraCredentialManager;
|
||||
use crate::services::sora::state::sora_task_queue::SoraTaskQueue;
|
||||
use crate::services::storyteller::state::storyteller_credential_manager::StorytellerCredentialManager;
|
||||
use log::info;
|
||||
use tauri::AppHandle;
|
||||
|
||||
pub async fn handle_nano_banana_pro_edit(
|
||||
request: &EnqueueEditImageCommand,
|
||||
app: &AppHandle,
|
||||
app_data_root: &AppDataRoot,
|
||||
app_env_configs: &AppEnvConfigs,
|
||||
provider_priority_store: &ProviderPriorityStore,
|
||||
storyteller_creds_manager: &StorytellerCredentialManager,
|
||||
sora_creds_manager: &SoraCredentialManager,
|
||||
sora_task_queue: &SoraTaskQueue,
|
||||
) -> Result<TaskEnqueueSuccess, GenerateError> {
|
||||
|
||||
let priority = provider_priority_store.get_priority()?;
|
||||
|
||||
// TODO: Check if providers are available before proceeding.
|
||||
|
||||
info!("Providers by priority: {:?}", priority);
|
||||
|
||||
for provider in priority.iter() {
|
||||
match provider {
|
||||
Provider::Fal => {
|
||||
// Fallthrough (TODO)
|
||||
}
|
||||
Provider::Sora => {
|
||||
// Fallthrough
|
||||
}
|
||||
Provider::Artcraft => {
|
||||
info!("Dispatching Nano Banana Pro (edit) via Artcraft...");
|
||||
return handle_nano_banana_pro_edit_artcraft(
|
||||
request,
|
||||
app,
|
||||
app_data_root,
|
||||
app_env_configs,
|
||||
storyteller_creds_manager
|
||||
).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Err(GenerateError::NoProviderAvailable)
|
||||
}
|
||||
+137
@@ -0,0 +1,137 @@
|
||||
use crate::core::commands::enqueue::generate_error::{BadInputReason, GenerateError};
|
||||
use crate::core::commands::enqueue::image_edit::enqueue_edit_image_command::{EditImageQuality, EditImageResolution, EditImageSize, EnqueueEditImageCommand};
|
||||
use crate::core::commands::enqueue::task_enqueue_success::TaskEnqueueSuccess;
|
||||
use crate::core::events::basic_sendable_event_trait::BasicSendableEvent;
|
||||
use crate::core::events::generation_events::common::{GenerationAction, GenerationModel, GenerationServiceProvider};
|
||||
use crate::core::events::generation_events::generation_enqueue_failure_event::GenerationEnqueueFailureEvent;
|
||||
use crate::core::state::app_env_configs::app_env_configs::AppEnvConfigs;
|
||||
use crate::core::state::data_dir::app_data_root::AppDataRoot;
|
||||
use crate::core::state::provider_priority::ProviderPriorityStore;
|
||||
use crate::services::sora::state::sora_credential_manager::SoraCredentialManager;
|
||||
use crate::services::sora::state::sora_task_queue::SoraTaskQueue;
|
||||
use crate::services::storyteller::state::storyteller_credential_manager::StorytellerCredentialManager;
|
||||
use anyhow::anyhow;
|
||||
use artcraft_api_defs::generate::image::edit::flux_pro_kontext_max_edit_image::{FluxProKontextMaxEditImageNumImages, FluxProKontextMaxEditImageRequest};
|
||||
use artcraft_api_defs::generate::image::edit::gemini_25_flash_edit_image::{Gemini25FlashEditImageNumImages, Gemini25FlashEditImageRequest};
|
||||
use artcraft_api_defs::generate::image::edit::gpt_image_1_edit_image::{GptImage1EditImageImageQuality, GptImage1EditImageImageSize, GptImage1EditImageNumImages, GptImage1EditImageRequest};
|
||||
use artcraft_api_defs::generate::image::generate_flux_1_dev_text_to_image::GenerateFlux1DevTextToImageRequest;
|
||||
use artcraft_api_defs::generate::image::generate_flux_1_schnell_text_to_image::GenerateFlux1SchnellTextToImageRequest;
|
||||
use artcraft_api_defs::generate::image::generate_flux_pro_11_text_to_image::GenerateFluxPro11TextToImageRequest;
|
||||
use artcraft_api_defs::generate::image::generate_flux_pro_11_ultra_text_to_image::GenerateFluxPro11UltraTextToImageRequest;
|
||||
use enums::common::generation_provider::GenerationProvider;
|
||||
use enums::tauri::tasks::task_type::TaskType;
|
||||
use idempotency::uuid::generate_random_uuid;
|
||||
use log::{error, info};
|
||||
use storyteller_client::endpoints::generate::image::edit::flux_pro_kontext_max_edit_image::flux_pro_kontext_max_edit_image;
|
||||
use storyteller_client::endpoints::generate::image::edit::gemini_25_flash_edit_image::gemini_25_flash_edit_image;
|
||||
use storyteller_client::endpoints::generate::image::edit::gpt_image_1_edit_image::gpt_image_1_edit_image;
|
||||
use tauri::AppHandle;
|
||||
use artcraft_api_defs::generate::image::multi_function::nano_banana_pro_multi_function_image_gen::{NanoBananaProMultiFunctionImageGenAspectRatio, NanoBananaProMultiFunctionImageGenImageResolution, NanoBananaProMultiFunctionImageGenNumImages, NanoBananaProMultiFunctionImageGenRequest};
|
||||
use storyteller_client::endpoints::generate::image::multi_function::nano_banana_pro_multi_function_image_gen_image::nano_banana_pro_multi_function_image_gen;
|
||||
use crate::core::commands::enqueue::image_edit::enqueue_edit_image_command::ImageEditModel::NanoBanana;
|
||||
|
||||
pub(super) const MAX_IMAGES: usize = 10;
|
||||
|
||||
pub async fn handle_nano_banana_pro_edit_artcraft(
|
||||
request: &EnqueueEditImageCommand,
|
||||
app: &AppHandle,
|
||||
app_data_root: &AppDataRoot,
|
||||
app_env_configs: &AppEnvConfigs,
|
||||
storyteller_creds_manager: &StorytellerCredentialManager,
|
||||
) -> Result<TaskEnqueueSuccess, GenerateError> {
|
||||
|
||||
let creds = match storyteller_creds_manager.get_credentials()? {
|
||||
Some(creds) => creds,
|
||||
None => {
|
||||
return Err(GenerateError::needs_storyteller_credentials());
|
||||
},
|
||||
};
|
||||
|
||||
info!("Calling Artcraft Nano Banana (edit) ...");
|
||||
|
||||
let uuid_idempotency_token = generate_random_uuid();
|
||||
|
||||
let num_images = match request.image_count {
|
||||
None => None,
|
||||
Some(1) => Some(NanoBananaProMultiFunctionImageGenNumImages::One),
|
||||
Some(2) => Some(NanoBananaProMultiFunctionImageGenNumImages::Two),
|
||||
Some(3) => Some(NanoBananaProMultiFunctionImageGenNumImages::Three),
|
||||
Some(4) => Some(NanoBananaProMultiFunctionImageGenNumImages::Four),
|
||||
Some(other) => {
|
||||
return Err(GenerateError::BadInput(BadInputReason::InvalidNumberOfRequestedImages {
|
||||
min: 1,
|
||||
max: 4,
|
||||
requested: other,
|
||||
}));
|
||||
},
|
||||
};
|
||||
|
||||
let aspect_ratio = request.aspect_ratio
|
||||
.map(|aspect_ratio| match aspect_ratio {
|
||||
EditImageSize::Auto => None,
|
||||
EditImageSize::Square => Some(NanoBananaProMultiFunctionImageGenAspectRatio::OneByOne),
|
||||
EditImageSize::Wide => Some(NanoBananaProMultiFunctionImageGenAspectRatio::SixteenByNine),
|
||||
EditImageSize::Tall => Some(NanoBananaProMultiFunctionImageGenAspectRatio::NineBySixteen),
|
||||
})
|
||||
.flatten();
|
||||
|
||||
let resolution = request.image_resolution
|
||||
.map(|image_resolution| match image_resolution {
|
||||
EditImageResolution::OneK => NanoBananaProMultiFunctionImageGenImageResolution::OneK,
|
||||
EditImageResolution::TwoK => NanoBananaProMultiFunctionImageGenImageResolution::TwoK,
|
||||
EditImageResolution::FourK => NanoBananaProMultiFunctionImageGenImageResolution::FourK,
|
||||
});
|
||||
|
||||
let mut media_tokens = Vec::with_capacity(10);
|
||||
|
||||
if let Some(scene_image_media_token) = request.scene_image_media_token.clone() {
|
||||
media_tokens.push(scene_image_media_token);
|
||||
}
|
||||
|
||||
if let Some(image_media_tokens) = request.image_media_tokens.as_ref() {
|
||||
media_tokens.extend_from_slice(image_media_tokens);
|
||||
}
|
||||
|
||||
if media_tokens.len() > MAX_IMAGES {
|
||||
return Err(GenerateError::BadInput(BadInputReason::InvalidNumberOfInputImages {
|
||||
min: 1,
|
||||
max: MAX_IMAGES as u32,
|
||||
provided: media_tokens.len() as u32,
|
||||
}));
|
||||
}
|
||||
|
||||
let request = NanoBananaProMultiFunctionImageGenRequest {
|
||||
uuid_idempotency_token,
|
||||
prompt: Some(request.prompt.clone()),
|
||||
image_media_tokens: Some(media_tokens),
|
||||
num_images,
|
||||
resolution,
|
||||
aspect_ratio,
|
||||
};
|
||||
|
||||
let result = nano_banana_pro_multi_function_image_gen(
|
||||
&app_env_configs.storyteller_host,
|
||||
Some(&creds),
|
||||
request,
|
||||
).await;
|
||||
|
||||
let job_id = match result {
|
||||
Ok(enqueued) => {
|
||||
// TODO(bt,2025-07-05): Enqueue job token?
|
||||
info!("Successfully enqueued Artcraft Nano Banana. Job token: {}",
|
||||
enqueued.inference_job_token);
|
||||
enqueued.inference_job_token
|
||||
}
|
||||
Err(err) => {
|
||||
error!("Failed to use Artcraft Nano Banana: {:?}", err);
|
||||
return Err(GenerateError::from(err));
|
||||
}
|
||||
};
|
||||
|
||||
Ok(TaskEnqueueSuccess {
|
||||
provider: GenerationProvider::Artcraft,
|
||||
model: Some(GenerationModel::NanoBananaPro),
|
||||
provider_job_id: Some(job_id.to_string()),
|
||||
task_type: TaskType::ImageGeneration,
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
pub (super) mod handle_nano_banana_pro_edit;
|
||||
pub (super) mod handle_nano_banana_pro_edit_artcraft;
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
use crate::core::commands::enqueue::generate_error::GenerateError;
|
||||
use crate::core::commands::enqueue::image_edit::enqueue_contextual_edit_image_command::EnqueueContextualEditImageCommand;
|
||||
use crate::core::commands::enqueue::image_edit::enqueue_edit_image_command::EnqueueEditImageCommand;
|
||||
use crate::core::commands::enqueue::image_edit::gpt_image_1::handle_gpt_image_1_edit_artcraft::handle_gpt_image_1_edit_artcraft;
|
||||
use crate::core::commands::enqueue::image_edit::gpt_image_1::handle_gpt_image_1_edit_sora::handle_gpt_image_1_edit_sora;
|
||||
use crate::core::commands::enqueue::image_inpaint::enqueue_image_inpaint_command::EnqueueInpaintImageCommand;
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
use crate::core::commands::enqueue::generate_error::{BadInputReason, GenerateError};
|
||||
use crate::core::commands::enqueue::image_bg_removal::enqueue_image_bg_removal_command::EnqueueImageBgRemovalCommand;
|
||||
use crate::core::commands::enqueue::image_edit::enqueue_contextual_edit_image_command::{EditImageQuality, EditImageSize, EnqueueContextualEditImageCommand};
|
||||
use crate::core::commands::enqueue::image_edit::enqueue_edit_image_command::{EditImageQuality, EditImageSize, EnqueueEditImageCommand};
|
||||
use crate::core::commands::enqueue::image_inpaint::enqueue_image_inpaint_command::EnqueueInpaintImageCommand;
|
||||
use crate::core::commands::enqueue::task_enqueue_success::TaskEnqueueSuccess;
|
||||
use crate::core::events::basic_sendable_event_trait::BasicSendableEvent;
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
use crate::core::commands::enqueue::generate_error::GenerateError;
|
||||
use crate::core::commands::enqueue::image_edit::enqueue_contextual_edit_image_command::EnqueueContextualEditImageCommand;
|
||||
use crate::core::commands::enqueue::image_edit::enqueue_edit_image_command::EnqueueEditImageCommand;
|
||||
use crate::core::commands::enqueue::image_edit::gpt_image_1::handle_gpt_image_1_edit_artcraft::handle_gpt_image_1_edit_artcraft;
|
||||
use crate::core::commands::enqueue::image_edit::gpt_image_1::handle_gpt_image_1_edit_sora::handle_gpt_image_1_edit_sora;
|
||||
use crate::core::commands::enqueue::image_inpaint::enqueue_image_inpaint_command::EnqueueInpaintImageCommand;
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
use crate::core::commands::enqueue::generate_error::{BadInputReason, GenerateError};
|
||||
use crate::core::commands::enqueue::image_bg_removal::enqueue_image_bg_removal_command::EnqueueImageBgRemovalCommand;
|
||||
use crate::core::commands::enqueue::image_edit::enqueue_contextual_edit_image_command::{EditImageQuality, EditImageSize, EnqueueContextualEditImageCommand};
|
||||
use crate::core::commands::enqueue::image_edit::enqueue_edit_image_command::{EditImageQuality, EditImageSize, EnqueueEditImageCommand};
|
||||
use crate::core::commands::enqueue::image_inpaint::enqueue_image_inpaint_command::EnqueueInpaintImageCommand;
|
||||
use crate::core::commands::enqueue::task_enqueue_success::TaskEnqueueSuccess;
|
||||
use crate::core::events::basic_sendable_event_trait::BasicSendableEvent;
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
use crate::core::commands::enqueue::generate_error::GenerateError;
|
||||
use crate::core::commands::enqueue::image_edit::enqueue_contextual_edit_image_command::EnqueueContextualEditImageCommand;
|
||||
use crate::core::commands::enqueue::image_edit::enqueue_edit_image_command::EnqueueEditImageCommand;
|
||||
use crate::core::commands::enqueue::image_inpaint::enqueue_image_inpaint_command::EnqueueInpaintImageCommand;
|
||||
use crate::core::commands::enqueue::image_inpaint::flux_pro_kontext_inpaint::handle_flux_pro_kontext_inpaint_artcraft::handle_flux_pro_kontext_inpaint_artcraft;
|
||||
use crate::core::commands::enqueue::task_enqueue_success::TaskEnqueueSuccess;
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
use crate::core::commands::enqueue::generate_error::{BadInputReason, GenerateError};
|
||||
use crate::core::commands::enqueue::image_bg_removal::enqueue_image_bg_removal_command::EnqueueImageBgRemovalCommand;
|
||||
use crate::core::commands::enqueue::image_edit::enqueue_contextual_edit_image_command::{EditImageQuality, EditImageSize, EnqueueContextualEditImageCommand};
|
||||
use crate::core::commands::enqueue::image_edit::enqueue_edit_image_command::{EditImageQuality, EditImageSize, EnqueueEditImageCommand};
|
||||
use crate::core::commands::enqueue::image_inpaint::enqueue_image_inpaint_command::EnqueueInpaintImageCommand;
|
||||
use crate::core::commands::enqueue::task_enqueue_success::TaskEnqueueSuccess;
|
||||
use crate::core::events::basic_sendable_event_trait::BasicSendableEvent;
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
use crate::core::commands::enqueue::generate_error::GenerateError;
|
||||
use crate::core::commands::enqueue::image_edit::enqueue_contextual_edit_image_command::EnqueueContextualEditImageCommand;
|
||||
use crate::core::commands::enqueue::image_edit::enqueue_edit_image_command::EnqueueEditImageCommand;
|
||||
use crate::core::commands::enqueue::image_inpaint::enqueue_image_inpaint_command::EnqueueInpaintImageCommand;
|
||||
use crate::core::commands::enqueue::image_inpaint::flux_pro_kontext_inpaint::handle_flux_pro_kontext_inpaint_artcraft::handle_flux_pro_kontext_inpaint_artcraft;
|
||||
use crate::core::commands::enqueue::image_inpaint::gemini_25_flash_inpaint::handle_gemini_25_flash_inpaint_artcraft::handle_gemini_25_flash_inpaint_artcraft;
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
use crate::core::commands::enqueue::generate_error::{BadInputReason, GenerateError};
|
||||
use crate::core::commands::enqueue::image_bg_removal::enqueue_image_bg_removal_command::EnqueueImageBgRemovalCommand;
|
||||
use crate::core::commands::enqueue::image_edit::enqueue_contextual_edit_image_command::{EditImageQuality, EditImageSize, EnqueueContextualEditImageCommand};
|
||||
use crate::core::commands::enqueue::image_edit::enqueue_edit_image_command::{EditImageQuality, EditImageSize, EnqueueEditImageCommand};
|
||||
use crate::core::commands::enqueue::image_inpaint::enqueue_image_inpaint_command::EnqueueInpaintImageCommand;
|
||||
use crate::core::commands::enqueue::task_enqueue_success::TaskEnqueueSuccess;
|
||||
use crate::core::events::basic_sendable_event_trait::BasicSendableEvent;
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
use crate::core::commands::enqueue::generate_error::{BadInputReason, GenerateError};
|
||||
use crate::core::commands::enqueue::image_edit::enqueue_contextual_edit_image_command::EnqueueContextualEditImageCommand;
|
||||
use crate::core::commands::enqueue::image_edit::enqueue_edit_image_command::EnqueueEditImageCommand;
|
||||
use crate::core::commands::enqueue::image_inpaint::enqueue_image_inpaint_command::EnqueueInpaintImageCommand;
|
||||
use crate::core::commands::enqueue::image_to_video::enqueue_image_to_video_command::{EnqueueImageToVideoRequest, GrokAspectRatio, SoraOrientation};
|
||||
use crate::core::commands::enqueue::task_enqueue_success::TaskEnqueueSuccess;
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
use crate::core::commands::enqueue::generate_error::GenerateError;
|
||||
use crate::core::commands::enqueue::image_edit::enqueue_contextual_edit_image_command::EnqueueContextualEditImageCommand;
|
||||
use crate::core::commands::enqueue::image_edit::enqueue_edit_image_command::EnqueueEditImageCommand;
|
||||
use crate::core::commands::enqueue::image_inpaint::enqueue_image_inpaint_command::EnqueueInpaintImageCommand;
|
||||
use crate::core::commands::enqueue::image_to_video::enqueue_image_to_video_command::EnqueueImageToVideoRequest;
|
||||
use crate::core::commands::enqueue::image_to_video::sora2::handle_sora2_video_sora::handle_sora2_video_sora;
|
||||
|
||||
@@ -66,6 +66,8 @@ impl TaskEnqueueSuccess{
|
||||
Some(GenerationModel::FluxPro11Ultra) => Some(TaskModelType::FluxPro11Ultra),
|
||||
Some(GenerationModel::FluxProKontextMax) => Some(TaskModelType::FluxProKontextMax),
|
||||
Some(GenerationModel::Gemini25Flash) => Some(TaskModelType::Gemini25Flash),
|
||||
Some(GenerationModel::NanoBanana) => Some(TaskModelType::NanoBanana),
|
||||
Some(GenerationModel::NanoBananaPro) => Some(TaskModelType::NanoBananaPro),
|
||||
Some(GenerationModel::GptImage1) => Some(TaskModelType::GptImage1),
|
||||
Some(GenerationModel::GrokImage) => Some(TaskModelType::GrokImage),
|
||||
Some(GenerationModel::Recraft3) => Some(TaskModelType::Recraft3),
|
||||
|
||||
+34
-3
@@ -1,13 +1,14 @@
|
||||
use crate::core::commands::enqueue::common::notify_frontend_of_errors::notify_frontend_of_errors;
|
||||
use crate::core::commands::enqueue::generate_error::{BadInputReason, GenerateError, MissingCredentialsReason};
|
||||
use crate::core::commands::enqueue::task_enqueue_success::TaskEnqueueSuccess;
|
||||
use crate::core::commands::enqueue::text_to_image::gemini_25_flash::handle_gemini_25_flash::handle_gemini_25_flash;
|
||||
use crate::core::commands::enqueue::text_to_image::generic::handle_image_artcraft::handle_image_artcraft;
|
||||
use crate::core::commands::enqueue::text_to_image::generic::handle_image_fal::handle_image_fal;
|
||||
use crate::core::commands::enqueue::text_to_image::gpt_image_1::handle_gpt_image_1::handle_gpt_image_1;
|
||||
use crate::core::commands::enqueue::text_to_image::gpt_image_1::handle_gpt_image_1_sora::handle_gpt_image_1_sora;
|
||||
use crate::core::commands::enqueue::text_to_image::grok::handle_grok::handle_grok;
|
||||
use crate::core::commands::enqueue::text_to_image::midjourney::handle_midjourney::handle_midjourney;
|
||||
use crate::core::commands::enqueue::text_to_image::nano_banana::handle_nano_banana::handle_nano_banana;
|
||||
use crate::core::commands::enqueue::text_to_image::nano_banana_pro::handle_nano_banana_pro::handle_nano_banana_pro;
|
||||
use crate::core::commands::response::failure_response_wrapper::{CommandErrorResponseWrapper, CommandErrorStatus};
|
||||
use crate::core::commands::response::shorthand::Response;
|
||||
use crate::core::commands::response::success_response_wrapper::SerializeMarker;
|
||||
@@ -58,6 +59,10 @@ pub enum ImageModel {
|
||||
|
||||
#[serde(rename = "gemini_25_flash")]
|
||||
Gemini25Flash,
|
||||
#[serde(rename = "nano_banana")]
|
||||
NanoBanana,
|
||||
#[serde(rename = "nano_banana_pro")]
|
||||
NanoBananaPro,
|
||||
|
||||
// Generic Midjourney model, version unknown.
|
||||
#[serde(rename = "midjourney")]
|
||||
@@ -75,6 +80,9 @@ pub struct EnqueueTextToImageRequest {
|
||||
/// Aspect ratio.
|
||||
pub aspect_ratio: Option<TextToImageSize>,
|
||||
|
||||
/// Aspect ratio.
|
||||
pub image_resolution: Option<TextToImageResolution>,
|
||||
|
||||
/// The number of images to generate.
|
||||
pub number_images: Option<u32>,
|
||||
|
||||
@@ -116,6 +124,17 @@ pub enum TextToImageSize {
|
||||
Tall,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug, Copy, Clone)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum TextToImageResolution {
|
||||
/// 1K - nano banana pro
|
||||
OneK,
|
||||
/// 2K - nano banana pro
|
||||
TwoK,
|
||||
/// 4K - nano banana pro
|
||||
FourK,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct EnqueueTextToImageSuccessResponse {
|
||||
}
|
||||
@@ -297,8 +316,20 @@ pub async fn dispatch_request(
|
||||
None => {
|
||||
return Err(GenerateError::BadInput(BadInputReason::NoModelSpecified));
|
||||
}
|
||||
Some(ImageModel::Gemini25Flash) => {
|
||||
return handle_gemini_25_flash(
|
||||
Some(ImageModel::NanoBanana | ImageModel::Gemini25Flash) => {
|
||||
return handle_nano_banana(
|
||||
request,
|
||||
app,
|
||||
app_data_root,
|
||||
app_env_configs,
|
||||
provider_priority_store,
|
||||
storyteller_creds_manager,
|
||||
sora_creds_manager,
|
||||
sora_task_queue,
|
||||
).await;
|
||||
}
|
||||
Some(ImageModel::NanoBananaPro) => {
|
||||
return handle_nano_banana_pro(
|
||||
request,
|
||||
app,
|
||||
app_data_root,
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
pub (crate) mod handle_gemini_25_flash_artcraft;
|
||||
pub (super) mod handle_gemini_25_flash;
|
||||
+2
@@ -57,6 +57,8 @@ pub async fn handle_image_artcraft(
|
||||
}
|
||||
Some(
|
||||
ImageModel::Gemini25Flash |
|
||||
ImageModel::NanoBanana |
|
||||
ImageModel::NanoBananaPro |
|
||||
ImageModel::GptImage1 |
|
||||
ImageModel::GrokImage |
|
||||
ImageModel::Midjourney
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
use crate::core::commands::enqueue::generate_error::GenerateError;
|
||||
use crate::core::commands::enqueue::image_edit::enqueue_contextual_edit_image_command::{EditImageQuality, EditImageSize};
|
||||
use crate::core::commands::enqueue::image_edit::enqueue_edit_image_command::{EditImageQuality, EditImageSize};
|
||||
use crate::core::commands::enqueue::task_enqueue_success::TaskEnqueueSuccess;
|
||||
use crate::core::commands::enqueue::text_to_image::enqueue_text_to_image_command::{EnqueueTextToImageRequest, TextToImageSize};
|
||||
use crate::core::events::basic_sendable_event_trait::BasicSendableEvent;
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
use crate::core::commands::enqueue::generate_error::{GenerateError, MissingCredentialsReason, ProviderFailureReason};
|
||||
use crate::core::commands::enqueue::image_edit::enqueue_contextual_edit_image_command::{EditImageQuality, EditImageSize};
|
||||
use crate::core::commands::enqueue::image_edit::enqueue_edit_image_command::{EditImageQuality, EditImageSize};
|
||||
use crate::core::commands::enqueue::task_enqueue_success::TaskEnqueueSuccess;
|
||||
use crate::core::commands::enqueue::text_to_image::enqueue_text_to_image_command::{EnqueueTextToImageRequest, TextToImageSize};
|
||||
use crate::core::events::basic_sendable_event_trait::BasicSendableEvent;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
pub mod enqueue_text_to_image_command;
|
||||
pub mod generic;
|
||||
mod gemini_25_flash;
|
||||
mod nano_banana;
|
||||
mod nano_banana_pro;
|
||||
mod gpt_image_1;
|
||||
mod midjourney;
|
||||
mod grok;
|
||||
+4
-4
@@ -3,9 +3,9 @@ use crate::core::commands::enqueue::image_edit::gpt_image_1::handle_gpt_image_1_
|
||||
use crate::core::commands::enqueue::image_edit::gpt_image_1::handle_gpt_image_1_edit_sora::handle_gpt_image_1_edit_sora;
|
||||
use crate::core::commands::enqueue::task_enqueue_success::TaskEnqueueSuccess;
|
||||
use crate::core::commands::enqueue::text_to_image::enqueue_text_to_image_command::{EnqueueTextToImageRequest, TextToImageSize};
|
||||
use crate::core::commands::enqueue::text_to_image::gemini_25_flash::handle_gemini_25_flash_artcraft::handle_gemini_25_flash_artcraft;
|
||||
use crate::core::commands::enqueue::text_to_image::gpt_image_1::handle_gpt_image_1_artcraft::handle_gpt_image_1_artcraft;
|
||||
use crate::core::commands::enqueue::text_to_image::gpt_image_1::handle_gpt_image_1_sora::handle_gpt_image_1_sora;
|
||||
use crate::core::commands::enqueue::text_to_image::nano_banana::handle_nano_banana_artcraft::handle_nano_banana_artcraft;
|
||||
use crate::core::events::basic_sendable_event_trait::BasicSendableEvent;
|
||||
use crate::core::events::generation_events::common::{GenerationAction, GenerationModel, GenerationServiceProvider};
|
||||
use crate::core::events::generation_events::generation_enqueue_failure_event::GenerationEnqueueFailureEvent;
|
||||
@@ -24,7 +24,7 @@ use openai_sora_client::requests::image_gen::common::{ImageSize, NumImages};
|
||||
use std::time::Duration;
|
||||
use tauri::AppHandle;
|
||||
|
||||
pub async fn handle_gemini_25_flash(
|
||||
pub async fn handle_nano_banana(
|
||||
request: &EnqueueTextToImageRequest,
|
||||
app: &AppHandle,
|
||||
app_data_root: &AppDataRoot,
|
||||
@@ -50,8 +50,8 @@ pub async fn handle_gemini_25_flash(
|
||||
// Fallthrough
|
||||
}
|
||||
Provider::Artcraft => {
|
||||
info!("Dispatching gemini 2.5 flash via Artcraft...");
|
||||
return handle_gemini_25_flash_artcraft(
|
||||
info!("Dispatching Nano Banana via Artcraft...");
|
||||
return handle_nano_banana_artcraft(
|
||||
request,
|
||||
app_env_configs,
|
||||
storyteller_creds_manager
|
||||
+5
-5
@@ -1,5 +1,5 @@
|
||||
use crate::core::commands::enqueue::generate_error::{GenerateError, MissingCredentialsReason, ProviderFailureReason};
|
||||
use crate::core::commands::enqueue::image_edit::enqueue_contextual_edit_image_command::{EditImageQuality, EditImageSize};
|
||||
use crate::core::commands::enqueue::image_edit::enqueue_edit_image_command::{EditImageQuality, EditImageSize};
|
||||
use crate::core::commands::enqueue::task_enqueue_success::TaskEnqueueSuccess;
|
||||
use crate::core::commands::enqueue::text_to_image::enqueue_text_to_image_command::{EnqueueTextToImageRequest, TextToImageSize};
|
||||
use crate::core::events::basic_sendable_event_trait::BasicSendableEvent;
|
||||
@@ -26,7 +26,7 @@ use storyteller_client::endpoints::generate::image::edit::gpt_image_1_edit_image
|
||||
use storyteller_client::endpoints::generate::image::generate_gpt_image_1_text_to_image::generate_gpt_image_1_text_to_image;
|
||||
use tauri::AppHandle;
|
||||
|
||||
pub async fn handle_gemini_25_flash_artcraft(
|
||||
pub async fn handle_nano_banana_artcraft(
|
||||
request: &EnqueueTextToImageRequest,
|
||||
app_env_configs: &AppEnvConfigs,
|
||||
storyteller_creds_manager: &StorytellerCredentialManager,
|
||||
@@ -39,7 +39,7 @@ pub async fn handle_gemini_25_flash_artcraft(
|
||||
},
|
||||
};
|
||||
|
||||
info!("Calling Artcraft gemini 2.5 flash ...");
|
||||
info!("Calling Artcraft Nano Banana...");
|
||||
|
||||
let uuid_idempotency_token = generate_random_uuid();
|
||||
|
||||
@@ -69,12 +69,12 @@ pub async fn handle_gemini_25_flash_artcraft(
|
||||
let job_id = match result {
|
||||
Ok(enqueued) => {
|
||||
// TODO(bt,2025-07-05): Enqueue job token?
|
||||
info!("Successfully enqueued Artcraft gemini 2.5 flash. Job token: {}",
|
||||
info!("Successfully enqueued Artcraft Nano Banana. Job token: {}",
|
||||
enqueued.inference_job_token);
|
||||
enqueued.inference_job_token
|
||||
}
|
||||
Err(err) => {
|
||||
error!("Failed to use Artcraft gemini 2.5 flash: {:?}", err);
|
||||
error!("Failed to use Artcraft Nano Banana: {:?}", err);
|
||||
return Err(GenerateError::ProviderFailure(ProviderFailureReason::StorytellerError(err)));
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,2 @@
|
||||
pub (crate) mod handle_nano_banana_artcraft;
|
||||
pub (super) mod handle_nano_banana;
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
use crate::core::commands::enqueue::generate_error::GenerateError;
|
||||
use crate::core::commands::enqueue::task_enqueue_success::TaskEnqueueSuccess;
|
||||
use crate::core::commands::enqueue::text_to_image::enqueue_text_to_image_command::{EnqueueTextToImageRequest, TextToImageSize};
|
||||
use crate::core::commands::enqueue::text_to_image::nano_banana_pro::handle_nano_banana_pro_artcraft::handle_nano_banana_pro_artcraft;
|
||||
use crate::core::events::basic_sendable_event_trait::BasicSendableEvent;
|
||||
use crate::core::events::generation_events::common::{GenerationAction, GenerationModel, GenerationServiceProvider};
|
||||
use crate::core::events::generation_events::generation_enqueue_failure_event::GenerationEnqueueFailureEvent;
|
||||
use crate::core::state::app_env_configs::app_env_configs::AppEnvConfigs;
|
||||
use crate::core::state::data_dir::app_data_root::AppDataRoot;
|
||||
use crate::core::state::provider_priority::{Provider, ProviderPriorityStore};
|
||||
use crate::services::sora::state::sora_credential_manager::SoraCredentialManager;
|
||||
use crate::services::sora::state::sora_task_queue::SoraTaskQueue;
|
||||
use crate::services::storyteller::state::storyteller_credential_manager::StorytellerCredentialManager;
|
||||
use enums::common::generation_provider::GenerationProvider;
|
||||
use enums::tauri::tasks::task_type::TaskType;
|
||||
use log::{error, info};
|
||||
use openai_sora_client::recipes::simple_image_gen_with_session_auto_renew::{simple_image_gen_with_session_auto_renew, SimpleImageGenAutoRenewRequest};
|
||||
use openai_sora_client::requests::image_gen::common::{ImageSize, NumImages};
|
||||
use std::time::Duration;
|
||||
use tauri::AppHandle;
|
||||
|
||||
pub async fn handle_nano_banana_pro(
|
||||
request: &EnqueueTextToImageRequest,
|
||||
app: &AppHandle,
|
||||
app_data_root: &AppDataRoot,
|
||||
app_env_configs: &AppEnvConfigs,
|
||||
provider_priority_store: &ProviderPriorityStore,
|
||||
storyteller_creds_manager: &StorytellerCredentialManager,
|
||||
sora_creds_manager: &SoraCredentialManager,
|
||||
sora_task_queue: &SoraTaskQueue,
|
||||
) -> Result<TaskEnqueueSuccess, GenerateError> {
|
||||
|
||||
let priority = provider_priority_store.get_priority()?;
|
||||
|
||||
// TODO: Check if providers are available before proceeding.
|
||||
|
||||
info!("Providers by priority: {:?}", priority);
|
||||
|
||||
for provider in priority.iter() {
|
||||
match provider {
|
||||
Provider::Fal => {
|
||||
// Fallthrough (for now)
|
||||
}
|
||||
Provider::Sora => {
|
||||
// Fallthrough
|
||||
}
|
||||
Provider::Artcraft => {
|
||||
info!("Dispatching Nano Banana Pro via Artcraft...");
|
||||
return handle_nano_banana_pro_artcraft(
|
||||
request,
|
||||
app_env_configs,
|
||||
storyteller_creds_manager
|
||||
).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Err(GenerateError::NoProviderAvailable)
|
||||
}
|
||||
+111
@@ -0,0 +1,111 @@
|
||||
use crate::core::commands::enqueue::generate_error::{BadInputReason, GenerateError, MissingCredentialsReason, ProviderFailureReason};
|
||||
use crate::core::commands::enqueue::image_edit::enqueue_edit_image_command::{EditImageQuality, EditImageResolution, EditImageSize};
|
||||
use crate::core::commands::enqueue::task_enqueue_success::TaskEnqueueSuccess;
|
||||
use crate::core::commands::enqueue::text_to_image::enqueue_text_to_image_command::{EnqueueTextToImageRequest, TextToImageResolution, TextToImageSize};
|
||||
use crate::core::events::basic_sendable_event_trait::BasicSendableEvent;
|
||||
use crate::core::events::generation_events::common::{GenerationAction, GenerationModel, GenerationServiceProvider};
|
||||
use crate::core::events::generation_events::generation_enqueue_failure_event::GenerationEnqueueFailureEvent;
|
||||
use crate::core::state::app_env_configs::app_env_configs::AppEnvConfigs;
|
||||
use crate::core::state::data_dir::app_data_root::AppDataRoot;
|
||||
use crate::core::state::provider_priority::ProviderPriorityStore;
|
||||
use crate::services::sora::state::sora_credential_manager::SoraCredentialManager;
|
||||
use crate::services::sora::state::sora_task_queue::SoraTaskQueue;
|
||||
use crate::services::storyteller::state::storyteller_credential_manager::StorytellerCredentialManager;
|
||||
use artcraft_api_defs::generate::image::edit::gemini_25_flash_edit_image::{Gemini25FlashEditImageNumImages, Gemini25FlashEditImageRequest};
|
||||
use artcraft_api_defs::generate::image::generate_gpt_image_1_text_to_image::{GenerateGptImage1TextToImageImageQuality, GenerateGptImage1TextToImageImageSize, GenerateGptImage1TextToImageNumImages, GenerateGptImage1TextToImageRequest};
|
||||
use artcraft_api_defs::generate::image::multi_function::nano_banana_pro_multi_function_image_gen::{NanoBananaProMultiFunctionImageGenAspectRatio, NanoBananaProMultiFunctionImageGenImageResolution, NanoBananaProMultiFunctionImageGenNumImages, NanoBananaProMultiFunctionImageGenRequest};
|
||||
use enums::common::generation_provider::GenerationProvider;
|
||||
use enums::tauri::tasks::task_type::TaskType;
|
||||
use idempotency::uuid::generate_random_uuid;
|
||||
use log::{error, info};
|
||||
use openai_sora_client::recipes::maybe_upgrade_or_renew_session::maybe_upgrade_or_renew_session;
|
||||
use openai_sora_client::recipes::simple_image_gen_with_session_auto_renew::{simple_image_gen_with_session_auto_renew, SimpleImageGenAutoRenewRequest};
|
||||
use openai_sora_client::requests::image_gen::common::{ImageSize, NumImages};
|
||||
use std::time::Duration;
|
||||
use storyteller_client::endpoints::generate::image::edit::gemini_25_flash_edit_image::gemini_25_flash_edit_image;
|
||||
use storyteller_client::endpoints::generate::image::multi_function::nano_banana_pro_multi_function_image_gen_image::nano_banana_pro_multi_function_image_gen;
|
||||
use tauri::AppHandle;
|
||||
|
||||
pub async fn handle_nano_banana_pro_artcraft(
|
||||
request: &EnqueueTextToImageRequest,
|
||||
app_env_configs: &AppEnvConfigs,
|
||||
storyteller_creds_manager: &StorytellerCredentialManager,
|
||||
) -> Result<TaskEnqueueSuccess, GenerateError> {
|
||||
|
||||
let creds = match storyteller_creds_manager.get_credentials()? {
|
||||
Some(creds) => creds,
|
||||
None => {
|
||||
return Err(GenerateError::MissingCredentials(MissingCredentialsReason::NeedsStorytellerCredentials));
|
||||
},
|
||||
};
|
||||
|
||||
info!("Calling Artcraft Nano Banana Pro...");
|
||||
|
||||
let uuid_idempotency_token = generate_random_uuid();
|
||||
|
||||
let num_images = match request.number_images {
|
||||
None => None,
|
||||
Some(1) => Some(NanoBananaProMultiFunctionImageGenNumImages::One),
|
||||
Some(2) => Some(NanoBananaProMultiFunctionImageGenNumImages::Two),
|
||||
Some(3) => Some(NanoBananaProMultiFunctionImageGenNumImages::Three),
|
||||
Some(4) => Some(NanoBananaProMultiFunctionImageGenNumImages::Four),
|
||||
Some(other) => {
|
||||
return Err(GenerateError::BadInput(BadInputReason::InvalidNumberOfRequestedImages {
|
||||
min: 1,
|
||||
max: 4,
|
||||
requested: other,
|
||||
}));
|
||||
},
|
||||
};
|
||||
|
||||
let aspect_ratio = request.aspect_ratio
|
||||
.map(|aspect_ratio| match aspect_ratio {
|
||||
TextToImageSize::Auto => None,
|
||||
TextToImageSize::Square => Some(NanoBananaProMultiFunctionImageGenAspectRatio::OneByOne),
|
||||
TextToImageSize::Wide => Some(NanoBananaProMultiFunctionImageGenAspectRatio::SixteenByNine),
|
||||
TextToImageSize::Tall => Some(NanoBananaProMultiFunctionImageGenAspectRatio::NineBySixteen),
|
||||
})
|
||||
.flatten();
|
||||
|
||||
let resolution = request.image_resolution
|
||||
.map(|image_resolution| match image_resolution {
|
||||
TextToImageResolution::OneK => NanoBananaProMultiFunctionImageGenImageResolution::OneK,
|
||||
TextToImageResolution::TwoK => NanoBananaProMultiFunctionImageGenImageResolution::TwoK,
|
||||
TextToImageResolution::FourK => NanoBananaProMultiFunctionImageGenImageResolution::FourK,
|
||||
});
|
||||
|
||||
let request = NanoBananaProMultiFunctionImageGenRequest {
|
||||
uuid_idempotency_token,
|
||||
prompt: request.prompt.clone(),
|
||||
image_media_tokens: None,
|
||||
num_images,
|
||||
resolution,
|
||||
aspect_ratio,
|
||||
};
|
||||
|
||||
let result = nano_banana_pro_multi_function_image_gen(
|
||||
&app_env_configs.storyteller_host,
|
||||
Some(&creds),
|
||||
request,
|
||||
).await;
|
||||
|
||||
let job_id = match result {
|
||||
Ok(enqueued) => {
|
||||
// TODO(bt,2025-07-05): Enqueue job token?
|
||||
info!("Successfully enqueued Artcraft Nano Banana. Job token: {}",
|
||||
enqueued.inference_job_token);
|
||||
enqueued.inference_job_token
|
||||
}
|
||||
Err(err) => {
|
||||
error!("Failed to use Artcraft Nano Banana: {:?}", err);
|
||||
return Err(GenerateError::ProviderFailure(ProviderFailureReason::StorytellerError(err)));
|
||||
}
|
||||
};
|
||||
|
||||
Ok(TaskEnqueueSuccess {
|
||||
provider: GenerationProvider::Artcraft,
|
||||
model: Some(GenerationModel::NanoBananaPro),
|
||||
provider_job_id: Some(job_id.to_string()),
|
||||
task_type: TaskType::ImageGeneration,
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
pub (crate) mod handle_nano_banana_pro_artcraft;
|
||||
pub (super) mod handle_nano_banana_pro;
|
||||
@@ -34,7 +34,11 @@ pub enum GenerationModel {
|
||||
GrokImage,
|
||||
#[serde(rename = "gemini_25_flash")]
|
||||
Gemini25Flash,
|
||||
|
||||
#[serde(rename = "nano_banana")]
|
||||
NanoBanana,
|
||||
#[serde(rename = "nano_banana_pro")]
|
||||
NanoBananaPro,
|
||||
|
||||
#[serde(rename = "hunyuan_3d_2_0")]
|
||||
Hunyuan3d2_0,
|
||||
#[serde(rename = "hunyuan_3d_2_1")]
|
||||
|
||||
@@ -6,7 +6,7 @@ use tauri::Manager;
|
||||
use crate::core::commands::app_preferences::get_app_preferences_command::get_app_preferences_command;
|
||||
use crate::core::commands::app_preferences::update_app_preference_command::update_app_preferences_command;
|
||||
use crate::core::commands::enqueue::image_bg_removal::enqueue_image_bg_removal_command::enqueue_image_bg_removal_command;
|
||||
use crate::core::commands::enqueue::image_edit::enqueue_contextual_edit_image_command::enqueue_contextual_edit_image_command;
|
||||
use crate::core::commands::enqueue::image_edit::enqueue_edit_image_command::enqueue_edit_image_command;
|
||||
use crate::core::commands::enqueue::image_inpaint::enqueue_image_inpaint_command::enqueue_image_inpaint_command;
|
||||
use crate::core::commands::enqueue::image_to_object::enqueue_image_to_3d_object_command::enqueue_image_to_3d_object_command;
|
||||
use crate::core::commands::enqueue::image_to_video::enqueue_image_to_video_command::enqueue_image_to_video_command;
|
||||
@@ -172,7 +172,7 @@ pub fn run() {
|
||||
// My first attempt at naively doing this didn't work because the macros can't find their codegen'd targets.
|
||||
let builder = builder.invoke_handler(tauri::generate_handler![
|
||||
check_sora_session_command,
|
||||
enqueue_contextual_edit_image_command,
|
||||
enqueue_edit_image_command,
|
||||
enqueue_image_bg_removal_command,
|
||||
enqueue_image_inpaint_command,
|
||||
enqueue_image_to_3d_object_command,
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
use crate::core::commands::enqueue::image_edit::enqueue_contextual_edit_image_command::{ContextualImageEditModel, EditImageQuality, EditImageSize};
|
||||
use crate::core::commands::enqueue::image_edit::enqueue_edit_image_command::{ImageEditModel, EditImageQuality, EditImageSize};
|
||||
use crate::core::commands::response::shorthand::ResponseOrErrorMessage;
|
||||
use crate::core::state::app_env_configs::app_env_configs::AppEnvConfigs;
|
||||
use crate::core::state::data_dir::app_data_root::AppDataRoot;
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
use chrono::{DateTime, Utc};
|
||||
use crate::core::commands::enqueue::image_edit::enqueue_contextual_edit_image_command::{ContextualImageEditModel, EditImageQuality, EditImageSize};
|
||||
use crate::core::commands::enqueue::image_edit::enqueue_edit_image_command::{ImageEditModel, EditImageQuality, EditImageSize};
|
||||
use crate::core::commands::providers::get_provider_order_command::GetProviderOrderResponse;
|
||||
use crate::core::commands::response::shorthand::ResponseOrErrorMessage;
|
||||
use crate::core::commands::response::success_response_wrapper::SerializeMarker;
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
use crate::core::commands::enqueue::image_edit::enqueue_contextual_edit_image_command::EnqueueContextualEditImageSuccessResponse;
|
||||
use crate::core::commands::enqueue::image_edit::enqueue_edit_image_command::EnqueueEditImageSuccessResponse;
|
||||
use crate::core::commands::response::shorthand::ResponseOrErrorMessage;
|
||||
use crate::core::commands::response::success_response_wrapper::SerializeMarker;
|
||||
use crate::core::state::app_preferences::app_preferences_manager::AppPreferencesManager;
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
use crate::core::commands::enqueue::image_edit::enqueue_contextual_edit_image_command::{ContextualImageEditModel, EditImageQuality, EditImageSize};
|
||||
use crate::core::commands::enqueue::image_edit::enqueue_edit_image_command::{ImageEditModel, EditImageQuality, EditImageSize};
|
||||
use crate::core::state::app_env_configs::app_env_configs::AppEnvConfigs;
|
||||
use crate::core::state::data_dir::app_data_root::AppDataRoot;
|
||||
use crate::services::midjourney::state::midjourney_credential_manager::MidjourneyCredentialManager;
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
use crate::core::commands::enqueue::image_edit::enqueue_contextual_edit_image_command::{ContextualImageEditModel, EditImageQuality, EditImageSize};
|
||||
use crate::core::commands::enqueue::image_edit::enqueue_edit_image_command::{ImageEditModel, EditImageQuality, EditImageSize};
|
||||
use crate::core::state::app_env_configs::app_env_configs::AppEnvConfigs;
|
||||
use crate::core::state::data_dir::app_data_root::AppDataRoot;
|
||||
use crate::services::midjourney::state::midjourney_credential_manager::MidjourneyCredentialManager;
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
use crate::core::commands::enqueue::image_edit::enqueue_contextual_edit_image_command::{ContextualImageEditModel, EditImageQuality, EditImageSize};
|
||||
use crate::core::commands::enqueue::image_edit::enqueue_edit_image_command::{ImageEditModel, EditImageQuality, EditImageSize};
|
||||
use crate::core::state::app_env_configs::app_env_configs::AppEnvConfigs;
|
||||
use crate::core::state::data_dir::app_data_root::AppDataRoot;
|
||||
use crate::services::midjourney::state::midjourney_credential_manager::MidjourneyCredentialManager;
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
use crate::core::commands::enqueue::image_edit::enqueue_contextual_edit_image_command::{ContextualImageEditModel, EditImageQuality, EditImageSize};
|
||||
use crate::core::commands::enqueue::image_edit::enqueue_edit_image_command::{ImageEditModel, EditImageQuality, EditImageSize};
|
||||
use crate::core::state::app_env_configs::app_env_configs::AppEnvConfigs;
|
||||
use crate::core::state::data_dir::app_data_root::AppDataRoot;
|
||||
use crate::services::midjourney::state::midjourney_credential_manager::MidjourneyCredentialManager;
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
use crate::core::commands::enqueue::image_edit::enqueue_contextual_edit_image_command::{ContextualImageEditModel, EditImageQuality, EditImageSize};
|
||||
use crate::core::commands::enqueue::image_edit::enqueue_edit_image_command::{ImageEditModel, EditImageQuality, EditImageSize};
|
||||
use crate::core::state::app_env_configs::app_env_configs::AppEnvConfigs;
|
||||
use crate::core::state::data_dir::app_data_root::AppDataRoot;
|
||||
use crate::services::midjourney::state::midjourney_credential_manager::MidjourneyCredentialManager;
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
use crate::core::commands::enqueue::image_edit::enqueue_contextual_edit_image_command::{ContextualImageEditModel, EditImageQuality, EditImageSize};
|
||||
use crate::core::commands::enqueue::image_edit::enqueue_edit_image_command::{ImageEditModel, EditImageQuality, EditImageSize};
|
||||
use crate::core::state::app_env_configs::app_env_configs::AppEnvConfigs;
|
||||
use crate::core::state::data_dir::app_data_root::AppDataRoot;
|
||||
use crate::services::midjourney::state::midjourney_credential_manager::MidjourneyCredentialManager;
|
||||
|
||||
@@ -29,6 +29,10 @@ pub enum TaskModelType {
|
||||
FluxProKontextMax,
|
||||
#[serde(rename = "gemini_25_flash")]
|
||||
Gemini25Flash,
|
||||
#[serde(rename = "nano_banana")]
|
||||
NanoBanana,
|
||||
#[serde(rename = "nano_banana_pro")]
|
||||
NanoBananaPro,
|
||||
#[serde(rename = "gpt_image_1")]
|
||||
GptImage1,
|
||||
#[serde(rename = "grok_image")]
|
||||
@@ -85,6 +89,8 @@ impl TaskModelType {
|
||||
Self::FluxPro11Ultra => "flux_pro_1.1_ultra",
|
||||
Self::FluxProKontextMax => "flux_pro_kontext_max",
|
||||
Self::Gemini25Flash => "gemini_25_flash",
|
||||
Self::NanoBanana => "nano_banana",
|
||||
Self::NanoBananaPro => "nano_banana_pro",
|
||||
Self::GptImage1 => "gpt_image_1",
|
||||
Self::GrokImage => "grok_image",
|
||||
Self::Recraft3 => "recraft_3",
|
||||
@@ -116,6 +122,8 @@ impl TaskModelType {
|
||||
"flux_pro_1.1_ultra" => Ok(Self::FluxPro11Ultra),
|
||||
"flux_pro_kontext_max" => Ok(Self::FluxProKontextMax),
|
||||
"gemini_25_flash" => Ok(Self::Gemini25Flash),
|
||||
"nano_banana" => Ok(Self::NanoBanana),
|
||||
"nano_banana_pro" => Ok(Self::NanoBananaPro),
|
||||
"gpt_image_1" => Ok(Self::GptImage1),
|
||||
"grok_image" => Ok(Self::GrokImage),
|
||||
"recraft_3" => Ok(Self::Recraft3),
|
||||
@@ -150,6 +158,8 @@ impl TaskModelType {
|
||||
Self::FluxPro11Ultra,
|
||||
Self::FluxProKontextMax,
|
||||
Self::Gemini25Flash,
|
||||
Self::NanoBanana,
|
||||
Self::NanoBananaPro,
|
||||
Self::GptImage1,
|
||||
Self::GrokImage,
|
||||
Self::Recraft3,
|
||||
@@ -191,6 +201,8 @@ mod tests {
|
||||
assert_serialization(TaskModelType::FluxPro11Ultra, "flux_pro_1.1_ultra");
|
||||
assert_serialization(TaskModelType::FluxProKontextMax, "flux_pro_kontext_max");
|
||||
assert_serialization(TaskModelType::Gemini25Flash, "gemini_25_flash");
|
||||
assert_serialization(TaskModelType::NanoBanana, "nano_banana");
|
||||
assert_serialization(TaskModelType::NanoBananaPro, "nano_banana_pro");
|
||||
assert_serialization(TaskModelType::GptImage1, "gpt_image_1");
|
||||
assert_serialization(TaskModelType::GrokImage, "grok_image");
|
||||
assert_serialization(TaskModelType::Recraft3, "recraft_3");
|
||||
@@ -221,6 +233,8 @@ mod tests {
|
||||
assert_eq!(TaskModelType::FluxPro11Ultra.to_str(), "flux_pro_1.1_ultra");
|
||||
assert_eq!(TaskModelType::FluxProKontextMax.to_str(), "flux_pro_kontext_max");
|
||||
assert_eq!(TaskModelType::Gemini25Flash.to_str(), "gemini_25_flash");
|
||||
assert_eq!(TaskModelType::NanoBanana.to_str(), "nano_banana");
|
||||
assert_eq!(TaskModelType::NanoBananaPro.to_str(), "nano_banana_pro");
|
||||
assert_eq!(TaskModelType::GptImage1.to_str(), "gpt_image_1");
|
||||
assert_eq!(TaskModelType::GrokImage.to_str(), "grok_image");
|
||||
assert_eq!(TaskModelType::Recraft3.to_str(), "recraft_3");
|
||||
@@ -251,6 +265,8 @@ mod tests {
|
||||
assert_eq!(TaskModelType::from_str("flux_pro_1.1_ultra").unwrap(), TaskModelType::FluxPro11Ultra);
|
||||
assert_eq!(TaskModelType::from_str("flux_pro_kontext_max").unwrap(), TaskModelType::FluxProKontextMax);
|
||||
assert_eq!(TaskModelType::from_str("gemini_25_flash").unwrap(), TaskModelType::Gemini25Flash);
|
||||
assert_eq!(TaskModelType::from_str("nano_banana").unwrap(), TaskModelType::NanoBanana);
|
||||
assert_eq!(TaskModelType::from_str("nano_banana_pro").unwrap(), TaskModelType::NanoBananaPro);
|
||||
assert_eq!(TaskModelType::from_str("gpt_image_1").unwrap(), TaskModelType::GptImage1);
|
||||
assert_eq!(TaskModelType::from_str("grok_image").unwrap(), TaskModelType::GrokImage);
|
||||
assert_eq!(TaskModelType::from_str("recraft_3").unwrap(), TaskModelType::Recraft3);
|
||||
@@ -284,7 +300,7 @@ mod tests {
|
||||
#[test]
|
||||
fn all_variants() {
|
||||
let mut variants = TaskModelType::all_variants();
|
||||
assert_eq!(variants.len(), 23);
|
||||
assert_eq!(variants.len(), 25);
|
||||
// Image models
|
||||
assert_eq!(variants.pop_first(), Some(TaskModelType::Flux1Dev));
|
||||
assert_eq!(variants.pop_first(), Some(TaskModelType::Flux1Schnell));
|
||||
@@ -294,6 +310,8 @@ mod tests {
|
||||
assert_eq!(variants.pop_first(), Some(TaskModelType::FluxPro11Ultra));
|
||||
assert_eq!(variants.pop_first(), Some(TaskModelType::FluxProKontextMax));
|
||||
assert_eq!(variants.pop_first(), Some(TaskModelType::Gemini25Flash));
|
||||
assert_eq!(variants.pop_first(), Some(TaskModelType::NanoBanana));
|
||||
assert_eq!(variants.pop_first(), Some(TaskModelType::NanoBananaPro));
|
||||
assert_eq!(variants.pop_first(), Some(TaskModelType::GptImage1));
|
||||
assert_eq!(variants.pop_first(), Some(TaskModelType::GrokImage));
|
||||
assert_eq!(variants.pop_first(), Some(TaskModelType::Recraft3));
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useState, useRef, useEffect, useCallback } from "react";
|
||||
import Konva from "konva"; // just for types
|
||||
import { EnqueueImageInpaint } from "@storyteller/tauri-api";
|
||||
import { EnqueueEditImage, EnqueueImageInpaint } from "@storyteller/tauri-api";
|
||||
import { ContextMenuContainer } from "../PageDraw/components/ui/ContextMenu";
|
||||
import { useCopyPasteHotkeys } from "../PageDraw/hooks/useCopyPasteHotkeys";
|
||||
import { useDeleteHotkeys } from "../PageDraw/hooks/useDeleteHotkeys";
|
||||
@@ -285,15 +285,32 @@ const PageEdit = () => {
|
||||
`inpaint-${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
||||
|
||||
try {
|
||||
const result = await EnqueueImageInpaint({
|
||||
model: selectedImageModel,
|
||||
image_media_token: editedImageToken,
|
||||
mask_image_raw_bytes: arrayBuffer,
|
||||
prompt: prompt,
|
||||
image_count: generationCount,
|
||||
frontend_caller: "image_editor",
|
||||
frontend_subscriber_id: subscriberId,
|
||||
});
|
||||
let result;
|
||||
|
||||
if (selectedImageModel?.editingIsInpainting) {
|
||||
result = await EnqueueImageInpaint({
|
||||
model: selectedImageModel,
|
||||
image_media_token: editedImageToken,
|
||||
mask_image_raw_bytes: arrayBuffer,
|
||||
prompt: prompt,
|
||||
image_count: generationCount,
|
||||
frontend_caller: "image_editor",
|
||||
frontend_subscriber_id: subscriberId,
|
||||
});
|
||||
} else {
|
||||
result = await EnqueueEditImage({
|
||||
model: selectedImageModel,
|
||||
image_media_tokens: [editedImageToken],
|
||||
disable_system_prompt: true, // No meaning yet
|
||||
prompt: prompt,
|
||||
image_count: generationCount,
|
||||
frontend_caller: "image_editor",
|
||||
frontend_subscriber_id: subscriberId,
|
||||
//scene_image_media_token,
|
||||
//aspect_ratio: aspectRatio,
|
||||
});
|
||||
}
|
||||
|
||||
if (result.status === "success") {
|
||||
setPendingGenerations((prev) => [
|
||||
...prev,
|
||||
@@ -310,7 +327,8 @@ const PageEdit = () => {
|
||||
[generationCount, selectedImageModel, store.baseImageInfo?.mediaToken],
|
||||
);
|
||||
|
||||
const isNanoBananaModel = selectedImageModel?.id === "gemini_25_flash";
|
||||
const isNanoBananaModel = selectedImageModel?.isNanoBananaModel();
|
||||
|
||||
const supportsMaskedInpainting =
|
||||
selectedImageModel?.usesInpaintingMask ?? false;
|
||||
|
||||
|
||||
@@ -28,8 +28,8 @@ import { IconProp } from "@fortawesome/fontawesome-svg-core";
|
||||
import { IsDesktopApp } from "@storyteller/tauri-utils";
|
||||
import { PromptsApi } from "@storyteller/api";
|
||||
import {
|
||||
EnqueueContextualEditImage,
|
||||
EnqueueContextualEditImageSize,
|
||||
EnqueueEditImage,
|
||||
EnqueueEditImageSize,
|
||||
} from "@storyteller/tauri-api";
|
||||
import { usePrompt2DStore } from "./promptStore";
|
||||
import { gtagEvent } from "@storyteller/google-analytics";
|
||||
@@ -235,7 +235,7 @@ export const PromptBox2D = ({
|
||||
|
||||
const aspectRatio = getCurrentAspectRatio();
|
||||
|
||||
const generateResponse = await EnqueueContextualEditImage({
|
||||
const generateResponse = await EnqueueEditImage({
|
||||
model: selectedImageModel,
|
||||
scene_image_media_token: snapshotMediaToken.data!,
|
||||
image_media_tokens: referenceImages.map((image) => image.mediaToken),
|
||||
@@ -354,16 +354,16 @@ export const PromptBox2D = ({
|
||||
return iconElement.props.icon;
|
||||
};
|
||||
|
||||
const getCurrentAspectRatio = (): EnqueueContextualEditImageSize => {
|
||||
const getCurrentAspectRatio = (): EnqueueEditImageSize => {
|
||||
const selected = aspectRatioList.find((item) => item.selected);
|
||||
switch (selected?.label) {
|
||||
case "3:2":
|
||||
return EnqueueContextualEditImageSize.Wide;
|
||||
return EnqueueEditImageSize.Wide;
|
||||
case "2:3":
|
||||
return EnqueueContextualEditImageSize.Tall;
|
||||
return EnqueueEditImageSize.Tall;
|
||||
case "1:1":
|
||||
default:
|
||||
return EnqueueContextualEditImageSize.Square;
|
||||
return EnqueueEditImageSize.Square;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -38,8 +38,8 @@ import { Modal } from "@storyteller/ui-modal";
|
||||
import { Signal } from "@preact/signals-react";
|
||||
import {
|
||||
CommandSuccessStatus,
|
||||
EnqueueContextualEditImage,
|
||||
EnqueueContextualEditImageSize,
|
||||
EnqueueEditImage,
|
||||
EnqueueEditImageSize,
|
||||
} from "@storyteller/tauri-api";
|
||||
import { usePrompt3DStore } from "./promptStore";
|
||||
import { gtagEvent } from "@storyteller/google-analytics";
|
||||
@@ -336,7 +336,7 @@ export const PromptBox3D = ({
|
||||
|
||||
const aspectRatio = getCurrentAspectRatio();
|
||||
|
||||
const generateResponse = await EnqueueContextualEditImage({
|
||||
const generateResponse = await EnqueueEditImage({
|
||||
model: selectedImageModel,
|
||||
scene_image_media_token: snapshotResult.data!,
|
||||
image_media_tokens: referenceImages.map((image) => image.mediaToken),
|
||||
@@ -400,16 +400,16 @@ export const PromptBox3D = ({
|
||||
}
|
||||
};
|
||||
|
||||
const getCurrentAspectRatio = (): EnqueueContextualEditImageSize => {
|
||||
const getCurrentAspectRatio = (): EnqueueEditImageSize => {
|
||||
switch (cameraAspectRatio.value) {
|
||||
case CameraAspectRatio.HORIZONTAL_3_2:
|
||||
return EnqueueContextualEditImageSize.Wide;
|
||||
return EnqueueEditImageSize.Wide;
|
||||
case CameraAspectRatio.VERTICAL_2_3:
|
||||
case CameraAspectRatio.VERTICAL_9_16:
|
||||
return EnqueueContextualEditImageSize.Tall;
|
||||
return EnqueueEditImageSize.Tall;
|
||||
case CameraAspectRatio.SQUARE_1_1:
|
||||
default:
|
||||
return EnqueueContextualEditImageSize.Square;
|
||||
return EnqueueEditImageSize.Square;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -21,6 +21,11 @@ export class ImageModel extends Model {
|
||||
// For inpainting models, does it require sending a mask?
|
||||
readonly usesInpaintingMask: boolean;
|
||||
|
||||
// For editing models, is "editing" == "inpainting"?
|
||||
// Examples of "true" are "flux dev juggernaut"
|
||||
// Examples of "false" are "nano_banana_pro", which is just "editing".
|
||||
readonly editingIsInpainting: boolean;
|
||||
|
||||
// Whether this model supports additional image prompts (reference images)
|
||||
readonly canUseImagePrompt: boolean;
|
||||
|
||||
@@ -44,6 +49,7 @@ export class ImageModel extends Model {
|
||||
defaultGenerationCount: number;
|
||||
canEditImages?: boolean;
|
||||
usesInpaintingMask?: boolean;
|
||||
editingIsInpainting?: boolean;
|
||||
canUseImagePrompt?: boolean;
|
||||
maxImagePromptCount?: number;
|
||||
canTextToImage?: boolean;
|
||||
@@ -66,8 +72,24 @@ export class ImageModel extends Model {
|
||||
this.defaultGenerationCount = args.defaultGenerationCount;
|
||||
this.canEditImages = args.canEditImages ?? false;
|
||||
this.usesInpaintingMask = args.usesInpaintingMask ?? false;
|
||||
this.editingIsInpainting = args.editingIsInpainting ?? false;
|
||||
this.canUseImagePrompt = args.canUseImagePrompt ?? false;
|
||||
this.maxImagePromptCount = Math.max(0, args.maxImagePromptCount ?? 1);
|
||||
this.canTextToImage = args.canTextToImage === false ? false : true; // Default to true !
|
||||
}
|
||||
|
||||
// If the model is a "Nano Banana"-type model, we may want to enable certain features.
|
||||
// For example, in the editor, we may want to use the marker.
|
||||
// TODO: Rather than "isNanoBananaModel()", we should have: "enableEditorMarker" as it's
|
||||
// more semantic.
|
||||
isNanoBananaModel(): boolean {
|
||||
switch(this.id) {
|
||||
case "gemini_25_flash":
|
||||
case "nano_banana":
|
||||
case "nano_banana_pro":
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -134,6 +134,21 @@ const ALL_MODELS: ModelConfig[] = [
|
||||
},
|
||||
tags: [ModelTag.InstructiveEdit, ModelTag.NonMaskedInpainting],
|
||||
}),
|
||||
cfg({
|
||||
id: "nano_banana_pro",
|
||||
category: "image",
|
||||
info: {
|
||||
name: "Nano Banana Pro",
|
||||
tauri_id: "nano_banana_pro",
|
||||
creator: mc.Google,
|
||||
},
|
||||
description: "Extremely powerful",
|
||||
badges: [{ label: "35 sec." }],
|
||||
capabilities: {
|
||||
maxGenerationCount: 4,
|
||||
},
|
||||
tags: [ModelTag.InstructiveEdit, ModelTag.NonMaskedInpainting],
|
||||
}),
|
||||
cfg({
|
||||
id: "flux_pro_inpaint",
|
||||
category: "image",
|
||||
|
||||
@@ -2,6 +2,8 @@ import { ImageModel } from "../classes/ImageModel.js";
|
||||
import { ModelCreator } from "../classes/metadata/ModelCreator.js";
|
||||
import { ModelTag } from "../classes/metadata/ModelTag.js";
|
||||
|
||||
// TODO: Some of the model configs, such as generation counts, are authoritatively controlled in `legacy/Models.ts`
|
||||
|
||||
export const IMAGE_MODELS: ImageModel[] = [
|
||||
new ImageModel({
|
||||
id: "grok_image",
|
||||
@@ -111,6 +113,24 @@ export const IMAGE_MODELS: ImageModel[] = [
|
||||
tags: [ModelTag.InstructiveEdit],
|
||||
progressBarTime: 25000,
|
||||
}),
|
||||
new ImageModel({
|
||||
id: "nano_banana_pro",
|
||||
tauriId: "nano_banana_pro",
|
||||
fullName: "Nano Banana Pro",
|
||||
category: "image",
|
||||
creator: ModelCreator.Google,
|
||||
selectorName: "Nano Banana Pro",
|
||||
selectorDescription: "Extremely powerful",
|
||||
selectorBadges: ["30 sec."],
|
||||
maxGenerationCount: 4,
|
||||
defaultGenerationCount: 1,
|
||||
canEditImages: true,
|
||||
canUseImagePrompt: true,
|
||||
maxImagePromptCount: 4,
|
||||
canTextToImage: true,
|
||||
tags: [ModelTag.InstructiveEdit],
|
||||
progressBarTime: 25000,
|
||||
}),
|
||||
new ImageModel({
|
||||
id: "gpt_image_1",
|
||||
tauriId: "gpt_image_1",
|
||||
@@ -120,7 +140,7 @@ export const IMAGE_MODELS: ImageModel[] = [
|
||||
selectorName: "GPT Image 1 (GPT-4o)",
|
||||
selectorDescription: "Slow, but super smart",
|
||||
selectorBadges: ["60 sec."],
|
||||
maxGenerationCount: 1,
|
||||
maxGenerationCount: 4,
|
||||
defaultGenerationCount: 1,
|
||||
tags: [ModelTag.InstructiveEdit],
|
||||
canEditImages: true,
|
||||
@@ -150,13 +170,14 @@ export const IMAGE_MODELS: ImageModel[] = [
|
||||
fullName: "Flux Pro Inpaint",
|
||||
category: "image",
|
||||
creator: ModelCreator.BlackForestLabs,
|
||||
selectorName: "Flux Pro",
|
||||
selectorName: "Flux Pro (Inpainting)",
|
||||
selectorDescription: "Fast inpainting",
|
||||
selectorBadges: ["30 sec."],
|
||||
maxGenerationCount: 1, // NB: Fal only allows one image for some reason!
|
||||
defaultGenerationCount: 1, // NB: Fal only allows one image for some reason!
|
||||
canEditImages: true,
|
||||
usesInpaintingMask: true,
|
||||
editingIsInpainting: true,
|
||||
canTextToImage: false,
|
||||
progressBarTime: 30000,
|
||||
}),
|
||||
@@ -173,6 +194,7 @@ export const IMAGE_MODELS: ImageModel[] = [
|
||||
defaultGenerationCount: 4,
|
||||
canEditImages: true,
|
||||
usesInpaintingMask: true,
|
||||
editingIsInpainting: true,
|
||||
canTextToImage: false,
|
||||
progressBarTime: 10000,
|
||||
}),
|
||||
|
||||
@@ -2,7 +2,7 @@ export * from "./lib/artcraft/ArtcraftGetCredits.js";
|
||||
export * from "./lib/artcraft/ArtcraftGetSubscription.js";
|
||||
export * from "./lib/artcraft/media_files/MediaFileDelete.js";
|
||||
export * from "./lib/common/CommandStatus.js";
|
||||
export * from "./lib/enqueue/EnqueueContextualEditImage.js";
|
||||
export * from "./lib/enqueue/EnqueueEditImage.js";
|
||||
export * from "./lib/enqueue/EnqueueImageBgRemovalCommand.js";
|
||||
export * from "./lib/enqueue/EnqueueImageInpaint.js";
|
||||
export * from "./lib/enqueue/EnqueueImageTo3dObject.js";
|
||||
|
||||
+47
-22
@@ -2,10 +2,10 @@ import { invoke } from "@tauri-apps/api/core";
|
||||
import { CommandResult } from "../common/CommandStatus";
|
||||
import { ImageModel } from "@storyteller/model-list";
|
||||
|
||||
export interface EnqueueContextualEditImageRequest {
|
||||
export interface EnqueueEditImageRequest {
|
||||
|
||||
/// The model to use.
|
||||
model?: ImageModel | EnqueueContextualEditImageModel;
|
||||
model?: ImageModel | EnqueueEditImageModel;
|
||||
|
||||
/// If set, this is the first image in the contextual image set.
|
||||
/// This gets submitted along with `image_media_tokens` and will
|
||||
@@ -26,26 +26,37 @@ export interface EnqueueContextualEditImageRequest {
|
||||
/// Number of images to generate.
|
||||
image_count?: number;
|
||||
|
||||
aspect_ratio?: EnqueueContextualEditImageSize;
|
||||
aspect_ratio?: EnqueueEditImageSize;
|
||||
|
||||
image_quality?: EnqueueContextualEditImageQuality;
|
||||
image_quality?: EnqueueEditImageQuality;
|
||||
|
||||
/// Image resolution. Support: Nano Banana Pro
|
||||
image_resolution?: EnqueueEditImageResolution;
|
||||
|
||||
// TODO: Actual enum.
|
||||
frontend_caller?: string;
|
||||
|
||||
// Optional frontend state to return later.
|
||||
frontend_subscriber_id?: string;
|
||||
}
|
||||
|
||||
|
||||
// Shape of request sent to Tauri
|
||||
// (We do some transformations from the public-facing request object.)
|
||||
interface RawEnqueueContextualEditImageRequest {
|
||||
model?: EnqueueContextualEditImageModel | string;
|
||||
interface RawEnqueueEditImageRequest {
|
||||
model?: EnqueueEditImageModel | string;
|
||||
scene_image_media_token?: string;
|
||||
image_media_tokens?: string[];
|
||||
prompt?: string;
|
||||
disable_system_prompt?: boolean;
|
||||
image_count?: number;
|
||||
aspect_ratio?: EnqueueContextualEditImageSize;
|
||||
image_quality?: EnqueueContextualEditImageQuality;
|
||||
aspect_ratio?: EnqueueEditImageSize;
|
||||
image_quality?: EnqueueEditImageQuality;
|
||||
frontend_caller?: string;
|
||||
frontend_subscriber_id?: string;
|
||||
}
|
||||
|
||||
export enum EnqueueContextualEditImageErrorType {
|
||||
export enum EnqueueEditImageErrorType {
|
||||
ModelNotSpecified = "model_not_specified",
|
||||
NoProviderAvailable = "no_provider_available",
|
||||
BadRequest = "bad_request",
|
||||
@@ -56,40 +67,46 @@ export enum EnqueueContextualEditImageErrorType {
|
||||
SoraIsHavingProblems = "sora_is_having_problems",
|
||||
}
|
||||
|
||||
export enum EnqueueContextualEditImageModel {
|
||||
export enum EnqueueEditImageModel {
|
||||
GptImage1 = "gpt_image_1",
|
||||
FluxProKontextMax = "flux_pro_kontext_max",
|
||||
}
|
||||
|
||||
export enum EnqueueContextualEditImageSize {
|
||||
export enum EnqueueEditImageSize {
|
||||
Auto = "auto",
|
||||
Square = "square",
|
||||
Wide = "wide",
|
||||
Tall = "tall",
|
||||
}
|
||||
|
||||
export enum EnqueueContextualEditImageQuality {
|
||||
export enum EnqueueEditImageQuality {
|
||||
Auto = "auto",
|
||||
High = "high",
|
||||
Medium = "medium",
|
||||
Low = "low",
|
||||
}
|
||||
|
||||
export interface EnqueueContextualEditImageError extends CommandResult {
|
||||
error_type: EnqueueContextualEditImageErrorType;
|
||||
export enum EnqueueEditImageResolution {
|
||||
OneK = "one_k",
|
||||
TwoK = "two_k",
|
||||
FourK = "four_k",
|
||||
}
|
||||
|
||||
export interface EnqueueEditImageError extends CommandResult {
|
||||
error_type: EnqueueEditImageErrorType;
|
||||
error_message?: string;
|
||||
}
|
||||
|
||||
export interface EnqueueContextualEditImagePayload {
|
||||
export interface EnqueueEditImagePayload {
|
||||
}
|
||||
|
||||
export interface EnqueueContextualEditImageSuccess extends CommandResult {
|
||||
payload: EnqueueContextualEditImagePayload;
|
||||
export interface EnqueueEditImageSuccess extends CommandResult {
|
||||
payload: EnqueueEditImagePayload;
|
||||
}
|
||||
|
||||
export type EnqueueContextualEditImageResult = EnqueueContextualEditImageSuccess | EnqueueContextualEditImageError;
|
||||
export type EnqueueEditImageResult = EnqueueEditImageSuccess | EnqueueEditImageError;
|
||||
|
||||
export const EnqueueContextualEditImage = async (request: EnqueueContextualEditImageRequest) : Promise<EnqueueContextualEditImageResult> => {
|
||||
export const EnqueueEditImage = async (request: EnqueueEditImageRequest) : Promise<EnqueueEditImageResult> => {
|
||||
let modelName = undefined;
|
||||
|
||||
if (!!request.model) {
|
||||
@@ -105,7 +122,7 @@ export const EnqueueContextualEditImage = async (request: EnqueueContextualEditI
|
||||
throw new Error("No model specified in request: " + JSON.stringify(request));
|
||||
}
|
||||
|
||||
let mutableRequest : RawEnqueueContextualEditImageRequest = {
|
||||
let mutableRequest : RawEnqueueEditImageRequest = {
|
||||
model: modelName,
|
||||
prompt: request.prompt,
|
||||
};
|
||||
@@ -134,9 +151,17 @@ export const EnqueueContextualEditImage = async (request: EnqueueContextualEditI
|
||||
mutableRequest.disable_system_prompt = request.disable_system_prompt;
|
||||
}
|
||||
|
||||
const result = await invoke("enqueue_contextual_edit_image_command", {
|
||||
if (!!request.frontend_caller) {
|
||||
mutableRequest.frontend_caller = request.frontend_caller;
|
||||
}
|
||||
|
||||
if (!!request.frontend_subscriber_id) {
|
||||
mutableRequest.frontend_subscriber_id = request.frontend_subscriber_id;
|
||||
}
|
||||
|
||||
const result = await invoke("enqueue_edit_image_command", {
|
||||
request: mutableRequest,
|
||||
});
|
||||
|
||||
return (result as EnqueueContextualEditImageResult);
|
||||
return (result as EnqueueEditImageResult);
|
||||
}
|
||||
@@ -45,6 +45,11 @@ sqlx_offline() {
|
||||
#sqlx_migrate
|
||||
sqlx_offline
|
||||
|
||||
# Issue with Linux performance
|
||||
# https://github.com/tauri-apps/tauri/issues/13498
|
||||
export WEBKIT_DISABLE_DMABUF_RENDERER=1
|
||||
export WEBKIT_DISABLE_COMPOSITING_MODE=1
|
||||
|
||||
echo "Build and run..."
|
||||
RUSTFLAGS="-Awarnings" cargo tauri dev \
|
||||
--no-dev-server \
|
||||
|
||||
Reference in New Issue
Block a user