统一画布素材类型合同

补齐 Provider Prompt 的 icon-spec 类型

统一 Prompt、原生 schema 与执行校验的素材类型目录

增加权威集合、schema、执行校验和最终请求回归测试
This commit is contained in:
2026-08-03 20:12:29 +08:00
parent f725e15382
commit 0c44293341
5 changed files with 73 additions and 21 deletions
@@ -140,5 +140,5 @@ pub(crate) use tool_plan_protocol::{
};
pub(crate) use tool_policy_snapshot::{
agent_runtime_executable_tools, agent_runtime_native_executable_tools,
agent_runtime_tool_policy_snapshot_for_run_at,
agent_runtime_tool_policy_snapshot_for_run_at, AGENT_RUNTIME_CANVAS_ASSET_KINDS,
};
File diff suppressed because one or more lines are too long
@@ -1,5 +1,21 @@
use super::*;
pub(crate) const AGENT_RUNTIME_CANVAS_ASSET_KINDS: &[&str] =
&["game-art", "icon-spec", "ui-prototype", "art-spritesheet"];
#[cfg(test)]
mod canvas_asset_kind_contract_tests {
use super::*;
#[test]
fn canvas_asset_kind_catalog_preserves_authoritative_contract() {
assert_eq!(
AGENT_RUNTIME_CANVAS_ASSET_KINDS,
&["game-art", "icon-spec", "ui-prototype", "art-spritesheet"]
);
}
}
pub(crate) fn agent_runtime_executable_tools() -> Vec<&'static str> {
vec![
GAME_CREATOR_USER_INPUT_REQUEST_TOOL,
@@ -14,6 +14,10 @@ pub(crate) const AGENT_RUNTIME_UI_PROTOTYPE_LEGACY_VALIDATION_PROFILE: &str = "u
pub(crate) const AGENT_RUNTIME_UI_PROTOTYPE_VALIDATION_PROFILE: &str = "ui-prototype.v2";
pub(crate) const AGENT_RUNTIME_ART_SPEC_PATH: &str = "assets/art-spec.png";
fn agent_runtime_canvas_asset_kind_is_supported(asset_kind: &str) -> bool {
AGENT_RUNTIME_CANVAS_ASSET_KINDS.contains(&asset_kind)
}
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub(in crate::agent) struct AgentRuntimeUiPrototypeChecks {
@@ -652,10 +656,7 @@ pub(in crate::agent) async fn observe_agent_runtime_platform_art_asset_generatio
detail: None,
};
}
if !matches!(
options.asset_kind.as_str(),
"game-art" | "icon-spec" | "ui-prototype" | "art-spritesheet"
) {
if !agent_runtime_canvas_asset_kind_is_supported(&options.asset_kind) {
return AgentRuntimeToolObservation {
tool: "canvas.asset_generate".to_string(),
status: "failed".to_string(),
@@ -901,6 +902,14 @@ pub(crate) async fn observe_agent_runtime_platform_art_asset_generation_after_di
mod platform_art_generation_observation_tests {
use super::*;
#[test]
fn canvas_asset_kind_validation_accepts_shared_catalog() {
for asset_kind in AGENT_RUNTIME_CANVAS_ASSET_KINDS {
assert!(agent_runtime_canvas_asset_kind_is_supported(asset_kind));
}
assert!(!agent_runtime_canvas_asset_kind_is_supported("unsupported"));
}
#[test]
fn unknown_external_generation_result_requires_runtime_reconciliation() {
let root = tempfile::tempdir().expect("create observation status root");
@@ -12,7 +12,7 @@ use sha2::{Digest, Sha256};
use crate::agent::{
agent_runtime_native_executable_tools, AgentRuntimePlanUpdate, AgentRuntimeToolAction,
AgentRuntimeToolPlan, AGENT_RUNTIME_BACKGROUND_TOOL_ACTION_LIMIT,
AGENT_RUNTIME_PLAN_STEP_LIMIT,
AGENT_RUNTIME_CANVAS_ASSET_KINDS, AGENT_RUNTIME_PLAN_STEP_LIMIT,
};
use crate::mcp::{GameCreatorMcpCatalog, GameCreatorMcpCatalogTool, GAME_CREATOR_MCP_CALL_TOOL};
@@ -1177,20 +1177,27 @@ fn runtime_tool_input_schema(tool: &str) -> Value {
"question": { "type": ["string", "null"], "maxLength": 1000 }
}
}),
"canvas.asset_generate" => json!({
"type": "object",
"required": ["prompt", "outputPath", "aspectRatio", "imageSize", "assetKind", "assetLabel", "replaceExisting"],
"additionalProperties": false,
"properties": {
"prompt": { "type": "string", "minLength": 1, "maxLength": 4000 },
"outputPath": { "type": ["string", "null"], "maxLength": 240 },
"aspectRatio": { "type": ["string", "null"], "enum": ["1:1", "2:3", "3:2", "9:16", "16:9", null] },
"imageSize": { "type": ["string", "null"], "enum": ["0.5K", "1K", "2K", null] },
"assetKind": { "type": ["string", "null"], "enum": ["game-art", "icon-spec", "ui-prototype", "art-spritesheet", null] },
"assetLabel": { "type": ["string", "null"], "maxLength": 80 },
"replaceExisting": { "type": "boolean" }
}
}),
"canvas.asset_generate" => {
let mut asset_kinds = AGENT_RUNTIME_CANVAS_ASSET_KINDS
.iter()
.map(|kind| Value::String((*kind).to_string()))
.collect::<Vec<_>>();
asset_kinds.push(Value::Null);
json!({
"type": "object",
"required": ["prompt", "outputPath", "aspectRatio", "imageSize", "assetKind", "assetLabel", "replaceExisting"],
"additionalProperties": false,
"properties": {
"prompt": { "type": "string", "minLength": 1, "maxLength": 4000 },
"outputPath": { "type": ["string", "null"], "maxLength": 240 },
"aspectRatio": { "type": ["string", "null"], "enum": ["1:1", "2:3", "3:2", "9:16", "16:9", null] },
"imageSize": { "type": ["string", "null"], "enum": ["0.5K", "1K", "2K", null] },
"assetKind": { "type": ["string", "null"], "enum": asset_kinds },
"assetLabel": { "type": ["string", "null"], "maxLength": 80 },
"replaceExisting": { "type": "boolean" }
}
})
}
"blackboard.write" => two_string_input_schema("title", "content"),
"agent.message" => two_string_input_schema("agentId", "content"),
"agent.delegate" => json!({
@@ -1459,6 +1466,21 @@ mod tests {
assert!(issues.is_empty(), "{}", issues.join("\n"));
}
#[test]
fn canvas_asset_generate_schema_uses_shared_asset_kind_catalog() {
let schema = runtime_tool_input_schema("canvas.asset_generate");
let mut expected = AGENT_RUNTIME_CANVAS_ASSET_KINDS
.iter()
.map(|kind| Value::String((*kind).to_string()))
.collect::<Vec<_>>();
expected.push(Value::Null);
assert_eq!(
schema.pointer("/properties/assetKind/enum"),
Some(&Value::Array(expected))
);
}
#[test]
fn action_function_parameters_rebases_local_schema_refs_after_wrapping() {
let parameters = action_function_parameters(json!({