end-to-end 2.5

This commit is contained in:
Hanashi committed 2026-09-09 14:45:59 -04:00
1 parent 072d892430
commit e9b3559ead
19 files changed
+329 -1

No files matched your search

@@ -16,6 +16,8 @@ pub enum ApiClientImageModel {
GptImage1p5,
GptImage2,
GrokImagineImage,
GptImage2p5Flare,
GptImage2p5Sunburst,
GrokImagineImageQuality,
Midjourney7,
Midjourney7Niji,
@@ -42,6 +44,8 @@ impl From<String> for ApiClientImageModel {
"gpt_image_1" => Self::GptImage1,
"gpt_image_1p5" => Self::GptImage1p5,
"gpt_image_2" => Self::GptImage2,
"gpt_image_2p5_flare" => Self::GptImage2p5Flare,
"gpt_image_2p5_sunburst" => Self::GptImage2p5Sunburst,
"grok_imagine_image" => Self::GrokImagineImage,
"grok_imagine_image_q" => Self::GrokImagineImageQuality,
"midjourney_7" => Self::Midjourney7,
@@ -70,6 +74,8 @@ impl From<ApiClientImageModel> for String {
ApiClientImageModel::GptImage1 => "gpt_image_1".to_string(),
ApiClientImageModel::GptImage1p5 => "gpt_image_1p5".to_string(),
ApiClientImageModel::GptImage2 => "gpt_image_2".to_string(),
ApiClientImageModel::GptImage2p5Flare => "gpt_image_2p5_flare".to_string(),
ApiClientImageModel::GptImage2p5Sunburst => "gpt_image_2p5_sunburst".to_string(),
ApiClientImageModel::GrokImagineImage => "grok_imagine_image".to_string(),
ApiClientImageModel::GrokImagineImageQuality => "grok_imagine_image_q".to_string(),
ApiClientImageModel::Midjourney7 => "midjourney_7".to_string(),
@@ -116,6 +122,8 @@ mod tests {
(ApiClientImageModel::GptImage1, "gpt_image_1"),
(ApiClientImageModel::GptImage1p5, "gpt_image_1p5"),
(ApiClientImageModel::GptImage2, "gpt_image_2"),
(ApiClientImageModel::GptImage2p5Flare, "gpt_image_2p5_flare"),
(ApiClientImageModel::GptImage2p5Sunburst, "gpt_image_2p5_sunburst"),
(ApiClientImageModel::GrokImagineImage, "grok_imagine_image"),
(ApiClientImageModel::GrokImagineImageQuality, "grok_imagine_image_q"),
(ApiClientImageModel::Midjourney7, "midjourney_7"),
@@ -14,6 +14,8 @@ pub fn tauri_image_model_to_common_model_type(model: TauriImageModel) -> CommonM
TauriImageModel::GptImage1 => CommonModelType::GptImage1,
TauriImageModel::GptImage1p5 => CommonModelType::GptImage1p5,
TauriImageModel::GptImage2 => CommonModelType::GptImage2,
TauriImageModel::GptImage2p5Flare => CommonModelType::GptImage2p5Flare,
TauriImageModel::GptImage2p5Sunburst => CommonModelType::GptImage2p5Sunburst,
TauriImageModel::Gemini25Flash => CommonModelType::NanoBanana,
TauriImageModel::NanoBanana => CommonModelType::NanoBanana,
TauriImageModel::NanoBanana2 => CommonModelType::NanoBanana2,
@@ -14,6 +14,8 @@ pub fn tauri_image_model_to_enums_model(model: TauriImageModel) -> Option<Common
TauriImageModel::GptImage1 => Some(CommonImageModel::GptImage1),
TauriImageModel::GptImage1p5 => Some(CommonImageModel::GptImage1p5),
TauriImageModel::GptImage2 => Some(CommonImageModel::GptImage2),
TauriImageModel::GptImage2p5Flare => Some(CommonImageModel::GptImage2p5Flare),
TauriImageModel::GptImage2p5Sunburst => Some(CommonImageModel::GptImage2p5Sunburst),
TauriImageModel::NanoBanana => Some(CommonImageModel::NanoBanana),
TauriImageModel::NanoBanana2 => Some(CommonImageModel::NanoBanana2),
TauriImageModel::NanoBananaPro => Some(CommonImageModel::NanoBananaPro),
@@ -13,6 +13,8 @@ pub fn tauri_image_model_to_generation_model(model: TauriImageModel) -> Generati
TauriImageModel::GptImage1 => GenerationModel::GptImage1,
TauriImageModel::GptImage1p5 => GenerationModel::GptImage1p5,
TauriImageModel::GptImage2 => GenerationModel::GptImage2,
TauriImageModel::GptImage2p5Flare => GenerationModel::GptImage2p5Flare,
TauriImageModel::GptImage2p5Sunburst => GenerationModel::GptImage2p5Sunburst,
TauriImageModel::Gemini25Flash => GenerationModel::NanoBanana,
TauriImageModel::NanoBanana => GenerationModel::NanoBanana,
TauriImageModel::NanoBanana2 => GenerationModel::NanoBanana2,
@@ -14,6 +14,8 @@ pub fn tauri_image_model_to_router_model(model: TauriImageModel) -> Option<Route
TauriImageModel::GptImage1 => Some(RouterImageModel::GptImage1), // Text-to-Image
TauriImageModel::GptImage1p5 => Some(RouterImageModel::GptImage1p5), // Text-to-Image
TauriImageModel::GptImage2 => Some(RouterImageModel::GptImage2), // Text-to-Image
TauriImageModel::GptImage2p5Flare => Some(RouterImageModel::GptImage2p5Flare), // Text-to-Image
TauriImageModel::GptImage2p5Sunburst => Some(RouterImageModel::GptImage2p5Sunburst), // Text-to-Image
TauriImageModel::NanoBanana => Some(RouterImageModel::NanoBanana), // Text-to-Image
TauriImageModel::NanoBanana2 => Some(RouterImageModel::NanoBanana2), // Text-to-Image
TauriImageModel::NanoBananaPro => Some(RouterImageModel::NanoBananaPro), // Text-to-Image
@@ -85,6 +85,8 @@ impl TaskEnqueueSuccess{
Some(GenerationModel::GptImage1) => Some(TaskModelType::GptImage1),
Some(GenerationModel::GptImage1p5) => Some(TaskModelType::GptImage1p5),
Some(GenerationModel::GptImage2) => Some(TaskModelType::GptImage2),
Some(GenerationModel::GptImage2p5Flare) => Some(TaskModelType::GptImage2p5Flare),
Some(GenerationModel::GptImage2p5Sunburst) => Some(TaskModelType::GptImage2p5Sunburst),
Some(GenerationModel::Seedream4) => Some(TaskModelType::Seedream4),
Some(GenerationModel::Seedream4p5) => Some(TaskModelType::Seedream4p5),
Some(GenerationModel::Seedream5Lite) => Some(TaskModelType::Seedream5Lite),
@@ -32,6 +32,10 @@ pub enum TauriImageModel {
GptImage1p5,
#[serde(rename = "gpt_image_2")]
GptImage2,
#[serde(rename = "gpt_image_2p5_flare")]
GptImage2p5Flare,
#[serde(rename = "gpt_image_2p5_sunburst")]
GptImage2p5Sunburst,
#[serde(rename = "gemini_25_flash")]
Gemini25Flash,
#[serde(rename = "nano_banana")]
@@ -191,6 +191,8 @@ pub enum ListImageModelsImageModel {
GptImage1p5,
GptImage2,
GrokImagineImage,
GptImage2p5Flare,
GptImage2p5Sunburst,
GrokImagineImageQuality,
Midjourney7,
Midjourney7Niji,
@@ -216,6 +218,8 @@ impl From<ApiClientImageModel> for ListImageModelsImageModel {
ApiClientImageModel::GptImage1 => Self::GptImage1,
ApiClientImageModel::GptImage1p5 => Self::GptImage1p5,
ApiClientImageModel::GptImage2 => Self::GptImage2,
ApiClientImageModel::GptImage2p5Flare => Self::GptImage2p5Flare,
ApiClientImageModel::GptImage2p5Sunburst => Self::GptImage2p5Sunburst,
ApiClientImageModel::GrokImagineImage => Self::GrokImagineImage,
ApiClientImageModel::GrokImagineImageQuality => Self::GrokImagineImageQuality,
ApiClientImageModel::Midjourney7 => Self::Midjourney7,
@@ -244,6 +248,8 @@ impl From<ListImageModelsImageModel> for String {
ListImageModelsImageModel::GptImage1 => "gpt_image_1".to_string(),
ListImageModelsImageModel::GptImage1p5 => "gpt_image_1p5".to_string(),
ListImageModelsImageModel::GptImage2 => "gpt_image_2".to_string(),
ListImageModelsImageModel::GptImage2p5Flare => "gpt_image_2p5_flare".to_string(),
ListImageModelsImageModel::GptImage2p5Sunburst => "gpt_image_2p5_sunburst".to_string(),
ListImageModelsImageModel::GrokImagineImage => "grok_imagine_image".to_string(),
ListImageModelsImageModel::GrokImagineImageQuality => "grok_imagine_image_q".to_string(),
ListImageModelsImageModel::Midjourney7 => "midjourney_7".to_string(),
@@ -43,6 +43,10 @@ pub enum GenerationModel {
GptImage1p5,
#[serde(rename = "gpt_image_2")]
GptImage2,
#[serde(rename = "gpt_image_2p5_flare")]
GptImage2p5Flare,
#[serde(rename = "gpt_image_2p5_sunburst")]
GptImage2p5Sunburst,
#[serde(rename = "grok_image")]
GrokImage,
#[serde(rename = "gemini_25_flash")]
@@ -298,6 +298,8 @@ fn task_model_type_to_generation_model(model: TaskModelType) -> Option<Generatio
TaskModelType::GptImage1 => Some(GenerationModel::GptImage1),
TaskModelType::GptImage1p5 => Some(GenerationModel::GptImage1p5),
TaskModelType::GptImage2 => Some(GenerationModel::GptImage2),
TaskModelType::GptImage2p5Flare => Some(GenerationModel::GptImage2p5Flare),
TaskModelType::GptImage2p5Sunburst => Some(GenerationModel::GptImage2p5Sunburst),
TaskModelType::NanoBanana => Some(GenerationModel::NanoBanana),
TaskModelType::NanoBanana2 => Some(GenerationModel::NanoBanana2),
TaskModelType::NanoBananaPro => Some(GenerationModel::NanoBananaPro),
@@ -41,6 +41,10 @@ pub enum TaskModelType {
GptImage1p5,
#[serde(rename = "gpt_image_2")]
GptImage2,
#[serde(rename = "gpt_image_2p5_flare")]
GptImage2p5Flare,
#[serde(rename = "gpt_image_2p5_sunburst")]
GptImage2p5Sunburst,
#[serde(rename = "seedream_4")]
Seedream4,
#[serde(rename = "seedream_4p5")]
@@ -149,6 +153,8 @@ impl TaskModelType {
Self::GptImage1 => "gpt_image_1",
Self::GptImage1p5 => "gpt_image_1p5",
Self::GptImage2 => "gpt_image_2",
Self::GptImage2p5Flare => "gpt_image_2p5_flare",
Self::GptImage2p5Sunburst => "gpt_image_2p5_sunburst",
Self::Seedream4 => "seedream_4",
Self::Seedream4p5 => "seedream_4p5",
Self::Seedream5Lite => "seedream_5_lite",
@@ -209,6 +215,8 @@ impl TaskModelType {
"gpt_image_1" => Ok(Self::GptImage1),
"gpt_image_1p5" => Ok(Self::GptImage1p5),
"gpt_image_2" => Ok(Self::GptImage2),
"gpt_image_2p5_flare" => Ok(Self::GptImage2p5Flare),
"gpt_image_2p5_sunburst" => Ok(Self::GptImage2p5Sunburst),
"seedream_4" => Ok(Self::Seedream4),
"seedream_4p5" => Ok(Self::Seedream4p5),
"seedream_5_lite" => Ok(Self::Seedream5Lite),
@@ -272,6 +280,8 @@ impl TaskModelType {
Self::GptImage1,
Self::GptImage1p5,
Self::GptImage2,
Self::GptImage2p5Flare,
Self::GptImage2p5Sunburst,
Self::Seedream4,
Self::Seedream4p5,
Self::Seedream5Lite,
@@ -342,6 +352,8 @@ mod tests {
assert_serialization(TaskModelType::GptImage1, "gpt_image_1");
assert_serialization(TaskModelType::GptImage1p5, "gpt_image_1p5");
assert_serialization(TaskModelType::GptImage2, "gpt_image_2");
assert_serialization(TaskModelType::GptImage2p5Flare, "gpt_image_2p5_flare");
assert_serialization(TaskModelType::GptImage2p5Sunburst, "gpt_image_2p5_sunburst");
assert_serialization(TaskModelType::Seedream4, "seedream_4");
assert_serialization(TaskModelType::Seedream4p5, "seedream_4p5");
assert_serialization(TaskModelType::Seedream5Lite, "seedream_5_lite");
@@ -401,6 +413,8 @@ mod tests {
assert_eq!(TaskModelType::GptImage1.to_str(), "gpt_image_1");
assert_eq!(TaskModelType::GptImage1p5.to_str(), "gpt_image_1p5");
assert_eq!(TaskModelType::GptImage2.to_str(), "gpt_image_2");
assert_eq!(TaskModelType::GptImage2p5Flare.to_str(), "gpt_image_2p5_flare");
assert_eq!(TaskModelType::GptImage2p5Sunburst.to_str(), "gpt_image_2p5_sunburst");
assert_eq!(TaskModelType::Seedream4.to_str(), "seedream_4");
assert_eq!(TaskModelType::Seedream4p5.to_str(), "seedream_4p5");
assert_eq!(TaskModelType::Seedream5Lite.to_str(), "seedream_5_lite");
@@ -457,6 +471,8 @@ mod tests {
assert_eq!(TaskModelType::from_str("gpt_image_1").unwrap(), TaskModelType::GptImage1);
assert_eq!(TaskModelType::from_str("gpt_image_1p5").unwrap(), TaskModelType::GptImage1p5);
assert_eq!(TaskModelType::from_str("gpt_image_2").unwrap(), TaskModelType::GptImage2);
assert_eq!(TaskModelType::from_str("gpt_image_2p5_flare").unwrap(), TaskModelType::GptImage2p5Flare);
assert_eq!(TaskModelType::from_str("gpt_image_2p5_sunburst").unwrap(), TaskModelType::GptImage2p5Sunburst);
assert_eq!(TaskModelType::from_str("seedream_4").unwrap(), TaskModelType::Seedream4);
assert_eq!(TaskModelType::from_str("seedream_4p5").unwrap(), TaskModelType::Seedream4p5);
assert_eq!(TaskModelType::from_str("seedream_5_lite").unwrap(), TaskModelType::Seedream5Lite);
@@ -510,7 +526,7 @@ mod tests {
#[test]
fn all_variants() {
let mut variants = TaskModelType::all_variants();
assert_eq!(variants.len(), 52);
assert_eq!(variants.len(), 54);
// Image models
assert_eq!(variants.pop_first(), Some(TaskModelType::Flux1Dev));
assert_eq!(variants.pop_first(), Some(TaskModelType::Flux1Schnell));
@@ -526,6 +542,8 @@ mod tests {
assert_eq!(variants.pop_first(), Some(TaskModelType::GptImage1));
assert_eq!(variants.pop_first(), Some(TaskModelType::GptImage1p5));
assert_eq!(variants.pop_first(), Some(TaskModelType::GptImage2));
assert_eq!(variants.pop_first(), Some(TaskModelType::GptImage2p5Flare));
assert_eq!(variants.pop_first(), Some(TaskModelType::GptImage2p5Sunburst));
assert_eq!(variants.pop_first(), Some(TaskModelType::Seedream4));
assert_eq!(variants.pop_first(), Some(TaskModelType::Seedream4p5));
assert_eq!(variants.pop_first(), Some(TaskModelType::Seedream5Lite));
@@ -366,6 +366,82 @@ fn build_omni_gen_image_models() -> Vec<OmniGenImageModelDetails> {
..Default::default()
});
models.push(OmniGenImageModelDetails {
model: CommonImageModel::GptImage2p5Flare,
model_creator: Some(ModelCreator::OpenAi),
full_name: Some("GPT Image 2.5 Flare".to_string()),
text_prompt_supported: Some(true),
image_refs_supported: Some(true),
aspect_ratio_options: Some(vec![
CommonAspectRatio::Auto,
CommonAspectRatio::Square,
CommonAspectRatio::SquareHd,
CommonAspectRatio::TallThreeByFour,
CommonAspectRatio::TallNineBySixteen,
CommonAspectRatio::WideFourByThree,
CommonAspectRatio::WideSixteenByNine,
]),
aspect_ratio_default: Some(CommonAspectRatio::Square),
aspect_ratio_default_when_editing: Some(CommonAspectRatio::Auto),
quality_options: Some(vec![
CommonQuality::High,
CommonQuality::Medium,
CommonQuality::Low,
]),
default_quality: Some(CommonQuality::High),
// NOTE: GPT Image 2.5 does not have "resolution" natively. We're emulating this.
resolution_options: Some(vec![
CommonResolution::OneK,
CommonResolution::TwoK,
CommonResolution::ThreeK,
CommonResolution::FourK,
]),
// NOTE: GPT Image 2.5 does not have "resolution" natively. We're emulating this.
resolution_default: Some(CommonResolution::OneK),
batch_size_min: Some(1),
batch_size_max: Some(4),
batch_size_default: Some(1),
..Default::default()
});
models.push(OmniGenImageModelDetails {
model: CommonImageModel::GptImage2p5Sunburst,
model_creator: Some(ModelCreator::OpenAi),
full_name: Some("GPT Image 2.5 Sunburst".to_string()),
text_prompt_supported: Some(true),
image_refs_supported: Some(true),
aspect_ratio_options: Some(vec![
CommonAspectRatio::Auto,
CommonAspectRatio::Square,
CommonAspectRatio::SquareHd,
CommonAspectRatio::TallThreeByFour,
CommonAspectRatio::TallNineBySixteen,
CommonAspectRatio::WideFourByThree,
CommonAspectRatio::WideSixteenByNine,
]),
aspect_ratio_default: Some(CommonAspectRatio::Square),
aspect_ratio_default_when_editing: Some(CommonAspectRatio::Auto),
quality_options: Some(vec![
CommonQuality::High,
CommonQuality::Medium,
CommonQuality::Low,
]),
default_quality: Some(CommonQuality::High),
// NOTE: GPT Image 2.5 does not have "resolution" natively. We're emulating this.
resolution_options: Some(vec![
CommonResolution::OneK,
CommonResolution::TwoK,
CommonResolution::ThreeK,
CommonResolution::FourK,
]),
// NOTE: GPT Image 2.5 does not have "resolution" natively. We're emulating this.
resolution_default: Some(CommonResolution::OneK),
batch_size_min: Some(1),
batch_size_max: Some(4),
batch_size_default: Some(1),
..Default::default()
});
models.push(OmniGenImageModelDetails {
model: CommonImageModel::Seedream4,
model_creator: Some(ModelCreator::Bytedance),
@@ -534,6 +610,14 @@ fn build_omni_gen_image_model_providers() -> Vec<OmniGenImageModelProviderDetail
model: CommonImageModel::GptImage2,
overrides: None,
},
OmniGenImageProviderModelDetails {
model: CommonImageModel::GptImage2p5Flare,
overrides: None,
},
OmniGenImageProviderModelDetails {
model: CommonImageModel::GptImage2p5Sunburst,
overrides: None,
},
OmniGenImageProviderModelDetails {
model: CommonImageModel::Seedream4,
overrides: None,
@@ -571,3 +655,40 @@ fn build_omni_gen_image_model_providers() -> Vec<OmniGenImageModelProviderDetail
providers
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn gpt_image_2p5_models_are_listed_with_quality_and_resolution_options() {
let models = build_omni_gen_image_models();
for (model, full_name) in [
(CommonImageModel::GptImage2p5Flare, "GPT Image 2.5 Flare"),
(CommonImageModel::GptImage2p5Sunburst, "GPT Image 2.5 Sunburst"),
] {
let details = models.iter().find(|m| m.model == model).unwrap_or_else(|| panic!("{model:?} missing"));
assert_eq!(details.full_name.as_deref(), Some(full_name));
assert_eq!(details.model_creator, Some(ModelCreator::OpenAi));
assert_eq!(details.text_prompt_supported, Some(true));
assert_eq!(details.image_refs_supported, Some(true));
assert_eq!(details.default_quality, Some(CommonQuality::High));
assert_eq!(details.quality_options.as_ref().map(|q| q.len()), Some(3));
assert_eq!(details.resolution_default, Some(CommonResolution::OneK));
assert_eq!(details.resolution_options.as_ref().map(|r| r.len()), Some(4));
assert_eq!(details.aspect_ratio_default_when_editing, Some(CommonAspectRatio::Auto));
assert_eq!(details.batch_size_max, Some(4));
}
}
#[test]
fn gpt_image_2p5_models_are_served_by_the_artcraft_provider() {
let providers = build_omni_gen_image_model_providers();
let artcraft = providers.iter()
.find(|p| p.provider == GenerationProvider::Artcraft)
.expect("artcraft provider");
for model in [CommonImageModel::GptImage2p5Flare, CommonImageModel::GptImage2p5Sunburst] {
assert!(artcraft.models.iter().any(|m| m.model == model), "{model:?} missing from artcraft provider");
}
}
}
@@ -98,3 +98,42 @@ fn convert_quality(
)
})
}
#[cfg(test)]
mod tests {
use super::*;
use artcraft_router::api::router_image_model::RouterImageModel;
use enums::common::generation::common_image_model::CommonImageModel as CommonImageModelEnum;
#[test]
fn gpt_image_2p5_models_convert_to_router_models() {
assert_eq!(convert_model(&CommonImageModelEnum::GptImage2p5Flare).unwrap(), RouterImageModel::GptImage2p5Flare);
assert_eq!(convert_model(&CommonImageModelEnum::GptImage2p5Sunburst).unwrap(), RouterImageModel::GptImage2p5Sunburst);
}
#[test]
fn gpt_image_2p5_request_hydrates_to_artcraft_router_builder() {
let request = OmniApiImageGenerateRequest {
image_urls: None,
idempotency_token: Some("idem".to_string()),
model: Some(CommonImageModelEnum::GptImage2p5Sunburst),
prompt: Some("a cat in space".to_string()),
image_media_tokens: None,
resolution: Some(CommonResolutionEnum::TwoK),
aspect_ratio: Some(CommonAspectRatioEnum::WideSixteenByNine),
quality: Some(CommonQualityEnum::Medium),
image_batch_count: Some(2),
adjust_horizontal_angle: None,
adjust_vertical_angle: None,
adjust_zoom: None,
};
let builder = hydrate_to_router_request(&request).unwrap();
assert_eq!(builder.model, RouterImageModel::GptImage2p5Sunburst);
assert!(matches!(builder.provider, RouterProvider::Artcraft));
assert_eq!(builder.prompt.as_deref(), Some("a cat in space"));
assert_eq!(format!("{:?}", builder.resolution), "Some(TwoK)");
assert_eq!(format!("{:?}", builder.aspect_ratio), "Some(WideSixteenByNine)");
assert_eq!(format!("{:?}", builder.quality), "Some(Medium)");
assert_eq!(builder.image_batch_count, Some(2));
}
}
@@ -98,3 +98,41 @@ fn convert_quality(
)
})
}
#[cfg(test)]
mod tests {
use super::*;
use artcraft_router::api::router_image_model::RouterImageModel;
use enums::common::generation::common_image_model::CommonImageModel as CommonImageModelEnum;
#[test]
fn gpt_image_2p5_models_convert_to_router_models() {
assert_eq!(convert_model(&CommonImageModelEnum::GptImage2p5Flare).unwrap(), RouterImageModel::GptImage2p5Flare);
assert_eq!(convert_model(&CommonImageModelEnum::GptImage2p5Sunburst).unwrap(), RouterImageModel::GptImage2p5Sunburst);
}
#[test]
fn gpt_image_2p5_request_hydrates_to_artcraft_router_builder() {
let request = OmniGenImageCostAndGenerateRequest {
idempotency_token: Some("idem".to_string()),
model: Some(CommonImageModelEnum::GptImage2p5Sunburst),
prompt: Some("a cat in space".to_string()),
image_media_tokens: None,
resolution: Some(CommonResolutionEnum::TwoK),
aspect_ratio: Some(CommonAspectRatioEnum::WideSixteenByNine),
quality: Some(CommonQualityEnum::Medium),
image_batch_count: Some(2),
adjust_horizontal_angle: None,
adjust_vertical_angle: None,
adjust_zoom: None,
};
let builder = hydrate_to_router_request(&request).unwrap();
assert_eq!(builder.model, RouterImageModel::GptImage2p5Sunburst);
assert!(matches!(builder.provider, RouterProvider::Artcraft));
assert_eq!(builder.prompt.as_deref(), Some("a cat in space"));
assert_eq!(format!("{:?}", builder.resolution), "Some(TwoK)");
assert_eq!(format!("{:?}", builder.aspect_ratio), "Some(WideSixteenByNine)");
assert_eq!(format!("{:?}", builder.quality), "Some(Medium)");
assert_eq!(builder.image_batch_count, Some(2));
}
}
@@ -10,6 +10,8 @@ export enum CommonImageModel {
GptImage1 = "gpt_image_1",
GptImage1p5 = "gpt_image_1p5",
GptImage2 = "gpt_image_2",
GptImage2p5Flare = "gpt_image_2p5_flare",
GptImage2p5Sunburst = "gpt_image_2p5_sunburst",
NanaBanana = "nano_banana",
NanaBanana2 = "nano_banana_2",
NanaBananaPro = "nano_banana_pro",
@@ -284,6 +284,8 @@ export function CostBreakdownModal({ activeTabId }: CostBreakdownModalProps) {
"flux_pro_1p1_ultra",
"gpt_image_1p5",
"gpt_image_2",
"gpt_image_2p5_flare",
"gpt_image_2p5_sunburst",
"nano_banana",
"nano_banana_2",
"nano_banana_pro",
@@ -9,6 +9,8 @@ const MODEL_DESCRIPTIONS: Record<string, string> = {
gpt_image_1: "OpenAI's original image model",
gpt_image_1p5: "OpenAI imagery with reliable text",
gpt_image_2: "4K images with crisp text rendering",
gpt_image_2p5_flare: "Fast, precise edits that keep subjects intact",
gpt_image_2p5_sunburst: "Premium detail and fidelity, slower",
midjourney_7: "Signature artistic, stylized imagery",
midjourney_7_niji: "Anime and illustration styles",
midjourney_8: "Midjourney's latest flagship model",
@@ -64,6 +64,8 @@ export const getModelDisplayName = (
gpt_image_1: "GPT Image 1",
gpt_image_1p5: "GPT Image 1.5",
gpt_image_2: "GPT Image 2",
gpt_image_2p5_flare: "GPT Image 2.5 Flare",
gpt_image_2p5_sunburst: "GPT Image 2.5 Sunburst",
sora_2: "Sora 2",
sora_2_pro: "Sora 2 Pro",
@@ -385,6 +385,76 @@ export const IMAGE_MODELS: ImageModel[] = [
CommonQuality.Low,
],
}),
new ImageModel({
id: "gpt_image_2p5_flare",
tauriId: "gpt_image_2p5_flare",
fullName: "GPT Image 2.5 Flare",
category: "image",
creator: ModelCreator.OpenAi,
selectorName: "GPT Image 2.5 Flare",
selectorDescription: "Precise edits, keeps subjects intact",
selectorBadges: ["60 sec."],
maxGenerationCount: 4,
defaultGenerationCount: 1,
tags: [ModelTag.InstructiveEdit],
canEditImages: true,
canUseImagePrompt: true,
maxImagePromptCount: 6,
progressBarTime: 60000,
maxPromptLength: Infinity,
canChangeAspectRatio: true,
defaultAspectRatio: CommonAspectRatio.WideSixteenByNine,
aspectRatios: [
CommonAspectRatio.Auto,
CommonAspectRatio.Square,
CommonAspectRatio.SquareHd,
CommonAspectRatio.TallThreeByFour,
CommonAspectRatio.TallNineBySixteen,
CommonAspectRatio.WideFourByThree,
CommonAspectRatio.WideSixteenByNine,
],
defaultQuality: CommonQuality.High,
qualityOptions: [
CommonQuality.High,
CommonQuality.Medium,
CommonQuality.Low,
],
}),
new ImageModel({
id: "gpt_image_2p5_sunburst",
tauriId: "gpt_image_2p5_sunburst",
fullName: "GPT Image 2.5 Sunburst",
category: "image",
creator: ModelCreator.OpenAi,
selectorName: "GPT Image 2.5 Sunburst",
selectorDescription: "Premium detail, slower",
selectorBadges: ["2 min."],
maxGenerationCount: 4,
defaultGenerationCount: 1,
tags: [ModelTag.InstructiveEdit],
canEditImages: true,
canUseImagePrompt: true,
maxImagePromptCount: 6,
progressBarTime: 120000,
maxPromptLength: Infinity,
canChangeAspectRatio: true,
defaultAspectRatio: CommonAspectRatio.WideSixteenByNine,
aspectRatios: [
CommonAspectRatio.Auto,
CommonAspectRatio.Square,
CommonAspectRatio.SquareHd,
CommonAspectRatio.TallThreeByFour,
CommonAspectRatio.TallNineBySixteen,
CommonAspectRatio.WideFourByThree,
CommonAspectRatio.WideSixteenByNine,
],
defaultQuality: CommonQuality.High,
qualityOptions: [
CommonQuality.High,
CommonQuality.Medium,
CommonQuality.Low,
],
}),
new ImageModel({
id: "seedream_4",
tauriId: "seedream_4",