Puzzle: support history images & partial generation
Allow history-generated image paths to be submitted where Data URLs were previously required and avoid treating partial/result-page generations as blocking the whole draft. Backend: resolve history /generated-* references via resolve_puzzle_reference_image_as_data_url and convert to PuzzleDownloadedImage; add PuzzleDownloadedImage::from_resolved_reference_image; extend draft handling to apply generated level metadata (auto-naming) and normalize generation_status to treat levels with images as ready. API: add shouldAutoNameLevel to action contracts and use it to request/refine generated level names. Spacetime/module and mappers: normalize completed level statuses when saving/reading so result-page background or per-level generation doesn't mask completed drafts. Frontend: expose resolver helpers, only mark a work as generating when no usable cover or ready level exists, keep level controls enabled during UI-background regeneration, and add tests covering history-image submission, auto-naming, and UI-background/partial-generation behaviors.
This commit is contained in:
@@ -1415,13 +1415,22 @@ pub(crate) async fn compile_puzzle_draft_with_uploaded_cover(
|
||||
"message": "关闭 AI 重绘时必须上传拼图图片。",
|
||||
}))
|
||||
})?;
|
||||
let uploaded_image = parse_puzzle_image_data_url(uploaded_image_src).ok_or_else(|| {
|
||||
AppError::from_status(StatusCode::BAD_REQUEST).with_details(json!({
|
||||
"provider": PUZZLE_AGENT_API_BASE_PROVIDER,
|
||||
"field": "referenceImageSrc",
|
||||
"message": "关闭 AI 重绘时上传图必须是图片 Data URL。",
|
||||
}))
|
||||
})?;
|
||||
let http_client = reqwest::Client::new();
|
||||
let uploaded_downloaded_image =
|
||||
resolve_puzzle_reference_image_as_data_url(state, &http_client, uploaded_image_src)
|
||||
.await
|
||||
.map(PuzzleDownloadedImage::from_resolved_reference_image)
|
||||
.map_err(|error| {
|
||||
if error.status_code() == StatusCode::BAD_REQUEST {
|
||||
AppError::from_status(StatusCode::BAD_REQUEST).with_details(json!({
|
||||
"provider": PUZZLE_AGENT_API_BASE_PROVIDER,
|
||||
"field": "referenceImageSrc",
|
||||
"message": "关闭 AI 重绘时上传图必须是图片 Data URL 或历史生成图片路径。",
|
||||
}))
|
||||
} else {
|
||||
error
|
||||
}
|
||||
})?;
|
||||
let compiled_session = state
|
||||
.spacetime_client()
|
||||
.compile_puzzle_agent_draft(session_id.clone(), owner_user_id.clone(), now)
|
||||
@@ -1446,11 +1455,6 @@ pub(crate) async fn compile_puzzle_draft_with_uploaded_cover(
|
||||
compiled_session.session_id,
|
||||
target_level.candidates.len() + 1
|
||||
);
|
||||
let uploaded_downloaded_image = PuzzleDownloadedImage {
|
||||
extension: puzzle_mime_to_extension(uploaded_image.mime_type.as_str()).to_string(),
|
||||
mime_type: normalize_puzzle_downloaded_image_mime_type(uploaded_image.mime_type.as_str()),
|
||||
bytes: uploaded_image.bytes,
|
||||
};
|
||||
let level_name_future =
|
||||
generate_puzzle_first_level_name(state, &target_level.picture_description);
|
||||
let image_level_name_future = generate_puzzle_first_level_name_from_image(
|
||||
@@ -1807,6 +1811,45 @@ pub(crate) fn apply_generated_puzzle_initial_metadata_to_session_snapshot(
|
||||
session
|
||||
}
|
||||
|
||||
pub(crate) fn apply_generated_puzzle_metadata_to_session_snapshot(
|
||||
mut session: PuzzleAgentSessionRecord,
|
||||
target_level_id: &str,
|
||||
metadata: &PuzzleLevelNaming,
|
||||
previous_level_name: &str,
|
||||
updated_at_micros: i64,
|
||||
) -> PuzzleAgentSessionRecord {
|
||||
let Some(draft) = session.draft.as_mut() else {
|
||||
return session;
|
||||
};
|
||||
let Some(target_index) = draft
|
||||
.levels
|
||||
.iter()
|
||||
.position(|level| level.level_id == target_level_id)
|
||||
.or_else(|| (!draft.levels.is_empty()).then_some(0))
|
||||
else {
|
||||
return session;
|
||||
};
|
||||
|
||||
draft.levels[target_index].level_name = metadata.level_name.clone();
|
||||
if metadata.ui_background_prompt.is_some() {
|
||||
draft.levels[target_index].ui_background_prompt = metadata.ui_background_prompt.clone();
|
||||
}
|
||||
|
||||
if target_index == 0 {
|
||||
apply_generated_puzzle_initial_metadata_to_draft(
|
||||
draft,
|
||||
metadata,
|
||||
previous_level_name,
|
||||
updated_at_micros,
|
||||
);
|
||||
} else {
|
||||
sync_puzzle_primary_draft_fields_from_level(draft);
|
||||
}
|
||||
|
||||
session.updated_at = format_timestamp_micros(updated_at_micros);
|
||||
session
|
||||
}
|
||||
|
||||
pub(crate) fn apply_generated_puzzle_initial_metadata_to_draft(
|
||||
draft: &mut PuzzleResultDraftRecord,
|
||||
metadata: &PuzzleLevelNaming,
|
||||
|
||||
Reference in New Issue
Block a user