diff --git a/src/modules/llms/server/openai/openai.router.ts b/src/modules/llms/server/openai/openai.router.ts index dc5b22bc8..15ab36221 100644 --- a/src/modules/llms/server/openai/openai.router.ts +++ b/src/modules/llms/server/openai/openai.router.ts @@ -30,9 +30,9 @@ const _createImageConfigBase = z.object({ user: z.string().optional(), }); -// GPT Image family (gpt-image-1.5, gpt-image-1, gpt-image-1-mini share all parameters) +// GPT Image family (all members share the same parameter surface) const createImageConfigGI = _createImageConfigBase.extend({ - model: z.enum(['gpt-image-1.5', 'gpt-image-1', 'gpt-image-1-mini']), + model: OpenAIWire_API_Images_Generations.GptImageModels_schema, prompt: z.string().max(32000), size: z.enum([/*'auto',*/ '1024x1024', '1536x1024', '1024x1536']), quality: z.enum(['high', 'medium', 'low']).optional(), @@ -153,8 +153,8 @@ export const llmOpenAIRouter = createTRPCRouter({ const { access, generationConfig: config, editConfig } = input; - // Determine if this is an edit request - const isGptImageFamily = config.model === 'gpt-image-1' || config.model === 'gpt-image-1-mini'; + // Determine if this is an edit request (any member of the GPT Image family supports edits) + const isGptImageFamily = config.model === 'gpt-image-2' || config.model === 'gpt-image-1.5' || config.model === 'gpt-image-1' || config.model === 'gpt-image-1-mini'; const isEdit = !!editConfig?.inputImages?.length && isGptImageFamily; // validate input diff --git a/src/modules/t2i/dalle/DallESettings.tsx b/src/modules/t2i/dalle/DallESettings.tsx index db444417e..0408b3f6e 100644 --- a/src/modules/t2i/dalle/DallESettings.tsx +++ b/src/modules/t2i/dalle/DallESettings.tsx @@ -17,6 +17,7 @@ import { openAIImageModelsPricing } from './openaiGenerateImages'; const CONF = { MODEL_OPTS: [ + { value: 'gpt-image-2', label: 'GPT Image 2' }, { value: 'gpt-image-1.5', label: 'GPT Image 1.5' }, { value: 'gpt-image-1', label: 'GPT Image 1' }, { value: 'gpt-image-1-mini', label: 'GPT Image Mini' }, diff --git a/src/modules/t2i/dalle/openaiGenerateImages.ts b/src/modules/t2i/dalle/openaiGenerateImages.ts index 0a8565e7a..55aad446c 100644 --- a/src/modules/t2i/dalle/openaiGenerateImages.ts +++ b/src/modules/t2i/dalle/openaiGenerateImages.ts @@ -85,11 +85,12 @@ export async function openAIGenerateImagesOrThrow( access: findServiceAccessOrThrow<{}, OpenAIAccessSchema>(modelServiceIdForAccess).transportAccess, // [LocalAI, 2025-11-18] LocalAI uses the default model 'stablediffusion' and we don't have any dynamic model selection yet generationConfig: modelVendor === 'localai' ? { - model: dalleModelId === 'gpt-image-1' ? 'stablediffusion' - : dalleModelId === 'gpt-image-1-mini' ? 'dreamshaper' - : dalleModelId === 'dall-e-3' ? 'sd-3.5-large-ggml' - : dalleModelId === 'dall-e-2' ? 'sd-3.5-medium-ggml' - : 'dreamshaper', + model: dalleModelId === 'gpt-image-2' ? 'stablediffusion' + : dalleModelId === 'gpt-image-1' ? 'stablediffusion' + : dalleModelId === 'gpt-image-1-mini' ? 'dreamshaper' + : dalleModelId === 'dall-e-3' ? 'sd-3.5-large-ggml' + : dalleModelId === 'dall-e-2' ? 'sd-3.5-medium-ggml' + : 'dreamshaper', prompt, count: imageCount, // [LocalAI] size mapping - FIXME! - TEMP CODE @@ -98,7 +99,7 @@ export async function openAIGenerateImagesOrThrow( : '1024x1024', response_format: 'b64_json', } : getImageModelFamily(dalleModelId) === 'gpt-image' ? { - model: dalleModelId as 'gpt-image-1.5' | 'gpt-image-1' | 'gpt-image-1-mini', + model: dalleModelId as 'gpt-image-2' | 'gpt-image-1.5' | 'gpt-image-1' | 'gpt-image-1-mini', prompt: prompt.slice(0, 32000 - 1), // GPT Image family accepts much longer prompts count: imageCount, size: dalleSizeGI, @@ -199,6 +200,7 @@ export async function openAIGenerateImagesOrThrow( export function openAIImageModelsCurrentGeneratorName() { const dalleModelSelection = useDalleStore.getState().dalleModelId; const dalleModelId = resolveDalleModelId(dalleModelSelection); + if (dalleModelId === 'gpt-image-2') return 'GPT Image 2'; if (dalleModelId === 'gpt-image-1.5') return 'GPT Image 1.5'; if (dalleModelId === 'gpt-image-1') return 'GPT Image 1'; if (dalleModelId === 'gpt-image-1-mini') return 'GPT Image Mini'; @@ -217,10 +219,13 @@ export function openAIImageModelsCurrentGeneratorName() { * - Deduct credits after successful generation */ const IMAGE_MODEL_PRICING = { - // Token-based pricing (GPT Image family) - Note: chatgpt-image-latest has same pricing as gpt-image-1.5 - 'gpt-image-1.5': { inputText: 5.00, inputImage: 8.0, outputImage: 32.0 }, - 'gpt-image-1': { inputText: 5.00, inputImage: 10.0, outputImage: 40.0 }, - 'gpt-image-1-mini': { inputText: 2.00, inputImage: 2.50, outputImage: 8.00 }, + // Token-based pricing (GPT Image family). Per $1M tokens. Note: chatgpt-image-latest mirrors gpt-image-1.5. + // Cached-input discounts exist (gpt-image-2/1.5: $2/img $1.25/txt, gpt-image-1: $2.50/img $1.25/txt, + // gpt-image-1-mini: $0.25/img $0.20/txt) but are not tracked here yet - add when the usage field is wired up. + 'gpt-image-2': { inputText: 5.00, inputImage: 8.00, outputImage: 30.00 }, + 'gpt-image-1.5': { inputText: 5.00, inputImage: 8.00, outputImage: 32.00 }, + 'gpt-image-1': { inputText: 5.00, inputImage: 10.00, outputImage: 40.00 }, + 'gpt-image-1-mini': { inputText: 2.00, inputImage: 2.50, outputImage: 8.00 }, // Fixed pricing models handled separately in openAIImageModelsPricing() 'dall-e-3': null, 'dall-e-2': null, @@ -235,8 +240,7 @@ function openAIImageModelsPrice(modelId: DalleModelId): undefined | { inputText: * TODO: update this when the OpenAI pricing changes. */ export function openAIImageModelsPricing(modelId: DalleModelId, quality: DalleImageQuality, size: DalleSize): string { - // GPT Image family (gpt-image-1.5, gpt-image-1, gpt-image-1-mini) - if (modelId === 'gpt-image-1.5' || modelId === 'gpt-image-1' || modelId === 'gpt-image-1-mini') { + if (getImageModelFamily(modelId) === 'gpt-image') { // gpt-image-1-mini does not support high quality if (modelId === 'gpt-image-1-mini' && quality === 'high') quality = 'medium'; diff --git a/src/modules/t2i/dalle/store-module-dalle.ts b/src/modules/t2i/dalle/store-module-dalle.ts index 1dd770513..8a3ef1a28 100644 --- a/src/modules/t2i/dalle/store-module-dalle.ts +++ b/src/modules/t2i/dalle/store-module-dalle.ts @@ -11,7 +11,8 @@ export const DALLE_DEFAULT_IMAGE_SIZE: DalleImageSize = '1024x1024'; // this wor export type DalleImageSize = DalleSizeGI | DalleSizeD3 | DalleSizeD2; // Note: 'chatgpt-image-latest' also exists with same pricing as gpt-image-1.5 -export type DalleModelId = 'gpt-image-1.5' | 'gpt-image-1' | 'gpt-image-1-mini' | 'dall-e-3' | 'dall-e-2'; +type GPTImageMoldelId = 'gpt-image-2' | 'gpt-image-1.5' | 'gpt-image-1' | 'gpt-image-1-mini'; +export type DalleModelId = GPTImageMoldelId | 'dall-e-3' | 'dall-e-2'; export type DalleModelSelection = DalleModelId | null; // null = auto-select latest /** @@ -22,7 +23,7 @@ export type DalleModelSelection = DalleModelId | null; // null = auto-select lat export function resolveDalleModelId(selection: DalleModelSelection): DalleModelId { // Auto-select latest model when null if (selection === null) { - return 'gpt-image-1.5'; // Current latest model + return 'gpt-image-2'; // Current latest image drawing model } return selection; } @@ -40,7 +41,7 @@ export function resolveDalleModelId(selection: DalleModelSelection): DalleModelI * - Each family can have its own settings/pricing structure */ export function getImageModelFamily(modelId: DalleModelId): 'gpt-image' | 'dall-e-3' | 'dall-e-2' { - if (modelId === 'gpt-image-1.5' || modelId === 'gpt-image-1' || modelId === 'gpt-image-1-mini') + if (modelId === 'gpt-image-2' || modelId === 'gpt-image-1.5' || modelId === 'gpt-image-1' || modelId === 'gpt-image-1-mini') return 'gpt-image'; if (modelId === 'dall-e-3') return 'dall-e-3';