From 0d21f838ed0f873b015f66c773992fb1cbd227f4 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 14:35:53 +0800 Subject: [PATCH] =?UTF-8?q?=E7=94=9F=E6=88=90=E5=9B=BE=E7=89=87=E7=9A=84?= =?UTF-8?q?=E5=85=81=E8=AE=B8=E5=80=BC=E4=B8=8E=E8=BE=B9=E7=95=8C=E6=8F=90?= =?UTF-8?q?=E6=88=90=E5=B8=B8=E9=87=8F=EF=BC=8C=E6=A0=A1=E9=AA=8C=E4=B8=8E?= =?UTF-8?q?=E8=AF=8A=E6=96=AD=E6=96=87=E6=A1=88=E5=85=B1=E7=94=A8=E5=90=8C?= =?UTF-8?q?=E4=B8=80=E4=BB=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 GENERATE_IMAGE_ASPECT_RATIOS / _IMAGE_SIZES / _SLICE_MODES 与 grid 轴、sliceCount 的 MIN/MAX 常量,放在 direct_tool_bridge 的 GENERATE_IMAGE_ARGUMENTS 旁 - generate_image_input 的 aspectRatio / imageSize / sliceMode / grid 轴区间 / sliceCount 区间校验改为读常量;generate_image/error.rs 的对应文案也改为读同一批常量(KindUnsupported 早已从 PLATFORM_ART_ASSET_GENERATION_KINDS 派生,这次把其余几组对齐) - 只改取值来源,判定范围与文案取值不变 - 未纳入本次:commands.rs 的 LOCAL_PROJECT_ASSET_ASPECT_RATIOS/_IMAGE_SIZES 与 agent_native_tools.rs 的 canvas.asset_generate schema 仍是各自的副本(面向非 agent 的栏目画布入口),统一它们属于另一次改动 验证:cargo test --bin genarrative-ai-game-creator-shell -- agent::direct_tool_bridge:: agent::direct_tools_mcp:: 71 passed --- .../src-tauri/src/agent/direct_tool_bridge.rs | 29 ++++++++++++++----- .../src/agent/tool/generate_image/error.rs | 22 ++++++++++---- 2 files changed, 38 insertions(+), 13 deletions(-) diff --git a/apps/ai-game-creator-shell/src-tauri/src/agent/direct_tool_bridge.rs b/apps/ai-game-creator-shell/src-tauri/src/agent/direct_tool_bridge.rs index 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。",