更新主站 GPT Image 2.5 生成边界

将 GPT 专用新任务与锁定模型切换为 gpt-image-2.5

保留 nanobanana 普通生成默认行为

将历史 gpt-image-2 与 gpt-image-2-c 归一并触发参数回退告警
This commit is contained in:
2026-09-18 23:30:48 +08:00
parent beb3c99240
commit f4315ffd23
7 changed files with 43 additions and 21 deletions
@@ -3,7 +3,10 @@ import { type Dispatch, type SetStateAction } from 'react';
import { UnifiedModal } from '../common/UnifiedModal';
import { ImageCanvasBasicGenerationComposerView } from './ImageCanvasBasicGenerationComposerView';
import type { GenerateDialogState } from './ImageCanvasEditorTypes';
import { calculateEditorImageGenerationPrice } from './ImageCanvasGenerationModel';
import {
calculateEditorImageGenerationPrice,
IMAGE_MODEL_GPT_IMAGE_2_5,
} from './ImageCanvasGenerationModel';
type ImageCanvasEditGenerationModalViewProps = {
dialog: GenerateDialogState | null;
@@ -17,7 +20,7 @@ export function ImageCanvasEditGenerationModalView({
onSubmit,
}: ImageCanvasEditGenerationModalViewProps) {
const isOpen = dialog?.mode === 'edit' && dialog.status !== 'generating';
const dialogImageModel = dialog?.imageModel ?? 'gpt-image-2';
const dialogImageModel = dialog?.imageModel ?? IMAGE_MODEL_GPT_IMAGE_2_5;
const dialogImageSize = dialog?.imageSize ?? '1K';
const editPrice = calculateEditorImageGenerationPrice({
kind: 'quick-edit',
@@ -43,7 +43,7 @@ import {
EDITOR_IMAGE_DIMENSION_OPTIONS,
EDITOR_IMAGE_MODEL_OPTIONS,
formatGenerationInputValue,
IMAGE_MODEL_GPT_IMAGE_2,
IMAGE_MODEL_GPT_IMAGE_2_5,
inferEditorImageAspectRatio,
inferEditorImageSizeLabel,
isNormalizedCanvasGenerationInputs,
@@ -426,7 +426,7 @@ export function createPublicationGenerationDialogDraft({
publicationWorkflowId: workflowId,
publicationGameInfo: { ...DEFAULT_PUBLICATION_GAME_INFO },
publicationReferences: [],
imageModel: IMAGE_MODEL_GPT_IMAGE_2,
imageModel: IMAGE_MODEL_GPT_IMAGE_2_5,
aspectRatio: workflow.aspectRatio,
imageSize: workflow.imageSize,
placeholder: {
@@ -1149,7 +1149,7 @@ function createNormalizedGenerationDialogDraft({
imageModel: getNormalizedString(
sourceLayer,
'model',
IMAGE_MODEL_GPT_IMAGE_2,
IMAGE_MODEL_GPT_IMAGE_2_5,
),
}),
prompt,
@@ -1757,7 +1757,7 @@ export function createSameSourceGenerationDialogDraft({
...createUiDesignGenerationDialogDraft({
canvasSize,
viewport,
imageModel: sourceDialog?.imageModel ?? IMAGE_MODEL_GPT_IMAGE_2,
imageModel: sourceDialog?.imageModel ?? IMAGE_MODEL_GPT_IMAGE_2_5,
}),
prompt,
sourceLayerId: sourceLayer.id,
@@ -2000,7 +2000,7 @@ export function createQuickEditGenerationDialogDraft({
assetLabel,
status = 'idle',
references = [],
model = IMAGE_MODEL_GPT_IMAGE_2,
model = IMAGE_MODEL_GPT_IMAGE_2_5,
aspectRatio,
imageSize,
frame,
@@ -44,6 +44,7 @@ import {
// 中文注释:与 api-server/src/editor_generation_config.rs 保持同名模型定价语义,前端仅作为展示兜底。
export const IMAGE_MODEL_GPT_IMAGE_2 = 'gpt-image-2';
export const IMAGE_MODEL_GPT_IMAGE_2_C = 'gpt-image-2-c';
export const IMAGE_MODEL_GPT_IMAGE_2_5 = 'gpt-image-2.5';
export const IMAGE_MODEL_NANOBANANA2 = 'gemini-3.1-flash-image-preview';
export const DEFAULT_IMAGE_MODEL = IMAGE_MODEL_NANOBANANA2;
@@ -638,9 +639,23 @@ export function normalizeEditorImageModel(model: string | null | undefined) {
if (IMAGE_MODEL_NANOBANANA_ALIASES.has(normalizedModel)) {
return IMAGE_MODEL_NANOBANANA2;
}
if (
normalizedModel === IMAGE_MODEL_GPT_IMAGE_2 ||
normalizedModel === IMAGE_MODEL_GPT_IMAGE_2_C
) {
return IMAGE_MODEL_GPT_IMAGE_2_5;
}
return normalizedModel;
}
export function isLegacyEditorImageModel(model: string | null | undefined) {
const normalizedModel = model?.trim();
return (
normalizedModel === IMAGE_MODEL_GPT_IMAGE_2 ||
normalizedModel === IMAGE_MODEL_GPT_IMAGE_2_C
);
}
export function getEditorImageModelDisplayName(
model: string | null | undefined,
) {
@@ -1398,6 +1413,7 @@ export function decodeCanvasGenerationInputs(
defaultAspectRatio: string;
fixedAspectRatios?: readonly string[];
}) => {
const rawModel = fields.find((field) => field.id === 'model')?.value;
const imageModels = EDITOR_IMAGE_MODEL_OPTIONS.map(
(option) => option.value,
);
@@ -1408,6 +1424,9 @@ export function decodeCanvasGenerationInputs(
defaultModel,
normalizeEditorImageModel,
);
if (typeof rawModel === 'string' && isLegacyEditorImageModel(rawModel)) {
addFallbackWarning(['model']);
}
const dimensions =
EDITOR_IMAGE_DIMENSION_OPTIONS[
model as keyof typeof EDITOR_IMAGE_DIMENSION_OPTIONS
@@ -51,7 +51,7 @@ import {
DEFAULT_VIDEO_WEB_SEARCH_ENABLED,
EDITOR_IMAGE_DIMENSION_OPTIONS,
EDITOR_IMAGE_MODEL_OPTIONS,
IMAGE_MODEL_GPT_IMAGE_2,
IMAGE_MODEL_GPT_IMAGE_2_5,
inferEditorImageAspectRatio,
inferEditorImageSizeLabel,
isQuickEditSupportedLayer,
@@ -584,17 +584,17 @@ export function buildImageGenerationSubmissionPlan({
const basePrompt = dialog.prompt.trim() || '快速编辑图片';
const normalizedQuickEditPrompt = basePrompt;
const requestedImageModel = normalizeEditorImageModel(
dialog.imageModel ?? IMAGE_MODEL_GPT_IMAGE_2,
dialog.imageModel ?? IMAGE_MODEL_GPT_IMAGE_2_5,
);
const imageModel = EDITOR_IMAGE_MODEL_OPTIONS.some(
(option) => option.value === requestedImageModel,
)
? requestedImageModel
: IMAGE_MODEL_GPT_IMAGE_2;
: IMAGE_MODEL_GPT_IMAGE_2_5;
const dimensionOptions =
EDITOR_IMAGE_DIMENSION_OPTIONS[
imageModel as keyof typeof EDITOR_IMAGE_DIMENSION_OPTIONS
] ?? EDITOR_IMAGE_DIMENSION_OPTIONS[IMAGE_MODEL_GPT_IMAGE_2];
] ?? EDITOR_IMAGE_DIMENSION_OPTIONS[IMAGE_MODEL_GPT_IMAGE_2_5];
const supportedAspectRatios =
dimensionOptions.aspectRatios as readonly string[];
const supportedImageSizes =
@@ -852,7 +852,7 @@ export function buildImageGenerationSubmissionPlan({
}
if (dialog.mode === 'ui-design') {
const imageModel = IMAGE_MODEL_GPT_IMAGE_2;
const imageModel = IMAGE_MODEL_GPT_IMAGE_2_5;
const referenceImageSrcs = [
dialog.uiDesignSpecReference
? resolveImageReferenceSubmissionSource(dialog.uiDesignSpecReference)
@@ -897,7 +897,7 @@ export function buildImageGenerationSubmissionPlan({
const workflow = getPublicationMaterialsWorkflow(
dialog.publicationWorkflowId ?? 'publication-cover-image',
);
const imageModel = IMAGE_MODEL_GPT_IMAGE_2;
const imageModel = IMAGE_MODEL_GPT_IMAGE_2_5;
const publicationPrompt =
buildPublicationMaterialsPrompt(dialog.publicationGameInfo) ||
normalizedPrompt;
@@ -26,7 +26,7 @@ import {
buildPublicationMaterialsPrompt,
calculateEditorImageModelPrice,
DEFAULT_PUBLICATION_GAME_INFO,
IMAGE_MODEL_GPT_IMAGE_2,
IMAGE_MODEL_GPT_IMAGE_2_5,
} from './ImageCanvasGenerationModel';
import type { PublicationMaterialsWorkflow } from './ImageCanvasPublicationMaterialsModel';
import { ImageCanvasReferenceSlot } from './ImageCanvasReferenceSlot';
@@ -299,10 +299,10 @@ export function ImageCanvasPublicationMaterialsDemoPanelView({
includeDimensions={false}
onRememberImageModel={onRememberImageModel}
hasPendingImageReferenceUploads={hasPendingImageReferenceUploads}
lockedModel={IMAGE_MODEL_GPT_IMAGE_2}
lockedModel={IMAGE_MODEL_GPT_IMAGE_2_5}
optionLabelPrefix="宣发素材"
cost={calculateEditorImageModelPrice(
IMAGE_MODEL_GPT_IMAGE_2,
IMAGE_MODEL_GPT_IMAGE_2_5,
workflow.imageSize,
)}
submitLabel="生成"
@@ -51,7 +51,7 @@ import {
calculateEditorUiDesignPrice,
CHARACTER_SPEC_VIEW_OPTIONS,
getEditorImageModelDisplayName,
IMAGE_MODEL_GPT_IMAGE_2,
IMAGE_MODEL_GPT_IMAGE_2_5,
resolveEditorImageSizeLabel,
SPEC_GENERATION_ASPECT_RATIO,
SPEC_GENERATION_IMAGE_SIZE,
@@ -758,9 +758,9 @@ export function ImageCanvasSpecGenerationPanelView({
includeDimensions
onRememberImageModel={onRememberImageModel}
hasPendingImageReferenceUploads={hasPendingImageReferenceUploads}
lockedModel={IMAGE_MODEL_GPT_IMAGE_2}
lockedModel={IMAGE_MODEL_GPT_IMAGE_2_5}
cost={calculateEditorUiDesignPrice(
IMAGE_MODEL_GPT_IMAGE_2,
IMAGE_MODEL_GPT_IMAGE_2_5,
dialog.imageSize,
)}
submitLabel="生成"
@@ -99,7 +99,7 @@ import {
decodeCanvasGenerationInputs,
DEFAULT_IMAGE_MODEL,
EDITOR_IMAGE_DIMENSION_OPTIONS,
IMAGE_MODEL_GPT_IMAGE_2,
IMAGE_MODEL_GPT_IMAGE_2_5,
isCanvasGenerationDialog,
isQuickEditSupportedLayer,
normalizeEditorImageModel,
@@ -1702,7 +1702,7 @@ export function useImageCanvasGenerationWorkflow({
const draft = createUiDesignGenerationDialogDraft({
canvasSize,
viewport,
imageModel: IMAGE_MODEL_GPT_IMAGE_2,
imageModel: IMAGE_MODEL_GPT_IMAGE_2_5,
});
openPlacedCanvasGenerationDialog({
...draft,