fix: support conversational mode draft updates

This commit is contained in:
vee
2026-09-07 23:59:42 +08:00
parent 6b2b644ee6
commit f0a248ede3
19 changed files with 2326 additions and 109 deletions
+121 -2
View File
@@ -186,7 +186,10 @@ class FakeVehicleService:
if self.fail:
raise RuntimeError("private adapter failure")
utterance = str(kwargs["utterance"])
mode_preview = utterance.startswith(("请帮我创建一个", "构建一个"))
draft_reference = kwargs.get("mode_profile_draft_context")
mode_preview = utterance.startswith(("请帮我创建一个", "构建一个")) or isinstance(
draft_reference, dict
)
callback = kwargs["on_commands_validated"]
assert callable(callback)
callback(
@@ -199,7 +202,13 @@ class FakeVehicleService:
)
if mode_preview:
movie_mode = "观影模式" in utterance
mode_name = "观影模式" if movie_mode else "宝宝睡眠模式"
mode_name = (
str(draft_reference.get("name"))
if isinstance(draft_reference, dict)
else "观影模式"
if movie_mode
else "宝宝睡眠模式"
)
instruction = (
"遮阳帘关闭,车窗关闭,副驾座椅调整为零重力模式,灯光全部关闭"
if movie_mode
@@ -297,6 +306,17 @@ class FakeVehicleService:
"vehicle_id": "denza_n7",
"domains": domains,
"operation_count": len(actions),
**(
{
"generation_source": "draft_revision",
"draft_revision": 2,
"revises_source_request_id": draft_reference[
"source_request_id"
],
}
if isinstance(draft_reference, dict)
else {}
),
},
"intent_ir": {"spec": {"actions": actions}},
}
@@ -690,6 +710,105 @@ class TestCodexAssistantWebApi:
}
]
def test_assistant_forwards_only_the_opaque_reference_for_pending_draft_update(
self,
) -> None:
reference = {
"name": "小憩模式",
"source_request_id": "req-mode-preview",
"vehicle_id": "denza_n7",
}
events = self.post_assistant(
{
"message": "把小憩模式的空调温度调整到25度",
"conversation_id": "conv-api-update-pending-mode",
"execution_mode": "hardware",
"mode_profile_draft_context": reference,
}
)
assert not FAKE_CODEX.turn_calls
call = AssistantApiHandler.service.calls[-1]
assert call["utterance"] == "把小憩模式的空调温度调整到25度"
assert call["mode_profile_draft_context"] == reference
action = next(event for event in events if event["type"] == "action.completed")
assert action["success"] is True
assert action["payload"]["execution_mode"] == "dry_run"
assert action["payload"]["hardware_execution_allowed"] is False
assert action["payload"]["mode_profile_draft"]["draft_revision"] == 2
assert (
action["payload"]["mode_profile_draft"]["revises_source_request_id"]
== "req-mode-preview"
)
assert events[0]["engine"] == "n7web-mode-builder"
assert "调整待保存" in events[-1]["answer"]
def test_spaced_device_action_update_keeps_the_draft_route_end_to_end(
self,
) -> None:
reference = {
"name": "小憩模式",
"source_request_id": "req-mode-preview",
"vehicle_id": "denza_n7",
}
events = self.post_assistant(
{
"message": "请帮我 把 小憩模式 里的 遮阳帘关闭",
"conversation_id": "conv-api-spaced-mode-update",
"execution_mode": "hardware",
"mode_profile_draft_context": reference,
}
)
assert not FAKE_CODEX.turn_calls
call = AssistantApiHandler.service.calls[-1]
assert call["mode_profile_draft_context"] == reference
assert call["execution_mode"] == "hardware"
action = next(event for event in events if event["type"] == "action.completed")
assert action["success"] is True
assert action["payload"]["execution_mode"] == "dry_run"
assert action["payload"]["hardware_execution_allowed"] is False
assert events[0]["engine"] == "n7web-mode-builder"
def test_active_draft_reference_is_not_forwarded_for_an_unrelated_question(
self,
) -> None:
events = self.post_assistant(
{
"message": "解释一下空调内循环和外循环的区别",
"conversation_id": "conv-api-question-with-active-draft",
"mode_profile_draft_context": {
"name": "小憩模式",
"source_request_id": "req-mode-preview",
"vehicle_id": "denza_n7",
},
}
)
assert events[0]["route"] == "agent"
assert events[-1]["answer"] == "这是 Codex 的测试回答。"
assert not AssistantApiHandler.service.calls
def test_mode_question_with_control_words_never_reaches_vehicle_service(
self,
) -> None:
events = self.post_assistant(
{
"message": "修改小憩模式的空调设置会怎样?",
"conversation_id": "conv-api-mode-question-no-control",
"execution_mode": "hardware",
"mode_profile_draft_context": {
"name": "小憩模式",
"source_request_id": "req-mode-preview",
"vehicle_id": "denza_n7",
},
}
)
assert events[0]["route"] == "agent"
assert events[-1]["answer"] == "这是 Codex 的测试回答。"
assert not AssistantApiHandler.service.calls
def test_exact_movie_mode_request_streams_a_nine_action_preview(self) -> None:
events = self.post_assistant(
{
+6 -6
View File
@@ -656,7 +656,7 @@ operations:
) as response:
extension_styles = response.read().decode("utf-8").replace("\r\n", "\n")
with self.opener.open(
f"{self.base_url}/assistant-chat.js?v=codex-assistant-22", timeout=3
f"{self.base_url}/assistant-chat.js?v=codex-assistant-23", timeout=3
) as response:
assistant_script = response.read().decode("utf-8")
with self.opener.open(
@@ -718,8 +718,8 @@ operations:
self.assertNotIn("滚动查看更多", html)
self.assertIn('aria-label="发送消息"', html)
self.assertIn('id="assistantTranscript"', html)
self.assertIn('href="/assistant-chat.css?v=codex-assistant-22"', html)
self.assertIn('src="/assistant-chat.js?v=codex-assistant-22"', html)
self.assertIn('href="/assistant-chat.css?v=codex-assistant-23"', html)
self.assertIn('src="/assistant-chat.js?v=codex-assistant-23"', html)
self.assertIn('href="/energy-monitor.css?v=energy-monitor-4"', html)
self.assertIn('src="/energy-monitor.js?v=energy-monitor-4"', html)
self.assertNotIn('"admin"', console_script)
@@ -992,11 +992,11 @@ operations:
self.assertIn("CAN 指令已提交,等待车辆状态确认", console_script)
self.assertIn('canvas.addEventListener("webglcontextlost"', app_script)
self.assertIn('"cognitive-agent:module-change"', app_script)
self.assertIn('const CACHE_VERSION = "cognitive-agent-pad-v271"', service_worker)
self.assertIn('const CACHE_VERSION = "cognitive-agent-pad-v272"', service_worker)
self.assertIn('/console-v2.css?v=delivery-state-65', service_worker)
self.assertIn('/console-v2.js?v=delivery-state-62', service_worker)
self.assertIn('/assistant-chat.css?v=codex-assistant-22', service_worker)
self.assertIn('/assistant-chat.js?v=codex-assistant-22', service_worker)
self.assertIn('/assistant-chat.css?v=codex-assistant-23', service_worker)
self.assertIn('/assistant-chat.js?v=codex-assistant-23', service_worker)
self.assertIn('/energy-monitor.css?v=energy-monitor-4', service_worker)
self.assertIn('/energy-monitor.js?v=energy-monitor-4', service_worker)
self.assertIn('"/app.js?v=dual-seat-127"', service_worker)