diff --git a/apps/ai-game-creator-shell/src-tauri/src/agent/direct_codex_user_item/wire.rs b/apps/ai-game-creator-shell/src-tauri/src/agent/direct_codex_user_item/wire.rs index 6fae76ca5..fe9c76157 100644 --- a/apps/ai-game-creator-shell/src-tauri/src/agent/direct_codex_user_item/wire.rs +++ b/apps/ai-game-creator-shell/src-tauri/src/agent/direct_codex_user_item/wire.rs @@ -1,6 +1,10 @@ -use super::model::{DirectCodexUserContentPart, DirectCodexUserItem}; +use super::model::{ + DirectCodexUserContentPart, DirectCodexUserItem, DirectCodexUserRuntimeRegionPart, +}; use super::validation::validate_direct_codex_user_item; -use crate::agent::{read_manifest_for_project, sanitize_attachment_local_path}; +use crate::agent::{ + read_manifest_for_project, sanitize_attachment_local_path, GameCreationAppManifest, +}; use serde_json::Value; use std::path::Path; @@ -51,6 +55,47 @@ fn direct_codex_user_item_to_response_content( .collect() } +fn resource_reference_summary( + manifest: &GameCreationAppManifest, + resource_id: &str, +) -> Result { + let resource_id = resource_id.trim(); + let asset = manifest + .assets + .iter() + .find(|asset| asset.id == resource_id) + .ok_or_else(|| "引用的素材已不存在,请移除后重新选择".to_string())?; + let path = sanitize_attachment_local_path(&asset.local_path) + .ok_or_else(|| "引用的素材路径无效,请移除后重新选择".to_string())?; + Ok(format!( + "[素材引用 resourceId={resource_id};项目路径={path}]" + )) +} + +fn runtime_region_summary(reference: &DirectCodexUserRuntimeRegionPart) -> String { + let resources = reference + .resource_ids + .iter() + .map(|id| id.trim()) + .collect::>() + .join(","); + let mut summary = format!("[运行画面区域:名称={} ", reference.label.trim()); + if let Some(run_id) = reference.run_id.as_deref() { + summary.push_str(&format!("运行标识={} ", run_id.trim())); + } + if let Some(role) = reference.element_role.as_deref() { + summary.push_str(&format!("角色={} ", role.trim())); + } + if let Some(text) = reference.text.as_deref() { + summary.push_str(&format!("文本={} ", text.trim())); + } + if !resources.is_empty() { + summary.push_str(&format!("关联素材={resources}")); + } + summary.push(']'); + summary +} + /// 将 canonical user item 转为 app-server `turn/start.input` 可接受的文本数组。 /// AGC 私有 part 只在这里投影为安全摘要,canonical item 本身不被修改。 pub(crate) fn direct_codex_user_item_to_wire_input( @@ -65,43 +110,13 @@ pub(crate) fn direct_codex_user_item_to_wire_input( let text = match part { DirectCodexUserContentPart::InputText { text } => text.clone(), DirectCodexUserContentPart::AgcResourceReference { resource_id } => { - let asset = manifest - .assets - .iter() - .find(|asset| asset.id == resource_id.trim()) - .ok_or_else(|| "引用的素材已不存在,请移除后重新选择".to_string())?; - let path = sanitize_attachment_local_path(&asset.local_path) - .ok_or_else(|| "引用的素材路径无效,请移除后重新选择".to_string())?; - format!( - "[素材引用 resourceId={};项目路径={path}]", - resource_id.trim() - ) + resource_reference_summary(&manifest, resource_id)? } DirectCodexUserContentPart::AgcSkillReference { name } => { format!("${}", name.trim()) } DirectCodexUserContentPart::AgcRuntimeRegionReference(reference) => { - let resources = reference - .resource_ids - .iter() - .map(|id| id.trim()) - .collect::>() - .join(","); - let mut summary = format!("[运行画面区域:名称={} ", reference.label.trim()); - if let Some(run_id) = reference.run_id.as_deref() { - summary.push_str(&format!("运行标识={} ", run_id.trim())); - } - if let Some(role) = reference.element_role.as_deref() { - summary.push_str(&format!("角色={} ", role.trim())); - } - if let Some(text) = reference.text.as_deref() { - summary.push_str(&format!("文本={} ", text.trim())); - } - if !resources.is_empty() { - summary.push_str(&format!("关联素材={resources}")); - } - summary.push(']'); - summary + runtime_region_summary(reference) } DirectCodexUserContentPart::AgcAttachmentReference(reference) => { let mut summary = format!( @@ -137,16 +152,9 @@ pub(crate) fn direct_codex_user_item_to_codex_turn_input( input.push(serde_json::json!({ "type": "text", "text": text })); } DirectCodexUserContentPart::AgcResourceReference { resource_id } => { - let asset = manifest - .assets - .iter() - .find(|asset| asset.id == resource_id.trim()) - .ok_or_else(|| "引用的素材已不存在,请移除后重新选择".to_string())?; - let path = sanitize_attachment_local_path(&asset.local_path) - .ok_or_else(|| "引用的素材路径无效,请移除后重新选择".to_string())?; input.push(serde_json::json!({ "type": "text", - "text": format!("[素材引用 resourceId={};项目路径={path}]", resource_id.trim()), + "text": resource_reference_summary(&manifest, resource_id)?, })); } DirectCodexUserContentPart::AgcSkillReference { name } => { @@ -163,29 +171,9 @@ pub(crate) fn direct_codex_user_item_to_codex_turn_input( })); } DirectCodexUserContentPart::AgcRuntimeRegionReference(reference) => { - let resources = reference - .resource_ids - .iter() - .map(|id| id.trim()) - .collect::>() - .join(","); - let mut summary = format!("[运行画面区域:名称={} ", reference.label.trim()); - if let Some(run_id) = reference.run_id.as_deref() { - summary.push_str(&format!("运行标识={} ", run_id.trim())); - } - if let Some(role) = reference.element_role.as_deref() { - summary.push_str(&format!("角色={} ", role.trim())); - } - if let Some(text) = reference.text.as_deref() { - summary.push_str(&format!("文本={} ", text.trim())); - } - if !resources.is_empty() { - summary.push_str(&format!("关联素材={resources}")); - } - summary.push(']'); input.push(serde_json::json!({ "type": "text", - "text": summary, + "text": runtime_region_summary(reference), })); } }