自动命名仅使用用户需求
删除自动项目命名专用附件结构体和附件元数据拼接 前端取名请求只传用户需求 更新前后端回归测试以固定输入边界
This commit is contained in:
@@ -14,20 +14,10 @@ const UI_EDITOR_IMAGE_MAX_COUNT: usize = 100;
|
||||
const AGENT_EDITOR_ASSET_LIBRARY_MAX_ITEMS: usize = 500;
|
||||
const AGENT_EDITOR_ASSET_ID_MAX_CHARS: usize = 512;
|
||||
const AUTOMATIC_PROJECT_NAME_MAX_PROMPT_CHARS: usize = 8_000;
|
||||
const AUTOMATIC_PROJECT_NAME_MAX_ATTACHMENTS: usize = 20;
|
||||
const AUTOMATIC_PROJECT_NAME_MAX_OUTPUT_TOKENS: u32 = 64;
|
||||
const AUTOMATIC_PROJECT_NAME_SYSTEM_PROMPT: &str =
|
||||
include_str!("../prompts/automatic-project-name.md");
|
||||
|
||||
// 自动命名只需要附件名称和媒体类型提示。保持独立的窄 DTO 并拒绝未知字段,
|
||||
// 避免把 Direct 回合附件中的本地路径、导入状态和文件大小引入无项目命名边界。
|
||||
#[derive(Clone, Debug, Deserialize, Eq, PartialEq)]
|
||||
#[serde(rename_all = "camelCase", deny_unknown_fields)]
|
||||
pub(crate) struct AutomaticProjectNameAttachment {
|
||||
pub(crate) name: String,
|
||||
pub(crate) media_type: Option<String>,
|
||||
}
|
||||
|
||||
fn is_chinese_project_name_character(value: char) -> bool {
|
||||
matches!(
|
||||
value,
|
||||
@@ -49,51 +39,19 @@ pub(crate) fn normalize_suggested_project_name(value: &str) -> Option<String> {
|
||||
normalize_game_creation_project_name(value).ok()
|
||||
}
|
||||
|
||||
pub(crate) fn build_automatic_project_name_prompt(
|
||||
prompt: &str,
|
||||
attachments: &[AutomaticProjectNameAttachment],
|
||||
) -> Result<String, String> {
|
||||
pub(crate) fn build_automatic_project_name_prompt(prompt: &str) -> Result<String, String> {
|
||||
let prompt = prompt.trim();
|
||||
let mut attachment_lines = Vec::new();
|
||||
for attachment in attachments
|
||||
.iter()
|
||||
.take(AUTOMATIC_PROJECT_NAME_MAX_ATTACHMENTS)
|
||||
{
|
||||
let name = attachment.name.trim();
|
||||
if name.is_empty() || name.chars().any(char::is_control) {
|
||||
continue;
|
||||
}
|
||||
let name: String = name.chars().take(160).collect();
|
||||
let media_type = attachment
|
||||
.media_type
|
||||
.as_deref()
|
||||
.map(str::trim)
|
||||
.filter(|value| !value.is_empty() && !value.chars().any(char::is_control))
|
||||
.map(|value| value.chars().take(80).collect::<String>());
|
||||
attachment_lines.push(match media_type {
|
||||
Some(media_type) => format!("- 附件:{name}({media_type})"),
|
||||
None => format!("- 附件:{name}"),
|
||||
});
|
||||
}
|
||||
if prompt.is_empty() && attachment_lines.is_empty() {
|
||||
if prompt.is_empty() {
|
||||
return Err("首页创作需求为空,不能提炼项目名称".to_string());
|
||||
}
|
||||
let bounded_prompt: String = prompt
|
||||
Ok(prompt
|
||||
.chars()
|
||||
.take(AUTOMATIC_PROJECT_NAME_MAX_PROMPT_CHARS)
|
||||
.collect();
|
||||
Ok(match attachment_lines.is_empty() {
|
||||
true => bounded_prompt,
|
||||
false if prompt.is_empty() => attachment_lines.join("\n"),
|
||||
false => format!("{bounded_prompt}\n{}", attachment_lines.join("\n")),
|
||||
})
|
||||
.collect())
|
||||
}
|
||||
|
||||
async fn request_automatic_project_name(
|
||||
prompt: &str,
|
||||
attachments: &[AutomaticProjectNameAttachment],
|
||||
) -> Result<Option<String>, String> {
|
||||
let user_prompt = build_automatic_project_name_prompt(prompt, attachments)?;
|
||||
async fn request_automatic_project_name(prompt: &str) -> Result<Option<String>, String> {
|
||||
let user_prompt = build_automatic_project_name_prompt(prompt)?;
|
||||
let app_config = load_game_creator_app_config()?;
|
||||
if app_config.agent_mode == GAME_CREATOR_AGENT_MODE_CODEX_APP_SERVER {
|
||||
let reply = crate::agent::direct_game_creator_home_codex_chat(
|
||||
@@ -1992,9 +1950,8 @@ pub(crate) fn check_game_creator_llm_config() -> GameCreatorLlmConfigStatus {
|
||||
#[tauri::command]
|
||||
pub(crate) async fn suggest_automatic_project_name(
|
||||
prompt: String,
|
||||
attachments: Option<Vec<AutomaticProjectNameAttachment>>,
|
||||
) -> Result<Option<String>, String> {
|
||||
request_automatic_project_name(prompt.trim(), attachments.as_deref().unwrap_or_default())
|
||||
request_automatic_project_name(prompt.trim())
|
||||
.await
|
||||
.map_err(|error| redact_agent_runtime_error(Path::new("."), &error, 320))
|
||||
}
|
||||
|
||||
@@ -1649,31 +1649,21 @@ fn automatic_project_name_suggestions_are_normalized_fail_closed() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn automatic_project_name_prompt_is_bounded_and_uses_attachment_metadata_only() {
|
||||
fn automatic_project_name_prompt_is_bounded_and_uses_user_requirement_only() {
|
||||
let system_prompt = include_str!("../../prompts/automatic-project-name.md");
|
||||
assert!(system_prompt.contains("长度为 2 到 16 个字符"));
|
||||
assert!(system_prompt.contains("不要使用知名作品名、路径、URL、凭据、Markdown、引号或解释"));
|
||||
|
||||
let prompt = build_automatic_project_name_prompt(
|
||||
"做一个在月球邮局送信的解谜游戏",
|
||||
&[
|
||||
AutomaticProjectNameAttachment {
|
||||
name: " 月球邮局参考.png ".to_string(),
|
||||
media_type: Some("image/png".to_string()),
|
||||
},
|
||||
AutomaticProjectNameAttachment {
|
||||
name: "bad\nname".to_string(),
|
||||
media_type: Some("image/png".to_string()),
|
||||
},
|
||||
],
|
||||
)
|
||||
.expect("build prompt");
|
||||
let prompt = build_automatic_project_name_prompt("做一个在月球邮局送信的解谜游戏")
|
||||
.expect("build prompt");
|
||||
|
||||
assert!(prompt.contains("做一个在月球邮局送信的解谜游戏"));
|
||||
assert!(prompt.contains("附件:月球邮局参考.png(image/png)"));
|
||||
assert!(!prompt.contains("bad\nname"));
|
||||
assert_eq!(prompt, "做一个在月球邮局送信的解谜游戏");
|
||||
assert_eq!(
|
||||
build_automatic_project_name_prompt(&"长".repeat(8_001)).expect("bound prompt"),
|
||||
"长".repeat(8_000)
|
||||
);
|
||||
|
||||
assert!(build_automatic_project_name_prompt("", &[]).is_err());
|
||||
assert!(build_automatic_project_name_prompt("").is_err());
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
|
||||
@@ -78,10 +78,6 @@ async function suggestAutomaticProjectName(
|
||||
try {
|
||||
return await invoke<string | null>('suggest_automatic_project_name', {
|
||||
prompt: draft.prompt,
|
||||
attachments: draft.attachments.map((attachment) => ({
|
||||
name: attachment.file.name,
|
||||
mediaType: attachment.file.type || null,
|
||||
})),
|
||||
});
|
||||
} catch {
|
||||
// 项目命名是增强能力:配置缺失、Provider 失败或非法输出都回退默认名,
|
||||
|
||||
@@ -1490,12 +1490,6 @@ export function registerHomeProjectCreationTests() {
|
||||
if (command === 'suggest_automatic_project_name') {
|
||||
expect(args).toEqual({
|
||||
prompt: '按这个角色做游戏',
|
||||
attachments: [
|
||||
{
|
||||
name: '角色参考.png',
|
||||
mediaType: 'image/png',
|
||||
},
|
||||
],
|
||||
});
|
||||
return '角色参考游戏';
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user