新增图集连通域与可配置网格切分
Project CI / AI game creator shell Rust shard 1/4 (push) Failing after 4m37s
Project CI / AI game creator shell Rust shard 2/4 (push) Failing after 4m36s
Project CI / AI game creator shell Rust smoke (push) Successful in 1m31s
Project CI / Backend tests (push) Failing after 10s
Project CI / AI game creator shell Rust shard 4/4 (push) Failing after 4m2s
Project CI / AI game creator shell Rust shard 3/4 (push) Failing after 4m45s
Project CI / Repository checks (push) Failing after 11s
Project CI / AI game creator shell web tests (push) Failing after 1m18s
Project CI / AI game creator shell Rust crates (push) Successful in 2m36s
Project CI / Native shell tests (push) Failing after 2m34s
Project CI / Frontend tests (push) Successful in 4m52s
Project CI / AI game creator shell Rust shard 1/4 (push) Failing after 4m37s
Project CI / AI game creator shell Rust shard 2/4 (push) Failing after 4m36s
Project CI / AI game creator shell Rust smoke (push) Successful in 1m31s
Project CI / Backend tests (push) Failing after 10s
Project CI / AI game creator shell Rust shard 4/4 (push) Failing after 4m2s
Project CI / AI game creator shell Rust shard 3/4 (push) Failing after 4m45s
Project CI / Repository checks (push) Failing after 11s
Project CI / AI game creator shell web tests (push) Failing after 1m18s
Project CI / AI game creator shell Rust crates (push) Successful in 2m36s
Project CI / Native shell tests (push) Failing after 2m34s
Project CI / Frontend tests (push) Successful in 4m52s
增加 connected-components 与 grid 切分模式 支持 gridX/gridY 并同步 API、MCP、Skill、AGC 客户端 移除固定 2x2 图集切分契约与文档
This commit is contained in:
@@ -19,6 +19,12 @@ image, UI design image, or publication material; use `agc_edit_image` for an
|
||||
edit of an existing registered image; use `taonier_prepare_game_art` only for
|
||||
the complete game-art package and its canonical slices.
|
||||
|
||||
When `agc_generate_image` is used with `kind="art-spritesheet"`, pass
|
||||
`sliceMode="connected-components"` (the default alpha-connectivity splitter)
|
||||
or `sliceMode="grid"` with `gridX` and `gridY` (1-32 each). The selected mode is carried
|
||||
through the client request and returned result; do not infer it from the number
|
||||
of slices.
|
||||
|
||||
## Authorization boundary
|
||||
|
||||
`agc_tools` is an AGC client-owned bridge to the AGC backend. In the normal client build it uses the current client login session and account routes; the user and model never need to provide, configure, paste, create, or rotate an API Key, Token, Cookie, URL, or `.env` value. If the tool returns `401` or `403`, report only that the AGC client login or permission state is unavailable, stop the operation, and do not ask the user for credentials or expose an internal URL.
|
||||
|
||||
+1
@@ -15,6 +15,7 @@
|
||||
- On timeout or uncertain delivery, reuse the recorded operation; never create a replacement request.
|
||||
- `postprocess-failed-source-preserved` means the complete provider source remains usable, but the requested transparent derivative is absent.
|
||||
- `sliceWarning` means the complete transparent sheet remains usable, but individual slices are absent.
|
||||
- For direct `agc_generate_image` spritesheet requests, `sliceMode="connected-components"` selects alpha-connectivity detection and `sliceMode="grid"` uses the caller-provided `gridX` and `gridY` (1-32 each). The client preserves the selected mode and grid dimensions in the request identity and result metadata.
|
||||
- General and slice warnings can coexist. The tool returns them separately through `warnings` and `sliceWarnings`; callers must preserve every entry and must not downgrade a slice warning into a successful independent-asset claim.
|
||||
- `assetPaths` contains the complete package paths. `slicePaths` contains only slices that the client downloaded, validated, and registered with their platform source identities.
|
||||
- `resources` contains only safe registered identity fields: local asset/path/kind/media type, Canvas project/resource/asset/task IDs, and reference resource IDs. It never exposes prompts, models, provider routes, absolute paths, URLs, tokens, cookies, or API keys.
|
||||
|
||||
@@ -549,6 +549,7 @@ fn extract_mcp_arguments(root: &Path, tool: &str, arguments: &Value) -> Value {
|
||||
}
|
||||
"agc_generate_image" => {
|
||||
copy_string(object, "kind", &mut out);
|
||||
copy_string(object, "sliceMode", &mut out);
|
||||
copy_string(object, "aspectRatio", &mut out);
|
||||
copy_string(object, "imageSize", &mut out);
|
||||
copy_string(object, "assetName", &mut out);
|
||||
|
||||
@@ -2783,6 +2783,9 @@ async fn generate_direct_taonier_art_asset_at(
|
||||
asset_label: asset_label.to_string(),
|
||||
replace_existing: root.join(output_path).is_file(),
|
||||
slice_count: None,
|
||||
slice_mode: None,
|
||||
grid_x: None,
|
||||
grid_y: None,
|
||||
};
|
||||
let runtime_context =
|
||||
direct_taonier_art_generation_runtime_context(root, output_path, asset_kind)?;
|
||||
|
||||
@@ -2104,6 +2104,9 @@ async fn bridge_generate_image(state: &DirectToolBridgeState, arguments: &Value)
|
||||
"imageSize",
|
||||
"assetName",
|
||||
"outputPath",
|
||||
"sliceMode",
|
||||
"gridX",
|
||||
"gridY",
|
||||
],
|
||||
)?;
|
||||
enforce_project_permission_policy(&state.root, "canvas.asset_generate")?;
|
||||
@@ -2145,6 +2148,44 @@ async fn bridge_generate_image(state: &DirectToolBridgeState, arguments: &Value)
|
||||
.transpose()?
|
||||
.unwrap_or_else(|| "AI 生成图片".to_string());
|
||||
let output_path = bridge_optional_bounded_string(arguments, "outputPath", 512)?;
|
||||
let slice_mode = arguments
|
||||
.get("sliceMode")
|
||||
.map(|_| bridge_bounded_string(arguments, "sliceMode", 32))
|
||||
.transpose()?;
|
||||
if slice_mode
|
||||
.as_deref()
|
||||
.is_some_and(|mode| !matches!(mode, "connected-components" | "grid"))
|
||||
{
|
||||
return Err("工具参数 sliceMode 只允许 connected-components 或 grid".to_string());
|
||||
}
|
||||
let grid_x = arguments
|
||||
.get("gridX")
|
||||
.map(|_| {
|
||||
arguments
|
||||
.get("gridX")
|
||||
.and_then(Value::as_u64)
|
||||
.map(|value| value as u32)
|
||||
.ok_or_else(|| "工具参数 gridX 必须是整数".to_string())
|
||||
})
|
||||
.transpose()?;
|
||||
let grid_y = arguments
|
||||
.get("gridY")
|
||||
.map(|_| {
|
||||
arguments
|
||||
.get("gridY")
|
||||
.and_then(Value::as_u64)
|
||||
.map(|value| value as u32)
|
||||
.ok_or_else(|| "工具参数 gridY 必须是整数".to_string())
|
||||
})
|
||||
.transpose()?;
|
||||
if slice_mode.as_deref() == Some("grid") && (grid_x.is_none() || grid_y.is_none()) {
|
||||
return Err("grid 模式必须同时提供 gridX 与 gridY".to_string());
|
||||
}
|
||||
if grid_x.is_some_and(|value| !(1..=32).contains(&value))
|
||||
|| grid_y.is_some_and(|value| !(1..=32).contains(&value))
|
||||
{
|
||||
return Err("工具参数 gridX/gridY 必须在 1 到 32 之间".to_string());
|
||||
}
|
||||
let options = PlatformArtAssetGenerationOptions {
|
||||
output_path,
|
||||
aspect_ratio,
|
||||
@@ -2153,6 +2194,9 @@ async fn bridge_generate_image(state: &DirectToolBridgeState, arguments: &Value)
|
||||
asset_label: asset_name.clone(),
|
||||
replace_existing: false,
|
||||
slice_count: None,
|
||||
slice_mode,
|
||||
grid_x,
|
||||
grid_y,
|
||||
};
|
||||
let _generation_guard = state.image_generation_gate.lock().await;
|
||||
let generated = with_direct_editor_api_credentials(
|
||||
|
||||
@@ -244,6 +244,24 @@ fn direct_tools_mcp_specs_for(controlled_web_search: bool, _cocos_editor_availab
|
||||
"type": "string",
|
||||
"maxLength": 512,
|
||||
"description": "可选项目相对输出路径,必须位于 assets/ 且不能覆盖已有文件"
|
||||
},
|
||||
"sliceMode": {
|
||||
"type": "string",
|
||||
"enum": ["connected-components", "grid"],
|
||||
"default": "connected-components",
|
||||
"description": "仅 kind=art-spritesheet 生效:connected-components 按透明像素连通域切分,grid 按 gridX×gridY 网格切分"
|
||||
},
|
||||
"gridX": {
|
||||
"type": "integer",
|
||||
"minimum": 1,
|
||||
"maximum": 32,
|
||||
"description": "grid 模式横向网格数量"
|
||||
},
|
||||
"gridY": {
|
||||
"type": "integer",
|
||||
"minimum": 1,
|
||||
"maximum": 32,
|
||||
"description": "grid 模式纵向网格数量"
|
||||
}
|
||||
},
|
||||
"required": ["prompt"],
|
||||
@@ -1014,6 +1032,9 @@ async fn call_agc_generate_image(arguments: &Value) -> Value {
|
||||
"imageSize",
|
||||
"assetName",
|
||||
"outputPath",
|
||||
"sliceMode",
|
||||
"gridX",
|
||||
"gridY",
|
||||
],
|
||||
) {
|
||||
return mcp_tool_result(error, Vec::new(), true);
|
||||
@@ -1041,6 +1062,7 @@ async fn call_agc_generate_image(arguments: &Value) -> Value {
|
||||
("imageSize", 4),
|
||||
("assetName", DIRECT_TOOLS_MCP_MAX_RESOURCE_NAME_CHARS),
|
||||
("outputPath", 512),
|
||||
("sliceMode", 32),
|
||||
] {
|
||||
if arguments.get(field).is_some() {
|
||||
if let Err(error) = bounded_tool_string(arguments, field, max_chars) {
|
||||
@@ -2226,6 +2248,10 @@ mod tests {
|
||||
assert!(image_tool["description"]
|
||||
.as_str()
|
||||
.is_some_and(|description| description.contains("不是本工具的限制")));
|
||||
assert_eq!(
|
||||
image_tool["inputSchema"]["properties"]["sliceMode"]["enum"],
|
||||
json!(["connected-components", "grid"])
|
||||
);
|
||||
let edit_tool = specs["tools"]
|
||||
.as_array()
|
||||
.expect("tool array")
|
||||
|
||||
@@ -413,6 +413,9 @@ pub(crate) struct PlatformArtAssetGenerationOptions {
|
||||
pub(crate) asset_label: String,
|
||||
pub(crate) replace_existing: bool,
|
||||
pub(crate) slice_count: Option<usize>,
|
||||
pub(crate) slice_mode: Option<String>,
|
||||
pub(crate) grid_x: Option<u32>,
|
||||
pub(crate) grid_y: Option<u32>,
|
||||
}
|
||||
|
||||
impl Default for PlatformArtAssetGenerationOptions {
|
||||
@@ -425,6 +428,9 @@ impl Default for PlatformArtAssetGenerationOptions {
|
||||
asset_label: "AI 游戏首版美术素材".to_string(),
|
||||
replace_existing: false,
|
||||
slice_count: None,
|
||||
slice_mode: None,
|
||||
grid_x: None,
|
||||
grid_y: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1574,7 +1580,7 @@ pub(in crate::agent) struct PreparedPlatformArtAssetGeneration {
|
||||
warning: Option<String>,
|
||||
slice_warning: Option<String>,
|
||||
slices: Vec<PreparedPlatformArtAssetSlice>,
|
||||
spritesheet_slice_layout: Option<String>,
|
||||
spritesheet_slice_mode: Option<String>,
|
||||
generation_route: String,
|
||||
generation_kind: String,
|
||||
reference_resource_ids: Vec<String>,
|
||||
@@ -2213,11 +2219,8 @@ pub(crate) async fn generate_platform_art_asset_with_required_slices_at(
|
||||
/// 而任何输入不同(提示词、输出路径、比例、尺寸、类型、标签、严格切片)都是另一个
|
||||
/// 动作,必须各自独立成槽,才能在同一项目里同时在途。
|
||||
///
|
||||
/// **字段集合与取值方式必须与升级前逐字节一致**:升级前遗留账本里持久化的
|
||||
/// `actionFingerprint` 就是这个材料的历史哈希,改动材料会让旧账本无法按精确动作被
|
||||
/// 识别与迁移(见 `adopt_legacy_standalone_platform_art_generation_runtime_state_at`)。
|
||||
/// 已知边界:`slice_count` 不进身份(与升级前一致),仅切片数不同的两条图集请求仍落到
|
||||
/// 同一槽,第二条在账本请求正文校验处失败关闭,不会二次 POST。
|
||||
/// 升级前遗留账本仍由旧材料函数定位;新请求把显式切分模式纳入身份,避免同一图集
|
||||
/// 请求在网格与连通域之间误复用。`slice_count` 继续保持历史兼容语义,不进身份。
|
||||
#[derive(Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct StandalonePlatformArtGenerationFingerprintMaterial<'a> {
|
||||
@@ -2229,6 +2232,9 @@ struct StandalonePlatformArtGenerationFingerprintMaterial<'a> {
|
||||
asset_label: &'a str,
|
||||
replace_existing: bool,
|
||||
require_slices: bool,
|
||||
slice_mode: Option<&'a str>,
|
||||
grid_x: Option<u32>,
|
||||
grid_y: Option<u32>,
|
||||
}
|
||||
|
||||
/// 把输出路径收口成稳定的旧槽材料:空路径与未指定路径都落到 `(automatic-output)`,
|
||||
@@ -2265,6 +2271,9 @@ fn standalone_platform_art_generation_runtime_context(
|
||||
asset_label: &options.asset_label,
|
||||
replace_existing: options.replace_existing,
|
||||
require_slices,
|
||||
slice_mode: options.slice_mode.as_deref(),
|
||||
grid_x: options.grid_x,
|
||||
grid_y: options.grid_y,
|
||||
})
|
||||
.map_err(|error| format!("序列化 standalone 图片生成动作身份失败:{error}"))?;
|
||||
let action_fingerprint = format!("{:x}", Sha256::digest(&identity_bytes));
|
||||
@@ -2811,6 +2820,9 @@ pub(in crate::agent) async fn request_platform_art_asset_with_runtime_options_at
|
||||
"referenceId": reference_id,
|
||||
"iconDescriptions": canonical_art_spritesheet_icon_descriptions(&generation_prompt),
|
||||
"sliceCount": options.slice_count,
|
||||
"sliceMode": options.slice_mode,
|
||||
"gridX": options.grid_x,
|
||||
"gridY": options.grid_y,
|
||||
"screenColor": "auto",
|
||||
"aspectRatio": options.aspect_ratio,
|
||||
"imageSize": options.image_size,
|
||||
@@ -3106,8 +3118,8 @@ pub(in crate::agent) async fn request_platform_art_asset_with_runtime_options_at
|
||||
Vec::new()
|
||||
};
|
||||
let warning = platform_art_generation_warning(generated);
|
||||
let spritesheet_slice_layout = if is_canonical_art_spritesheet {
|
||||
json_string_field(generated, "sliceLayout")
|
||||
let spritesheet_slice_mode = if is_canonical_art_spritesheet {
|
||||
json_string_field(generated, "sliceMode")
|
||||
} else {
|
||||
None
|
||||
};
|
||||
@@ -3168,7 +3180,7 @@ pub(in crate::agent) async fn request_platform_art_asset_with_runtime_options_at
|
||||
warning,
|
||||
slice_warning,
|
||||
slices,
|
||||
spritesheet_slice_layout,
|
||||
spritesheet_slice_mode,
|
||||
generation_route,
|
||||
generation_kind,
|
||||
reference_resource_ids,
|
||||
@@ -6508,7 +6520,7 @@ fn validate_strict_platform_art_spritesheet_contract(
|
||||
task_id: Option<&str>,
|
||||
generation_route: &str,
|
||||
generation_kind: &str,
|
||||
spritesheet_slice_layout: Option<&str>,
|
||||
spritesheet_slice_mode: Option<&str>,
|
||||
reference_resource_ids: &[String],
|
||||
has_transparent_pixels: bool,
|
||||
has_visible_pixels: bool,
|
||||
@@ -6545,7 +6557,7 @@ fn validate_strict_platform_art_spritesheet_contract(
|
||||
{
|
||||
return Err("strict spritesheet 图集生成 route/kind 与严格图集合同不一致".to_string());
|
||||
}
|
||||
let _requested_slice_layout = spritesheet_slice_layout;
|
||||
let _requested_slice_mode = spritesheet_slice_mode;
|
||||
if reference_resource_ids.len() != 1
|
||||
|| reference_resource_ids[0].trim().is_empty()
|
||||
|| reference_resource_ids[0].trim() == resource_id
|
||||
@@ -7307,7 +7319,7 @@ fn commit_prepared_platform_art_asset_with_before_replace_hook(
|
||||
warning,
|
||||
mut slice_warning,
|
||||
slices,
|
||||
spritesheet_slice_layout,
|
||||
spritesheet_slice_mode,
|
||||
generation_route,
|
||||
generation_kind,
|
||||
reference_resource_ids,
|
||||
@@ -7326,7 +7338,7 @@ fn commit_prepared_platform_art_asset_with_before_replace_hook(
|
||||
task_id.as_deref(),
|
||||
&generation_route,
|
||||
&generation_kind,
|
||||
spritesheet_slice_layout.as_deref(),
|
||||
spritesheet_slice_mode.as_deref(),
|
||||
&reference_resource_ids,
|
||||
spritesheet_has_transparent_pixels,
|
||||
spritesheet_has_visible_pixels,
|
||||
@@ -7901,8 +7913,8 @@ mod canvas_generation_tests {
|
||||
let body = serde_json::json!({
|
||||
"error": {
|
||||
"code": "invalid-request",
|
||||
"field": "sliceLayout",
|
||||
"message": "只支持 grid-2x2;operationId=private-operation-id;api_key=private-key",
|
||||
"field": "sliceMode",
|
||||
"message": "只支持 grid;operationId=private-operation-id;api_key=private-key",
|
||||
},
|
||||
"details": {
|
||||
"path": "C:\\Users\\private\\secret.json",
|
||||
@@ -7911,8 +7923,8 @@ mod canvas_generation_tests {
|
||||
.to_string();
|
||||
let summary = summarize_external_http_error_body(&body).expect("summary");
|
||||
assert!(summary.contains("code=invalid-request"), "{summary}");
|
||||
assert!(summary.contains("field=sliceLayout"), "{summary}");
|
||||
assert!(summary.contains("只支持 grid-2x2"), "{summary}");
|
||||
assert!(summary.contains("field=sliceMode"), "{summary}");
|
||||
assert!(summary.contains("只支持 grid"), "{summary}");
|
||||
assert!(!summary.contains("private-operation-id"), "{summary}");
|
||||
assert!(!summary.contains("private-key"), "{summary}");
|
||||
assert!(!summary.contains("C:\\Users\\private"), "{summary}");
|
||||
@@ -8312,6 +8324,9 @@ mod canvas_generation_tests {
|
||||
asset_label: "手工背景".to_string(),
|
||||
replace_existing: true,
|
||||
slice_count: None,
|
||||
slice_mode: None,
|
||||
grid_x: None,
|
||||
grid_y: None,
|
||||
};
|
||||
let ordinary =
|
||||
standalone_platform_art_generation_runtime_context("完整生成提示词", &options, false)
|
||||
@@ -9826,7 +9841,7 @@ mod canvas_generation_tests {
|
||||
Some("spritesheet-task"),
|
||||
"/api/external/v1/editor/icon-spritesheets/generations",
|
||||
"icon-spritesheet",
|
||||
Some("grid-2x2"),
|
||||
Some("grid"),
|
||||
&["art-spec-resource".to_string()],
|
||||
true,
|
||||
true,
|
||||
@@ -10325,6 +10340,9 @@ mod canvas_generation_tests {
|
||||
asset_label: "整包规范图".to_string(),
|
||||
replace_existing: false,
|
||||
slice_count: None,
|
||||
slice_mode: None,
|
||||
grid_x: None,
|
||||
grid_y: None,
|
||||
};
|
||||
let prompt = "生成同一套整包美术";
|
||||
let generation_prompt = build_platform_art_asset_prompt(prompt, &[], &options);
|
||||
@@ -11220,6 +11238,9 @@ mod canvas_generation_tests {
|
||||
asset_label: "整包背景图".to_string(),
|
||||
replace_existing: false,
|
||||
slice_count: None,
|
||||
slice_mode: None,
|
||||
grid_x: None,
|
||||
grid_y: None,
|
||||
};
|
||||
let prompt = "保持同一个生成提示词";
|
||||
let generation_prompt = build_platform_art_asset_prompt(prompt, &[], &options);
|
||||
@@ -11681,6 +11702,9 @@ mod canvas_generation_tests {
|
||||
asset_label: "游戏统一视觉规范图".to_string(),
|
||||
replace_existing: false,
|
||||
slice_count: None,
|
||||
slice_mode: None,
|
||||
grid_x: None,
|
||||
grid_y: None,
|
||||
};
|
||||
let prompt = "恢复已受理视觉规范图";
|
||||
let generation_prompt = build_platform_art_asset_prompt(prompt, &[], &options);
|
||||
@@ -12287,6 +12311,9 @@ mod canvas_generation_tests {
|
||||
asset_label: "游戏首版核心美术素材".to_string(),
|
||||
replace_existing: true,
|
||||
slice_count: None,
|
||||
slice_mode: None,
|
||||
grid_x: None,
|
||||
grid_y: None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12317,7 +12344,7 @@ mod canvas_generation_tests {
|
||||
warning: None,
|
||||
slice_warning: None,
|
||||
slices: Vec::new(),
|
||||
spritesheet_slice_layout: Some("grid-2x2".to_string()),
|
||||
spritesheet_slice_mode: Some("grid".to_string()),
|
||||
generation_route: "/api/external/v1/editor/icon-spritesheets/generations".to_string(),
|
||||
generation_kind: "icon-spritesheet".to_string(),
|
||||
reference_resource_ids: vec!["art-spec-resource".to_string()],
|
||||
@@ -12636,7 +12663,7 @@ mod canvas_generation_tests {
|
||||
warning: None,
|
||||
slice_warning: None,
|
||||
slices,
|
||||
spritesheet_slice_layout: Some("grid-2x2".to_string()),
|
||||
spritesheet_slice_mode: Some("grid".to_string()),
|
||||
generation_route: "/api/external/v1/editor/icon-spritesheets/generations".to_string(),
|
||||
generation_kind: "icon-spritesheet".to_string(),
|
||||
reference_resource_ids: vec!["art-spec-resource".to_string()],
|
||||
|
||||
@@ -555,6 +555,17 @@ pub(in crate::agent) async fn observe_agent_runtime_platform_art_asset_generatio
|
||||
.or_else(|| input.get("slice_count"))
|
||||
.and_then(serde_json::Value::as_u64)
|
||||
.map(|value| value as usize);
|
||||
let slice_mode = agent_runtime_tool_input_text(input, &["sliceMode", "slice_mode"]);
|
||||
let grid_x = input
|
||||
.get("gridX")
|
||||
.or_else(|| input.get("grid_x"))
|
||||
.and_then(serde_json::Value::as_u64)
|
||||
.map(|value| value as u32);
|
||||
let grid_y = input
|
||||
.get("gridY")
|
||||
.or_else(|| input.get("grid_y"))
|
||||
.and_then(serde_json::Value::as_u64)
|
||||
.map(|value| value as u32);
|
||||
let mut requested_options = PlatformArtAssetGenerationOptions {
|
||||
output_path: (!output_path.trim().is_empty()).then_some(output_path),
|
||||
aspect_ratio,
|
||||
@@ -563,6 +574,9 @@ pub(in crate::agent) async fn observe_agent_runtime_platform_art_asset_generatio
|
||||
asset_label,
|
||||
replace_existing,
|
||||
slice_count,
|
||||
slice_mode: (!slice_mode.trim().is_empty()).then_some(slice_mode.clone()),
|
||||
grid_x,
|
||||
grid_y,
|
||||
};
|
||||
if let Some(pending) = pending_action {
|
||||
match recover_persisted_visual_generation_options(
|
||||
@@ -609,6 +623,11 @@ pub(in crate::agent) async fn observe_agent_runtime_platform_art_asset_generatio
|
||||
},
|
||||
replace_existing,
|
||||
slice_count,
|
||||
slice_mode: requested_options
|
||||
.slice_mode
|
||||
.or_else(|| (!slice_mode.trim().is_empty()).then_some(slice_mode)),
|
||||
grid_x,
|
||||
grid_y,
|
||||
}
|
||||
};
|
||||
options.replace_existing = replace_existing;
|
||||
@@ -654,6 +673,28 @@ pub(in crate::agent) async fn observe_agent_runtime_platform_art_asset_generatio
|
||||
detail: None,
|
||||
};
|
||||
}
|
||||
if options
|
||||
.slice_mode
|
||||
.as_deref()
|
||||
.is_some_and(|slice_mode| !matches!(slice_mode, "connected-components" | "grid"))
|
||||
{
|
||||
return AgentRuntimeToolObservation {
|
||||
tool: "canvas.asset_generate".to_string(),
|
||||
status: "failed".to_string(),
|
||||
summary: "图片生成 sliceMode 不受支持".to_string(),
|
||||
detail: None,
|
||||
};
|
||||
}
|
||||
if options.slice_mode.as_deref() == Some("grid")
|
||||
&& (options.grid_x.is_none() || options.grid_y.is_none())
|
||||
{
|
||||
return AgentRuntimeToolObservation {
|
||||
tool: "canvas.asset_generate".to_string(),
|
||||
status: "failed".to_string(),
|
||||
summary: "grid 模式必须同时提供 gridX 与 gridY".to_string(),
|
||||
detail: None,
|
||||
};
|
||||
}
|
||||
if !agent_runtime_canvas_asset_kind_is_supported(&options.asset_kind) {
|
||||
return AgentRuntimeToolObservation {
|
||||
tool: "canvas.asset_generate".to_string(),
|
||||
|
||||
@@ -4594,6 +4594,9 @@ pub(crate) fn prepare_local_project_asset_generation(
|
||||
.unwrap_or_else(|| LOCAL_PROJECT_ASSET_DEFAULT_ASSET_NAME.to_string()),
|
||||
replace_existing: false,
|
||||
slice_count: None,
|
||||
slice_mode: None,
|
||||
grid_x: None,
|
||||
grid_y: None,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
@@ -3192,7 +3192,9 @@ fn spawn_mock_external_canvas_api_server_with_capture_and_generation_gate(
|
||||
"spritesheetImageSrc": "/generated/canvas/spritesheet.png",
|
||||
"spritesheetWidth": 2,
|
||||
"spritesheetHeight": 1,
|
||||
"sliceLayout": "grid-2x2",
|
||||
"sliceMode": "grid",
|
||||
"gridX": 2,
|
||||
"gridY": 2,
|
||||
"iconImageSrcs": icon_image_srcs,
|
||||
"sliceWarning": null,
|
||||
"prompt": "原创游戏素材图集",
|
||||
|
||||
@@ -1076,6 +1076,9 @@ async fn canonical_art_spec_and_ui_requests_use_the_shared_reference_chain() {
|
||||
asset_label: "游戏横屏界面原型图".to_string(),
|
||||
replace_existing: false,
|
||||
slice_count: None,
|
||||
slice_mode: None,
|
||||
grid_x: None,
|
||||
grid_y: None,
|
||||
},
|
||||
)
|
||||
.await;
|
||||
@@ -5493,6 +5496,9 @@ fn ui_prototype_generation_uses_dedicated_prompt_and_art_spec() {
|
||||
asset_label: "游戏横屏界面原型图".to_string(),
|
||||
replace_existing: false,
|
||||
slice_count: None,
|
||||
slice_mode: None,
|
||||
grid_x: None,
|
||||
grid_y: None,
|
||||
};
|
||||
let prompt = build_platform_art_asset_prompt(
|
||||
"原创网格贪吃蛇:分数与状态 HUD、四类不同分值食物、开始、方向键/WASD、触控方向键、失败与重开",
|
||||
|
||||
Reference in New Issue
Block a user