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 bbf31cf21..510f1e309 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 @@ -2711,6 +2711,17 @@ async fn bridge_prepare_game_art( Ok(result) } +/// `agc_generate_image` 的受支持取值与边界:解析校验与失败文案共用同一份, +/// 避免两边各写一遍、改动时静默漂移。 +pub(in crate::agent) const GENERATE_IMAGE_ASPECT_RATIOS: &[&str] = + &["1:1", "2:3", "3:2", "9:16", "16:9"]; +pub(in crate::agent) const GENERATE_IMAGE_IMAGE_SIZES: &[&str] = &["0.5K", "1K", "2K"]; +pub(in crate::agent) const GENERATE_IMAGE_SLICE_MODES: &[&str] = &["connected-components", "grid"]; +pub(in crate::agent) const GENERATE_IMAGE_GRID_AXIS_MIN: u32 = 1; +pub(in crate::agent) const GENERATE_IMAGE_GRID_AXIS_MAX: u32 = 32; +pub(in crate::agent) const GENERATE_IMAGE_SLICE_COUNT_MIN: u64 = 1; +pub(in crate::agent) const GENERATE_IMAGE_SLICE_COUNT_MAX: u64 = 256; + /// `agc_generate_image` 允许的入参字段。 const GENERATE_IMAGE_ARGUMENTS: &[&str] = &[ "prompt", @@ -2962,15 +2973,12 @@ pub(in crate::agent) fn generate_image_input( let aspect_ratio = generate_image_optional_text(arguments, GenerateImageTextField::AspectRatio)? .unwrap_or_else(|| "1:1".to_string()); - if !matches!( - aspect_ratio.as_str(), - "1:1" | "2:3" | "3:2" | "9:16" | "16:9" - ) { + 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)? .unwrap_or_else(|| "1K".to_string()); - if !matches!(image_size.as_str(), "0.5K" | "1K" | "2K") { + 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)? @@ -2979,7 +2987,7 @@ pub(in crate::agent) fn generate_image_input( let slice_mode = generate_image_optional_text(arguments, GenerateImageTextField::SliceMode)?; if slice_mode .as_deref() - .is_some_and(|mode| !matches!(mode, "connected-components" | "grid")) + .is_some_and(|mode| !GENERATE_IMAGE_SLICE_MODES.contains(&mode)) { return Err(GenerateImageError::SliceModeUnsupported { got: slice_mode.unwrap_or_default(), @@ -2991,7 +2999,9 @@ pub(in crate::agent) fn generate_image_input( return Err(GenerateImageError::GridAxesMissing); } for (axis, value) in [("gridX", grid_x), ("gridY", grid_y)] { - if let Some(got) = value.filter(|value| !(1..=32).contains(value)) { + if let Some(got) = value.filter(|value| { + !(GENERATE_IMAGE_GRID_AXIS_MIN..=GENERATE_IMAGE_GRID_AXIS_MAX).contains(value) + }) { return Err(GenerateImageError::GridOutOfRange { axis, got }); } } @@ -3001,7 +3011,10 @@ pub(in crate::agent) fn generate_image_input( .map(|value| { value .as_u64() - .filter(|count| (1..=256).contains(count)) + .filter(|count| { + (GENERATE_IMAGE_SLICE_COUNT_MIN..=GENERATE_IMAGE_SLICE_COUNT_MAX) + .contains(count) + }) .map(|count| count as usize) .ok_or_else(|| GenerateImageError::SliceCountInvalid { got: value.to_string(), 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 62f5125d7..542a86e08 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 @@ -2,6 +2,11 @@ //! //! 这里只放错误本身;参数解析与工具执行留在 `direct_tool_bridge`。 +use crate::agent::direct_tool_bridge::{ + GENERATE_IMAGE_ASPECT_RATIOS, GENERATE_IMAGE_GRID_AXIS_MAX, GENERATE_IMAGE_GRID_AXIS_MIN, + GENERATE_IMAGE_IMAGE_SIZES, GENERATE_IMAGE_SLICE_COUNT_MAX, GENERATE_IMAGE_SLICE_COUNT_MIN, + GENERATE_IMAGE_SLICE_MODES, +}; use crate::agent::generation::PLATFORM_ART_ASSET_GENERATION_KINDS; use crate::agent::tool::error::{ProjectPermissionRejection, ToolArgumentsRejection, ToolFailure}; use shared_contracts::game_creation_app::GameCreationAppAssetKind; @@ -84,7 +89,8 @@ impl ToolFailure for GenerateImageError { "生成图片失败:aspectRatio 含有控制字符。".to_string() } Self::AspectRatioUnsupported { got } => format!( - "生成图片失败:aspectRatio「{got}」不是受支持的比例,只能是 1:1、2:3、3:2、9:16、16:9。" + "生成图片失败:aspectRatio「{got}」不是受支持的比例,只能是 {}。", + GENERATE_IMAGE_ASPECT_RATIOS.join("、") ), Self::ImageSizeEmpty => "生成图片失败:imageSize 传了空值。".to_string(), Self::ImageSizeTooLong { @@ -95,7 +101,8 @@ impl ToolFailure for GenerateImageError { "生成图片失败:imageSize 含有控制字符。".to_string() } Self::ImageSizeUnsupported { got } => format!( - "生成图片失败:imageSize「{got}」不是受支持的图片尺寸,只能是 0.5K、1K、2K。" + "生成图片失败:imageSize「{got}」不是受支持的图片尺寸,只能是 {}。", + GENERATE_IMAGE_IMAGE_SIZES.join("、") ), Self::AssetNameEmpty => "生成图片失败:assetName 传了空值。".to_string(), Self::AssetNameTooLong { @@ -122,7 +129,8 @@ impl ToolFailure for GenerateImageError { "生成图片失败:sliceMode 含有控制字符。".to_string() } Self::SliceModeUnsupported { got } => format!( - "生成图片失败:sliceMode「{got}」不受支持,只允许 connected-components 或 grid。" + "生成图片失败:sliceMode「{got}」不受支持,只允许 {}。", + GENERATE_IMAGE_SLICE_MODES.join("、") ), Self::GridAxisNotInteger { axis } => { format!("生成图片失败:{axis} 必须是整数。") @@ -131,10 +139,14 @@ impl ToolFailure for GenerateImageError { "生成图片失败:sliceMode=grid 必须同时提供 gridX 与 gridY。".to_string() } Self::GridOutOfRange { axis, got } => { - format!("生成图片失败:{axis}={got} 超出范围,gridX/gridY 必须在 1 到 32 之间。") + format!( + "生成图片失败:{axis}={got} 超出范围,gridX/gridY 必须在 {GENERATE_IMAGE_GRID_AXIS_MIN} 到 {GENERATE_IMAGE_GRID_AXIS_MAX} 之间。" + ) } Self::SliceCountInvalid { got } => { - format!("生成图片失败:sliceCount「{got}」非法,必须是 1 到 256 的整数。") + format!( + "生成图片失败:sliceCount「{got}」非法,必须是 {GENERATE_IMAGE_SLICE_COUNT_MIN} 到 {GENERATE_IMAGE_SLICE_COUNT_MAX} 的整数。" + ) } Self::SpritesheetSliceModeMissing => format!( "生成图片失败:kind={} 必须显式声明 sliceMode,没有默认值:需求要求等分网格、固定槽位或指定行列数时传 sliceMode=grid 并提供 gridX/gridY;自由排布、数量不定或只要求一张图集时传 sliceMode=connected-components。",