mirror of
https://github.com/storytold/artcraft.git
synced 2026-11-12 12:53:04 +00:00
cleanup
This commit is contained in:
1 parent
04ffb75de9
commit
a54062d2bf
12 files changed
+503
-108
No files matched your search
+47
-22
@@ -1,40 +1,65 @@
|
||||
use log::info;
|
||||
|
||||
use crate::core::commands::enqueue::generate_error::GenerateError;
|
||||
use crate::core::commands::enqueue::generate_error::{GenerateError, MissingCredentialsReason};
|
||||
use crate::core::commands::enqueue::task_enqueue_success::TaskEnqueueSuccess;
|
||||
use crate::core::commands::generate::generate_image::providers::artcraft::handle_artcraft_via_legacy::handle_artcraft_via_legacy;
|
||||
use crate::core::commands::generate::generate_image::providers::artcraft::handle_artcraft_via_omni_endpoint::handle_artcraft_via_omni_endpoint;
|
||||
use crate::core::commands::generate::generate_image::tauri_generate_image_request::TauriGenerateImageRequest;
|
||||
use crate::core::commands::generate::generate_image::tauri_image_model::TauriImageModel;
|
||||
use crate::core::commands::generate::generate_image::utils::parse_semantic_media_files::parse_semantic_media_files;
|
||||
use crate::core::state::app_env_configs::app_env_configs::AppEnvConfigs;
|
||||
use crate::services::storyteller::state::storyteller_credential_manager::StorytellerCredentialManager;
|
||||
|
||||
|
||||
/// Dispatch an image generation request to Artcraft.
|
||||
///
|
||||
/// Tries the omni endpoint first. If the model isn't supported there,
|
||||
/// falls back to the legacy artcraft_router path.
|
||||
pub async fn handle_artcraft(
|
||||
request: &TauriGenerateImageRequest,
|
||||
app_env_configs: &AppEnvConfigs,
|
||||
storyteller_creds_manager: &StorytellerCredentialManager,
|
||||
) -> Result<TaskEnqueueSuccess, GenerateError> {
|
||||
// Try the omni endpoint first.
|
||||
let omni_result = handle_artcraft_via_omni_endpoint(
|
||||
request,
|
||||
app_env_configs,
|
||||
storyteller_creds_manager,
|
||||
).await;
|
||||
// Resolve credentials.
|
||||
let creds = match storyteller_creds_manager.get_credentials()? {
|
||||
Some(creds) => creds,
|
||||
None => return Err(GenerateError::MissingCredentials(MissingCredentialsReason::NeedsStorytellerCredentials)),
|
||||
};
|
||||
|
||||
match omni_result {
|
||||
Ok(success) => Ok(success),
|
||||
Err(GenerateError::NotYetImplemented(_)) => {
|
||||
// Model not supported by omni endpoint — fall back to legacy.
|
||||
info!("Model not supported by omni endpoint, falling back to legacy artcraft_router path.");
|
||||
handle_artcraft_via_legacy(
|
||||
request,
|
||||
app_env_configs,
|
||||
storyteller_creds_manager,
|
||||
).await
|
||||
}
|
||||
Err(err) => Err(err),
|
||||
// Resolve semantic media files (upload raw bytes if provided).
|
||||
let semantic_media_files = parse_semantic_media_files(
|
||||
request,
|
||||
&creds,
|
||||
&app_env_configs.storyteller_host,
|
||||
).await?;
|
||||
|
||||
let use_legacy = request.model
|
||||
.is_some_and(|m| is_legacy_only_model(m));
|
||||
|
||||
if use_legacy {
|
||||
info!("Model {:?} is legacy-only, routing to artcraft_router path.", request.model);
|
||||
handle_artcraft_via_legacy(
|
||||
request,
|
||||
&semantic_media_files,
|
||||
&creds,
|
||||
app_env_configs,
|
||||
).await
|
||||
} else {
|
||||
handle_artcraft_via_omni_endpoint(
|
||||
request,
|
||||
&semantic_media_files,
|
||||
&creds,
|
||||
app_env_configs,
|
||||
).await
|
||||
}
|
||||
}
|
||||
|
||||
fn is_legacy_only_model(model: TauriImageModel) -> bool {
|
||||
matches!(
|
||||
model,
|
||||
TauriImageModel::GrokImage
|
||||
| TauriImageModel::Recraft3
|
||||
| TauriImageModel::Midjourney
|
||||
| TauriImageModel::FluxProKontextMax
|
||||
| TauriImageModel::QwenEdit2511Angles
|
||||
| TauriImageModel::Flux2LoraAngles
|
||||
| TauriImageModel::FluxDevJuggernaut
|
||||
)
|
||||
}
|
||||
+35
-81
@@ -1,95 +1,49 @@
|
||||
use artcraft_router::api::common_image_model::CommonImageModel;
|
||||
use artcraft_router::api::image_list_ref::ImageListRef;
|
||||
use artcraft_router::api::provider::Provider;
|
||||
use artcraft_router::client::request_mismatch_mitigation_strategy::RequestMismatchMitigationStrategy;
|
||||
use artcraft_router::client::router_artcraft_client::RouterArtcraftClient;
|
||||
use artcraft_router::client::router_client::RouterClient;
|
||||
use artcraft_router::generate::generate_image::generate_image_request::GenerateImageRequest;
|
||||
use enums::common::generation_provider::GenerationProvider;
|
||||
use enums::tauri::tasks::task_type::TaskType;
|
||||
use log::{error, info};
|
||||
use artcraft_client::credentials::storyteller_credential_set::StorytellerCredentialSet;
|
||||
|
||||
use crate::core::commands::enqueue::generate_error::{GenerateError, MissingCredentialsReason};
|
||||
use crate::core::commands::enqueue::generate_error::GenerateError;
|
||||
use crate::core::commands::enqueue::task_enqueue_success::TaskEnqueueSuccess;
|
||||
use crate::core::commands::generate::generate_image::providers::artcraft::model_mapping::{
|
||||
map_to_generation_model, map_to_router_image_model,
|
||||
};
|
||||
use crate::core::commands::generate::generate_image::providers::artcraft::legacy::artcraft_flux_2_lora_angles::handle_flux_2_lora_angles;
|
||||
use crate::core::commands::generate::generate_image::providers::artcraft::legacy::artcraft_flux_dev_juggernaut_inpaint::handle_flux_dev_juggernaut_inpaint;
|
||||
use crate::core::commands::generate::generate_image::providers::artcraft::legacy::artcraft_flux_pro_kontext_edit::handle_flux_pro_kontext_edit;
|
||||
use crate::core::commands::generate::generate_image::providers::artcraft::legacy::artcraft_qwen_edit_2511_angles::handle_qwen_edit_2511_angles;
|
||||
use crate::core::commands::generate::generate_image::tauri_generate_image_request::TauriGenerateImageRequest;
|
||||
use crate::core::commands::generate::generate_image::tauri_image_model::TauriImageModel;
|
||||
use crate::core::commands::generate::generate_image::utils::parse_semantic_media_files::SemanticMediaFiles;
|
||||
use crate::core::state::app_env_configs::app_env_configs::AppEnvConfigs;
|
||||
use crate::services::storyteller::state::storyteller_credential_manager::StorytellerCredentialManager;
|
||||
|
||||
/// Handle image generation via the legacy artcraft_router path.
|
||||
/// Handle image generation via legacy model-specific paths.
|
||||
///
|
||||
/// This is used for models not yet supported by the omni endpoint
|
||||
/// (eg. Recraft, FluxProKontextMax, edit/inpaint models).
|
||||
/// Dispatches to dedicated handlers for each supported legacy model.
|
||||
pub async fn handle_artcraft_via_legacy(
|
||||
request: &TauriGenerateImageRequest,
|
||||
semantic_media_files: &SemanticMediaFiles,
|
||||
creds: &StorytellerCredentialSet,
|
||||
app_env_configs: &AppEnvConfigs,
|
||||
storyteller_creds_manager: &StorytellerCredentialManager,
|
||||
) -> Result<TaskEnqueueSuccess, GenerateError> {
|
||||
let model = request.model.ok_or(GenerateError::no_model_specified())?;
|
||||
|
||||
let router_model = map_to_router_image_model(model)
|
||||
.ok_or(GenerateError::NotYetImplemented(
|
||||
format!("Model {:?} is not supported via the legacy router path", model),
|
||||
))?;
|
||||
|
||||
let generation_model = map_to_generation_model(model);
|
||||
|
||||
let creds = match storyteller_creds_manager.get_credentials()? {
|
||||
Some(creds) => creds,
|
||||
None => return Err(GenerateError::MissingCredentials(MissingCredentialsReason::NeedsStorytellerCredentials)),
|
||||
};
|
||||
|
||||
let client = RouterClient::Artcraft(RouterArtcraftClient::new(
|
||||
app_env_configs.storyteller_host.clone(),
|
||||
creds,
|
||||
));
|
||||
|
||||
// Build image inputs: prepend canvas image if present.
|
||||
let mut image_tokens = request.image_media_tokens.clone().unwrap_or_default();
|
||||
if let Some(canvas_token) = &request.canvas_image_media_token {
|
||||
image_tokens.insert(0, canvas_token.clone());
|
||||
match model {
|
||||
TauriImageModel::FluxDevJuggernaut => {
|
||||
handle_flux_dev_juggernaut_inpaint(request, semantic_media_files, creds, app_env_configs).await
|
||||
}
|
||||
TauriImageModel::FluxProKontextMax => {
|
||||
handle_flux_pro_kontext_edit(request, semantic_media_files, creds, app_env_configs).await
|
||||
}
|
||||
TauriImageModel::QwenEdit2511Angles => {
|
||||
handle_qwen_edit_2511_angles(request, semantic_media_files, creds, app_env_configs).await
|
||||
}
|
||||
TauriImageModel::Flux2LoraAngles => {
|
||||
handle_flux_2_lora_angles(request, semantic_media_files, creds, app_env_configs).await
|
||||
}
|
||||
TauriImageModel::Midjourney | TauriImageModel::Recraft3 => {
|
||||
Err(GenerateError::NotYetImplemented(
|
||||
format!("Model {:?} is not supported by provider Artcraft", model),
|
||||
))
|
||||
}
|
||||
other => {
|
||||
Err(GenerateError::NotYetImplemented(
|
||||
format!("Model {:?} was incorrectly handled", other),
|
||||
))
|
||||
}
|
||||
}
|
||||
let image_inputs = if image_tokens.is_empty() { None } else { Some(ImageListRef::MediaFileTokens(image_tokens)) };
|
||||
|
||||
let router_request = GenerateImageRequest {
|
||||
model: router_model,
|
||||
provider: Provider::Artcraft,
|
||||
prompt: request.prompt.clone(),
|
||||
image_inputs,
|
||||
resolution: None, // TODO: Convert from request.resolution
|
||||
aspect_ratio: None, // TODO: Convert from request.aspect_ratio
|
||||
quality: None, // TODO: Convert from request.quality
|
||||
image_batch_count: request.batch_size.map(|n| n as u16),
|
||||
request_mismatch_mitigation_strategy: RequestMismatchMitigationStrategy::PayMoreUpgrade,
|
||||
generation_mode_mismatch_strategy: None,
|
||||
idempotency_token: None,
|
||||
horizontal_angle: request.adjust_horizontal_angle,
|
||||
vertical_angle: request.adjust_vertical_angle,
|
||||
zoom: request.adjust_zoom,
|
||||
};
|
||||
|
||||
let plan = router_request.build()?;
|
||||
|
||||
info!("Legacy image generation plan: {:?}", plan);
|
||||
|
||||
let response = plan.generate_image(&client).await.map_err(|err| {
|
||||
error!("Legacy image generation failed: {:?}", err);
|
||||
GenerateError::from(err)
|
||||
})?;
|
||||
|
||||
let job_id = response
|
||||
.get_artcraft_payload()
|
||||
.map(|p| p.inference_job_token.to_string())
|
||||
.ok_or(GenerateError::ResponseHadNoJobTokens)?;
|
||||
|
||||
info!("Legacy image generation succeeded: job_id={}", job_id);
|
||||
|
||||
Ok(TaskEnqueueSuccess {
|
||||
task_type: TaskType::ImageGeneration,
|
||||
model: Some(generation_model),
|
||||
provider: GenerationProvider::Artcraft,
|
||||
provider_job_id: Some(job_id),
|
||||
})
|
||||
}
|
||||
+93
@@ -0,0 +1,93 @@
|
||||
use log::{error, info};
|
||||
|
||||
use artcraft_client::credentials::storyteller_credential_set::StorytellerCredentialSet;
|
||||
use artcraft_router::api::common_image_model::CommonImageModel;
|
||||
use artcraft_router::api::image_list_ref::ImageListRef;
|
||||
use artcraft_router::api::provider::Provider;
|
||||
use artcraft_router::client::request_mismatch_mitigation_strategy::RequestMismatchMitigationStrategy;
|
||||
use artcraft_router::client::router_artcraft_client::RouterArtcraftClient;
|
||||
use artcraft_router::client::router_client::RouterClient;
|
||||
use artcraft_router::generate::generate_image::generate_image_request::GenerateImageRequest;
|
||||
use enums::common::generation_provider::GenerationProvider;
|
||||
use enums::tauri::tasks::task_type::TaskType;
|
||||
|
||||
use crate::core::commands::enqueue::generate_error::GenerateError;
|
||||
use crate::core::commands::enqueue::task_enqueue_success::TaskEnqueueSuccess;
|
||||
use crate::core::commands::generate::generate_image::tauri_generate_image_request::TauriGenerateImageRequest;
|
||||
use crate::core::commands::generate::generate_image::utils::parse_semantic_media_files::SemanticMediaFiles;
|
||||
use crate::core::events::generation_events::common::GenerationModel;
|
||||
use crate::core::state::app_env_configs::app_env_configs::AppEnvConfigs;
|
||||
|
||||
/// Handle Flux2LoraAngles image editing via the artcraft_router.
|
||||
pub async fn handle_flux_2_lora_angles(
|
||||
request: &TauriGenerateImageRequest,
|
||||
semantic_media_files: &SemanticMediaFiles,
|
||||
creds: &StorytellerCredentialSet,
|
||||
app_env_configs: &AppEnvConfigs,
|
||||
) -> Result<TaskEnqueueSuccess, GenerateError> {
|
||||
let client = RouterClient::Artcraft(RouterArtcraftClient::new(
|
||||
app_env_configs.storyteller_host.clone(),
|
||||
creds.clone(),
|
||||
));
|
||||
|
||||
let image_inputs = build_image_inputs(request, semantic_media_files);
|
||||
|
||||
let router_request = GenerateImageRequest {
|
||||
model: CommonImageModel::Flux2LoraAngles,
|
||||
provider: Provider::Artcraft,
|
||||
prompt: request.prompt.clone(),
|
||||
image_inputs,
|
||||
resolution: None,
|
||||
aspect_ratio: None,
|
||||
quality: None,
|
||||
image_batch_count: request.batch_size.map(|n| n as u16),
|
||||
request_mismatch_mitigation_strategy: RequestMismatchMitigationStrategy::PayMoreUpgrade,
|
||||
generation_mode_mismatch_strategy: None,
|
||||
idempotency_token: None,
|
||||
horizontal_angle: request.adjust_horizontal_angle,
|
||||
vertical_angle: request.adjust_vertical_angle,
|
||||
zoom: request.adjust_zoom,
|
||||
};
|
||||
|
||||
let plan = router_request.build()?;
|
||||
|
||||
info!("Flux2LoraAngles plan: {:?}", plan);
|
||||
|
||||
let response = plan.generate_image(&client).await.map_err(|err| {
|
||||
error!("Flux2LoraAngles generation failed: {:?}", err);
|
||||
GenerateError::from(err)
|
||||
})?;
|
||||
|
||||
let job_id = response
|
||||
.get_artcraft_payload()
|
||||
.map(|p| p.inference_job_token.to_string())
|
||||
.ok_or(GenerateError::ResponseHadNoJobTokens)?;
|
||||
|
||||
info!("Flux2LoraAngles succeeded: job_id={}", job_id);
|
||||
|
||||
Ok(TaskEnqueueSuccess {
|
||||
task_type: TaskType::ImageGeneration,
|
||||
model: Some(GenerationModel::Flux2LoraAngles),
|
||||
provider: GenerationProvider::Artcraft,
|
||||
provider_job_id: Some(job_id),
|
||||
})
|
||||
}
|
||||
|
||||
fn build_image_inputs(
|
||||
request: &TauriGenerateImageRequest,
|
||||
semantic_media_files: &SemanticMediaFiles,
|
||||
) -> Option<ImageListRef> {
|
||||
let mut tokens = Vec::new();
|
||||
|
||||
if let Some(scene_token) = &semantic_media_files.scene_image_media_token {
|
||||
tokens.push(scene_token.clone());
|
||||
}
|
||||
if let Some(canvas_token) = &semantic_media_files.canvas_image_media_token {
|
||||
tokens.push(canvas_token.clone());
|
||||
}
|
||||
if let Some(media_tokens) = &request.image_media_tokens {
|
||||
tokens.extend(media_tokens.clone());
|
||||
}
|
||||
|
||||
if tokens.is_empty() { None } else { Some(ImageListRef::MediaFileTokens(tokens)) }
|
||||
}
|
||||
+98
@@ -0,0 +1,98 @@
|
||||
use log::{error, info};
|
||||
|
||||
use artcraft_api_defs::generate::image::inpaint::flux_dev_juggernaut_inpaint_image::{
|
||||
FluxDevJuggernautInpaintImageNumImages, FluxDevJuggernautInpaintImageRequest,
|
||||
};
|
||||
use artcraft_client::credentials::storyteller_credential_set::StorytellerCredentialSet;
|
||||
use artcraft_client::endpoints::generate::image::inpaint::flux_dev_juggernaut_inpaint_image::flux_dev_juggernaut_inpaint_image;
|
||||
use enums::common::generation_provider::GenerationProvider;
|
||||
use enums::tauri::tasks::task_type::TaskType;
|
||||
use uuid_utils::uuid::generate_random_uuid;
|
||||
|
||||
use tokens::tokens::media_files::MediaFileToken;
|
||||
|
||||
use crate::core::commands::enqueue::generate_error::{BadInputReason, GenerateError};
|
||||
use crate::core::commands::enqueue::task_enqueue_success::TaskEnqueueSuccess;
|
||||
use crate::core::commands::generate::generate_image::tauri_generate_image_request::TauriGenerateImageRequest;
|
||||
use crate::core::commands::generate::generate_image::utils::parse_semantic_media_files::SemanticMediaFiles;
|
||||
use crate::core::events::generation_events::common::GenerationModel;
|
||||
use crate::core::state::app_env_configs::app_env_configs::AppEnvConfigs;
|
||||
|
||||
/// Handle FluxDevJuggernaut inpainting via the legacy dedicated endpoint.
|
||||
///
|
||||
/// Requires a source image (from scene, canvas, or image_media_tokens) and an inpainting mask.
|
||||
pub async fn handle_flux_dev_juggernaut_inpaint(
|
||||
request: &TauriGenerateImageRequest,
|
||||
semantic_media_files: &SemanticMediaFiles,
|
||||
creds: &StorytellerCredentialSet,
|
||||
app_env_configs: &AppEnvConfigs,
|
||||
) -> Result<TaskEnqueueSuccess, GenerateError> {
|
||||
let image_media_token = get_first_image_token(request, semantic_media_files)
|
||||
.ok_or(GenerateError::required_source_image_not_provided())?;
|
||||
|
||||
let mask_media_token = semantic_media_files.inpainting_mask_image_media_token.clone()
|
||||
.ok_or(GenerateError::required_source_image_mask_not_provided())?;
|
||||
|
||||
let num_images = match request.batch_size {
|
||||
None => None,
|
||||
Some(1) => Some(FluxDevJuggernautInpaintImageNumImages::One),
|
||||
Some(2) => Some(FluxDevJuggernautInpaintImageNumImages::Two),
|
||||
Some(3) => Some(FluxDevJuggernautInpaintImageNumImages::Three),
|
||||
Some(4) => Some(FluxDevJuggernautInpaintImageNumImages::Four),
|
||||
Some(other) => {
|
||||
return Err(GenerateError::BadInput(BadInputReason::InvalidNumberOfRequestedImages {
|
||||
min: 1,
|
||||
max: 4,
|
||||
requested: other,
|
||||
}));
|
||||
}
|
||||
};
|
||||
|
||||
let uuid_idempotency_token = generate_random_uuid();
|
||||
|
||||
let inpaint_request = FluxDevJuggernautInpaintImageRequest {
|
||||
uuid_idempotency_token,
|
||||
prompt: request.prompt.clone(),
|
||||
image_media_token,
|
||||
mask_media_token,
|
||||
num_images,
|
||||
};
|
||||
|
||||
info!("Calling FluxDevJuggernaut inpaint...");
|
||||
|
||||
let response = flux_dev_juggernaut_inpaint_image(
|
||||
&app_env_configs.storyteller_host,
|
||||
Some(creds),
|
||||
inpaint_request,
|
||||
).await.map_err(|err| {
|
||||
error!("FluxDevJuggernaut inpaint failed: {:?}", err);
|
||||
GenerateError::from(err)
|
||||
})?;
|
||||
|
||||
info!("FluxDevJuggernaut inpaint succeeded: job_token={}", response.inference_job_token);
|
||||
|
||||
Ok(TaskEnqueueSuccess {
|
||||
task_type: TaskType::ImageInpaintEdit,
|
||||
model: Some(GenerationModel::FluxDevJuggernaut),
|
||||
provider: GenerationProvider::Artcraft,
|
||||
provider_job_id: Some(response.inference_job_token.to_string()),
|
||||
})
|
||||
}
|
||||
|
||||
fn get_first_image_token(
|
||||
request: &TauriGenerateImageRequest,
|
||||
semantic_media_files: &SemanticMediaFiles,
|
||||
) -> Option<MediaFileToken> {
|
||||
if let Some(scene_token) = &semantic_media_files.scene_image_media_token {
|
||||
return Some(scene_token.clone());
|
||||
}
|
||||
if let Some(canvas_token) = &semantic_media_files.canvas_image_media_token {
|
||||
return Some(canvas_token.clone());
|
||||
}
|
||||
if let Some(media_tokens) = &request.image_media_tokens {
|
||||
if let Some(token) = media_tokens.first() {
|
||||
return Some(token.clone());
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
+96
@@ -0,0 +1,96 @@
|
||||
use log::{error, info};
|
||||
|
||||
use artcraft_api_defs::generate::image::edit::flux_pro_kontext_max_edit_image::{
|
||||
FluxProKontextMaxEditImageNumImages, FluxProKontextMaxEditImageRequest,
|
||||
};
|
||||
use artcraft_client::credentials::storyteller_credential_set::StorytellerCredentialSet;
|
||||
use artcraft_client::endpoints::generate::image::edit::flux_pro_kontext_max_edit_image::flux_pro_kontext_max_edit_image;
|
||||
use enums::common::generation_provider::GenerationProvider;
|
||||
use enums::tauri::tasks::task_type::TaskType;
|
||||
use tokens::tokens::media_files::MediaFileToken;
|
||||
use uuid_utils::uuid::generate_random_uuid;
|
||||
|
||||
use crate::core::commands::enqueue::generate_error::{BadInputReason, GenerateError};
|
||||
use crate::core::commands::enqueue::task_enqueue_success::TaskEnqueueSuccess;
|
||||
use crate::core::commands::generate::generate_image::tauri_generate_image_request::TauriGenerateImageRequest;
|
||||
use crate::core::commands::generate::generate_image::utils::parse_semantic_media_files::SemanticMediaFiles;
|
||||
use crate::core::events::generation_events::common::GenerationModel;
|
||||
use crate::core::state::app_env_configs::app_env_configs::AppEnvConfigs;
|
||||
|
||||
/// Handle FluxProKontextMax image editing via the legacy dedicated endpoint.
|
||||
///
|
||||
/// Consolidates both the "edit" and "inpaint" legacy paths — both call the same
|
||||
/// Kontext Max edit endpoint with a single source image.
|
||||
pub async fn handle_flux_pro_kontext_edit(
|
||||
request: &TauriGenerateImageRequest,
|
||||
semantic_media_files: &SemanticMediaFiles,
|
||||
creds: &StorytellerCredentialSet,
|
||||
app_env_configs: &AppEnvConfigs,
|
||||
) -> Result<TaskEnqueueSuccess, GenerateError> {
|
||||
let image_media_token = get_first_image_token(request, semantic_media_files)
|
||||
.ok_or(GenerateError::BadInput(BadInputReason::WrongImageArguments(
|
||||
"No input image specified for Flux Kontext Max edit".to_string(),
|
||||
)))?;
|
||||
|
||||
let num_images = match request.batch_size {
|
||||
None => None,
|
||||
Some(1) => Some(FluxProKontextMaxEditImageNumImages::One),
|
||||
Some(2) => Some(FluxProKontextMaxEditImageNumImages::Two),
|
||||
Some(3) => Some(FluxProKontextMaxEditImageNumImages::Three),
|
||||
Some(4) => Some(FluxProKontextMaxEditImageNumImages::Four),
|
||||
Some(other) => {
|
||||
return Err(GenerateError::BadInput(BadInputReason::InvalidNumberOfRequestedImages {
|
||||
min: 1,
|
||||
max: 4,
|
||||
requested: other,
|
||||
}));
|
||||
}
|
||||
};
|
||||
|
||||
let uuid_idempotency_token = generate_random_uuid();
|
||||
|
||||
let kontext_request = FluxProKontextMaxEditImageRequest {
|
||||
uuid_idempotency_token,
|
||||
prompt: request.prompt.clone(),
|
||||
image_media_token,
|
||||
num_images,
|
||||
};
|
||||
|
||||
info!("Calling FluxProKontextMax edit...");
|
||||
|
||||
let response = flux_pro_kontext_max_edit_image(
|
||||
&app_env_configs.storyteller_host,
|
||||
Some(creds),
|
||||
kontext_request,
|
||||
).await.map_err(|err| {
|
||||
error!("FluxProKontextMax edit failed: {:?}", err);
|
||||
GenerateError::from(err)
|
||||
})?;
|
||||
|
||||
info!("FluxProKontextMax edit succeeded: job_token={}", response.inference_job_token);
|
||||
|
||||
Ok(TaskEnqueueSuccess {
|
||||
task_type: TaskType::ImageGeneration,
|
||||
model: Some(GenerationModel::FluxProKontextMax),
|
||||
provider: GenerationProvider::Artcraft,
|
||||
provider_job_id: Some(response.inference_job_token.to_string()),
|
||||
})
|
||||
}
|
||||
|
||||
fn get_first_image_token(
|
||||
request: &TauriGenerateImageRequest,
|
||||
semantic_media_files: &SemanticMediaFiles,
|
||||
) -> Option<MediaFileToken> {
|
||||
if let Some(scene_token) = &semantic_media_files.scene_image_media_token {
|
||||
return Some(scene_token.clone());
|
||||
}
|
||||
if let Some(canvas_token) = &semantic_media_files.canvas_image_media_token {
|
||||
return Some(canvas_token.clone());
|
||||
}
|
||||
if let Some(media_tokens) = &request.image_media_tokens {
|
||||
if let Some(token) = media_tokens.first() {
|
||||
return Some(token.clone());
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
+93
@@ -0,0 +1,93 @@
|
||||
use log::{error, info};
|
||||
|
||||
use artcraft_client::credentials::storyteller_credential_set::StorytellerCredentialSet;
|
||||
use artcraft_router::api::common_image_model::CommonImageModel;
|
||||
use artcraft_router::api::image_list_ref::ImageListRef;
|
||||
use artcraft_router::api::provider::Provider;
|
||||
use artcraft_router::client::request_mismatch_mitigation_strategy::RequestMismatchMitigationStrategy;
|
||||
use artcraft_router::client::router_artcraft_client::RouterArtcraftClient;
|
||||
use artcraft_router::client::router_client::RouterClient;
|
||||
use artcraft_router::generate::generate_image::generate_image_request::GenerateImageRequest;
|
||||
use enums::common::generation_provider::GenerationProvider;
|
||||
use enums::tauri::tasks::task_type::TaskType;
|
||||
|
||||
use crate::core::commands::enqueue::generate_error::GenerateError;
|
||||
use crate::core::commands::enqueue::task_enqueue_success::TaskEnqueueSuccess;
|
||||
use crate::core::commands::generate::generate_image::tauri_generate_image_request::TauriGenerateImageRequest;
|
||||
use crate::core::commands::generate::generate_image::utils::parse_semantic_media_files::SemanticMediaFiles;
|
||||
use crate::core::events::generation_events::common::GenerationModel;
|
||||
use crate::core::state::app_env_configs::app_env_configs::AppEnvConfigs;
|
||||
|
||||
/// Handle QwenEdit2511Angles image editing via the artcraft_router.
|
||||
pub async fn handle_qwen_edit_2511_angles(
|
||||
request: &TauriGenerateImageRequest,
|
||||
semantic_media_files: &SemanticMediaFiles,
|
||||
creds: &StorytellerCredentialSet,
|
||||
app_env_configs: &AppEnvConfigs,
|
||||
) -> Result<TaskEnqueueSuccess, GenerateError> {
|
||||
let client = RouterClient::Artcraft(RouterArtcraftClient::new(
|
||||
app_env_configs.storyteller_host.clone(),
|
||||
creds.clone(),
|
||||
));
|
||||
|
||||
let image_inputs = build_image_inputs(request, semantic_media_files);
|
||||
|
||||
let router_request = GenerateImageRequest {
|
||||
model: CommonImageModel::QwenEdit2511Angles,
|
||||
provider: Provider::Artcraft,
|
||||
prompt: request.prompt.clone(),
|
||||
image_inputs,
|
||||
resolution: None,
|
||||
aspect_ratio: None,
|
||||
quality: None,
|
||||
image_batch_count: request.batch_size.map(|n| n as u16),
|
||||
request_mismatch_mitigation_strategy: RequestMismatchMitigationStrategy::PayMoreUpgrade,
|
||||
generation_mode_mismatch_strategy: None,
|
||||
idempotency_token: None,
|
||||
horizontal_angle: request.adjust_horizontal_angle,
|
||||
vertical_angle: request.adjust_vertical_angle,
|
||||
zoom: request.adjust_zoom,
|
||||
};
|
||||
|
||||
let plan = router_request.build()?;
|
||||
|
||||
info!("QwenEdit2511Angles plan: {:?}", plan);
|
||||
|
||||
let response = plan.generate_image(&client).await.map_err(|err| {
|
||||
error!("QwenEdit2511Angles generation failed: {:?}", err);
|
||||
GenerateError::from(err)
|
||||
})?;
|
||||
|
||||
let job_id = response
|
||||
.get_artcraft_payload()
|
||||
.map(|p| p.inference_job_token.to_string())
|
||||
.ok_or(GenerateError::ResponseHadNoJobTokens)?;
|
||||
|
||||
info!("QwenEdit2511Angles succeeded: job_id={}", job_id);
|
||||
|
||||
Ok(TaskEnqueueSuccess {
|
||||
task_type: TaskType::ImageGeneration,
|
||||
model: Some(GenerationModel::QwenEdit2511Angles),
|
||||
provider: GenerationProvider::Artcraft,
|
||||
provider_job_id: Some(job_id),
|
||||
})
|
||||
}
|
||||
|
||||
fn build_image_inputs(
|
||||
request: &TauriGenerateImageRequest,
|
||||
semantic_media_files: &SemanticMediaFiles,
|
||||
) -> Option<ImageListRef> {
|
||||
let mut tokens = Vec::new();
|
||||
|
||||
if let Some(scene_token) = &semantic_media_files.scene_image_media_token {
|
||||
tokens.push(scene_token.clone());
|
||||
}
|
||||
if let Some(canvas_token) = &semantic_media_files.canvas_image_media_token {
|
||||
tokens.push(canvas_token.clone());
|
||||
}
|
||||
if let Some(media_tokens) = &request.image_media_tokens {
|
||||
tokens.extend(media_tokens.clone());
|
||||
}
|
||||
|
||||
if tokens.is_empty() { None } else { Some(ImageListRef::MediaFileTokens(tokens)) }
|
||||
}
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
pub mod artcraft_flux_2_lora_angles;
|
||||
pub mod artcraft_flux_dev_juggernaut_inpaint;
|
||||
pub mod artcraft_flux_pro_kontext_edit;
|
||||
pub mod artcraft_qwen_edit_2511_angles;
|
||||
+32
@@ -1,9 +1,14 @@
|
||||
<<<<<<< HEAD
|
||||
use artcraft_router::api::common_aspect_ratio::CommonAspectRatio as RouterCommonAspectRatio;
|
||||
use artcraft_router::api::common_image_model::CommonImageModel as RouterCommonImageModel;
|
||||
use artcraft_router::api::common_resolution::CommonResolution as RouterCommonResolution;
|
||||
use enums::common::generation::common_aspect_ratio::CommonAspectRatio as OmniCommonAspectRatio;
|
||||
use enums::common::generation::common_image_model::CommonImageModel as OmniCommonImageModel;
|
||||
use enums::common::generation::common_resolution::CommonResolution as OmniCommonResolution;
|
||||
=======
|
||||
use artcraft_router::api::common_image_model::CommonImageModel as RouterCommonImageModel;
|
||||
use enums::common::generation::common_image_model::CommonImageModel as OmniCommonImageModel;
|
||||
>>>>>>> 13896d9306 (cleanup)
|
||||
|
||||
use crate::core::commands::generate::generate_image::tauri_image_model::TauriImageModel;
|
||||
use crate::core::events::generation_events::common::GenerationModel;
|
||||
@@ -14,7 +19,10 @@ pub fn map_to_omni_image_model(model: TauriImageModel) -> Option<OmniCommonImage
|
||||
match model {
|
||||
TauriImageModel::Flux1Dev => Some(OmniCommonImageModel::Flux1Dev),
|
||||
TauriImageModel::Flux1Schnell => Some(OmniCommonImageModel::Flux1Schnell),
|
||||
<<<<<<< HEAD
|
||||
TauriImageModel::FluxPro1 => Some(OmniCommonImageModel::FluxPro11), // TODO: Might be a slight mismatch
|
||||
=======
|
||||
>>>>>>> 13896d9306 (cleanup)
|
||||
TauriImageModel::FluxPro11 => Some(OmniCommonImageModel::FluxPro11),
|
||||
TauriImageModel::FluxPro11Ultra => Some(OmniCommonImageModel::FluxPro11Ultra),
|
||||
TauriImageModel::GptImage1 => Some(OmniCommonImageModel::GptImage1),
|
||||
@@ -27,6 +35,7 @@ pub fn map_to_omni_image_model(model: TauriImageModel) -> Option<OmniCommonImage
|
||||
TauriImageModel::Seedream4 => Some(OmniCommonImageModel::Seedream4),
|
||||
TauriImageModel::Seedream4p5 => Some(OmniCommonImageModel::Seedream4p5),
|
||||
TauriImageModel::Seedream5Lite => Some(OmniCommonImageModel::Seedream5Lite),
|
||||
<<<<<<< HEAD
|
||||
// Not accounted for yet
|
||||
TauriImageModel::GrokImage => None,
|
||||
TauriImageModel::Recraft3 => None,
|
||||
@@ -35,6 +44,9 @@ pub fn map_to_omni_image_model(model: TauriImageModel) -> Option<OmniCommonImage
|
||||
TauriImageModel::QwenEdit2511Angles => None,
|
||||
TauriImageModel::Flux2LoraAngles => None,
|
||||
TauriImageModel::FluxDevJuggernaut => None,
|
||||
=======
|
||||
_ => None,
|
||||
>>>>>>> 13896d9306 (cleanup)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,6 +54,7 @@ pub fn map_to_omni_image_model(model: TauriImageModel) -> Option<OmniCommonImage
|
||||
/// Returns None for models not supported by the router (Grok, Midjourney).
|
||||
pub fn map_to_router_image_model(model: TauriImageModel) -> Option<RouterCommonImageModel> {
|
||||
match model {
|
||||
<<<<<<< HEAD
|
||||
TauriImageModel::Flux1Dev => Some(RouterCommonImageModel::Flux1Dev), // Text-to-Image
|
||||
TauriImageModel::Flux1Schnell => Some(RouterCommonImageModel::Flux1Schnell), // Text-to-Image
|
||||
TauriImageModel::FluxPro1 => Some(RouterCommonImageModel::FluxPro11), // TODO: Might be a slight mismatch
|
||||
@@ -103,6 +116,25 @@ pub fn map_to_omni_resolution(res: RouterCommonResolution) -> OmniCommonResoluti
|
||||
RouterCommonResolution::FourEightyP => OmniCommonResolution::FourEightyP,
|
||||
RouterCommonResolution::SevenTwentyP => OmniCommonResolution::SevenTwentyP,
|
||||
RouterCommonResolution::TenEightyP => OmniCommonResolution::TenEightyP,
|
||||
=======
|
||||
TauriImageModel::Flux1Dev => Some(RouterCommonImageModel::Flux1Dev),
|
||||
TauriImageModel::Flux1Schnell => Some(RouterCommonImageModel::Flux1Schnell),
|
||||
TauriImageModel::FluxPro11 => Some(RouterCommonImageModel::FluxPro11),
|
||||
TauriImageModel::FluxPro11Ultra => Some(RouterCommonImageModel::FluxPro11Ultra),
|
||||
TauriImageModel::GptImage1 => Some(RouterCommonImageModel::GptImage1),
|
||||
TauriImageModel::GptImage1p5 => Some(RouterCommonImageModel::GptImage1p5),
|
||||
TauriImageModel::GptImage2 => Some(RouterCommonImageModel::GptImage2),
|
||||
TauriImageModel::NanoBanana => Some(RouterCommonImageModel::NanoBanana),
|
||||
TauriImageModel::NanoBanana2 => Some(RouterCommonImageModel::NanoBanana2),
|
||||
TauriImageModel::NanoBananaPro => Some(RouterCommonImageModel::NanoBananaPro),
|
||||
TauriImageModel::Gemini25Flash => Some(RouterCommonImageModel::NanoBanana),
|
||||
TauriImageModel::Seedream4 => Some(RouterCommonImageModel::Seedream4),
|
||||
TauriImageModel::Seedream4p5 => Some(RouterCommonImageModel::Seedream4p5),
|
||||
TauriImageModel::Seedream5Lite => Some(RouterCommonImageModel::Seedream5Lite),
|
||||
TauriImageModel::QwenEdit2511Angles => Some(RouterCommonImageModel::QwenEdit2511Angles),
|
||||
TauriImageModel::Flux2LoraAngles => Some(RouterCommonImageModel::Flux2LoraAngles),
|
||||
_ => None,
|
||||
>>>>>>> 13896d9306 (cleanup)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
use crate::core::api_adapters::aspect_ratio::convert::aspect_ratio_to_grok_image::aspect_ratio_to_grok_image;
|
||||
use crate::core::commands::enqueue::generate_error::GenerateError;
|
||||
use crate::core::commands::enqueue::task_enqueue_success::TaskEnqueueSuccess;
|
||||
use crate::core::commands::enqueue::text_to_image::enqueue_text_to_image_command::{EnqueueTextToImageRequest, TextToImageSize};
|
||||
use crate::core::commands::deprecated::text_to_image::enqueue_text_to_image_command::{EnqueueTextToImageRequest, TextToImageSize};
|
||||
use crate::core::events::generation_events::common::GenerationModel;
|
||||
use crate::core::state::app_env_configs::app_env_configs::AppEnvConfigs;
|
||||
use crate::services::grok::state::grok_credential_manager::GrokCredentialManager;
|
||||
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
use crate::core::commands::enqueue::generate_error::{GenerateError, MissingCredentialsReason, ProviderFailureReason};
|
||||
use crate::core::commands::enqueue::image_edit::enqueue_edit_image_command::{EditImageQuality, EditImageSize};
|
||||
use crate::core::commands::deprecated::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::commands::deprecated::text_to_image::enqueue_text_to_image_command::{EnqueueTextToImageRequest, TextToImageSize};
|
||||
use crate::core::events::basic_sendable_event_trait::BasicSendableEvent;
|
||||
use crate::core::events::functional_events::canvas_background_removal_complete_event::CanvasBackgroundRemovalCompleteEvent;
|
||||
use crate::core::events::functional_events::show_provider_login_modal_event::ShowProviderLoginModalEvent;
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@ use crate::core::api_adapters::aspect_ratio::convert::aspect_ratio_to_grok_image
|
||||
use crate::core::api_adapters::aspect_ratio::convert::aspect_ratio_to_sora_native_gpt_image_1::aspect_ratio_to_sora_native_gpt_image_1;
|
||||
use crate::core::commands::enqueue::generate_error::GenerateError;
|
||||
use crate::core::commands::enqueue::task_enqueue_success::TaskEnqueueSuccess;
|
||||
use crate::core::commands::enqueue::text_to_image::enqueue_text_to_image_command::{EnqueueTextToImageRequest, TextToImageSize};
|
||||
use crate::core::commands::deprecated::text_to_image::enqueue_text_to_image_command::{EnqueueTextToImageRequest, TextToImageSize};
|
||||
use crate::core::events::basic_sendable_event_trait::BasicSendableEvent;
|
||||
use crate::core::events::functional_events::show_provider_login_modal_event::ShowProviderLoginModalEvent;
|
||||
use crate::core::events::generation_events::common::{GenerationAction, GenerationModel, GenerationServiceProvider};
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
use crate::core::commands::enqueue::generate_error::GenerateError;
|
||||
use crate::core::commands::enqueue::task_enqueue_success::TaskEnqueueSuccess;
|
||||
use crate::core::commands::enqueue::text_to_image::enqueue_text_to_image_command::EnqueueTextToImageRequest;
|
||||
use crate::core::commands::deprecated::text_to_image::enqueue_text_to_image_command::EnqueueTextToImageRequest;
|
||||
use crate::core::commands::generate::generate_image::sora::handle_gpt_image_1_sora_text_to_image::handle_gpt_image_1_sora_text_to_image;
|
||||
use crate::services::sora::state::sora_credential_manager::SoraCredentialManager;
|
||||
use crate::services::sora::state::sora_task_queue::SoraTaskQueue;
|
||||
|
||||
Reference in new issue
Block a user