1
This commit is contained in:
86
server-rs/crates/api-server/src/prompt/puzzle/draft.rs
Normal file
86
server-rs/crates/api-server/src/prompt/puzzle/draft.rs
Normal file
@@ -0,0 +1,86 @@
|
||||
/// 拼图作品草稿生成动作的提示词主源。
|
||||
///
|
||||
/// 拼图结果页草稿本体仍由 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,
|
||||
) -> String {
|
||||
normalize_prompt_part(explicit_prompt)
|
||||
.or_else(|| normalize_prompt_part(Some(level_picture_description)))
|
||||
.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: Some(" 暖灯猫街 "),
|
||||
work_description: Some("雨夜礼物拼图"),
|
||||
picture_description: Some("猫咪在灯牌下回头"),
|
||||
});
|
||||
|
||||
assert_eq!(
|
||||
prompt,
|
||||
"作品名称:暖灯猫街\n作品描述:雨夜礼物拼图\n画面描述:猫咪在灯牌下回头"
|
||||
);
|
||||
}
|
||||
|
||||
#[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, "关卡画面描述");
|
||||
}
|
||||
}
|
||||
@@ -38,17 +38,15 @@ pub(crate) fn build_puzzle_image_prompt(level_name: &str, prompt: &str) -> Strin
|
||||
image_prompt
|
||||
}
|
||||
|
||||
fn build_puzzle_image_prompt_text(level_name: &str, prompt: &str) -> String {
|
||||
fn build_puzzle_image_prompt_text(_level_name: &str, prompt: &str) -> String {
|
||||
format!(
|
||||
concat!(
|
||||
"请生成一张高清插画。",
|
||||
"关卡名:{level_name}。",
|
||||
"画面主体:{prompt}。",
|
||||
"画面要求:1:1 正方形拼图关卡,适配 3x3 或 4x4 拼图切块,",
|
||||
"画面要求:1:1",
|
||||
"主体要清晰集中,前中后景层次明确,局部细节丰富但不要杂乱,",
|
||||
"避免文字、水印、边框和 UI 元素。"
|
||||
),
|
||||
level_name = level_name,
|
||||
prompt = prompt,
|
||||
)
|
||||
}
|
||||
@@ -78,10 +76,9 @@ mod tests {
|
||||
fn build_puzzle_image_prompt_keeps_puzzle_asset_constraints() {
|
||||
let prompt = build_puzzle_image_prompt("雨夜神庙", "猫咪在发光遗迹前寻找线索");
|
||||
|
||||
assert!(prompt.contains("雨夜神庙"));
|
||||
assert!(prompt.contains("猫咪在发光遗迹前寻找线索"));
|
||||
assert!(prompt.contains("1:1 正方形拼图关卡"));
|
||||
assert!(prompt.contains("3x3 或 4x4"));
|
||||
assert!(prompt.contains("1:1"));
|
||||
assert!(prompt.contains("主体要清晰集中"));
|
||||
assert!(prompt.contains("避免文字、水印、边框和 UI 元素"));
|
||||
}
|
||||
|
||||
@@ -93,8 +90,8 @@ mod tests {
|
||||
let prompt = build_puzzle_image_prompt(long_level_name.as_str(), long_description.as_str());
|
||||
|
||||
assert!(prompt.chars().count() <= PUZZLE_TEXT_TO_IMAGE_PROMPT_MAX_CHARS);
|
||||
assert!(prompt.contains("1:1 正方形拼图关卡"));
|
||||
assert!(prompt.contains("3x3 或 4x4"));
|
||||
assert!(prompt.contains("1:1"));
|
||||
assert!(prompt.contains("主体要清晰集中"));
|
||||
assert!(prompt.contains("避免文字、水印、边框和 UI 元素"));
|
||||
}
|
||||
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
pub(crate) mod agent_chat;
|
||||
pub(crate) mod draft;
|
||||
pub(crate) mod image;
|
||||
|
||||
Reference in New Issue
Block a user