同步 PR 137 最新主分支合并

纳入 PR 137 最新 head bb16c41938\n保留 PR 135、PR 137 与 master 的既有冲突解法

# Conflicts:
#	server-rs/crates/api-server/src/external_generation_worker.rs
#	src/services/image-editor/editorProjectClient.test.ts
This commit is contained in:
2026-08-08 22:10:23 +08:00
6 changed files with 18 additions and 16 deletions
@@ -71,7 +71,7 @@ fn sound_effect_prompt_bad_request(message: impl Into<String>) -> AppError {
}
fn require_sound_effect_prompt_llm_client(state: &AppState) -> Result<&LlmClient, AppError> {
state.editor_agent_llm_client().ok_or_else(|| {
state.vector_engine_llm_client().ok_or_else(|| {
AppError::from_status(StatusCode::SERVICE_UNAVAILABLE)
.with_message("服务端尚未配置可用的编辑器 LLM")
.with_details(json!({
@@ -44,9 +44,8 @@ use crate::{
EditorGenerationCaller, EditorGenerationOperationContext, EditorGenerationPhaseReporter,
EditorGenerationQueueResultContext, EditorImageEditRequest, EditorImageGenerationRequest,
EditorUiDesignAssetExtractionRequest, compact_editor_generation_result,
edit_editor_image_for_owner,
extract_editor_ui_design_assets_for_owner, generate_editor_image_for_owner,
remove_editor_image_background_for_owner,
edit_editor_image_for_owner, extract_editor_ui_design_assets_for_owner,
generate_editor_image_for_owner, remove_editor_image_background_for_owner,
},
editor_project_icon::{
EditorIconSpecGenerationRequest, EditorIconSpritesheetGenerationRequest,
@@ -355,7 +355,7 @@ pub(crate) async fn generate_editor_sound_effect_for_owner(
.map_err(map_platform_audio_error)
.map_err(&error_response)?;
let llm_client = state
.editor_agent_llm_client()
.vector_engine_llm_client()
.ok_or_else(|| {
AppError::from_status(StatusCode::SERVICE_UNAVAILABLE).with_details(json!({
"provider": "editor-sound-effect-translation",
@@ -350,7 +350,7 @@ mod tests {
);
let state = editor_llm_test_state(mock.base_url.clone(), 0);
let client = state
.editor_agent_llm_client()
.vector_engine_llm_client()
.expect("editor LLM should be configured");
for (input, expected) in cases {
@@ -400,7 +400,7 @@ mod tests {
let state = editor_llm_test_state(mock.base_url.clone(), 0);
let actual = translate_sound_effect_prompt_for_worker(
state
.editor_agent_llm_client()
.vector_engine_llm_client()
.expect("client should exist"),
"\u{feff}金币拾取\u{2003}",
None,
@@ -434,7 +434,7 @@ mod tests {
let state = editor_llm_test_state(mock.base_url.clone(), 0);
let error = translate_sound_effect_prompt_for_worker(
state
.editor_agent_llm_client()
.vector_engine_llm_client()
.expect("client should exist"),
"金币拾取",
None,
@@ -458,7 +458,7 @@ mod tests {
let state = editor_llm_test_state(mock.base_url.clone(), 0);
let error = translate_sound_effect_prompt_for_worker(
state
.editor_agent_llm_client()
.vector_engine_llm_client()
.expect("client should exist"),
"金币拾取",
None,
@@ -475,7 +475,7 @@ mod tests {
let state = editor_llm_test_state(mock.base_url.clone(), 0);
let error = translate_sound_effect_prompt_for_worker(
state
.editor_agent_llm_client()
.vector_engine_llm_client()
.expect("client should exist"),
"金币拾取",
Some(Instant::now() - std::time::Duration::from_millis(1)),
@@ -500,7 +500,7 @@ mod tests {
let state = editor_llm_test_state(mock.base_url.clone(), 0);
let actual = translate_sound_effect_prompt_for_worker(
state
.editor_agent_llm_client()
.vector_engine_llm_client()
.expect("client should exist"),
"金币拾取",
Some(Instant::now() + std::time::Duration::from_secs(30)),
@@ -521,7 +521,7 @@ mod tests {
let state = editor_llm_test_state(mock.base_url.clone(), 0);
let error = translate_sound_effect_prompt_for_worker(
state
.editor_agent_llm_client()
.vector_engine_llm_client()
.expect("client should exist"),
"金币拾取",
None,
@@ -551,7 +551,7 @@ mod tests {
let state = editor_llm_test_state(mock.base_url.clone(), 1);
let actual = translate_sound_effect_prompt_for_worker(
state
.editor_agent_llm_client()
.vector_engine_llm_client()
.expect("client should exist"),
"金币拾取",
None,
@@ -2348,7 +2348,6 @@ describe('useImageCanvasGenerationSubmissionWorkflow', () => {
render(
<SubmissionWorkflowHarness
currentUserId="user-a"
projectId="editor-project-sfx"
initialDialog={{
id: 'dialog-sfx-claim',
mode: 'audio-sound-effect',
@@ -2228,7 +2228,9 @@ describe('editorProjectClient', () => {
);
expect(EDITOR_BACKGROUND_MUSIC_PROMPT_ASSIST_TIMEOUT_MS).toBe(180_000);
expect(requestJsonMock.mock.calls[0]).toHaveLength(4);
expect(requestJsonMock.mock.calls[0][3]).toEqual(promptAssistRequestOptions);
expect(requestJsonMock.mock.calls[0][3]).toEqual(
promptAssistRequestOptions,
);
});
it('simplifies a background music prompt without forwarding client-controlled limits', async () => {
@@ -2266,7 +2268,9 @@ describe('editorProjectClient', () => {
promptAssistRequestOptions,
);
expect(requestJsonMock.mock.calls[0]).toHaveLength(4);
expect(requestJsonMock.mock.calls[0][3]).toEqual(promptAssistRequestOptions);
expect(requestJsonMock.mock.calls[0][3]).toEqual(
promptAssistRequestOptions,
);
});
it('optimizes an SFX prompt through the authenticated internal BFF without retries or client-controlled limits', async () => {