Replace uses of the legacy `gpt-image-2-all` model with `gpt-image-2` and standardize image workflows: no-reference generation uses POST /v1/images/generations, any-reference flows use POST /v1/images/edits with multipart `image` parts. Update SKILLs, generation scripts, decision logs, and docs to reflect the contract change and edits-vs-generations guidance. Apply corresponding changes across backend (api-server match3d/puzzle modules, openai image adapter, mappers, telemetry, spacetime client/module), frontend components and services (Match3D, Puzzle, CreativeImageInputPanel, runtime shells), and add new spritesheet/parser files and tests. Also add media/logo.png. These changes align repository code and documentation with the VectorEngine image API contract and update generation/upload handling (green-screen -> alpha processing, spritesheet handling, and related tests).
93 lines
3.3 KiB
Rust
93 lines
3.3 KiB
Rust
/// 拼图作品草稿生成动作的提示词主源。
|
||
///
|
||
/// 拼图结果页草稿本体仍由 SpacetimeDB reducer 按表单/锚点确定性编译;
|
||
/// 这里收口 api-server 在生成草稿前后需要写入 reducer 的表单 seed 文本,
|
||
/// 以及草稿首图生成时的 prompt 来源选择,避免业务路由直接拼提示词文本。
|
||
#[derive(Clone, Copy, Debug, Default)]
|
||
pub(crate) struct PuzzleFormSeedPromptParts<'a> {
|
||
pub(crate) title: Option<&'a str>,
|
||
pub(crate) work_description: Option<&'a str>,
|
||
pub(crate) picture_description: Option<&'a str>,
|
||
}
|
||
|
||
/// 将填表式拼图输入编译成 SpacetimeDB 可恢复的表单 seed prompt。
|
||
pub(crate) fn build_puzzle_form_seed_prompt(parts: PuzzleFormSeedPromptParts<'_>) -> String {
|
||
[
|
||
("作品名称", normalize_prompt_part(parts.title)),
|
||
("作品描述", normalize_prompt_part(parts.work_description)),
|
||
("画面描述", normalize_prompt_part(parts.picture_description)),
|
||
]
|
||
.into_iter()
|
||
.filter_map(|(label, value)| value.map(|value| format!("{label}:{value}")))
|
||
.collect::<Vec<_>>()
|
||
.join("\n")
|
||
}
|
||
|
||
/// 生成作品草稿时,首图 prompt 优先使用玩家当前表单里的画面描述。
|
||
pub(crate) fn resolve_puzzle_draft_cover_prompt(
|
||
explicit_prompt: Option<&str>,
|
||
level_picture_description: &str,
|
||
draft_summary: &str,
|
||
) -> String {
|
||
normalize_prompt_part(explicit_prompt)
|
||
.or_else(|| normalize_prompt_part(Some(level_picture_description)))
|
||
.or_else(|| normalize_prompt_part(Some(draft_summary)))
|
||
.unwrap_or_default()
|
||
.to_string()
|
||
}
|
||
|
||
/// 结果页单关重新生成时,优先使用面板当前编辑态 prompt,再回退关卡画面描述。
|
||
pub(crate) fn resolve_puzzle_level_image_prompt(
|
||
explicit_prompt: Option<&str>,
|
||
level_picture_description: &str,
|
||
draft_summary: &str,
|
||
) -> String {
|
||
normalize_prompt_part(explicit_prompt)
|
||
.or_else(|| normalize_prompt_part(Some(level_picture_description)))
|
||
.or_else(|| normalize_prompt_part(Some(draft_summary)))
|
||
.unwrap_or_default()
|
||
.to_string()
|
||
}
|
||
|
||
fn normalize_prompt_part(value: Option<&str>) -> Option<&str> {
|
||
value.map(str::trim).filter(|value| !value.is_empty())
|
||
}
|
||
|
||
#[cfg(test)]
|
||
mod tests {
|
||
use super::*;
|
||
|
||
#[test]
|
||
fn form_seed_prompt_keeps_only_user_visible_fields() {
|
||
let prompt = build_puzzle_form_seed_prompt(PuzzleFormSeedPromptParts {
|
||
title: None,
|
||
work_description: None,
|
||
picture_description: Some("猫咪在灯牌下回头"),
|
||
});
|
||
|
||
assert_eq!(prompt, "画面描述:猫咪在灯牌下回头");
|
||
}
|
||
|
||
#[test]
|
||
fn draft_cover_prompt_prefers_current_picture_description() {
|
||
let prompt =
|
||
resolve_puzzle_draft_cover_prompt(Some(" 当前表单画面 "), "旧关卡画面", "作品简介");
|
||
|
||
assert_eq!(prompt, "当前表单画面");
|
||
}
|
||
|
||
#[test]
|
||
fn level_image_prompt_falls_back_to_level_description() {
|
||
let prompt = resolve_puzzle_level_image_prompt(Some(" "), "关卡画面描述", "作品简介");
|
||
|
||
assert_eq!(prompt, "关卡画面描述");
|
||
}
|
||
|
||
#[test]
|
||
fn level_image_prompt_falls_back_to_draft_summary_like_initial_cover() {
|
||
let prompt = resolve_puzzle_level_image_prompt(Some(" "), " ", "作品简介");
|
||
|
||
assert_eq!(prompt, "作品简介");
|
||
}
|
||
}
|