Enable Parallel image Gen

This commit is contained in:
2026-06-12 11:15:50 +08:00
parent 3f1dafeee7
commit d648a1cb02

View File

@@ -1186,24 +1186,34 @@ async fn maybe_prepare_puzzle_clear_assets_inner(
let http_client = build_openai_image_http_client(&settings).map_err(|error| { let http_client = build_openai_image_http_client(&settings).map_err(|error| {
puzzle_clear_error_response(request_context, PUZZLE_CLEAR_CREATION_PROVIDER, error) puzzle_clear_error_response(request_context, PUZZLE_CLEAR_CREATION_PROVIDER, error)
})?; })?;
// 中文注释5 张 sheet 并行生成,每张内部最多重试 4 次上游错误
let theme_prompt = payload.theme_prompt.as_deref().unwrap_or_default();
let mut generated_sheets = Vec::with_capacity(sheet_specs.len()); let mut generated_sheets = Vec::with_capacity(sheet_specs.len());
for sheet_spec in sheet_specs { {
let sheet_prompt = build_puzzle_clear_atlas_prompt( let futures: Vec<_> = sheet_specs
payload.theme_prompt.as_deref().unwrap_or_default(), .iter()
&sheet_spec, .map(|sheet_spec| {
); let sheet_prompt = build_puzzle_clear_atlas_prompt(theme_prompt, sheet_spec);
let mut accepted_sheet = None; let client = http_client.clone();
let settings = settings.clone();
let debug_run = image_debug_run.clone();
async move {
for attempt_index in 0..PUZZLE_CLEAR_SHEET_GENERATION_MAX_ATTEMPTS { for attempt_index in 0..PUZZLE_CLEAR_SHEET_GENERATION_MAX_ATTEMPTS {
let failure_context = format!( let failure_context = format!(
"拼消消素材 {} 生成失败,第 {}", "拼消消素材 {} 生成失败,第 {}",
sheet_spec.sheet_id, sheet_spec.sheet_id,
attempt_index + 1 attempt_index + 1
); );
if let Some(debug_run) = image_debug_run.as_ref() { if let Some(ref debug_run) = debug_run {
debug_run.record_sheet_request(&sheet_spec, attempt_index, sheet_prompt.as_str()); debug_run.record_sheet_request(
sheet_spec,
attempt_index,
sheet_prompt.as_str(),
);
} }
let generated = match create_openai_image_generation( let generated = match create_openai_image_generation(
&http_client, &client,
&settings, &settings,
sheet_prompt.as_str(), sheet_prompt.as_str(),
Some(PUZZLE_CLEAR_ATLAS_NEGATIVE_PROMPT), Some(PUZZLE_CLEAR_ATLAS_NEGATIVE_PROMPT),
@@ -1216,11 +1226,16 @@ async fn maybe_prepare_puzzle_clear_assets_inner(
{ {
Ok(generated) => generated, Ok(generated) => generated,
Err(error) Err(error)
if attempt_index + 1 < PUZZLE_CLEAR_SHEET_GENERATION_MAX_ATTEMPTS if attempt_index + 1
< PUZZLE_CLEAR_SHEET_GENERATION_MAX_ATTEMPTS
&& is_retryable_puzzle_clear_sheet_generation_error(&error) => && is_retryable_puzzle_clear_sheet_generation_error(&error) =>
{ {
if let Some(debug_run) = image_debug_run.as_ref() { if let Some(ref debug_run) = debug_run {
debug_run.record_sheet_generation_error(&sheet_spec, attempt_index, &error); debug_run.record_sheet_generation_error(
sheet_spec,
attempt_index,
&error,
);
} }
tracing::warn!( tracing::warn!(
provider = PUZZLE_CLEAR_CREATION_PROVIDER, provider = PUZZLE_CLEAR_CREATION_PROVIDER,
@@ -1232,57 +1247,59 @@ async fn maybe_prepare_puzzle_clear_assets_inner(
continue; continue;
} }
Err(error) => { Err(error) => {
if let Some(debug_run) = image_debug_run.as_ref() { return Err(error);
debug_run.record_sheet_generation_error(&sheet_spec, attempt_index, &error);
} }
};
let task_id = generated.task_id;
let actual_prompt = generated.actual_prompt;
let image = generated.images.into_iter().next().ok_or_else(|| {
AppError::from_status(StatusCode::BAD_GATEWAY).with_details(json!({
"provider": "vector-engine",
"message": format!("拼消消素材 {} 生成成功但未返回图片。", sheet_spec.sheet_id),
}))
})?;
if let Some(ref debug_run) = debug_run {
debug_run.record_sheet_attempt_image(
sheet_spec,
attempt_index,
task_id.as_str(),
actual_prompt.as_deref(),
&image,
);
debug_run.record_sheet_accepted(
sheet_spec,
task_id.as_str(),
&image,
);
}
return Ok(PuzzleClearGeneratedSheet {
spec: *sheet_spec,
prompt: sheet_prompt.clone(),
task_id,
image,
});
}
Err(AppError::from_status(StatusCode::BAD_GATEWAY).with_details(json!({
"provider": PUZZLE_CLEAR_CREATION_PROVIDER,
"message": format!("拼消消素材 {} 多次生成后仍未得到可切图集。", sheet_spec.sheet_id),
})))
}
})
.collect();
let results = futures_util::future::join_all(futures).await;
for result in results {
match result {
Ok(sheet) => generated_sheets.push(sheet),
Err(error) => {
return Err(puzzle_clear_error_response( return Err(puzzle_clear_error_response(
request_context, request_context,
PUZZLE_CLEAR_CREATION_PROVIDER, PUZZLE_CLEAR_CREATION_PROVIDER,
error, error,
)); ));
} }
};
let task_id = generated.task_id;
let actual_prompt = generated.actual_prompt;
let image = generated.images.into_iter().next().ok_or_else(|| {
puzzle_clear_error_response(
request_context,
PUZZLE_CLEAR_CREATION_PROVIDER,
AppError::from_status(StatusCode::BAD_GATEWAY).with_details(json!({
"provider": "vector-engine",
"message": format!("拼消消素材 {} 生成成功但未返回图片。", sheet_spec.sheet_id),
})),
)
})?;
if let Some(debug_run) = image_debug_run.as_ref() {
debug_run.record_sheet_attempt_image(
&sheet_spec,
attempt_index,
task_id.as_str(),
actual_prompt.as_deref(),
&image,
);
debug_run.record_sheet_accepted(&sheet_spec, task_id.as_str(), &image);
} }
accepted_sheet = Some(PuzzleClearGeneratedSheet {
spec: sheet_spec,
prompt: sheet_prompt.clone(),
task_id,
image,
});
break;
} }
let Some(accepted_sheet) = accepted_sheet else {
return Err(puzzle_clear_error_response(
request_context,
PUZZLE_CLEAR_CREATION_PROVIDER,
AppError::from_status(StatusCode::BAD_GATEWAY).with_details(json!({
"provider": PUZZLE_CLEAR_CREATION_PROVIDER,
"message": format!("拼消消素材 {} 多次生成后仍未得到可切图集。", sheet_spec.sheet_id),
})),
));
};
generated_sheets.push(accepted_sheet);
} }
let mut slices = Vec::new(); let mut slices = Vec::new();