Enable Parallel image Gen

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

View File

@@ -1186,103 +1186,120 @@ async fn maybe_prepare_puzzle_clear_assets_inner(
let http_client = build_openai_image_http_client(&settings).map_err(|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());
for sheet_spec in sheet_specs {
let sheet_prompt = build_puzzle_clear_atlas_prompt(
payload.theme_prompt.as_deref().unwrap_or_default(),
&sheet_spec,
);
let mut accepted_sheet = None;
for attempt_index in 0..PUZZLE_CLEAR_SHEET_GENERATION_MAX_ATTEMPTS {
let failure_context = format!(
"拼消消素材 {} 生成失败,第 {}",
sheet_spec.sheet_id,
attempt_index + 1
);
if let Some(debug_run) = image_debug_run.as_ref() {
debug_run.record_sheet_request(&sheet_spec, attempt_index, sheet_prompt.as_str());
}
let generated = match create_openai_image_generation(
&http_client,
&settings,
sheet_prompt.as_str(),
Some(PUZZLE_CLEAR_ATLAS_NEGATIVE_PROMPT),
PUZZLE_CLEAR_ATLAS_GENERATION_SIZE,
1,
&[],
failure_context.as_str(),
)
.await
{
Ok(generated) => generated,
Err(error)
if attempt_index + 1 < PUZZLE_CLEAR_SHEET_GENERATION_MAX_ATTEMPTS
&& is_retryable_puzzle_clear_sheet_generation_error(&error) =>
{
if let Some(debug_run) = image_debug_run.as_ref() {
debug_run.record_sheet_generation_error(&sheet_spec, attempt_index, &error);
{
let futures: Vec<_> = sheet_specs
.iter()
.map(|sheet_spec| {
let sheet_prompt = build_puzzle_clear_atlas_prompt(theme_prompt, sheet_spec);
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 {
let failure_context = format!(
"拼消消素材 {} 生成失败,第 {}",
sheet_spec.sheet_id,
attempt_index + 1
);
if let Some(ref debug_run) = debug_run {
debug_run.record_sheet_request(
sheet_spec,
attempt_index,
sheet_prompt.as_str(),
);
}
let generated = match create_openai_image_generation(
&client,
&settings,
sheet_prompt.as_str(),
Some(PUZZLE_CLEAR_ATLAS_NEGATIVE_PROMPT),
PUZZLE_CLEAR_ATLAS_GENERATION_SIZE,
1,
&[],
failure_context.as_str(),
)
.await
{
Ok(generated) => generated,
Err(error)
if attempt_index + 1
< PUZZLE_CLEAR_SHEET_GENERATION_MAX_ATTEMPTS
&& is_retryable_puzzle_clear_sheet_generation_error(&error) =>
{
if let Some(ref debug_run) = debug_run {
debug_run.record_sheet_generation_error(
sheet_spec,
attempt_index,
&error,
);
}
tracing::warn!(
provider = PUZZLE_CLEAR_CREATION_PROVIDER,
sheet_id = sheet_spec.sheet_id,
attempt = attempt_index + 1,
generation_error = %error.body_text(),
"拼消消素材 sheet 生成遇到可重试上游错误,准备重试"
);
continue;
}
Err(error) => {
return Err(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,
});
}
tracing::warn!(
provider = PUZZLE_CLEAR_CREATION_PROVIDER,
sheet_id = sheet_spec.sheet_id,
attempt = attempt_index + 1,
generation_error = %error.body_text(),
"拼消消素材 sheet 生成遇到可重试上游错误,准备重试"
);
continue;
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) => {
if let Some(debug_run) = image_debug_run.as_ref() {
debug_run.record_sheet_generation_error(&sheet_spec, attempt_index, &error);
}
return Err(puzzle_clear_error_response(
request_context,
PUZZLE_CLEAR_CREATION_PROVIDER,
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();