入参给了非字符串值时按字段报类型错,不再混进「不能为空」
- 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,单独重跑通过)
This commit is contained in:
@@ -928,6 +928,18 @@ impl ToolTextField<CreateOrDeriveResourceError> 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<ListRegisteredAssetsError> 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<ListProjectFilesError> 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<ListAccountAssetsError> 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<RemoveBackgroundError> 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<PrepareGameArtError> 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<GenerateImageError> 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<Option<String>, 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<String, GenerateImageError> {
|
||||
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<GenerateImageInput, GenerateImageError> {
|
||||
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<EditImageError> 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<WebSearchError> 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| {
|
||||
|
||||
@@ -14,6 +14,8 @@ pub(crate) trait ToolTextField<E>: 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<F: ToolTextField<E>, 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()));
|
||||
}
|
||||
|
||||
+30
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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("、")
|
||||
|
||||
@@ -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}")
|
||||
}
|
||||
|
||||
@@ -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("、")
|
||||
|
||||
@@ -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}」。"
|
||||
),
|
||||
|
||||
@@ -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}」。"
|
||||
),
|
||||
|
||||
@@ -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}」。"
|
||||
),
|
||||
|
||||
@@ -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}」。")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user