T2I: GPT-Image-2 support

This commit is contained in:
Enrico Ros
2026-04-21 14:14:35 -07:00
parent ec76e1c5cf
commit 0efd87b522
4 changed files with 25 additions and 19 deletions
@@ -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
+1
View File
@@ -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' },
+16 -12
View File
@@ -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';
+4 -3
View File
@@ -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';