重构:统一编辑器工作流中的 "icon-spec" 处理

- 将所有相关工作流从 "ui-spec" 迁移至 "icon-spec"。
- 优化 `playSetting` 和 `artStyle` 的输入处理逻辑。
- 更新编辑器提示词、提交模型及测试用例以适配 "icon-spec"。
- 在 UI 及后端调用中集成 `EDITOR_ICON_SPEC_PROMPT_MAX_LENGTH` 及相关校验。
- 新增 "icon-spec" 生成与微调的专用 API 路径。
- 移除旧版 "ui-spec" 相关定义及提示词构建器。
This commit is contained in:
2026-08-04 20:04:07 +08:00
parent cc86d29b83
commit 3485bad2d3
19 changed files with 980 additions and 187 deletions
@@ -8,6 +8,12 @@ const EDITOR_SHOWCASE_RESOURCE_API = '/api/editor/showcase/resources';
const EDITOR_SHOWCASE_ASSET_API_BASE = '/api/editor/showcase/assets';
const EDITOR_PROJECT_RESOURCE_API_BASE = '/api/editor/project-resources';
const EDITOR_IMAGE_GENERATION_API = '/api/editor/images/generations';
const EDITOR_ICON_SPEC_GENERATION_API = '/api/editor/icon-specs/generations';
const EDITOR_ICON_SPEC_REFINE_GAME_PLAY_API =
'/api/editor/llm/icon-specs/refine-game-play';
const EDITOR_ICON_SPEC_REFINE_ART_STYLE_API =
'/api/editor/llm/icon-specs/refine-art-style';
export const EDITOR_ICON_SPEC_PROMPT_MAX_LENGTH = 200;
const EDITOR_IMAGE_EDIT_API = '/api/editor/images/edits';
const EDITOR_BACKGROUND_REMOVAL_API = '/api/editor/images/background-removals';
const EDITOR_ICON_SPRITESHEET_GENERATION_API =
@@ -41,7 +47,18 @@ function assertStableEditorMediaReferences(
values: readonly string[] | undefined,
fieldLabel: string,
) {
values?.forEach((value) => assertStableEditorMediaReference(value, fieldLabel));
values?.forEach((value) =>
assertStableEditorMediaReference(value, fieldLabel),
);
}
function requireEditorIconSpecPromptLength(value: string, fieldLabel: string) {
if (Array.from(value).length > EDITOR_ICON_SPEC_PROMPT_MAX_LENGTH) {
throw new Error(
`${fieldLabel}不能超过 ${EDITOR_ICON_SPEC_PROMPT_MAX_LENGTH} 个字符`,
);
}
return value;
}
function assertStableEditorVideoReferences(input: EditorVideoGenerationInput) {
@@ -86,9 +103,7 @@ export type EditorAssetGenerationInputs = {
};
export type EditorProjectResourceSourceType =
| 'uploaded'
| 'generated'
| 'mock_generated';
'uploaded' | 'generated' | 'mock_generated';
export type EditorProjectResourceSnapshot = {
resourceId: string;
@@ -190,11 +205,7 @@ export type EditorImageGenerationInput = {
prompt: string;
size?: string;
kind?:
| 'spec'
| 'character'
| 'quick-edit'
| 'ui-design'
| 'publication-material';
'spec' | 'character' | 'quick-edit' | 'ui-design' | 'publication-material';
model?: string;
screenColor?: string;
segModel?: string;
@@ -211,6 +222,18 @@ export type EditorImageGenerationInput = {
canvasCompletion?: EditorCanvasGenerationCompletionInput | null;
};
export type EditorIconSpecGenerationInput = {
playSetting: string;
artStyle: string;
referenceImageSrcs?: string[];
projectId?: string | null;
generationInputs?: EditorAssetGenerationInputs | null;
assetFolderId?: string | null;
assetLabel?: string | null;
sourceResourceId?: string | null;
canvasCompletion?: EditorCanvasGenerationCompletionInput | null;
};
export type EditorCanvasGenerationCompletionInput = {
dialogId?: string;
title: string;
@@ -358,12 +381,7 @@ export type EditorIconSpritesheetSliceResult = {
export type EditorCharacterAnimationResolution = '480p' | '720p';
export type EditorCharacterAnimationRatio =
| 'same'
| '1:1'
| '4:3'
| '16:9'
| '9:16'
| '3:4';
'same' | '1:1' | '4:3' | '16:9' | '9:16' | '3:4';
export type EditorCharacterAnimationFrameCount = 32 | 40 | 48;
@@ -424,12 +442,7 @@ export type EditorVideoModel =
export type EditorVideoResolution = '480p' | '720p' | '1080p';
export type EditorVideoSoundMode = 'on' | 'off';
export type EditorVideoAspectRatio =
| '16:9'
| '9:16'
| '1:1'
| '4:3'
| '3:4'
| '21:9';
'16:9' | '9:16' | '1:1' | '4:3' | '3:4' | '21:9';
export type EditorVideoGenerationInput = {
prompt: string;
@@ -948,14 +961,75 @@ export async function generateEditorImage(input: EditorImageGenerationInput) {
);
}
export async function refineEditorIconSpecPlaySetting(playSetting: string) {
const validPlaySetting = requireEditorIconSpecPromptLength(
playSetting,
'玩法设定',
);
const response = await requestJson<{ playSetting: string }>(
EDITOR_ICON_SPEC_REFINE_GAME_PLAY_API,
jsonRequest('POST', { playSetting: validPlaySetting }),
'优化玩法设定失败',
);
return requireEditorIconSpecPromptLength(response.playSetting, '玩法设定');
}
export async function refineEditorIconSpecArtStyle(artStyle: string) {
const validArtStyle = requireEditorIconSpecPromptLength(artStyle, '美术风格');
const response = await requestJson<{ artStyle: string }>(
EDITOR_ICON_SPEC_REFINE_ART_STYLE_API,
jsonRequest('POST', { artStyle: validArtStyle }),
'优化美术风格失败',
);
return requireEditorIconSpecPromptLength(response.artStyle, '美术风格');
}
export async function generateEditorIconSpec(
input: EditorIconSpecGenerationInput,
) {
const playSetting = requireEditorIconSpecPromptLength(
input.playSetting,
'玩法设定',
);
const artStyle = requireEditorIconSpecPromptLength(
input.artStyle,
'美术风格',
);
assertStableEditorMediaReferences(input.referenceImageSrcs, '图标规范参考图');
return requestJson<EditorImageGenerationResponse>(
EDITOR_ICON_SPEC_GENERATION_API,
jsonRequest('POST', {
playSetting,
artStyle,
...(input.referenceImageSrcs?.length
? { referenceImageSrcs: input.referenceImageSrcs }
: {}),
...(input.projectId ? { projectId: input.projectId } : {}),
...(input.generationInputs
? { generationInputs: input.generationInputs }
: {}),
...(input.assetFolderId ? { assetFolderId: input.assetFolderId } : {}),
...(input.assetLabel ? { assetLabel: input.assetLabel } : {}),
...(input.sourceResourceId
? { sourceResourceId: input.sourceResourceId }
: {}),
...(input.canvasCompletion
? { canvasCompletion: input.canvasCompletion }
: {}),
}),
'生成图标规范失败',
{
timeoutMs: 1_200_000,
retry: EDITOR_REQUEST_RETRY_OPTIONS,
},
);
}
export async function generateEditorIconSpritesheet(
input: EditorIconSpritesheetGenerationInput,
) {
assertStableEditorMediaReference(input.referenceImageSrc, '图标素材规范');
assertStableEditorMediaReferences(
input.referenceImageSrcs,
'图标素材参考图',
);
assertStableEditorMediaReferences(input.referenceImageSrcs, '图标素材参考图');
return requestJson<EditorIconSpritesheetGenerationResponse>(
EDITOR_ICON_SPRITESHEET_GENERATION_API,
jsonRequest('POST', {