重构:统一编辑器工作流中的 "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:
@@ -9,9 +9,11 @@ import {
|
||||
deleteEditorAssetFolder,
|
||||
deleteEditorProject,
|
||||
editEditorImage,
|
||||
EDITOR_ICON_SPEC_PROMPT_MAX_LENGTH,
|
||||
extractEditorUiDesignAssets,
|
||||
generateEditorBackgroundMusic,
|
||||
generateEditorCharacterAnimation,
|
||||
generateEditorIconSpec,
|
||||
generateEditorIconSpritesheet,
|
||||
generateEditorImage,
|
||||
generateEditorSoundEffect,
|
||||
@@ -23,6 +25,8 @@ import {
|
||||
loadEditorProject,
|
||||
loadOrCreateRecentEditorProject,
|
||||
removeEditorImageBackground,
|
||||
refineEditorIconSpecArtStyle,
|
||||
refineEditorIconSpecPlaySetting,
|
||||
renameEditorProject,
|
||||
saveEditorProjectLayout,
|
||||
splitEditorIconSpritesheet,
|
||||
@@ -282,7 +286,9 @@ describe('editorProjectClient', () => {
|
||||
nextCursor: null,
|
||||
});
|
||||
|
||||
const page = await listPublicEditorProjectResources({ cursor: ' cursor-1 ' });
|
||||
const page = await listPublicEditorProjectResources({
|
||||
cursor: ' cursor-1 ',
|
||||
});
|
||||
|
||||
expect(page.resources).toEqual([]);
|
||||
expect(page.nextCursor).toBeNull();
|
||||
@@ -749,8 +755,7 @@ describe('editorProjectClient', () => {
|
||||
taskId: 'vector-task-1',
|
||||
warning: {
|
||||
code: 'postprocess-failed-source-preserved',
|
||||
reason:
|
||||
'生成任务成功,后处理失败。',
|
||||
reason: '生成任务成功,后处理失败。',
|
||||
},
|
||||
});
|
||||
|
||||
@@ -761,8 +766,7 @@ describe('editorProjectClient', () => {
|
||||
expect(result.taskId).toBe('vector-task-1');
|
||||
expect(result.warning).toEqual({
|
||||
code: 'postprocess-failed-source-preserved',
|
||||
reason:
|
||||
'生成任务成功,后处理失败。',
|
||||
reason: '生成任务成功,后处理失败。',
|
||||
});
|
||||
expect(requestJsonMock).toHaveBeenCalledWith(
|
||||
'/api/editor/images/generations',
|
||||
@@ -812,7 +816,9 @@ describe('editorProjectClient', () => {
|
||||
resolution: '720p',
|
||||
mode: 'std',
|
||||
sound: 'off',
|
||||
referenceVideoSrcs: [`https://assets.example.test/${'a'.repeat(256 * 1024)}`],
|
||||
referenceVideoSrcs: [
|
||||
`https://assets.example.test/${'a'.repeat(256 * 1024)}`,
|
||||
],
|
||||
}),
|
||||
).rejects.toThrow('稳定引用字段总长度不能超过 256KB');
|
||||
|
||||
@@ -1242,6 +1248,116 @@ describe('editorProjectClient', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('uses dedicated icon spec refine endpoints', async () => {
|
||||
requestJsonMock
|
||||
.mockResolvedValueOnce({ playSetting: '优化玩法' })
|
||||
.mockResolvedValueOnce({ artStyle: '优化画风' });
|
||||
|
||||
await expect(refineEditorIconSpecPlaySetting('原玩法')).resolves.toBe(
|
||||
'优化玩法',
|
||||
);
|
||||
await expect(refineEditorIconSpecArtStyle('原画风')).resolves.toBe(
|
||||
'优化画风',
|
||||
);
|
||||
expect(requestJsonMock.mock.calls[0]).toEqual([
|
||||
'/api/editor/llm/icon-specs/refine-game-play',
|
||||
expect.objectContaining({
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ playSetting: '原玩法' }),
|
||||
}),
|
||||
'优化玩法设定失败',
|
||||
]);
|
||||
expect(requestJsonMock.mock.calls[1]).toEqual([
|
||||
'/api/editor/llm/icon-specs/refine-art-style',
|
||||
expect.objectContaining({
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ artStyle: '原画风' }),
|
||||
}),
|
||||
'优化美术风格失败',
|
||||
]);
|
||||
});
|
||||
|
||||
it('enforces icon spec prompt length before and after refine requests', async () => {
|
||||
const overLimit = '玩'.repeat(EDITOR_ICON_SPEC_PROMPT_MAX_LENGTH + 1);
|
||||
|
||||
await expect(refineEditorIconSpecPlaySetting(overLimit)).rejects.toThrow(
|
||||
'玩法设定不能超过 200 个字符',
|
||||
);
|
||||
await expect(refineEditorIconSpecArtStyle(overLimit)).rejects.toThrow(
|
||||
'美术风格不能超过 200 个字符',
|
||||
);
|
||||
expect(requestJsonMock).not.toHaveBeenCalled();
|
||||
|
||||
requestJsonMock.mockResolvedValueOnce({ playSetting: overLimit });
|
||||
await expect(refineEditorIconSpecPlaySetting('原玩法')).rejects.toThrow(
|
||||
'玩法设定不能超过 200 个字符',
|
||||
);
|
||||
});
|
||||
|
||||
it('rejects overlong icon spec generation fields before dispatch', async () => {
|
||||
await expect(
|
||||
generateEditorIconSpec({
|
||||
playSetting: '玩'.repeat(EDITOR_ICON_SPEC_PROMPT_MAX_LENGTH + 1),
|
||||
artStyle: '低多边形',
|
||||
}),
|
||||
).rejects.toThrow('玩法设定不能超过 200 个字符');
|
||||
expect(requestJsonMock).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('submits icon spec business fields without a client prompt or image kind', async () => {
|
||||
requestJsonMock.mockResolvedValueOnce({
|
||||
imageSrc: 'data:image/png;base64,spec',
|
||||
width: 2048,
|
||||
height: 1152,
|
||||
});
|
||||
|
||||
await generateEditorIconSpec({
|
||||
playSetting: '回合制占点',
|
||||
artStyle: '低多边形',
|
||||
referenceImageSrcs: ['resource-reference-1'],
|
||||
projectId: 'editor-project-1',
|
||||
generationInputs: {
|
||||
fields: [],
|
||||
references: [
|
||||
{
|
||||
title: '参考图',
|
||||
label: '参考图',
|
||||
refType: 'project-resource',
|
||||
refId: 'resource-reference-1',
|
||||
},
|
||||
],
|
||||
},
|
||||
assetFolderId: 'project',
|
||||
assetLabel: '图标规范 1',
|
||||
sourceResourceId: 'resource-source-1',
|
||||
});
|
||||
|
||||
const body = JSON.parse(requestJsonMock.mock.calls[0]?.[1]?.body as string);
|
||||
expect(body).toEqual({
|
||||
playSetting: '回合制占点',
|
||||
artStyle: '低多边形',
|
||||
referenceImageSrcs: ['resource-reference-1'],
|
||||
projectId: 'editor-project-1',
|
||||
generationInputs: {
|
||||
fields: [],
|
||||
references: [
|
||||
{
|
||||
title: '参考图',
|
||||
label: '参考图',
|
||||
refType: 'project-resource',
|
||||
refId: 'resource-reference-1',
|
||||
},
|
||||
],
|
||||
},
|
||||
assetFolderId: 'project',
|
||||
assetLabel: '图标规范 1',
|
||||
sourceResourceId: 'resource-source-1',
|
||||
});
|
||||
expect(body).not.toHaveProperty('prompt');
|
||||
expect(body).not.toHaveProperty('kind');
|
||||
expect(body).not.toHaveProperty('assetKind');
|
||||
});
|
||||
|
||||
it('passes publication material generation kind and size to the backend BFF', async () => {
|
||||
requestJsonMock.mockResolvedValueOnce({
|
||||
imageSrc: 'data:image/png;base64,publication',
|
||||
|
||||
Reference in New Issue
Block a user