From 66e4ece6922357ad1da3a571f94c6fa287ec5829 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:01:20 +0800 Subject: [PATCH] =?UTF-8?q?=E5=85=A5=E5=8F=82=E7=BB=99=E4=BA=86=E9=9D=9E?= =?UTF-8?q?=E5=AD=97=E7=AC=A6=E4=B8=B2=E5=80=BC=E6=97=B6=E6=8C=89=E5=AD=97?= =?UTF-8?q?=E6=AE=B5=E6=8A=A5=E7=B1=BB=E5=9E=8B=E9=94=99=EF=BC=8C=E4=B8=8D?= =?UTF-8?q?=E5=86=8D=E6=B7=B7=E8=BF=9B=E3=80=8C=E4=B8=8D=E8=83=BD=E4=B8=BA?= =?UTF-8?q?=E7=A9=BA=E3=80=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - ToolTextField 增加 not_text(got),optional_text 在 as_str() 失败时返回它;原来数字/布尔/数组/对象会和空串、null 一起落到 field.empty(),{"prompt":5} 报成「prompt 不能为空」 - 9 个工具 enum 按字段各加一个 NotText 变体(KindNotText、PromptNotText、AspectRatioNotText…),文案形如「生成图片失败:aspectRatio 必须是字符串,当前是「5」。」,仍是一 case 一变体、文案写在变体上 - GenerateImageTextField 改为实现 ToolTextField,删掉 generate_image_optional_text / generate_image_required_text 这两份与共享实现重复的拷贝,generate_image 改走 optional_text / required_text - 空串 / null / 缺省仍按原来的空值语义处理,只有「有值但不是字符串」换成类型错 - 新增 non_text_field_values_report_a_type_error_instead_of_an_empty_value 覆盖 create_or_derive、prepare_game_art、generate_image 三条路径 验证:cargo test --bin genarrative-ai-game-creator-shell -- agent::direct_tool_bridge:: 41 passed;全量 agent:: 952 passed(唯一失败 concurrent_distinct_frozen_root_steers_create_only_one_replacement 是既有 flaky,单独重跑通过) --- .../src-tauri/src/agent/direct_tool_bridge.rs | 160 ++++++++++++++---- .../src-tauri/src/agent/tool/arguments.rs | 14 +- .../tool/create_or_derive_resource/error.rs | 30 ++++ .../src/agent/tool/edit_image/error.rs | 12 ++ .../src/agent/tool/generate_image/error.rs | 24 +++ .../agent/tool/list_account_assets/error.rs | 8 + .../agent/tool/list_project_files/error.rs | 12 ++ .../tool/list_registered_assets/error.rs | 8 + .../src/agent/tool/prepare_game_art/error.rs | 4 + .../src/agent/tool/remove_background/error.rs | 8 + .../src/agent/tool/web_search/error.rs | 4 + 11 files changed, 242 insertions(+), 42 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 889f805b3..17883e93d 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 @@ -928,6 +928,18 @@ impl ToolTextField for ResourceGenerationTextField Self::AssetName => CreateOrDeriveResourceError::AssetNameHasControlCharacters, } } + + fn not_text(self, got: String) -> CreateOrDeriveResourceError { + match self { + Self::Kind => CreateOrDeriveResourceError::KindNotText { got }, + Self::Mode => CreateOrDeriveResourceError::ModeNotText { got }, + Self::SourceLocalAssetId => { + CreateOrDeriveResourceError::SourceLocalAssetIdNotText { got } + } + Self::Prompt => CreateOrDeriveResourceError::PromptNotText { got }, + Self::AssetName => CreateOrDeriveResourceError::AssetNameNotText { got }, + } + } } /// `agc_create_or_derive_resource` 的入参规则;工具桥与 MCP 预检共用这一份实现。 @@ -1148,6 +1160,13 @@ impl ToolTextField for RegisteredAssetTextField { Self::AssetId => ListRegisteredAssetsError::AssetIdHasControlCharacters, } } + + fn not_text(self, got: String) -> ListRegisteredAssetsError { + match self { + Self::Kind => ListRegisteredAssetsError::KindNotText { got }, + Self::AssetId => ListRegisteredAssetsError::AssetIdNotText { got }, + } + } } /// `agc_list_registered_assets` 的入参解析结果。 @@ -1493,6 +1512,14 @@ impl ToolTextField for ListProjectFilesTextField { Self::Kind => ListProjectFilesError::KindHasControlCharacters, } } + + fn not_text(self, got: String) -> ListProjectFilesError { + match self { + Self::Path => ListProjectFilesError::PathNotText { got }, + Self::Query => ListProjectFilesError::QueryNotText { got }, + Self::Kind => ListProjectFilesError::KindNotText { got }, + } + } } /// `agc_list_project_files` 的入参解析结果。 @@ -1934,6 +1961,13 @@ impl ToolTextField for AccountAssetTextField { Self::Query => ListAccountAssetsError::QueryHasControlCharacters, } } + + fn not_text(self, got: String) -> ListAccountAssetsError { + match self { + Self::FolderId => ListAccountAssetsError::FolderIdNotText { got }, + Self::Query => ListAccountAssetsError::QueryNotText { got }, + } + } } /// `agc_list_account_assets` 的入参解析结果。 @@ -2312,6 +2346,13 @@ impl ToolTextField for RemoveBackgroundTextField { Self::AssetName => RemoveBackgroundError::AssetNameHasControlCharacters, } } + + fn not_text(self, got: String) -> RemoveBackgroundError { + match self { + Self::SourceLocalAssetId => RemoveBackgroundError::SourceLocalAssetIdNotText { got }, + Self::AssetName => RemoveBackgroundError::AssetNameNotText { got }, + } + } } pub(in crate::agent) struct RemoveBackgroundInput { @@ -2646,6 +2687,12 @@ impl ToolTextField for PrepareGameArtTextField { Self::Brief => PrepareGameArtError::BriefHasControlCharacters, } } + + fn not_text(self, got: String) -> PrepareGameArtError { + match self { + Self::Brief => PrepareGameArtError::BriefNotText { got }, + } + } } /// `brief` 必填;`mode` 缺省为 `reuse-or-create`。 @@ -2739,7 +2786,7 @@ const GENERATE_IMAGE_ARGUMENTS: &[&str] = &[ "screenColor", ]; -/// 会做文本边界校验的字段。三者(空值/超长/控制字符)各自对应工具 enum 里自己的变体。 +/// 会做文本边界校验的字段。四者(空值/超长/控制字符/不是字符串)各自对应工具 enum 里自己的变体。 #[derive(Clone, Copy)] enum GenerateImageTextField { Prompt, @@ -2750,7 +2797,7 @@ enum GenerateImageTextField { SliceMode, } -impl GenerateImageTextField { +impl ToolTextField for GenerateImageTextField { fn key(self) -> &'static str { match self { Self::Prompt => "prompt", @@ -2823,35 +2870,17 @@ impl GenerateImageTextField { Self::SliceMode => GenerateImageError::SliceModeHasControlCharacters, } } -} -/// 可选文本字段:缺省或 null 视为未提供;给了就必须在字段自己的边界内。 -fn generate_image_optional_text( - arguments: &Value, - field: GenerateImageTextField, -) -> Result, GenerateImageError> { - let Some(value) = arguments.get(field.key()).filter(|value| !value.is_null()) else { - return Ok(None); - }; - let value = value - .as_str() - .map(str::trim) - .filter(|value| !value.is_empty()) - .ok_or_else(|| field.empty())?; - if value.chars().count() > field.max_chars() { - return Err(field.too_long(value.chars().count(), field.max_chars())); + fn not_text(self, got: String) -> GenerateImageError { + match self { + Self::Prompt => GenerateImageError::PromptNotText { got }, + Self::AspectRatio => GenerateImageError::AspectRatioNotText { got }, + Self::ImageSize => GenerateImageError::ImageSizeNotText { got }, + Self::AssetName => GenerateImageError::AssetNameNotText { got }, + Self::OutputPath => GenerateImageError::OutputPathNotText { got }, + Self::SliceMode => GenerateImageError::SliceModeNotText { got }, + } } - if value.chars().any(char::is_control) { - return Err(field.has_control_characters()); - } - Ok(Some(value.to_string())) -} - -fn generate_image_required_text( - arguments: &Value, - field: GenerateImageTextField, -) -> Result { - generate_image_optional_text(arguments, field)?.ok_or_else(|| field.empty()) } fn generate_image_grid_axis( @@ -2970,23 +2999,22 @@ pub(in crate::agent) fn generate_image_input( arguments: &Value, ) -> Result { ToolArgumentsRejection::check(arguments, GENERATE_IMAGE_ARGUMENTS)?; - let prompt = generate_image_required_text(arguments, GenerateImageTextField::Prompt)?; + let prompt = required_text(arguments, GenerateImageTextField::Prompt)?; let kind = bridge_image_generation_kind(arguments)?; - let aspect_ratio = - generate_image_optional_text(arguments, GenerateImageTextField::AspectRatio)? - .unwrap_or_else(|| "1:1".to_string()); + let aspect_ratio = optional_text(arguments, GenerateImageTextField::AspectRatio)? + .unwrap_or_else(|| "1:1".to_string()); if !GENERATE_IMAGE_ASPECT_RATIOS.contains(&aspect_ratio.as_str()) { return Err(GenerateImageError::AspectRatioUnsupported { got: aspect_ratio }); } - let image_size = generate_image_optional_text(arguments, GenerateImageTextField::ImageSize)? + let image_size = optional_text(arguments, GenerateImageTextField::ImageSize)? .unwrap_or_else(|| "1K".to_string()); if !GENERATE_IMAGE_IMAGE_SIZES.contains(&image_size.as_str()) { return Err(GenerateImageError::ImageSizeUnsupported { got: image_size }); } - let asset_name = generate_image_optional_text(arguments, GenerateImageTextField::AssetName)? + let asset_name = optional_text(arguments, GenerateImageTextField::AssetName)? .unwrap_or_else(|| "AI 生成图片".to_string()); - let output_path = generate_image_optional_text(arguments, GenerateImageTextField::OutputPath)?; - let slice_mode = generate_image_optional_text(arguments, GenerateImageTextField::SliceMode)?; + let output_path = optional_text(arguments, GenerateImageTextField::OutputPath)?; + let slice_mode = optional_text(arguments, GenerateImageTextField::SliceMode)?; if slice_mode .as_deref() .is_some_and(|mode| !GENERATE_IMAGE_SLICE_MODES.contains(&mode)) @@ -3197,6 +3225,14 @@ impl ToolTextField for EditImageTextField { Self::AssetName => EditImageError::AssetNameHasControlCharacters, } } + + fn not_text(self, got: String) -> EditImageError { + match self { + Self::SourceLocalAssetId => EditImageError::SourceLocalAssetIdNotText { got }, + Self::Prompt => EditImageError::PromptNotText { got }, + Self::AssetName => EditImageError::AssetNameNotText { got }, + } + } } /// `agc_edit_image` 只是 `agc_create_or_derive_resource` 的图片派生入口:把三个字段投影成 @@ -3416,6 +3452,12 @@ impl ToolTextField for WebSearchTextField { Self::Query => WebSearchError::QueryHasControlCharacters, } } + + fn not_text(self, got: String) -> WebSearchError { + match self { + Self::Query => WebSearchError::QueryNotText { got }, + } + } } /// `agc_web_search` 的入参规则:`query` 的文本边界与 `maxResults` 的取值区间。 @@ -4816,6 +4858,50 @@ mod tests { .is_ok()); } + #[test] + fn non_text_field_values_report_a_type_error_instead_of_an_empty_value() { + let prompt_not_text = match resource_generation_input(&json!({ + "kind": "video", + "mode": "create", + "prompt": 5, + "assetName": "开场" + })) { + Ok(_) => panic!("prompt 不是字符串时必须被拒绝"), + Err(error) => error.to_user_msg(), + }; + assert!( + prompt_not_text.contains("prompt 必须是字符串"), + "{prompt_not_text}" + ); + assert!(!prompt_not_text.contains("不能为空"), "{prompt_not_text}"); + + let brief_not_text = required_text(&json!({ "brief": 5 }), PrepareGameArtTextField::Brief) + .expect_err("brief 不是字符串时必须被拒绝") + .to_user_msg(); + assert!( + brief_not_text.contains("brief 必须是字符串"), + "{brief_not_text}" + ); + assert!(!brief_not_text.contains("不能为空"), "{brief_not_text}"); + + let empty_brief = required_text(&json!({ "brief": "" }), PrepareGameArtTextField::Brief) + .expect_err("空 brief 仍然按空值处理") + .to_user_msg(); + assert!(empty_brief.contains("brief 不能为空"), "{empty_brief}"); + + let aspect_ratio_not_text = match generate_image_input(&json!({ + "prompt": "像素风主角", + "aspectRatio": 5 + })) { + Ok(_) => panic!("aspectRatio 不是字符串时必须被拒绝"), + Err(error) => error.to_user_msg(), + }; + assert!( + aspect_ratio_not_text.contains("aspectRatio 必须是字符串"), + "{aspect_ratio_not_text}" + ); + } + #[test] fn large_mcp_images_are_reduced_to_bounded_jpeg_previews() { let image = image::RgbaImage::from_fn(1600, 1200, |x, y| { 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 c3ff3e63a..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 @@ -14,6 +14,8 @@ pub(crate) trait ToolTextField: Copy { fn empty(self) -> E; fn too_long(self, got_chars: usize, max_chars: usize) -> E; fn has_control_characters(self) -> E; + /// 字段给了值但不是字符串:必须报类型错,不能混进「不能为空」。 + fn not_text(self, got: String) -> E; } /// 必填文本字段:缺省、`null`、空串都算空值。 @@ -32,11 +34,13 @@ pub(crate) fn optional_text, E>( let Some(value) = arguments.get(field.key()).filter(|value| !value.is_null()) else { return Ok(None); }; - let value = value - .as_str() - .map(str::trim) - .filter(|value| !value.is_empty()) - .ok_or_else(|| field.empty())?; + let Some(value) = value.as_str() else { + return Err(field.not_text(value.to_string())); + }; + let value = value.trim(); + if value.is_empty() { + return Err(field.empty()); + } if value.chars().count() > field.max_chars() { return Err(field.too_long(value.chars().count(), field.max_chars())); } diff --git a/apps/ai-game-creator-shell/src-tauri/src/agent/tool/create_or_derive_resource/error.rs b/apps/ai-game-creator-shell/src-tauri/src/agent/tool/create_or_derive_resource/error.rs index f2e74c31f..a6e529e74 100644 --- a/apps/ai-game-creator-shell/src-tauri/src/agent/tool/create_or_derive_resource/error.rs +++ b/apps/ai-game-creator-shell/src-tauri/src/agent/tool/create_or_derive_resource/error.rs @@ -26,6 +26,9 @@ pub(crate) enum CreateOrDeriveResourceError { max_chars: usize, }, KindHasControlCharacters, + KindNotText { + got: String, + }, KindUnsupported { got: String, }, @@ -35,6 +38,9 @@ pub(crate) enum CreateOrDeriveResourceError { max_chars: usize, }, ModeHasControlCharacters, + ModeNotText { + got: String, + }, ModeUnsupported { got: String, }, @@ -44,12 +50,18 @@ pub(crate) enum CreateOrDeriveResourceError { max_chars: usize, }, SourceLocalAssetIdHasControlCharacters, + SourceLocalAssetIdNotText { + got: String, + }, PromptEmpty, PromptTooLong { got_chars: usize, max_chars: usize, }, PromptHasControlCharacters, + PromptNotText { + got: String, + }, PromptTooLongForKind { kind: DirectResourceGenerationKind, got_chars: usize, @@ -61,6 +73,9 @@ pub(crate) enum CreateOrDeriveResourceError { max_chars: usize, }, AssetNameHasControlCharacters, + AssetNameNotText { + got: String, + }, ImageCreationRequiresRegisteredSource, CharacterAnimationRequiresRegisteredSource, CreateModeWithSource, @@ -127,6 +142,9 @@ impl ToolFailure for CreateOrDeriveResourceError { Self::KindHasControlCharacters => { "生成或派生资源失败:kind 含有控制字符。".to_string() } + Self::KindNotText { got } => format!( + "生成或派生资源失败:kind 必须是字符串,当前是「{got}」。" + ), Self::KindUnsupported { got } => format!( "生成或派生资源失败:kind「{got}」不是受支持的媒体资源类型,只允许 image、video、character-animation、sound-effect、background-music。" ), @@ -138,6 +156,9 @@ impl ToolFailure for CreateOrDeriveResourceError { Self::ModeHasControlCharacters => { "生成或派生资源失败:mode 含有控制字符。".to_string() } + Self::ModeNotText { got } => format!( + "生成或派生资源失败:mode 必须是字符串,当前是「{got}」。" + ), Self::ModeUnsupported { got } => format!( "生成或派生资源失败:mode「{got}」不是受支持的模式,只能是 create 或 derive。" ), @@ -153,6 +174,9 @@ impl ToolFailure for CreateOrDeriveResourceError { Self::SourceLocalAssetIdHasControlCharacters => { "生成或派生资源失败:sourceLocalAssetId 含有控制字符。".to_string() } + Self::SourceLocalAssetIdNotText { got } => format!( + "生成或派生资源失败:sourceLocalAssetId 必须是字符串,当前是「{got}」。" + ), Self::PromptEmpty => "生成或派生资源失败:prompt 不能为空。".to_string(), Self::PromptTooLong { got_chars, @@ -163,6 +187,9 @@ impl ToolFailure for CreateOrDeriveResourceError { Self::PromptHasControlCharacters => { "生成或派生资源失败:prompt 含有控制字符。".to_string() } + Self::PromptNotText { got } => format!( + "生成或派生资源失败:prompt 必须是字符串,当前是「{got}」。" + ), Self::PromptTooLongForKind { kind, got_chars, @@ -182,6 +209,9 @@ impl ToolFailure for CreateOrDeriveResourceError { Self::AssetNameHasControlCharacters => { "生成或派生资源失败:assetName 含有控制字符。".to_string() } + Self::AssetNameNotText { got } => format!( + "生成或派生资源失败:assetName 必须是字符串,当前是「{got}」。" + ), Self::ImageCreationRequiresRegisteredSource => { "生成或派生资源失败:图片编辑必须基于已登记图片资源派生。".to_string() } diff --git a/apps/ai-game-creator-shell/src-tauri/src/agent/tool/edit_image/error.rs b/apps/ai-game-creator-shell/src-tauri/src/agent/tool/edit_image/error.rs index bbe87c541..1425dd0dc 100644 --- a/apps/ai-game-creator-shell/src-tauri/src/agent/tool/edit_image/error.rs +++ b/apps/ai-game-creator-shell/src-tauri/src/agent/tool/edit_image/error.rs @@ -12,12 +12,15 @@ pub(crate) enum EditImageError { SourceLocalAssetIdEmpty, SourceLocalAssetIdTooLong { got_chars: usize, max_chars: usize }, SourceLocalAssetIdHasControlCharacters, + SourceLocalAssetIdNotText { got: String }, PromptEmpty, PromptTooLong { got_chars: usize, max_chars: usize }, PromptHasControlCharacters, + PromptNotText { got: String }, AssetNameEmpty, AssetNameTooLong { got_chars: usize, max_chars: usize }, AssetNameHasControlCharacters, + AssetNameNotText { got: String }, ResourceEdit(CreateOrDeriveResourceError), } @@ -49,12 +52,18 @@ impl ToolFailure for EditImageError { Self::SourceLocalAssetIdHasControlCharacters => { "编辑图片失败:sourceLocalAssetId 含有控制字符。".to_string() } + Self::SourceLocalAssetIdNotText { got } => { + format!("编辑图片失败:sourceLocalAssetId 必须是字符串,当前是「{got}」。") + } Self::PromptEmpty => "编辑图片失败:prompt 不能为空。".to_string(), Self::PromptTooLong { got_chars, max_chars, } => format!("编辑图片失败:prompt 有 {got_chars} 个字符,超过上限 {max_chars}。"), Self::PromptHasControlCharacters => "编辑图片失败:prompt 含有控制字符。".to_string(), + Self::PromptNotText { got } => { + format!("编辑图片失败:prompt 必须是字符串,当前是「{got}」。") + } Self::AssetNameEmpty => "编辑图片失败:assetName 不能为空。".to_string(), Self::AssetNameTooLong { got_chars, @@ -63,6 +72,9 @@ impl ToolFailure for EditImageError { Self::AssetNameHasControlCharacters => { "编辑图片失败:assetName 含有控制字符。".to_string() } + Self::AssetNameNotText { got } => { + format!("编辑图片失败:assetName 必须是字符串,当前是「{got}」。") + } Self::ResourceEdit(rejection) => rejection.to_user_msg(), } } diff --git a/apps/ai-game-creator-shell/src-tauri/src/agent/tool/generate_image/error.rs b/apps/ai-game-creator-shell/src-tauri/src/agent/tool/generate_image/error.rs index 542a86e08..847c35299 100644 --- a/apps/ai-game-creator-shell/src-tauri/src/agent/tool/generate_image/error.rs +++ b/apps/ai-game-creator-shell/src-tauri/src/agent/tool/generate_image/error.rs @@ -19,24 +19,30 @@ pub(crate) enum GenerateImageError { PromptEmpty, PromptTooLong { got_chars: usize, max_chars: usize }, PromptHasControlCharacters, + PromptNotText { got: String }, KindUnsupported { got: String }, AspectRatioEmpty, AspectRatioTooLong { got_chars: usize, max_chars: usize }, AspectRatioHasControlCharacters, + AspectRatioNotText { got: String }, AspectRatioUnsupported { got: String }, ImageSizeEmpty, ImageSizeTooLong { got_chars: usize, max_chars: usize }, ImageSizeHasControlCharacters, + ImageSizeNotText { got: String }, ImageSizeUnsupported { got: String }, AssetNameEmpty, AssetNameTooLong { got_chars: usize, max_chars: usize }, AssetNameHasControlCharacters, + AssetNameNotText { got: String }, OutputPathEmpty, OutputPathTooLong { got_chars: usize, max_chars: usize }, OutputPathHasControlCharacters, + OutputPathNotText { got: String }, SliceModeEmpty, SliceModeTooLong { got_chars: usize, max_chars: usize }, SliceModeHasControlCharacters, + SliceModeNotText { got: String }, SliceModeUnsupported { got: String }, GridAxisNotInteger { axis: &'static str }, GridAxesMissing, @@ -74,6 +80,9 @@ impl ToolFailure for GenerateImageError { max_chars, } => format!("生成图片失败:prompt 有 {got_chars} 个字符,超过上限 {max_chars}。"), Self::PromptHasControlCharacters => "生成图片失败:prompt 含有控制字符。".to_string(), + Self::PromptNotText { got } => format!( + "生成图片失败:prompt 必须是字符串,当前是「{got}」。" + ), Self::KindUnsupported { got } => format!( "生成图片失败:kind「{got}」不是受支持的素材类型,只允许 {}。", platform_art_asset_generation_kind_list() @@ -88,6 +97,9 @@ impl ToolFailure for GenerateImageError { Self::AspectRatioHasControlCharacters => { "生成图片失败:aspectRatio 含有控制字符。".to_string() } + Self::AspectRatioNotText { got } => format!( + "生成图片失败:aspectRatio 必须是字符串,当前是「{got}」。" + ), Self::AspectRatioUnsupported { got } => format!( "生成图片失败:aspectRatio「{got}」不是受支持的比例,只能是 {}。", GENERATE_IMAGE_ASPECT_RATIOS.join("、") @@ -100,6 +112,9 @@ impl ToolFailure for GenerateImageError { Self::ImageSizeHasControlCharacters => { "生成图片失败:imageSize 含有控制字符。".to_string() } + Self::ImageSizeNotText { got } => format!( + "生成图片失败:imageSize 必须是字符串,当前是「{got}」。" + ), Self::ImageSizeUnsupported { got } => format!( "生成图片失败:imageSize「{got}」不是受支持的图片尺寸,只能是 {}。", GENERATE_IMAGE_IMAGE_SIZES.join("、") @@ -112,6 +127,9 @@ impl ToolFailure for GenerateImageError { Self::AssetNameHasControlCharacters => { "生成图片失败:assetName 含有控制字符。".to_string() } + Self::AssetNameNotText { got } => format!( + "生成图片失败:assetName 必须是字符串,当前是「{got}」。" + ), Self::OutputPathEmpty => "生成图片失败:outputPath 传了空值。".to_string(), Self::OutputPathTooLong { got_chars, @@ -120,6 +138,9 @@ impl ToolFailure for GenerateImageError { Self::OutputPathHasControlCharacters => { "生成图片失败:outputPath 含有控制字符。".to_string() } + Self::OutputPathNotText { got } => format!( + "生成图片失败:outputPath 必须是字符串,当前是「{got}」。" + ), Self::SliceModeEmpty => "生成图片失败:sliceMode 传了空值。".to_string(), Self::SliceModeTooLong { got_chars, @@ -128,6 +149,9 @@ impl ToolFailure for GenerateImageError { Self::SliceModeHasControlCharacters => { "生成图片失败:sliceMode 含有控制字符。".to_string() } + Self::SliceModeNotText { got } => format!( + "生成图片失败:sliceMode 必须是字符串,当前是「{got}」。" + ), Self::SliceModeUnsupported { got } => format!( "生成图片失败:sliceMode「{got}」不受支持,只允许 {}。", GENERATE_IMAGE_SLICE_MODES.join("、") diff --git a/apps/ai-game-creator-shell/src-tauri/src/agent/tool/list_account_assets/error.rs b/apps/ai-game-creator-shell/src-tauri/src/agent/tool/list_account_assets/error.rs index 1fe0e25de..be6003b67 100644 --- a/apps/ai-game-creator-shell/src-tauri/src/agent/tool/list_account_assets/error.rs +++ b/apps/ai-game-creator-shell/src-tauri/src/agent/tool/list_account_assets/error.rs @@ -16,9 +16,11 @@ pub(crate) enum ListAccountAssetsError { FolderIdEmpty, FolderIdTooLong { got_chars: usize, max_chars: usize }, FolderIdHasControlCharacters, + FolderIdNotText { got: String }, QueryEmpty, QueryTooLong { got_chars: usize, max_chars: usize }, QueryHasControlCharacters, + QueryNotText { got: String }, AccountAssetsUnreadable { cause: String }, } @@ -56,12 +58,18 @@ impl ToolFailure for ListAccountAssetsError { Self::FolderIdHasControlCharacters => { "列出账号素材失败:folderId 含有控制字符。".to_string() } + Self::FolderIdNotText { got } => { + format!("列出账号素材失败:folderId 必须是字符串,当前是「{got}」。") + } Self::QueryEmpty => "列出账号素材失败:query 传了空值。".to_string(), Self::QueryTooLong { got_chars, max_chars, } => format!("列出账号素材失败:query 有 {got_chars} 个字符,超过上限 {max_chars}。"), Self::QueryHasControlCharacters => "列出账号素材失败:query 含有控制字符。".to_string(), + Self::QueryNotText { got } => { + format!("列出账号素材失败:query 必须是字符串,当前是「{got}」。") + } Self::AccountAssetsUnreadable { cause } => { format!("列出账号素材失败:读取账号素材列表失败:{cause}") } diff --git a/apps/ai-game-creator-shell/src-tauri/src/agent/tool/list_project_files/error.rs b/apps/ai-game-creator-shell/src-tauri/src/agent/tool/list_project_files/error.rs index 21aafce66..5e32b4c3a 100644 --- a/apps/ai-game-creator-shell/src-tauri/src/agent/tool/list_project_files/error.rs +++ b/apps/ai-game-creator-shell/src-tauri/src/agent/tool/list_project_files/error.rs @@ -22,15 +22,18 @@ pub(crate) enum ListProjectFilesError { PathEmpty, PathTooLong { got_chars: usize, max_chars: usize }, PathHasControlCharacters, + PathNotText { got: String }, PathNotReadable { cause: String }, PathProtectedProjectControl, PathProtectedExternal, QueryEmpty, QueryTooLong { got_chars: usize, max_chars: usize }, QueryHasControlCharacters, + QueryNotText { got: String }, KindEmpty, KindTooLong { got_chars: usize, max_chars: usize }, KindHasControlCharacters, + KindNotText { got: String }, KindUnsupported { got: String }, ProjectListingUnreadable { cause: String }, ExternalListingUnreadable { cause: String }, @@ -73,6 +76,9 @@ impl ToolFailure for ListProjectFilesError { max_chars, } => format!("列出项目文件失败:path 有 {got_chars} 个字符,超过上限 {max_chars}。"), Self::PathHasControlCharacters => "列出项目文件失败:path 含有控制字符。".to_string(), + Self::PathNotText { got } => { + format!("列出项目文件失败:path 必须是字符串,当前是「{got}」。") + } Self::PathNotReadable { cause } => { format!("列出项目文件失败:path 读不出来:{cause}") } @@ -88,12 +94,18 @@ impl ToolFailure for ListProjectFilesError { max_chars, } => format!("列出项目文件失败:query 有 {got_chars} 个字符,超过上限 {max_chars}。"), Self::QueryHasControlCharacters => "列出项目文件失败:query 含有控制字符。".to_string(), + Self::QueryNotText { got } => { + format!("列出项目文件失败:query 必须是字符串,当前是「{got}」。") + } Self::KindEmpty => "列出项目文件失败:kind 传了空值。".to_string(), Self::KindTooLong { got_chars, max_chars, } => format!("列出项目文件失败:kind 有 {got_chars} 个字符,超过上限 {max_chars}。"), Self::KindHasControlCharacters => "列出项目文件失败:kind 含有控制字符。".to_string(), + Self::KindNotText { got } => { + format!("列出项目文件失败:kind 必须是字符串,当前是「{got}」。") + } Self::KindUnsupported { got } => format!( "列出项目文件失败:kind「{got}」不是受支持的项目文件类别,只允许 {}。", LIST_PROJECT_FILES_KINDS.join("、") diff --git a/apps/ai-game-creator-shell/src-tauri/src/agent/tool/list_registered_assets/error.rs b/apps/ai-game-creator-shell/src-tauri/src/agent/tool/list_registered_assets/error.rs index 8809e3048..3f8ae43bf 100644 --- a/apps/ai-game-creator-shell/src-tauri/src/agent/tool/list_registered_assets/error.rs +++ b/apps/ai-game-creator-shell/src-tauri/src/agent/tool/list_registered_assets/error.rs @@ -18,10 +18,12 @@ pub(crate) enum ListRegisteredAssetsError { KindEmpty, KindTooLong { got_chars: usize, max_chars: usize }, KindHasControlCharacters, + KindNotText { got: String }, KindNotManifestClass { got: String }, AssetIdEmpty, AssetIdTooLong { got_chars: usize, max_chars: usize }, AssetIdHasControlCharacters, + AssetIdNotText { got: String }, IncludeSequenceFramesNotBoolean { got: String }, ProjectPathUnprojectable, PendingEditsUnreadable { cause: String }, @@ -66,6 +68,9 @@ impl ToolFailure for ListRegisteredAssetsError { Self::KindHasControlCharacters => { "列出已登记资源失败:kind 含有控制字符。".to_string() } + Self::KindNotText { got } => format!( + "列出已登记资源失败:kind 必须是字符串,当前是「{got}」。" + ), Self::KindNotManifestClass { got } => format!( "列出已登记资源失败:kind「{got}」不是已知的 manifest 资源 kind;请改用 canonical kind。" ), @@ -79,6 +84,9 @@ impl ToolFailure for ListRegisteredAssetsError { Self::AssetIdHasControlCharacters => { "列出已登记资源失败:assetId 含有控制字符。".to_string() } + Self::AssetIdNotText { got } => format!( + "列出已登记资源失败:assetId 必须是字符串,当前是「{got}」。" + ), Self::IncludeSequenceFramesNotBoolean { got } => format!( "列出已登记资源失败:includeSequenceFrames 必须是布尔值,当前是「{got}」。" ), diff --git a/apps/ai-game-creator-shell/src-tauri/src/agent/tool/prepare_game_art/error.rs b/apps/ai-game-creator-shell/src-tauri/src/agent/tool/prepare_game_art/error.rs index 28260ca24..57ea56ed1 100644 --- a/apps/ai-game-creator-shell/src-tauri/src/agent/tool/prepare_game_art/error.rs +++ b/apps/ai-game-creator-shell/src-tauri/src/agent/tool/prepare_game_art/error.rs @@ -47,6 +47,7 @@ pub(crate) enum PrepareGameArtError { BriefEmpty, BriefTooLong { got_chars: usize, max_chars: usize }, BriefHasControlCharacters, + BriefNotText { got: String }, ModeNotText { got: String }, ModeUnsupported { got: String }, RegenerationGateClosed { cause: String }, @@ -86,6 +87,9 @@ impl ToolFailure for PrepareGameArtError { Self::BriefHasControlCharacters => { "准备美术资产失败:brief 含有控制字符。".to_string() } + Self::BriefNotText { got } => format!( + "准备美术资产失败:brief 必须是字符串,当前是「{got}」。" + ), Self::ModeNotText { got } => format!( "准备美术资产失败:mode 必须是字符串,当前是「{got}」。" ), diff --git a/apps/ai-game-creator-shell/src-tauri/src/agent/tool/remove_background/error.rs b/apps/ai-game-creator-shell/src-tauri/src/agent/tool/remove_background/error.rs index b13b71c64..d70da55a2 100644 --- a/apps/ai-game-creator-shell/src-tauri/src/agent/tool/remove_background/error.rs +++ b/apps/ai-game-creator-shell/src-tauri/src/agent/tool/remove_background/error.rs @@ -18,9 +18,11 @@ pub(crate) enum RemoveBackgroundError { SourceLocalAssetIdEmpty, SourceLocalAssetIdTooLong { got_chars: usize, max_chars: usize }, SourceLocalAssetIdHasControlCharacters, + SourceLocalAssetIdNotText { got: String }, AssetNameEmpty, AssetNameTooLong { got_chars: usize, max_chars: usize }, AssetNameHasControlCharacters, + AssetNameNotText { got: String }, BackgroundModeNotText { got: String }, BackgroundModeUnsupported { got: String }, ScreenColorNotText { got: String }, @@ -81,6 +83,9 @@ impl ToolFailure for RemoveBackgroundError { Self::SourceLocalAssetIdHasControlCharacters => { "抠图失败:sourceLocalAssetId 含有控制字符。".to_string() } + Self::SourceLocalAssetIdNotText { got } => format!( + "抠图失败:sourceLocalAssetId 必须是字符串,当前是「{got}」。" + ), Self::AssetNameEmpty => "抠图失败:assetName 不能为空。".to_string(), Self::AssetNameTooLong { got_chars, @@ -89,6 +94,9 @@ impl ToolFailure for RemoveBackgroundError { Self::AssetNameHasControlCharacters => { "抠图失败:assetName 含有控制字符。".to_string() } + Self::AssetNameNotText { got } => { + format!("抠图失败:assetName 必须是字符串,当前是「{got}」。") + } Self::BackgroundModeNotText { got } => format!( "抠图失败:backgroundMode 必须是 complex 或 flat,当前是「{got}」。" ), diff --git a/apps/ai-game-creator-shell/src-tauri/src/agent/tool/web_search/error.rs b/apps/ai-game-creator-shell/src-tauri/src/agent/tool/web_search/error.rs index 8296586e9..8abf08c0e 100644 --- a/apps/ai-game-creator-shell/src-tauri/src/agent/tool/web_search/error.rs +++ b/apps/ai-game-creator-shell/src-tauri/src/agent/tool/web_search/error.rs @@ -13,6 +13,7 @@ pub(crate) enum WebSearchError { QueryEmpty, QueryTooLong { got_chars: usize, max_chars: usize }, QueryHasControlCharacters, + QueryNotText { got: String }, MaxResultsNotInteger { got: String, max: u64 }, MaxResultsOutOfRange { got: u64, max: u64 }, ClientUnavailable { cause: String }, @@ -47,6 +48,9 @@ impl ToolFailure for WebSearchError { max_chars, } => format!("联网搜索失败:query 有 {got_chars} 个字符,超过上限 {max_chars}。"), Self::QueryHasControlCharacters => "联网搜索失败:query 含有控制字符。".to_string(), + Self::QueryNotText { got } => { + format!("联网搜索失败:query 必须是字符串,当前是「{got}」。") + } Self::MaxResultsNotInteger { got, max } => { format!("联网搜索失败:maxResults 必须是 1 到 {max} 的整数,当前是「{got}」。") }