feat: improve mode and peripheral orchestration

This commit is contained in:
Miayui
2026-09-21 00:13:47 +08:00
parent 89707a42e2
commit 448c8b31a0
39 changed files with 3068 additions and 277 deletions
+145 -54
View File
@@ -997,14 +997,14 @@ operations:
def runtime_url(file_name: str, source_url: str) -> str:
hashed = runtime_assets.get(file_name)
return f"/{hashed}" if hashed else source_url
with self.opener.open(f"{self.base_url}/app.js?v=dual-seat-145-n7-dispatch", timeout=3) as response:
with self.opener.open(f"{self.base_url}/app.js?v=dual-seat-147-video-media", timeout=3) as response:
app_script = response.read().decode("utf-8")
with self.opener.open(
f"{self.base_url}/gesture-control.js?v=android-pad-9", timeout=3
) as response:
gesture_script = response.read().decode("utf-8")
with self.opener.open(
f"{self.base_url}/console-v2.js?v=local-first-77-performance", timeout=3
f"{self.base_url}/console-v2.js?v=local-first-79-cinema-status", timeout=3
) as response:
console_script = response.read().decode("utf-8")
with self.opener.open(
@@ -1024,7 +1024,7 @@ operations:
) as response:
speech_worklet = response.read().decode("utf-8")
with self.opener.open(
f"{self.base_url}/self-extension.js?v=nfc-mqtt-56", timeout=3
f"{self.base_url}/self-extension.js?v=nfc-persistent-57", timeout=3
) as response:
extension_script = response.read().decode("utf-8")
with self.opener.open(
@@ -1032,7 +1032,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-35", timeout=3
f"{self.base_url}/assistant-chat.js?v=codex-assistant-39-video-media", timeout=3
) as response:
assistant_script = response.read().decode("utf-8")
with self.opener.open(
@@ -1084,13 +1084,13 @@ operations:
self.assertIn("CMD 0x431 · FB 0x388 · CH1", template)
self.assertNotIn("透明车体 · 实体设备", template)
self.assertIn(
f'src="{runtime_url("app.js", "/app.js?v=dual-seat-145-n7-dispatch")}"',
f'src="{runtime_url("app.js", "/app.js?v=dual-seat-147-video-media")}"',
html,
)
self.assertIn('import "../styles.css";', vite_entry)
self.assertNotIn('class="vehicle-system-hud"', template)
self.assertIn(
f'src="{runtime_url("console-v2.js", "/console-v2.js?v=local-first-77-performance")}"',
f'src="{runtime_url("console-v2.js", "/console-v2.js?v=local-first-79-cinema-status")}"',
html,
)
self.assertIn('import "../console-v2.css";', vite_entry)
@@ -1105,7 +1105,7 @@ operations:
self.assertIn('id="v2BootScreen"', template)
self.assertIn('正在加载车机控制与实时状态', template)
self.assertIn(
f'src="{runtime_url("self-extension.js", "/self-extension.js?v=nfc-mqtt-56")}"',
f'src="{runtime_url("self-extension.js", "/self-extension.js?v=nfc-persistent-57")}"',
html,
)
self.assertIn('import "../self-extension.css";', vite_entry)
@@ -1117,7 +1117,7 @@ operations:
self.assertIn('id="assistantTranscript"', template)
self.assertIn('import "../assistant-chat.css";', vite_entry)
self.assertIn(
f'src="{runtime_url("assistant-chat.js", "/assistant-chat.js?v=codex-assistant-35")}"',
f'src="{runtime_url("assistant-chat.js", "/assistant-chat.js?v=codex-assistant-39-video-media")}"',
html,
)
self.assertIn('import "../energy-monitor.css";', vite_entry)
@@ -1433,16 +1433,16 @@ operations:
self.assertIn(f'/{runtime_assets[asset_name]}', service_worker)
else:
self.assertIn(
'const CACHE_VERSION = "cognitive-agent-pad-v329-mode-preview"',
'const CACHE_VERSION = "cognitive-agent-pad-v334-video-media"',
service_worker,
)
self.assertIn('/console-v2.css?v=delivery-state-69', service_worker)
self.assertIn('/console-v2.js?v=local-first-77-performance', service_worker)
self.assertIn('/console-v2.js?v=local-first-79-cinema-status', service_worker)
self.assertIn('/assistant-chat.css?v=codex-assistant-30', service_worker)
self.assertIn('/assistant-chat.js?v=codex-assistant-35', service_worker)
self.assertIn('/assistant-chat.js?v=codex-assistant-39-video-media', service_worker)
self.assertIn('/energy-monitor.css?v=energy-monitor-5-dialog-size', service_worker)
self.assertIn('/energy-monitor.js?v=energy-monitor-5-performance', service_worker)
self.assertIn('"/app.js?v=dual-seat-145-n7-dispatch"', service_worker)
self.assertIn('"/app.js?v=dual-seat-147-video-media"', service_worker)
self.assertIn('/firmware-workbench-entry.css?v=2', service_worker)
self.assertIn('/firmware-workbench-entry.js?v=3', service_worker)
self.assertIn('/firmware-workbench-theme.js?v=1', service_worker)
@@ -1910,7 +1910,7 @@ operations:
) as response:
extension_script = response.read().decode("utf-8")
with self.opener.open(
f"{self.base_url}/app.js?v=dual-seat-145-n7-dispatch", timeout=3
f"{self.base_url}/app.js?v=dual-seat-147-video-media", timeout=3
) as response:
app_script = response.read().decode("utf-8")
@@ -2904,47 +2904,51 @@ operations:
).encode("utf-8"),
)
def fail_can_after_validation(*_args, **kwargs):
callback = kwargs.get("_on_commands_validated")
self.assertTrue(callable(callback))
callback(
{
"request_id": "req-can-validation-only",
"utterance": "打开空调,温度设置为24度,风速调整为3档,打开遮阳帘",
"status": "processing",
"execution_mode": "hardware",
"control_transport": "wired_can",
"provider_used": "local_rules",
"decision_summary": [],
"function_configs": {
"native_climate": {
"spec": {
"operations": [
{
"id": "vehicle_set_power_1",
"capability": "climate.set_power",
"target": {"zone": "vehicle"},
"parameters": {"state": "on"},
"parse_source": "local_rule",
}
]
can_preview = {
"request_id": "req-can-validation-only",
"utterance": "打开空调,温度设置为24度,风速调整为3档,打开遮阳帘",
"status": "succeeded",
"execution_mode": "dry_run",
"control_transport": "wired_can",
"provider_used": "local_rules",
"decision_summary": [],
"function_configs": {
"native_climate": {
"spec": {
"operations": [
{
"id": "vehicle_set_power_1",
"capability": "climate.set_power",
"target": {"zone": "vehicle"},
"parameters": {"state": "on"},
"parse_source": "local_rule",
}
}
},
"command_plans": {
"native_climate": {"spec": {"commands": []}}
},
"operation_results": [],
"execution_report": None,
]
}
}
)
},
"command_plans": {
"native_climate": {"spec": {"commands": []}}
},
"operation_results": [],
"execution_report": None,
}
def fail_can_after_validation(*_args, **_kwargs):
raise CanGatewayError("simulated CAN transport failure")
try:
with patch.object(
self.service,
"process",
side_effect=fail_can_after_validation,
with (
patch.object(
self.service,
"preview_native_can",
return_value=can_preview,
),
patch.object(
self.service,
"process",
side_effect=fail_can_after_validation,
),
):
with self.opener.open(request, timeout=3) as response:
events = [
@@ -2963,11 +2967,8 @@ operations:
for item in events
if item["type"] == "validated_commands"
]
self.assertEqual(set(validated_payloads[0]["function_configs"]), {"self_extension"})
validated = next(
payload for payload in validated_payloads
if {"native_climate", "self_extension"}.issubset(payload["function_configs"])
)
self.assertEqual(len(validated_payloads), 1)
validated = validated_payloads[0]
self.assertEqual(validated["status"], "processing")
self.assertEqual(
set(validated["function_configs"]),
@@ -2977,6 +2978,10 @@ operations:
[item["slot"] for item in validated["infrared_actions"]],
[1, 4],
)
self.assertEqual(
validated["parallel_dispatch"]["preflight_status"],
"succeeded",
)
def test_disabled_nfc_ir_command_does_not_discard_allowed_can_and_ir(
self,
@@ -3173,6 +3178,18 @@ operations:
)
extension.sync_peripheral_catalog(
[
{
"id": "toy-001-light-on",
"status": "learned",
"enabled": True,
"device_id": "toy-001",
"device_name": "闪烁灯玩具",
"object_names": ["闪烁灯玩具", "闪烁灯"],
"card_uid": "2CC8B103",
"action": "打开",
"action_names": ["打开", "开启", "启动"],
"slot": 1,
},
{
"id": "toy-001-light-red",
"status": "learned",
@@ -3239,6 +3256,11 @@ operations:
self.assertEqual(
result["infrared_actions"][0]["parse_source"], "cloud_model"
)
self.assertEqual(
[action["slot"] for action in result["infrared_actions"]],
[1, 3],
)
self.assertTrue(result["infrared_actions"][0]["auto_inserted"])
self.assertFalse(result["supplement_required"])
def test_main_request_executes_gpt_nfc_sequence_without_vehicle_can(self) -> None:
@@ -4113,6 +4135,75 @@ operations:
self.assertEqual(released["state"], "none")
self.assertIsNone(self.service.hardware_access.admin_snapshot()["active"])
def test_legacy_hardware_client_id_can_request_access_and_execute_mode_card(
self,
) -> None:
legacy_client_id = "legacy_mode_client_with_underscores"
def post(path: str, payload: dict, *, public: bool = False):
headers = {"Content-Type": "application/json"}
if public:
headers["X-Forwarded-For"] = "203.0.113.31"
request = urllib.request.Request(
f"{self.base_url}{path}",
method="POST",
headers=headers,
data=json.dumps(payload, ensure_ascii=False).encode("utf-8"),
)
return self.opener.open(request, timeout=3)
with post(
"/api/hardware/access/request",
{"client_id": legacy_client_id, "label": "旧版模式终端"},
public=True,
) as response:
requested = json.loads(response.read().decode("utf-8"))
canonical_client_id = requested["client_id"]
self.assertRegex(canonical_client_id, r"^hwc-legacy-[0-9a-f]{32}$")
with post(
"/api/local/hardware-access/approve",
{"client_id": canonical_client_id},
):
pass
fake_result = {
"status": "succeeded",
"request_id": "req-legacy-mode-client",
"execution_mode": "hardware",
}
with patch.object(
self.service,
"execute_mode_profile_card",
return_value=fake_result,
) as execute:
with post(
"/api/mode-profiles/office/execute",
{
"conversation_id": "conv-legacy-mode-client",
"vehicle_id": "denza_n7",
"execution_mode": "hardware",
"hardware_client_id": legacy_client_id,
"client_type": "web_console_mode_card",
},
public=True,
) as response:
result = json.loads(response.read().decode("utf-8"))
self.assertEqual(result["status"], "succeeded")
self.assertEqual(
execute.call_args.kwargs["hardware_client_id"],
canonical_client_id,
)
with post(
"/api/hardware/access/release",
{"client_id": legacy_client_id},
public=True,
) as response:
released = json.loads(response.read().decode("utf-8"))
self.assertEqual(released["state"], "none")
def test_local_hardware_decision_page_is_not_exposed_through_proxy(self) -> None:
with self.opener.open(
f"{self.base_url}/hardware-control", timeout=3