From 5f50d775243ae8e5ed3c4634b1fe445dbbbaaf9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E5=BE=B7=E5=AE=87?= Date: Tue, 15 Sep 2026 16:57:04 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=AD=A3=20Response=20item=20content?= =?UTF-8?q?=20=E7=B1=BB=E5=9E=8B=E6=8A=95=E5=BD=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 区分 turn/start 的 text 与历史 message 的 input_text 将 AGC 私有引用转换为标准 input_text 新增回归测试避免未知 variant text --- .../src/agent/direct_codex_user_item/wire.rs | 40 +++++++++++++++++-- 1 file changed, 37 insertions(+), 3 deletions(-) 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 99d4262c4..2ab90cc62 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 @@ -20,9 +20,7 @@ pub(crate) fn direct_codex_user_item_to_response_item( } let canonical: DirectCodexUserItem = serde_json::from_value(item.clone()) .map_err(|error| format!("DirectProject user item 无法转换为 Codex item:{error}"))?; - let Value::Array(content) = direct_codex_user_item_to_wire_input(root, &canonical)? else { - return Err("DirectProject user item wire content 不是数组".to_string()); - }; + let content = direct_codex_user_item_to_response_content(root, &canonical)?; let mut projected = serde_json::json!({ "type": "message", "role": "user", @@ -34,6 +32,25 @@ pub(crate) fn direct_codex_user_item_to_response_item( Ok(projected) } +fn direct_codex_user_item_to_response_content( + root: &Path, + item: &DirectCodexUserItem, +) -> Result, String> { + let Value::Array(input) = direct_codex_user_item_to_wire_input(root, item)? else { + return Err("DirectProject user item wire content 不是数组".to_string()); + }; + input + .into_iter() + .map(|part| { + let text = part + .get("text") + .and_then(Value::as_str) + .ok_or_else(|| "DirectProject user item wire part 缺少 text".to_string())?; + Ok(serde_json::json!({ "type": "input_text", "text": text })) + }) + .collect() +} + /// 将 canonical user item 转为 app-server `turn/start.input` 可接受的文本数组。 /// AGC 私有 part 只在这里投影为安全摘要,canonical item 本身不被修改。 pub(crate) fn direct_codex_user_item_to_wire_input( @@ -131,6 +148,23 @@ mod tests { ); } + #[test] + fn response_item_projection_uses_input_text_not_turn_input_text() { + let root = tempfile::tempdir().expect("temp project"); + crate::init_local_game_project_at(root.path(), "wire-test", "wire 投影测试") + .expect("init project"); + let item = json!({ + "type": "message", + "role": "user", + "id": "turn-1:user", + "content": [{"type": "input_text", "text": "你好"}] + }); + let projected = direct_codex_user_item_to_response_item(root.path(), &item) + .expect("user response item should project"); + assert_eq!(projected["content"][0]["type"], "input_text"); + assert_ne!(projected["content"][0]["type"], "text"); + } + #[test] fn history_item_without_type_fails_closed() { let error = direct_codex_user_item_to_response_item(