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}」。") }