From 75d9e25634346ddb23efc99d67cab2f65ab3166c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E5=BE=B7=E5=AE=87?= Date: Thu, 1 Oct 2026 15:56:12 +0800 Subject: [PATCH] =?UTF-8?q?=E9=A6=96=E5=B0=BE=E6=8D=A2=E8=A1=8C=E5=9B=9E?= =?UTF-8?q?=E5=88=B0=E6=8C=89=E7=A9=BA=E7=99=BD=E8=A3=81=E5=89=AA=EF=BC=8C?= =?UTF-8?q?=E4=B8=8D=E5=86=8D=E5=BD=93=E6=8E=A7=E5=88=B6=E5=AD=97=E7=AC=A6?= =?UTF-8?q?=E6=8A=A5=E9=94=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 回退 3f70c6911 的收紧:optional_text 恢复成先 trim、再查 is_control - prompt 末尾带 \n(\r\n 等)视为普通首尾空白裁掉,与首尾空格同口径;中间的控制字符仍然报 has_control_characters - 测试改为断言行尾换行被接受且裁成 "生成过场",另加一条中间换行仍被拒的断言 验证:cargo test --bin genarrative-ai-game-creator-shell -- agent::direct_tool_bridge:: 41 passed / 0 failed --- .../src-tauri/src/agent/direct_tool_bridge.rs | 22 ++++++++++++++----- .../src-tauri/src/agent/tool/arguments.rs | 14 +++++------- 2 files changed, 23 insertions(+), 13 deletions(-) diff --git a/apps/ai-game-creator-shell/src-tauri/src/agent/direct_tool_bridge.rs b/apps/ai-game-creator-shell/src-tauri/src/agent/direct_tool_bridge.rs index 00940673a..d35ae9383 100644 --- a/apps/ai-game-creator-shell/src-tauri/src/agent/direct_tool_bridge.rs +++ b/apps/ai-game-creator-shell/src-tauri/src/agent/direct_tool_bridge.rs @@ -4898,18 +4898,30 @@ mod tests { GameCreationAppAssetKind::Image ); - let trailing_newline = match resource_generation_input(&json!({ + assert_eq!( + resource_generation_input(&json!({ + "kind": "video", + "mode": "create", + "prompt": "生成过场\n", + "assetName": "开场" + })) + .expect("首尾换行按普通空白裁掉,不能报控制字符") + .prompt, + "生成过场" + ); + + let interior_newline = match resource_generation_input(&json!({ "kind": "video", "mode": "create", - "prompt": "生成过场\n", + "prompt": "生成\n过场", "assetName": "开场" })) { - Ok(_) => panic!("落在首尾的控制字符不能被 trim 静默吞掉"), + Ok(_) => panic!("中间的控制字符仍必须被拒绝"), Err(error) => error.to_user_msg(), }; assert!( - trailing_newline.contains("prompt 含有控制字符"), - "{trailing_newline}" + interior_newline.contains("prompt 含有控制字符"), + "{interior_newline}" ); let whitespace_only = diff --git a/apps/ai-game-creator-shell/src-tauri/src/agent/tool/arguments.rs b/apps/ai-game-creator-shell/src-tauri/src/agent/tool/arguments.rs index 67c29cbcb..1460a49ea 100644 --- a/apps/ai-game-creator-shell/src-tauri/src/agent/tool/arguments.rs +++ b/apps/ai-game-creator-shell/src-tauri/src/agent/tool/arguments.rs @@ -37,19 +37,17 @@ pub(crate) fn optional_text, E>( let Some(value) = value.as_str() else { return Err(field.not_text(value.to_string())); }; - let trimmed = value.trim(); - if trimmed.is_empty() { + let value = value.trim(); + if value.is_empty() { return Err(field.empty()); } - // 控制字符必须按原串判定:否则落在首尾的 `\n`/`\t` 会被 trim 静默吞掉, - // 同一个字符在中间报错、在边上通过。 + if value.chars().count() > field.max_chars() { + return Err(field.too_long(value.chars().count(), field.max_chars())); + } if value.chars().any(char::is_control) { return Err(field.has_control_characters()); } - if trimmed.chars().count() > field.max_chars() { - return Err(field.too_long(trimmed.chars().count(), field.max_chars())); - } - Ok(Some(trimmed.to_string())) + Ok(Some(value.to_string())) } /// 一页列表结果:`offset` 是起始下标,`limit` 是这一次最多返回多少项。