fix: stabilize puzzle vector engine asset generation
This commit is contained in:
@@ -341,6 +341,8 @@ fn record_external_api_failure_otlp(failure: &ExternalApiFailureDraft) {
|
||||
prompt_chars = failure.prompt_chars,
|
||||
reference_image_count = failure.reference_image_count,
|
||||
image_model = failure.image_model,
|
||||
request_id = %failure.request_id.as_deref().unwrap_or_default(),
|
||||
error_source = %failure.error_source.as_deref().unwrap_or_default(),
|
||||
error = %failure.error_message,
|
||||
"外部 API 调用失败"
|
||||
);
|
||||
@@ -394,6 +396,10 @@ mod tests {
|
||||
)
|
||||
.with_status_code(Some(429))
|
||||
.with_retryable(true)
|
||||
.with_error_source(Some(
|
||||
"client error (SendRequest) -> connection closed before message completed"
|
||||
.to_string(),
|
||||
))
|
||||
.with_latency_ms(Some(1234))
|
||||
.with_prompt_chars(Some(88))
|
||||
.with_reference_image_count(Some(2))
|
||||
@@ -414,6 +420,10 @@ mod tests {
|
||||
assert_eq!(metadata["promptChars"], 88);
|
||||
assert_eq!(metadata["referenceImageCount"], 2);
|
||||
assert_eq!(metadata["imageModel"], "gpt-image-2-all");
|
||||
assert_eq!(
|
||||
metadata["errorSource"],
|
||||
"client error (SendRequest) -> connection closed before message completed"
|
||||
);
|
||||
assert!(matches!(metadata["occurredAt"], Value::String(_)));
|
||||
}
|
||||
|
||||
|
||||
@@ -424,6 +424,7 @@ pub(crate) fn map_platform_image_error(error: PlatformImageError) -> AppError {
|
||||
details["referenceImageCount"] = json!(audit.reference_image_count);
|
||||
details["imageModel"] = json!(audit.image_model);
|
||||
details["rawExcerpt"] = json!(audit.raw_excerpt);
|
||||
details["errorSource"] = json!(audit.error_source);
|
||||
}
|
||||
|
||||
AppError::from_status(status).with_details(details)
|
||||
|
||||
@@ -317,7 +317,16 @@ pub(crate) async fn generate_puzzle_level_asset_bundle(
|
||||
);
|
||||
let http_client = build_puzzle_image_http_client(state, PuzzleImageModel::GptImage2)?;
|
||||
let puzzle_reference = build_puzzle_downloaded_image_reference(puzzle_image);
|
||||
let scene_generated = create_puzzle_vector_engine_image_generation(
|
||||
let bundle_started_at = Instant::now();
|
||||
tracing::info!(
|
||||
provider = VECTOR_ENGINE_PROVIDER,
|
||||
image_model = PuzzleImageModel::GptImage2.request_model_name(),
|
||||
session_id,
|
||||
level_name,
|
||||
"拼图关卡资产包生成开始"
|
||||
);
|
||||
let scene_started_at = Instant::now();
|
||||
let scene_generated = match create_puzzle_vector_engine_image_generation(
|
||||
&http_client,
|
||||
&settings,
|
||||
PuzzleImageModel::GptImage2,
|
||||
@@ -328,7 +337,34 @@ pub(crate) async fn generate_puzzle_level_asset_bundle(
|
||||
Some(&puzzle_reference),
|
||||
)
|
||||
.await
|
||||
.map_err(map_puzzle_generation_endpoint_error)?;
|
||||
.map_err(map_puzzle_generation_endpoint_error)
|
||||
{
|
||||
Ok(generated) => {
|
||||
tracing::info!(
|
||||
provider = VECTOR_ENGINE_PROVIDER,
|
||||
image_model = PuzzleImageModel::GptImage2.request_model_name(),
|
||||
session_id,
|
||||
level_name,
|
||||
slot = "level_scene",
|
||||
elapsed_ms = scene_started_at.elapsed().as_millis() as u64,
|
||||
"拼图关卡场景图生成完成"
|
||||
);
|
||||
generated
|
||||
}
|
||||
Err(error) => {
|
||||
tracing::warn!(
|
||||
provider = VECTOR_ENGINE_PROVIDER,
|
||||
image_model = PuzzleImageModel::GptImage2.request_model_name(),
|
||||
session_id,
|
||||
level_name,
|
||||
slot = "level_scene",
|
||||
elapsed_ms = scene_started_at.elapsed().as_millis() as u64,
|
||||
error = %error,
|
||||
"拼图关卡场景图生成失败"
|
||||
);
|
||||
return Err(error);
|
||||
}
|
||||
};
|
||||
let scene_image = scene_generated.images.into_iter().next().ok_or_else(|| {
|
||||
AppError::from_status(StatusCode::BAD_GATEWAY).with_details(json!({
|
||||
"provider": VECTOR_ENGINE_PROVIDER,
|
||||
@@ -336,7 +372,8 @@ pub(crate) async fn generate_puzzle_level_asset_bundle(
|
||||
}))
|
||||
})?;
|
||||
let scene_reference = build_puzzle_downloaded_image_reference(&scene_image);
|
||||
let scene_persist_future = persist_puzzle_level_asset_image(
|
||||
let scene_persist_started_at = Instant::now();
|
||||
let level_scene = persist_puzzle_level_asset_image(
|
||||
state,
|
||||
owner_user_id,
|
||||
session_id,
|
||||
@@ -347,8 +384,18 @@ pub(crate) async fn generate_puzzle_level_asset_bundle(
|
||||
"level_scene",
|
||||
"scene",
|
||||
scene_image,
|
||||
)
|
||||
.await?;
|
||||
tracing::info!(
|
||||
provider = VECTOR_ENGINE_PROVIDER,
|
||||
image_model = PuzzleImageModel::GptImage2.request_model_name(),
|
||||
session_id,
|
||||
level_name,
|
||||
slot = "level_scene",
|
||||
elapsed_ms = scene_persist_started_at.elapsed().as_millis() as u64,
|
||||
"拼图关卡场景图持久化完成"
|
||||
);
|
||||
let spritesheet_future = generate_and_persist_puzzle_level_asset(
|
||||
let ui_spritesheet = generate_and_persist_puzzle_level_asset(
|
||||
state,
|
||||
&http_client,
|
||||
&settings,
|
||||
@@ -362,8 +409,9 @@ pub(crate) async fn generate_puzzle_level_asset_bundle(
|
||||
"puzzle_ui_spritesheet_image",
|
||||
"ui_spritesheet",
|
||||
"spritesheet",
|
||||
);
|
||||
let background_future = generate_and_persist_puzzle_level_asset(
|
||||
)
|
||||
.await?;
|
||||
let level_background = generate_and_persist_puzzle_level_asset(
|
||||
state,
|
||||
&http_client,
|
||||
&settings,
|
||||
@@ -377,14 +425,21 @@ pub(crate) async fn generate_puzzle_level_asset_bundle(
|
||||
"puzzle_level_background_image",
|
||||
"level_background",
|
||||
"background",
|
||||
);
|
||||
let (level_scene, ui_spritesheet, level_background) =
|
||||
tokio::join!(scene_persist_future, spritesheet_future, background_future);
|
||||
)
|
||||
.await?;
|
||||
|
||||
tracing::info!(
|
||||
provider = VECTOR_ENGINE_PROVIDER,
|
||||
image_model = PuzzleImageModel::GptImage2.request_model_name(),
|
||||
session_id,
|
||||
level_name,
|
||||
elapsed_ms = bundle_started_at.elapsed().as_millis() as u64,
|
||||
"拼图关卡资产包生成完成"
|
||||
);
|
||||
Ok(GeneratedPuzzleLevelAssetBundle {
|
||||
level_scene: level_scene?,
|
||||
ui_spritesheet: ui_spritesheet?,
|
||||
level_background: level_background?,
|
||||
level_scene,
|
||||
ui_spritesheet,
|
||||
level_background,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -403,7 +458,20 @@ async fn generate_and_persist_puzzle_level_asset(
|
||||
slot: &str,
|
||||
file_stem: &str,
|
||||
) -> Result<GeneratedPuzzleLevelAssetResponse, AppError> {
|
||||
let generated = create_puzzle_vector_engine_image_generation(
|
||||
let started_at = Instant::now();
|
||||
tracing::info!(
|
||||
provider = VECTOR_ENGINE_PROVIDER,
|
||||
image_model = PuzzleImageModel::GptImage2.request_model_name(),
|
||||
session_id,
|
||||
level_name,
|
||||
slot,
|
||||
asset_kind,
|
||||
size,
|
||||
prompt_chars = prompt.chars().count(),
|
||||
reference_image_bytes = reference_image.bytes_len,
|
||||
"拼图关卡资产生成请求开始"
|
||||
);
|
||||
let generated = match create_puzzle_vector_engine_image_generation(
|
||||
http_client,
|
||||
settings,
|
||||
PuzzleImageModel::GptImage2,
|
||||
@@ -414,7 +482,36 @@ async fn generate_and_persist_puzzle_level_asset(
|
||||
Some(reference_image),
|
||||
)
|
||||
.await
|
||||
.map_err(map_puzzle_generation_endpoint_error)?;
|
||||
.map_err(map_puzzle_generation_endpoint_error)
|
||||
{
|
||||
Ok(generated) => {
|
||||
tracing::info!(
|
||||
provider = VECTOR_ENGINE_PROVIDER,
|
||||
image_model = PuzzleImageModel::GptImage2.request_model_name(),
|
||||
session_id,
|
||||
level_name,
|
||||
slot,
|
||||
asset_kind,
|
||||
elapsed_ms = started_at.elapsed().as_millis() as u64,
|
||||
"拼图关卡资产生成请求完成"
|
||||
);
|
||||
generated
|
||||
}
|
||||
Err(error) => {
|
||||
tracing::warn!(
|
||||
provider = VECTOR_ENGINE_PROVIDER,
|
||||
image_model = PuzzleImageModel::GptImage2.request_model_name(),
|
||||
session_id,
|
||||
level_name,
|
||||
slot,
|
||||
asset_kind,
|
||||
elapsed_ms = started_at.elapsed().as_millis() as u64,
|
||||
error = %error,
|
||||
"拼图关卡资产生成请求失败"
|
||||
);
|
||||
return Err(error);
|
||||
}
|
||||
};
|
||||
let image = generated.images.into_iter().next().ok_or_else(|| {
|
||||
AppError::from_status(StatusCode::BAD_GATEWAY).with_details(json!({
|
||||
"provider": VECTOR_ENGINE_PROVIDER,
|
||||
@@ -427,7 +524,8 @@ async fn generate_and_persist_puzzle_level_asset(
|
||||
image
|
||||
};
|
||||
|
||||
persist_puzzle_level_asset_image(
|
||||
let persist_started_at = Instant::now();
|
||||
let persisted = persist_puzzle_level_asset_image(
|
||||
state,
|
||||
owner_user_id,
|
||||
session_id,
|
||||
@@ -439,7 +537,19 @@ async fn generate_and_persist_puzzle_level_asset(
|
||||
file_stem,
|
||||
image,
|
||||
)
|
||||
.await
|
||||
.await?;
|
||||
tracing::info!(
|
||||
provider = VECTOR_ENGINE_PROVIDER,
|
||||
image_model = PuzzleImageModel::GptImage2.request_model_name(),
|
||||
session_id,
|
||||
level_name,
|
||||
slot,
|
||||
asset_kind,
|
||||
elapsed_ms = persist_started_at.elapsed().as_millis() as u64,
|
||||
"拼图关卡资产持久化完成"
|
||||
);
|
||||
|
||||
Ok(persisted)
|
||||
}
|
||||
|
||||
pub(crate) fn make_puzzle_ui_spritesheet_image_transparent(
|
||||
|
||||
Reference in New Issue
Block a user