From 3550f046e4150b10a1ec6ebf468c0e2b8bb342ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E5=BE=B7=E5=AE=87?= Date: Thu, 6 Aug 2026 11:25:48 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E5=9B=BE=E6=A0=87=E8=A7=84?= =?UTF-8?q?=E8=8C=83=E5=8F=82=E8=80=83=E5=9B=BE=E8=B6=85=E9=99=90=E9=A2=84?= =?UTF-8?q?=E6=A3=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 后端在调用 LLM 前按模型上限拒绝超限参考图。 前端在请求发送前限制图标规范参考图数量。 补充超限零请求回归测试与预检顺序断言。 --- .../crates/api-server/src/editor_project.rs | 30 ++++++++++++------- .../image-editor/editorProjectClient.test.ts | 14 +++++++++ .../image-editor/editorProjectClient.ts | 5 ++++ 3 files changed, 38 insertions(+), 11 deletions(-) diff --git a/server-rs/crates/api-server/src/editor_project.rs b/server-rs/crates/api-server/src/editor_project.rs index e576c9518..e4cf6e788 100644 --- a/server-rs/crates/api-server/src/editor_project.rs +++ b/server-rs/crates/api-server/src/editor_project.rs @@ -1808,18 +1808,19 @@ pub(crate) async fn validate_editor_image_generation_parameters_for_owner( payload.image_size.as_deref(), ); let reference_limit = if matches!(normalized_kind, Some("quick-edit")) { - 9 + EDITOR_QUICK_EDIT_REFERENCE_LIMIT + .min(editor_provider_reference_limit(generation_options.model)) } else { - 5 + EDITOR_IMAGE_GENERATION_REFERENCE_LIMIT }; - for source in payload - .reference_image_srcs - .as_deref() - .unwrap_or_default() - .iter() - .map(|source| source.trim()) - .filter(|source| !source.is_empty()) - .take(reference_limit) + ensure_editor_reference_image_source_limit( + payload.reference_image_srcs.as_deref(), + reference_limit, + "editor-image-generation", + "referenceImageSrcs", + "生成参考图", + )?; + for source in normalize_editor_reference_image_sources(payload.reference_image_srcs.as_deref()) { parse_editor_reference_image(state, caller.owner_user_id.as_str(), source).await?; } @@ -14784,18 +14785,25 @@ mod tests { fn image_generation_preflight_checks_references_provider_and_pricing() { let source = include_str!("editor_project.rs"); - assert_function_contains( + assert_function_contains_in_order( source, "pub(crate) async fn validate_editor_image_generation_parameters_for_owner", "pub(crate) async fn generate_editor_image_for_owner", &[ "ensure_editor_reference_image_sources_are_stable(", + "ensure_editor_reference_image_source_limit(", "parse_editor_reference_image(", "require_openai_image_settings(", "build_openai_image_http_client(", ".editor_generation_pricing()", ], ); + assert_function_not_contains( + source, + "pub(crate) async fn validate_editor_image_generation_parameters_for_owner", + "pub(crate) async fn generate_editor_image_for_owner", + &[".take(reference_limit)"], + ); } #[test] diff --git a/src/services/image-editor/editorProjectClient.test.ts b/src/services/image-editor/editorProjectClient.test.ts index e0cbe35c6..388646173 100644 --- a/src/services/image-editor/editorProjectClient.test.ts +++ b/src/services/image-editor/editorProjectClient.test.ts @@ -1452,6 +1452,20 @@ describe('editorProjectClient', () => { expect(requestJsonMock).not.toHaveBeenCalled(); }); + it('rejects excess icon spec references before dispatch', async () => { + await expect( + generateEditorIconSpec({ + playSetting: '回合制占点', + artStyle: '低多边形', + referenceImageSrcs: Array.from( + { length: 6 }, + (_, index) => `resource-reference-${index + 1}`, + ), + }), + ).rejects.toThrow('图标规范参考图最多允许 5 张,当前选择 6 张'); + 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', diff --git a/src/services/image-editor/editorProjectClient.ts b/src/services/image-editor/editorProjectClient.ts index cbdb2067e..e2969149a 100644 --- a/src/services/image-editor/editorProjectClient.ts +++ b/src/services/image-editor/editorProjectClient.ts @@ -1106,6 +1106,11 @@ export async function generateEditorIconSpec( '美术风格', ); assertStableEditorMediaReferences(input.referenceImageSrcs, '图标规范参考图'); + assertEditorReferenceLimit( + input.referenceImageSrcs, + EDITOR_IMAGE_REFERENCE_LIMIT, + '图标规范参考图', + ); return requestJson( EDITOR_ICON_SPEC_GENERATION_API, jsonRequest('POST', {