diff --git a/_tools/postman/collections/ArtCraft API/generate/cost/Estimate Image Cost.request.yaml b/_tools/postman/collections/ArtCraft API/generate/cost/Estimate Image Cost.request.yaml index 4720fc5613..7574a75ba0 100644 --- a/_tools/postman/collections/ArtCraft API/generate/cost/Estimate Image Cost.request.yaml +++ b/_tools/postman/collections/ArtCraft API/generate/cost/Estimate Image Cost.request.yaml @@ -15,11 +15,12 @@ body: type: json content: | { - "model": "seedream_5_lite", + "model": "gpt_image_2", "provider": "artcraft", "generation_mode": {"type": "text_to_image" }, "aspect_ratio": "wide_sixteen_by_nine", "resolution": "two_k", + "quality": "low", "image_batch_count": 1 } order: 2000 diff --git a/crates/api_clients/artcraft/artcraft_api_defs/src/generate/cost_estimate/estimate_image_cost.rs b/crates/api_clients/artcraft/artcraft_api_defs/src/generate/cost_estimate/estimate_image_cost.rs index 68bece3cfb..0cf0b57423 100644 --- a/crates/api_clients/artcraft/artcraft_api_defs/src/generate/cost_estimate/estimate_image_cost.rs +++ b/crates/api_clients/artcraft/artcraft_api_defs/src/generate/cost_estimate/estimate_image_cost.rs @@ -3,13 +3,14 @@ use utoipa::ToSchema; use enums::common::generation::common_aspect_ratio::CommonAspectRatio; use enums::common::generation::common_image_model::CommonImageModel; +use enums::common::generation::common_quality::CommonQuality; use enums::common::generation::common_resolution::CommonResolution; use enums::common::generation_provider::GenerationProvider; pub const ESTIMATE_IMAGE_COST_PATH: &str = "/v1/generate/cost_estimate/image"; /// Request body for the image cost estimate endpoint. -#[derive(Serialize, Deserialize, ToSchema)] +#[derive(Serialize, Deserialize, ToSchema, Debug)] pub struct EstimateImageCostRequest { /// The image model to estimate costs for. pub model: CommonImageModel, @@ -29,12 +30,15 @@ pub struct EstimateImageCostRequest { /// Optional resolution. pub resolution: Option, + // Optional quality. + pub quality: Option, + /// Number of images to generate in parallel. pub image_batch_count: Option, } /// Describes the type of image generation being requested. -#[derive(Serialize, Deserialize, ToSchema)] +#[derive(Serialize, Deserialize, ToSchema, Debug)] #[serde(tag = "type", rename_all = "snake_case")] pub enum GenerationMode { /// No input images — generate from prompt only. @@ -94,6 +98,7 @@ mod tests { generation_mode: GenerationMode::TextToImage, aspect_ratio: None, resolution: None, + quality: None, image_batch_count: None, }; let serialized = serde_json::to_string(&request).unwrap(); @@ -108,6 +113,7 @@ mod tests { generation_mode: GenerationMode::ImageEdit { count: 2 }, aspect_ratio: None, resolution: None, + quality: None, image_batch_count: None, }; let serialized = serde_json::to_string(&request).unwrap(); diff --git a/crates/desktop/artcraft/src/core/commands/cost_estimate/estimate_image_cost_command.rs b/crates/desktop/artcraft/src/core/commands/cost_estimate/estimate_image_cost_command.rs index 086e8acf8b..f81de997c0 100644 --- a/crates/desktop/artcraft/src/core/commands/cost_estimate/estimate_image_cost_command.rs +++ b/crates/desktop/artcraft/src/core/commands/cost_estimate/estimate_image_cost_command.rs @@ -17,7 +17,7 @@ pub async fn estimate_image_cost_command( request: EstimateImageCostRequest, app_env_configs: State<'_, AppEnvConfigs>, ) -> ResponseOrError { - debug!("estimate_image_cost_command called"); + debug!("estimate_image_cost_command called: {:?}", request); let result = estimate_image_cost( &app_env_configs.storyteller_host, diff --git a/crates/service/web/storyteller_web/src/configs/omni_gen/image_models.rs b/crates/service/web/storyteller_web/src/configs/omni_gen/image_models.rs index 7b2c46383a..35ae468938 100644 --- a/crates/service/web/storyteller_web/src/configs/omni_gen/image_models.rs +++ b/crates/service/web/storyteller_web/src/configs/omni_gen/image_models.rs @@ -378,6 +378,10 @@ fn build_omni_gen_image_model_providers() -> Vec Result RouterImageModel::FluxPro11Ultra, CommonImageModel::GptImage1 => RouterImageModel::GptImage1, CommonImageModel::GptImage1p5 => RouterImageModel::GptImage1p5, + CommonImageModel::GptImage2 => RouterImageModel::GptImage2, CommonImageModel::NanoBanana => RouterImageModel::NanoBanana, CommonImageModel::NanoBanana2 => RouterImageModel::NanoBanana2, CommonImageModel::NanoBananaPro => RouterImageModel::NanoBananaPro, CommonImageModel::Seedream4 => RouterImageModel::Seedream4, CommonImageModel::Seedream4p5 => RouterImageModel::Seedream4p5, CommonImageModel::Seedream5Lite => RouterImageModel::Seedream5Lite, - CommonImageModel::GptImage2 => RouterImageModel::GptImage2, }; Ok(router_model) } @@ -178,3 +181,11 @@ fn map_resolution(res: CommonResolution) -> RouterResolution { CommonResolution::TenEightyP => RouterResolution::TenEightyP, } } + +fn map_quality(res: CommonQuality) -> RouterQuality { + match res { + CommonQuality::Low => RouterQuality::Low, + CommonQuality::Medium => RouterQuality::Medium, + CommonQuality::High => RouterQuality::High, + } +} diff --git a/frontend/libs/api-enums/src/lib/common/generation/CommonImageModel.ts b/frontend/libs/api-enums/src/lib/common/generation/CommonImageModel.ts index 425a515cba..4a92dda42d 100644 --- a/frontend/libs/api-enums/src/lib/common/generation/CommonImageModel.ts +++ b/frontend/libs/api-enums/src/lib/common/generation/CommonImageModel.ts @@ -9,6 +9,7 @@ export enum CommonImageModel { FluxPro11Ultra = "flux_pro_1p1_ultra", GptImage1 = "gpt_image_1", GptImage1p5 = "gpt_image_1p5", + GptImage2 = "gpt_image_2", NanaBanana = "nano_banana", NanaBanana2 = "nano_banana_2", NanaBananaPro = "nano_banana_pro", diff --git a/frontend/libs/api-enums/src/lib/common/generation/CommonQuality.ts b/frontend/libs/api-enums/src/lib/common/generation/CommonQuality.ts new file mode 100644 index 0000000000..5e35935d44 --- /dev/null +++ b/frontend/libs/api-enums/src/lib/common/generation/CommonQuality.ts @@ -0,0 +1,9 @@ + +// NOTE: These are defined in Rust (as the source of truth) and duplicated in the frontend. +// In the future, we should use code gen (protobufs or similar) to keep the two sides in sync. + +export enum CommonQuality { + High = "high", + Medium = "medium", + Low = "low", +} diff --git a/frontend/libs/api-enums/src/lib/common/generation/index.ts b/frontend/libs/api-enums/src/lib/common/generation/index.ts index 4a3922ff64..199355876a 100644 --- a/frontend/libs/api-enums/src/lib/common/generation/index.ts +++ b/frontend/libs/api-enums/src/lib/common/generation/index.ts @@ -1,6 +1,7 @@ export * from "./CommonImageModel.js"; export * from "./CommonVideoModel.js"; export * from "./CommonAspectRatio.js"; +export * from "./CommonQuality.js"; export * from "./CommonVideoResolution.js"; export * from "./GenerationMode.js"; export * from "./ImageGenerationMode.js"; diff --git a/frontend/libs/components/pricing-modal/src/lib/convert/imageModelToCommonImageModel.ts b/frontend/libs/components/pricing-modal/src/lib/convert/imageModelToCommonImageModel.ts index 9db84918ba..f77934a630 100644 --- a/frontend/libs/components/pricing-modal/src/lib/convert/imageModelToCommonImageModel.ts +++ b/frontend/libs/components/pricing-modal/src/lib/convert/imageModelToCommonImageModel.ts @@ -14,8 +14,12 @@ export function imageModelToCommonImageModel( return CommonImageModel.FluxPro11; case "flux_pro_11_ultra": return CommonImageModel.FluxPro11Ultra; + case "gpt_image_1": + return CommonImageModel.GptImage1; case "gpt_image_1p5": return CommonImageModel.GptImage1p5; + case "gpt_image_2": + return CommonImageModel.GptImage2; case "nano_banana": return CommonImageModel.NanaBanana; case "nano_banana_2": diff --git a/frontend/libs/components/pricing-modal/src/lib/convert/imageQualityToCommonQuality.ts b/frontend/libs/components/pricing-modal/src/lib/convert/imageQualityToCommonQuality.ts new file mode 100644 index 0000000000..5ba9c2304f --- /dev/null +++ b/frontend/libs/components/pricing-modal/src/lib/convert/imageQualityToCommonQuality.ts @@ -0,0 +1,21 @@ +import { CommonQuality } from "@storyteller/api-enums"; + +/** + * Convert a quality value to CommonQuality for the image cost estimate API. + * + * Accepts a CommonQuality string value from @storyteller/model-list ("high", "medium", "low"). + */ +export function imageQualityToCommonQuality( + quality: string | undefined, +): CommonQuality | null { + switch (quality) { + case "high": + return CommonQuality.High; + case "medium": + return CommonQuality.Medium; + case "low": + return CommonQuality.Low; + default: + return null; + } +} diff --git a/frontend/libs/components/pricing-modal/src/lib/convert/index.ts b/frontend/libs/components/pricing-modal/src/lib/convert/index.ts index ee778ad453..d631aaf06c 100644 --- a/frontend/libs/components/pricing-modal/src/lib/convert/index.ts +++ b/frontend/libs/components/pricing-modal/src/lib/convert/index.ts @@ -3,5 +3,6 @@ export * from "./videoAspectRatioToCommonAspectRatio.js"; export * from "./videoStoreToGenerationMode.js"; export * from "./imageModelToCommonImageModel.js"; export * from "./imageAspectRatioToCommonAspectRatio.js"; +export * from "./imageQualityToCommonQuality.js"; export * from "./imageResolutionToCommonVideoResolution.js"; export * from "./imageStoreToGenerationMode.js"; diff --git a/frontend/libs/components/pricing-modal/src/lib/cost-breakdown-modal.tsx b/frontend/libs/components/pricing-modal/src/lib/cost-breakdown-modal.tsx index b11990deea..0ed6eb4058 100644 --- a/frontend/libs/components/pricing-modal/src/lib/cost-breakdown-modal.tsx +++ b/frontend/libs/components/pricing-modal/src/lib/cost-breakdown-modal.tsx @@ -277,6 +277,7 @@ export function CostBreakdownModal({ activeTabId }: CostBreakdownModalProps) { "flux_pro_11", "flux_pro_11_ultra", "gpt_image_1p5", + "gpt_image_2", "nano_banana", "nano_banana_2", "nano_banana_pro", diff --git a/frontend/libs/components/pricing-modal/src/lib/useImageCostEstimate.ts b/frontend/libs/components/pricing-modal/src/lib/useImageCostEstimate.ts index ffbca6fbe9..b8d3cbb477 100644 --- a/frontend/libs/components/pricing-modal/src/lib/useImageCostEstimate.ts +++ b/frontend/libs/components/pricing-modal/src/lib/useImageCostEstimate.ts @@ -16,6 +16,7 @@ import { useCostBreakdownModalStore } from "./cost-breakdown-modal-store"; import { imageModelToCommonImageModel, imageAspectRatioToCommonAspectRatio, + imageQualityToCommonQuality, imageResolutionToCommonVideoResolution, } from "./convert/index.js"; @@ -44,6 +45,7 @@ export function useImageCostEstimate( const imageLegacyResolution = usePromptImageStore((s) => s.resolution); const imageReferenceImages = usePromptImageStore((s) => s.referenceImages); const imageGenerationCount = usePromptImageStore((s) => s.generationCount); + const imageQuality = usePromptImageStore((s) => s.commonQuality); // Canvas2D store const prompt2DAspectRatio = usePrompt2DStore((s) => s.aspectRatio); @@ -74,6 +76,7 @@ export function useImageCostEstimate( let aspectRatioStr: string | undefined; let legacyAspectRatioStr: string | undefined; let resolutionStr: string | undefined; + let qualityStr: string | undefined; let referenceImageCount = 0; let generationCount = 1; @@ -82,6 +85,7 @@ export function useImageCostEstimate( aspectRatioStr = imageAspectRatio; legacyAspectRatioStr = imageLegacyAspectRatio; resolutionStr = imageResolution ?? imageLegacyResolution; + qualityStr = imageQuality; referenceImageCount = imageReferenceImages.length; generationCount = imageGenerationCount; break; @@ -114,6 +118,7 @@ export function useImageCostEstimate( ); const commonResolution = imageResolutionToCommonVideoResolution(resolutionStr); + const commonQuality = imageQualityToCommonQuality(qualityStr); const generationMode = referenceImageCount > 0 ? { type: "image_edit" as const, count: referenceImageCount } @@ -131,6 +136,7 @@ export function useImageCostEstimate( generation_mode: generationMode, aspect_ratio: commonAspectRatio ?? undefined, resolution: commonResolution ?? undefined, + quality: commonQuality ?? undefined, }) .then((result) => { if (isEstimateImageCostSuccess(result)) { @@ -160,6 +166,7 @@ export function useImageCostEstimate( imageLegacyResolution, imageReferenceImages.length, imageGenerationCount, + imageQuality, prompt2DAspectRatio, prompt2DResolution, prompt2DReferenceImages.length, diff --git a/frontend/libs/model-list/src/lib/lists/ImageModels.ts b/frontend/libs/model-list/src/lib/lists/ImageModels.ts index deb519a0d7..e63fae9826 100644 --- a/frontend/libs/model-list/src/lib/lists/ImageModels.ts +++ b/frontend/libs/model-list/src/lib/lists/ImageModels.ts @@ -296,12 +296,12 @@ export const IMAGE_MODELS: ImageModel[] = [ progressBarTime: 60000, maxPromptLength: 1500, canChangeAspectRatio: true, - // qualityOptions: [ - // CommonQuality.High, - // CommonQuality.Medium, - // CommonQuality.Low, - // ], - // defaultQuality: CommonQuality.High, + defaultQuality: CommonQuality.High, + qualityOptions: [ + CommonQuality.High, + CommonQuality.Medium, + CommonQuality.Low, + ], }), new ImageModel({ id: "gpt_image_1p5", @@ -327,12 +327,12 @@ export const IMAGE_MODELS: ImageModel[] = [ CommonAspectRatio.Wide, CommonAspectRatio.Tall, ], - // qualityOptions: [ - // CommonQuality.High, - // CommonQuality.Medium, - // CommonQuality.Low, - // ], - // defaultQuality: CommonQuality.High, + defaultQuality: CommonQuality.High, + qualityOptions: [ + CommonQuality.High, + CommonQuality.Medium, + CommonQuality.Low, + ], }), new ImageModel({ id: "gpt_image_2", diff --git a/frontend/libs/tauri-api/src/lib/cost_estimate/EstimateImageCost.ts b/frontend/libs/tauri-api/src/lib/cost_estimate/EstimateImageCost.ts index 97da3219f6..d3c897694e 100644 --- a/frontend/libs/tauri-api/src/lib/cost_estimate/EstimateImageCost.ts +++ b/frontend/libs/tauri-api/src/lib/cost_estimate/EstimateImageCost.ts @@ -3,6 +3,7 @@ import { CommandResult, CommandSuccessStatus } from "../common/CommandStatus"; import { CommonImageModel, CommonAspectRatio, + CommonQuality, CommonVideoResolution, ImageGenerationMode, GenerationProvider, @@ -14,6 +15,7 @@ export interface EstimateImageCostRequest { generation_mode: ImageGenerationMode; aspect_ratio?: CommonAspectRatio; resolution?: CommonVideoResolution; + quality?: CommonQuality; image_batch_count?: number; }