mqtt切换为远程服务器部署,优化简单指令解析时长

This commit is contained in:
Miayui
2026-09-11 10:22:24 +08:00
parent 51ca12b624
commit 3fc6f2c2a4
34 changed files with 4290 additions and 484 deletions
+6 -9
View File
@@ -1101,7 +1101,7 @@ class TestCodexAssistantWebApi:
assert "not-for-mcp" not in serialized_events
assert "模拟编排" in events[-1]["answer"]
def test_structured_plan_crosses_the_internal_bridge_without_replacing_original_text(
def test_explicit_roof_control_uses_local_preview_without_codex(
self,
) -> None:
events = self.post_assistant(
@@ -1112,17 +1112,14 @@ class TestCodexAssistantWebApi:
}
)
assert FAKE_CODEX.bridge_results[-1][0] == 200
call = AssistantApiHandler.service.calls[-1]
assert call["utterance"] == "天幕透光率调到30%"
assert call["provider"] == "local"
forwarded_plan = call["assistant_plan"]
assert forwarded_plan["requests"][0]["capability"] == "roof.set_transmittance"
assert forwarded_plan["requests"][0]["parameters"] == [
{"name": "transmittance_pct", "value": 30}
]
assert forwarded_plan["requests"][0]["reason"] == "原始用户请求的结构化候选动作。"
assert "instruction" not in call
assert "assistant_plan" not in call
assert not FAKE_CODEX.turn_calls
assert not FAKE_CODEX.bridge_results
assert events[0]["route"] == "vehicle_control"
assert events[0]["engine"] == "n7web-control-router"
assert events[-1]["type"] == "answer.completed"
def test_assistant_returns_named_mode_draft_without_saving_it(self) -> None:
+151 -18
View File
@@ -13,6 +13,7 @@ from pathlib import Path
from types import SimpleNamespace
from unittest.mock import patch
from cognitive_agent.application.codex_assistant import CodexAssistantService
from cognitive_agent.application.mode_profile import ModeProfileRegistry
from cognitive_agent.application.seat_service import REPOSITORY_ROOT, SeatDemoService
from cognitive_agent.application.self_extension import (
@@ -438,6 +439,12 @@ operations:
}
)
try:
with self.opener.open(
f"{self.base_url}/api/vehicle-display-sync", timeout=3
) as response:
display_before_compile = json.loads(
response.read().decode("utf-8")
)
generate_request = urllib.request.Request(
f"{self.base_url}/api/requests/preview",
method="POST",
@@ -457,6 +464,16 @@ operations:
)
with self.opener.open(generate_request, timeout=3) as response:
generated = json.loads(response.read().decode("utf-8"))
with self.opener.open(
f"{self.base_url}/api/vehicle-display-sync", timeout=3
) as response:
display_after_compile = json.loads(
response.read().decode("utf-8")
)
self.assertEqual(
display_after_compile["task"],
display_before_compile["task"],
)
save_request = urllib.request.Request(
f"{self.base_url}/api/mode-profiles/office",
@@ -650,18 +667,18 @@ operations:
def test_static_console_and_runtime_endpoint(self) -> None:
with self.opener.open(f"{self.base_url}/", timeout=3) as response:
html = response.read().decode("utf-8")
with self.opener.open(f"{self.base_url}/app.js?v=dual-seat-130", timeout=3) as response:
with self.opener.open(f"{self.base_url}/app.js?v=dual-seat-131", 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=delivery-state-62", timeout=3
f"{self.base_url}/console-v2.js?v=local-first-66", timeout=3
) as response:
console_script = response.read().decode("utf-8")
with self.opener.open(
f"{self.base_url}/console-v2.css?v=delivery-state-66", timeout=3
f"{self.base_url}/console-v2.css?v=delivery-state-68", timeout=3
) as response:
console_styles = response.read().decode("utf-8")
with self.opener.open(
@@ -685,7 +702,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-26", timeout=3
f"{self.base_url}/assistant-chat.js?v=codex-assistant-27", timeout=3
) as response:
assistant_script = response.read().decode("utf-8")
with self.opener.open(
@@ -726,12 +743,12 @@ operations:
self.assertIn("CMD 0x431 · FB 0x41A · CH1", html)
self.assertIn("CMD 0x431 · FB 0x388 · CH1", html)
self.assertNotIn("透明车体 · 实体设备", html)
self.assertIn('src="/app.js?v=dual-seat-130"', html)
self.assertIn('src="/app.js?v=dual-seat-131"', html)
self.assertIn('href="/styles.css?v=dual-seat-23"', html)
self.assertIn('src="/gesture-control.js?v=android-pad-9"', html)
self.assertNotIn('class="vehicle-system-hud"', html)
self.assertIn('src="/console-v2.js?v=delivery-state-62"', html)
self.assertIn('href="/console-v2.css?v=delivery-state-66"', html)
self.assertIn('src="/console-v2.js?v=local-first-66"', html)
self.assertIn('href="/console-v2.css?v=delivery-state-68"', html)
self.assertIn('src="/speech-input.js?v=sensevoice-13"', html)
self.assertIn('href="/speech-input.css?v=sensevoice-7"', html)
self.assertIn('id="speechDictationButton"', html)
@@ -748,7 +765,7 @@ operations:
self.assertIn('aria-label="发送消息"', html)
self.assertIn('id="assistantTranscript"', html)
self.assertIn('href="/assistant-chat.css?v=codex-assistant-26"', html)
self.assertIn('src="/assistant-chat.js?v=codex-assistant-26"', html)
self.assertIn('src="/assistant-chat.js?v=codex-assistant-27"', 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)
@@ -931,7 +948,12 @@ operations:
self.assertNotIn('data-mode-command="seat driver vent low"', app_script)
self.assertIn("CH0 副驾座椅与 CH1 主驾座椅独立收发、独立反馈", app_script)
self.assertIn("CH1 主驾实车测试不启用无闭环的座椅位置手势", app_script)
self.assertIn("车端 CAN 未启用,其他领域保持虚拟执行", app_script)
self.assertNotIn("车端 CAN 未启用,其他领域保持虚拟执行", app_script)
self.assertNotIn('id="hardwareNote"', html)
self.assertNotIn('id="hardwareAliasNote"', html)
self.assertNotIn('id="vehicleTransportNote"', html)
self.assertIn("下发控制指令", console_script)
self.assertIn('new Set(["mode_editor_compile"])', console_script)
self.assertIn("function extractReferenceWindowPanes", app_script)
self.assertIn("function collectReferenceFrontLampMeshes", app_script)
self.assertIn("function emphasizeReferenceWindowPanes", app_script)
@@ -1021,14 +1043,14 @@ 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-v277"', service_worker)
self.assertIn('/console-v2.css?v=delivery-state-66', service_worker)
self.assertIn('/console-v2.js?v=delivery-state-62', service_worker)
self.assertIn('const CACHE_VERSION = "cognitive-agent-pad-v282"', service_worker)
self.assertIn('/console-v2.css?v=delivery-state-68', service_worker)
self.assertIn('/console-v2.js?v=local-first-66', service_worker)
self.assertIn('/assistant-chat.css?v=codex-assistant-26', service_worker)
self.assertIn('/assistant-chat.js?v=codex-assistant-26', service_worker)
self.assertIn('/assistant-chat.js?v=codex-assistant-27', 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-130"', service_worker)
self.assertIn('"/app.js?v=dual-seat-131"', service_worker)
self.assertIn('"/speech-input.js?v=sensevoice-13"', service_worker)
self.assertIn('"/speech-input.css?v=sensevoice-7"', service_worker)
self.assertIn('"/speech-audio-worklet.js?v=sensevoice-5"', service_worker)
@@ -1485,7 +1507,7 @@ operations:
) as response:
extension_script = response.read().decode("utf-8")
with self.opener.open(
f"{self.base_url}/app.js?v=dual-seat-130", timeout=3
f"{self.base_url}/app.js?v=dual-seat-131", timeout=3
) as response:
app_script = response.read().decode("utf-8")
@@ -2134,7 +2156,7 @@ operations:
},
data=json.dumps(
{
"utterance": "打开闪烁灯,颜色切换到红色,打开空调",
"utterance": "空调温度设置到23度,打开闪烁灯",
"provider": "local",
"conversation_id": "conv-mixed-nfc-can",
"execution_mode": "dry_run",
@@ -2160,6 +2182,14 @@ operations:
self.assertEqual(synced["learned_count"], 2)
self.assertEqual(events[-1]["type"], "result")
self.assertTrue(any(item["type"] == "validated_commands" for item in events))
self.assertLess(
next(
index
for index, item in enumerate(events)
if item["type"] == "validated_commands"
),
len(events) - 1,
)
validated_payloads = [
item["payload"]
for item in events
@@ -2168,15 +2198,118 @@ operations:
self.assertTrue(validated_payloads)
self.assertTrue(
all(
"self_extension" in payload["function_configs"]
{"native_climate", "self_extension"}.issubset(
payload["function_configs"]
)
for payload in validated_payloads
)
)
first_validated = validated_payloads[0]
self.assertTrue(
{"native_climate", "self_extension"}.issubset(
first_validated["command_plans"]
)
)
validated_operations = [
operation
for configuration in first_validated["function_configs"].values()
for operation in configuration["spec"].get("operations", [])
]
self.assertEqual(
{operation["capability"] for operation in validated_operations},
{"climate.set_temperature", "infrared.emit"},
)
self.assertTrue(
all(
operation.get("parse_source") == "local_rule"
for operation in validated_operations
)
)
validated_commands = [
command
for plan in first_validated["command_plans"].values()
for command in plan["spec"].get("commands", [])
]
climate_command = next(
command
for command in validated_commands
if command.get("signals", {}).get("debug_command")
== "ac temp driver 23"
)
self.assertEqual(climate_command["signals"]["frame_id"], "0x1DE")
self.assertEqual(
climate_command["signals"]["data_hex"],
"00 00 00 00 17 00 00 00",
)
infrared_command = next(
command
for command in validated_commands
if command.get("message") == "infrared.emit"
)
self.assertEqual(infrared_command["signals"]["slot"], 1)
assistant_projection = CodexAssistantService._validated_control_payload(
first_validated
)
self.assertIsNotNone(assistant_projection)
projected_commands = assistant_projection["validated_control_commands"]
projected_by_capability = {
item["operation"]["capability"]: item for item in projected_commands
}
self.assertEqual(
projected_by_capability["climate.set_temperature"]["actual_command"],
"ac temp driver 23",
)
self.assertEqual(
projected_by_capability["climate.set_temperature"]["frames"][0][
"value"
],
"0x1DE · 00 00 00 00 17 00 00 00",
)
self.assertEqual(
projected_by_capability["infrared.emit"]["actual_command"],
"infrared emit 1",
)
serialized_projection = json.dumps(
assistant_projection, ensure_ascii=False
)
self.assertNotIn("command_plans", serialized_projection)
self.assertNotIn("data_hex", serialized_projection)
compile_trace = next(
event
for event in first_validated["trace_events"]
if event["stage"] == "compile"
)
validated_traces = first_validated["trace_events"][
: first_validated["trace_events"].index(compile_trace) + 1
]
required_validation_stages = {
"intent_parse",
"context_resolve",
"domain_route",
"function_generate",
"schema_validate",
"compile",
}
actual_validation_stages = {
event["stage"] for event in validated_traces
}
self.assertTrue(
required_validation_stages.issubset(actual_validation_stages),
sorted(actual_validation_stages),
)
self.assertTrue(
all(
isinstance(event.get("duration_ms"), int)
and event["duration_ms"] >= 0
for event in validated_traces
)
)
self.assertEqual(result["status"], "succeeded")
self.assertEqual(result["orchestration_route"], "unified_can_infrared")
self.assertEqual(
[item["slot"] for item in result["infrared_actions"]], [1, 3]
result["provider_used"], "local_rules+infrared_local_rules"
)
self.assertEqual([item["slot"] for item in result["infrared_actions"]], [1])
self.assertEqual(
result["infrared_actions"][0]["parse_source"], "local_rule"
)
@@ -287,6 +287,59 @@ test("a completed NDJSON answer remains the authoritative visible response", asy
assert.equal(harness.completed[0].error, null);
});
test("assistant bridges request start and validated local controls to the console", async () => {
const payload = {
request_id: "req-local-mixed",
utterance: "空调温度设置到23度,打开闪烁灯",
execution_mode: "dry_run",
provider_used: "local_rules+infrared_local_rules",
validated_control_commands: [
{
operation: {
id: "climate-temperature-1",
capability: "climate.set_temperature",
target: { zone: "driver" },
parameters: { temperature_c: 23 },
parse_source: "local_rule",
},
channel: "CAN1",
actual_command: "ac temp driver 23",
frames: [{
label: "CAN1 帧",
value: "0x1DE · 00 00 00 00 17 00 00 00",
}],
},
],
trace_events: [{ stage: "compile", duration_ms: 2 }],
};
const harness = createHarness([
{ type: "validated_control_commands", payload },
{ type: "answer.completed", route: "vehicle_control", answer: "模拟编排已完成。" },
]);
const starts = [];
const validated = [];
harness.sandbox.addEventListener("cognitive-agent:control-request-start", (event) => {
starts.push(event.detail);
});
harness.sandbox.addEventListener("cognitive-agent:validated-can-commands", (event) => {
validated.push(event.detail);
});
harness.input.value = payload.utterance;
harness.input.dataset.speechUtteranceId = "speech-local-mixed";
harness.input.dataset.speechValue = payload.utterance;
await harness.sandbox.CognitiveAgentAssistant.submit();
assert.deepEqual(JSON.parse(JSON.stringify(starts)), [{
utterance: payload.utterance,
speech_utterance_id: "speech-local-mixed",
speech_value: payload.utterance,
}]);
assert.deepEqual(JSON.parse(JSON.stringify(validated)), [payload]);
assert.equal(harness.requestCalls.length, 1);
assert.equal(JSON.parse(harness.requestCalls[0].options.body).execution_mode, "dry_run");
});
test("unsupported-control suggestions render once and click through as a new command", async () => {
const suggestions = [
{ id: "climate-on", label: "打开空调", command: "打开空调", domain: "climate" },
@@ -14,14 +14,14 @@ def test_console_contains_ai_assistant_surface():
assert 'placeholder="向 AI助手提问或交代工作"' in html
assert 'aria-label="发送消息"' in html
assert 'href="/assistant-chat.css?v=codex-assistant-26"' in html
assert 'src="/assistant-chat.js?v=codex-assistant-26"' in html
assert 'src="/assistant-chat.js?v=codex-assistant-27"' in html
def test_codex_assistant_assets_are_versioned_together_in_the_service_worker():
service_worker = (ROOT / "apps/web-console/service-worker.js").read_text(encoding="utf-8")
assert 'const CACHE_VERSION = "cognitive-agent-pad-v277"' in service_worker
assert 'const CACHE_VERSION = "cognitive-agent-pad-v282"' in service_worker
assert '"/assistant-chat.css?v=codex-assistant-26"' in service_worker
assert '"/assistant-chat.js?v=codex-assistant-26"' in service_worker
assert '"/assistant-chat.js?v=codex-assistant-27"' in service_worker
def test_codex_frontend_uses_one_streaming_endpoint_and_agent_work_events():
@@ -33,6 +33,11 @@ def test_codex_frontend_uses_one_streaming_endpoint_and_agent_work_events():
assert 'event.type === "answer.delta"' in script
assert '"work.started", "work.progress"' in script
assert 'event.type === "work.completed"' in script
assert 'event.type === "validated_control_commands"' in script
assert '"cognitive-agent:validated-can-commands"' in script
assert '"cognitive-agent:control-request-start"' in script
assert "speech_utterance_id:" in script
assert "speech_value:" in script
assert 'Object.prototype.hasOwnProperty.call(event, "answer")' in script
assert 'pendingController.abort("user_stop")' in script
assert 'signal.addEventListener("abort", cancelReader, { once: true })' in script
@@ -0,0 +1,410 @@
from __future__ import annotations
import json
import threading
import unittest
from copy import deepcopy
from cognitive_agent.application.codex_assistant import (
CodexAssistantService,
_VehicleAuthorization,
_VehicleTurnContext,
)
UTTERANCE = "空调温度设置到23度,打开闪烁灯"
def compiled_mixed_result() -> dict[str, object]:
return {
"request_id": "req-assistant-local-mixed",
"utterance": UTTERANCE,
"status": "processing",
"phase": "commands_validated",
"provider_used": "local_rules+infrared_local_rules",
"execution_mode": "dry_run",
"control_transport": "wired_can",
"function_configs": {
"native_climate": {
"spec": {
"domain": "climate",
"operations": [
{
"id": "climate-temperature-1",
"capability": "climate.set_temperature",
"target": {"zone": "driver"},
"parameters": {"temperature_c": 23},
"parse_source": "local_rule",
}
],
}
},
"self_extension": {
"spec": {
"domain": "self_extension",
"operations": [
{
"id": "infrared-1",
"capability": "infrared.emit",
"target": {"device": "toy-001", "name": "闪灯玩具"},
"parameters": {"action": "开启", "slot": 1},
"parse_source": "local_rule",
}
],
}
},
},
"command_plans": {
"self_extension": {
"spec": {
"domain": "self_extension",
"commands": [
{
"operation_ref": "unrelated-operation",
"message": "Native_0xBAD",
"signals": {
"debug_command": "must-not-cross-tool-boundary",
"frame_id": "0xBAD",
"data_hex": "DE AD BE EF",
},
},
{
"operation_ref": "infrared-1",
"bus": "TCP",
"message": "infrared.emit",
"signals": {"slot": 1},
},
],
}
},
"native_climate": {
"spec": {
"domain": "climate",
"commands": [
{
"operation_ref": "climate-temperature-1",
"bus": "body_can",
"message": "Native_0x1DE",
"signals": {
"debug_command": "ac temp driver 23",
"frame_id": "0x1DE",
"data_hex": "00 00 00 00 17 00 00 00",
},
}
],
}
},
},
"trace_events": [
{"stage": "intent_parse", "duration_ms": 2},
{"stage": "context_resolve", "duration_ms": 1},
{"stage": "domain_route", "duration_ms": 1},
{"stage": "function_generate", "duration_ms": 2},
{"stage": "schema_validate", "duration_ms": 1},
{"stage": "compile", "duration_ms": 2},
{"stage": "execute", "duration_ms": 999},
],
"raw_can": "must-not-cross-tool-boundary",
}
class ValidatedControlProjectionTest(unittest.TestCase):
def test_projection_exposes_only_exact_display_commands(self) -> None:
projection = CodexAssistantService._validated_control_payload(
compiled_mixed_result()
)
self.assertIsNotNone(projection)
assert projection is not None
commands = projection["validated_control_commands"]
assert isinstance(commands, list)
self.assertEqual(len(commands), 2)
by_capability = {
command["operation"]["capability"]: command for command in commands
}
climate = by_capability["climate.set_temperature"]
infrared = by_capability["infrared.emit"]
self.assertEqual(climate["actual_command"], "ac temp driver 23")
self.assertEqual(
climate["frames"],
[
{
"label": "CAN1 帧",
"value": "0x1DE · 00 00 00 00 17 00 00 00",
}
],
)
self.assertEqual(infrared["actual_command"], "infrared emit 1")
self.assertEqual(
infrared["frames"],
[
{
"label": "GSCU1B TCP",
"value": '{"action":"emit","data":{"no":1},"type":"infrared"}',
}
],
)
self.assertEqual(
[event["stage"] for event in projection["trace_events"]],
[
"intent_parse",
"context_resolve",
"domain_route",
"function_generate",
"schema_validate",
"compile",
],
)
serialized = json.dumps(projection, ensure_ascii=False)
self.assertNotIn("command_plans", serialized)
self.assertNotIn("data_hex", serialized)
self.assertNotIn("raw_can", serialized)
self.assertNotIn("must-not-cross-tool-boundary", serialized)
def test_projection_drops_secrets_and_sensitive_nested_keys(self) -> None:
result = compiled_mixed_result()
secret = "projection-secret-token"
climate_config = result["function_configs"]["native_climate"]
climate_config["spec"]["domain"] = f"climate-{secret}"
operation = climate_config["spec"]["operations"][0]
operation["target"].update(
{
"api_key": "API-LEAK",
"cookie": "COOKIE-LEAK",
}
)
operation["parameters"].update(
{
"credential": "CRED-LEAK",
"access_key": "ACCESS-LEAK",
}
)
operation["parse_source"] = f"local-{secret}"
result["command_plans"]["native_climate"]["spec"]["commands"][0][
"signals"
]["debug_command"] = f"ac temp driver 23 {secret}"
projection = CodexAssistantService._validated_control_payload(
result,
secret=secret,
)
self.assertIsNotNone(projection)
assert projection is not None
serialized = json.dumps(projection, ensure_ascii=False)
for forbidden in (
secret,
"API-LEAK",
"COOKIE-LEAK",
"CRED-LEAK",
"ACCESS-LEAK",
"api_key",
"credential",
"access_key",
):
self.assertNotIn(forbidden, serialized)
climate = next(
item
for item in projection["validated_control_commands"]
if item["operation"]["capability"] == "climate.set_temperature"
)
self.assertEqual(climate["domain"], "")
self.assertNotIn("parse_source", climate["operation"])
self.assertEqual(climate["operation"]["target"], {"zone": "driver"})
self.assertEqual(
climate["operation"]["parameters"],
{"temperature_c": 23},
)
self.assertEqual(climate["actual_command"], "")
def test_projection_drops_operation_when_identity_contains_secret(self) -> None:
result = compiled_mixed_result()
secret = "projection-secret-token"
operation = result["function_configs"]["native_climate"]["spec"][
"operations"
][0]
operation["id"] = f"climate-{secret}"
result["command_plans"]["native_climate"]["spec"]["commands"][0][
"operation_ref"
] = operation["id"]
projection = CodexAssistantService._validated_control_payload(
result,
secret=secret,
)
self.assertIsNotNone(projection)
assert projection is not None
serialized = json.dumps(projection, ensure_ascii=False)
self.assertNotIn(secret, serialized)
self.assertEqual(
[
item["operation"]["capability"]
for item in projection["validated_control_commands"]
],
["infrared.emit"],
)
def test_duplicate_operation_refs_never_cross_configuration_boundaries(self) -> None:
result = {
"function_configs": {
"native_climate": {
"spec": {
"domain": "climate",
"operations": [
{
"id": "op-1",
"capability": "climate.set_temperature",
"target": {"zone": "driver"},
"parameters": {"temperature_c": 23},
}
],
}
},
"native_seat": {
"spec": {
"domain": "seat",
"operations": [
{
"id": "op-1",
"capability": "seat.set_heating",
"target": {"zone": "passenger"},
"parameters": {"level": 2},
}
],
}
},
},
"command_plans": {
"native_seat": {
"spec": {
"domain": "seat",
"commands": [
{
"operation_ref": "op-1",
"bus": "seat_can0",
"signals": {
"debug_command": "seat passenger heat 2"
},
}
],
}
}
},
}
projection = CodexAssistantService._validated_control_payload(result)
self.assertIsNotNone(projection)
assert projection is not None
commands = projection["validated_control_commands"]
self.assertEqual(len(commands), 1)
self.assertEqual(
commands[0]["operation"]["capability"],
"seat.set_heating",
)
self.assertEqual(commands[0]["actual_command"], "seat passenger heat 2")
def test_compiled_semantic_operation_is_kept_without_debug_command(self) -> None:
result = deepcopy(compiled_mixed_result())
climate_command = result["command_plans"]["native_climate"]["spec"][
"commands"
][0]
climate_command["signals"].pop("debug_command")
projection = CodexAssistantService._validated_control_payload(result)
self.assertIsNotNone(projection)
assert projection is not None
climate = next(
item
for item in projection["validated_control_commands"]
if item["operation"]["capability"] == "climate.set_temperature"
)
self.assertEqual(climate["actual_command"], "")
self.assertEqual(
climate["frames"][0]["value"],
"0x1DE · 00 00 00 00 17 00 00 00",
)
def test_direct_stream_emits_projection_before_progress_and_completion(self) -> None:
client = type("UnavailableClient", (), {"available": False})()
assistant = CodexAssistantService(client)
events: list[dict[str, object]] = []
def execute_control(instruction, progress):
self.assertEqual(instruction, UTTERANCE)
result = compiled_mixed_result()
progress(result)
return {**result, "status": "succeeded"}
result = assistant.stream(
UTTERANCE,
conversation_id="conv-validated-projection",
principal="local-test",
history=[],
emit=events.append,
execute_control=execute_control,
control_terms=("空调", "闪烁灯"),
quick_control_commands=[UTTERANCE],
)
event_types = [event["type"] for event in events]
validated_index = event_types.index("validated_control_commands")
self.assertLess(validated_index, event_types.index("action.progress"))
self.assertLess(validated_index, event_types.index("action.completed"))
self.assertLess(validated_index, event_types.index("answer.completed"))
self.assertEqual(result["route"], "vehicle_control")
self.assertEqual(result["success"], True)
safe_result = CodexAssistantService._safe_vehicle_result(
compiled_mixed_result()
)
self.assertNotIn("command_plans", safe_result)
self.assertNotIn("validated_control_commands", safe_result)
def test_agent_tool_path_queues_projection_for_the_browser_thread(self) -> None:
client = type("UnavailableClient", (), {"available": False})()
assistant = CodexAssistantService(client)
request_token = "projection-secret-token"
def execute_control(instruction, progress):
self.assertEqual(instruction, UTTERANCE)
result = compiled_mixed_result()
progress(result)
return {**result, "status": "succeeded"}
context = _VehicleTurnContext(
original_message=UTTERANCE,
execute_control=execute_control,
authorization=_VehicleAuthorization(
instruction=UTTERANCE,
reason="explicit_registered_control",
),
capability_snapshot={},
query_capabilities=lambda _snapshot, _query: {},
lock=threading.Lock(),
)
assistant._tool_contexts[request_token] = context
safe_result = assistant.execute_vehicle_control(request_token, UTTERANCE)
queued_events = []
while not context.events.empty():
queued_events.append(context.events.get_nowait())
event_types = [event["type"] for event in queued_events]
self.assertEqual(event_types[0], "action.started")
self.assertLess(
event_types.index("validated_control_commands"),
event_types.index("action.progress"),
)
self.assertLess(
event_types.index("validated_control_commands"),
event_types.index("action.completed"),
)
self.assertNotIn("command_plans", safe_result)
self.assertNotIn(request_token, json.dumps(queued_events, ensure_ascii=False))
if __name__ == "__main__":
unittest.main()
+285 -23
View File
@@ -395,6 +395,8 @@ def _stream(
history: list[dict[str, str]] | None = None,
emit: Callable[[dict[str, object]], bool | None] | None = None,
execute_control: Callable[..., dict[str, object]] | None = None,
preview_control: Callable[..., dict[str, object]] | None = None,
supplement_control: Callable[..., dict[str, object]] | None = None,
capability_snapshot: dict[str, object] | None = None,
capability_context: dict[str, object] | None = None,
mode_profile_draft_context: dict[str, object] | None = None,
@@ -409,6 +411,8 @@ def _stream(
emit=emit or (lambda event: events.append(event)),
execute_control=execute_control
or (lambda _instruction, _progress: pytest.fail("vehicle control was unexpected")),
preview_control=preview_control,
supplement_control=supplement_control,
control_terms=CONTROL_TERMS,
capability_snapshot=capability_snapshot,
capability_context=capability_context,
@@ -453,6 +457,15 @@ def test_general_question_streams_from_codex_without_vehicle_execution() -> None
assert "Codex AI 助手" not in client.thread_instructions[0]
assert client.thread_ephemeral == [True]
assert client.turn_calls[0]["text"] == "解释一下 CAN FD"
assert client.turn_calls[0]["application_context"][
"natural_language_routing"
] == {
"original_utterance": "解释一下 CAN FD",
"parsed_clauses": [],
"resolved_clauses": [],
"unresolved_clauses": ["解释一下 CAN FD"],
"cloud_input_utterance": "解释一下 CAN FD",
}
assert "不可信的浏览器系统指令" not in client.turn_calls[0]["text"]
assert client.finished[0].turn_id == "turn-1"
@@ -991,34 +1004,52 @@ def test_hot_cabin_calls_one_explicit_cooling_instruction_and_streams_verified_r
assistant.execute_vehicle_control(token, COOLING_INSTRUCTION)
def test_explicit_vehicle_request_without_tool_call_fails_closed() -> None:
client = FakeCodexClient()
def test_explicit_vehicle_request_uses_local_route_without_codex() -> None:
client = FakeCodexClient(available=False)
assistant = CodexAssistantService(client)
control_calls: list[str] = []
def on_start_turn(
_text: str,
_request_token: str,
turn: ActiveCodexTurn,
) -> None:
_put_answer(turn, "已经为你打开遮阳帘。", deltas=("已经为你", "打开遮阳帘。"))
def execute_control(
instruction: str,
progress: Callable[[dict[str, object]], None],
) -> dict[str, object]:
control_calls.append(instruction)
progress({"status": "processing", "request_id": "req-local-shade"})
return {
"request_id": "req-local-shade",
"status": "succeeded",
"execution_mode": "dry_run",
"utterance": instruction,
}
client.on_start_turn = on_start_turn
result, events = _stream(assistant, "打开遮阳帘")
result, events = _stream(
assistant,
"打开遮阳帘",
execute_control=execute_control,
preview_control=execute_control,
supplement_control=lambda *_args: pytest.fail(
"fully local control must not invoke cloud supplement"
),
)
assert result["answer"].startswith("车辆控制未完成")
assert "未向车辆下发任何操作" in result["answer"]
assert "已经为你" not in str(events)
assert control_calls == ["打开遮阳帘"]
assert result["route"] == "vehicle_control"
assert result["success"] is True
assert "打开遮阳帘" in result["answer"]
assert [event["type"] for event in events] == [
"message.accepted",
"work.started",
"action.started",
"action.progress",
"action.completed",
"answer.delta",
"work.completed",
"answer.completed",
]
assert events[2]["success"] is False
assert events[4]["success"] is False
assert events[-1]["answer"] == result["answer"]
assert events[0]["engine"] == "n7web-control-router"
assert events[1]["display_text"] == "正在本地解析并下发明确控制指令……"
assert not client.thread_instructions
assert not client.turn_calls
def test_exact_server_owned_quick_control_executes_without_codex() -> None:
@@ -1057,22 +1088,253 @@ def test_exact_server_owned_quick_control_executes_without_codex() -> None:
assert not client.turn_calls
def test_quick_control_requires_exact_normalized_command_match() -> None:
client = FakeCodexClient()
def test_explicit_control_does_not_require_exact_quick_card_match() -> None:
client = FakeCodexClient(available=False)
assistant = CodexAssistantService(client)
control_calls: list[str] = []
def execute_control(
instruction: str,
_progress: Callable[[dict[str, object]], None],
) -> dict[str, object]:
control_calls.append(instruction)
return {
"request_id": "req-explicit-local",
"status": "succeeded",
"execution_mode": "dry_run",
"utterance": instruction,
}
result, events = _stream(
assistant,
"请关闭所有车窗",
execute_control=lambda instruction, _progress: control_calls.append(instruction),
execute_control=execute_control,
preview_control=execute_control,
supplement_control=lambda *_args: pytest.fail(
"fully local control must not invoke cloud supplement"
),
quick_control_commands=["关闭所有车窗"],
)
assert events[0]["route"] == "agent"
assert result["answer"].startswith("车辆控制未完成")
assert not control_calls
assert client.turn_calls
assert events[0]["route"] == "vehicle_control"
assert result["success"] is True
assert control_calls == ["请关闭所有车窗"]
assert not client.turn_calls
def test_mixed_control_streams_local_result_before_unresolved_codex_turn() -> None:
client = FakeCodexClient()
assistant = CodexAssistantService(client)
order: list[str] = []
original = "空调温度设置为23度并把遮阳帘调整到适合观影"
local_clause = "空调温度设置为23度"
unresolved_clause = "把遮阳帘调整到适合观影"
preview_result = {
"request_id": "req-local-preview",
"status": "partially_succeeded",
"execution_mode": "dry_run",
"utterance": original,
"provider_used": "local_rules",
"local_control_budget_ms": 1000,
"local_control_elapsed_ms": 18.0,
"local_control_within_budget": True,
"natural_language_routing": {
"original_utterance": original,
"parsed_clauses": [local_clause],
"resolved_clauses": [local_clause],
"unresolved_clauses": [unresolved_clause],
},
"function_configs": {
"climate": {
"spec": {
"domain": "climate",
"operations": [
{
"id": "local-temperature",
"capability": "climate.set_temperature",
"target": {"zone": "driver"},
"parameters": {"temperature_c": 23},
"parse_source": "local_rule",
}
],
}
}
},
"command_plans": {
"climate": {
"spec": {
"domain": "climate",
"commands": [
{
"operation_ref": "local-temperature",
"bus": "body-can1",
"message": "climate.set_temperature",
"signals": {"debug_command": "ac temp driver 23"},
}
],
}
}
},
}
def preview_control(
instruction: str,
progress: Callable[[dict[str, object]], None],
) -> dict[str, object]:
assert instruction == original
order.append("local_preview")
progress(preview_result)
return preview_result
def supplement_control(
instruction: str,
progress: Callable[[dict[str, object]], None],
plan: dict[str, object],
) -> dict[str, object]:
assert instruction == unresolved_clause
assert plan["requests"][0]["capability"] == "roof.set_shade_position"
order.append("cloud_supplement")
result = {
"request_id": "req-cloud-supplement",
"status": "succeeded",
"execution_mode": "dry_run",
"utterance": original,
}
progress(result)
return result
cloud_plan = {
"detected_domains": ["roof"],
"requests": [
{
"domain": "roof",
"capability": "roof.set_shade_position",
"target_scope": "vehicle",
"operation": "set",
"duration_ms": None,
"parameters": [{"name": "position_pct", "value": 100}],
"reason": "补足未解析的观影遮光需求。",
}
],
"ambiguities": [],
"decision_summary": ["只处理未解析的遮阳帘子句。"],
"confidence": 0.9,
}
def on_start_turn(
text: str,
request_token: str,
turn: ActiveCodexTurn,
) -> None:
order.append("cloud_turn")
assert text == unresolved_clause
assistant.execute_cockpit_request(request_token, cloud_plan)
_put_answer(turn, "本地温度已先行处理,遮阳帘补充控制已完成。")
client.on_start_turn = on_start_turn
result, events = _stream(
assistant,
original,
preview_control=preview_control,
supplement_control=supplement_control,
)
assert order == ["local_preview", "cloud_turn", "cloud_supplement"]
assert client.turn_calls[0]["text"] == unresolved_clause
assert client.turn_calls[0]["application_context"][
"natural_language_routing"
] == {
"original_utterance": original,
"parsed_clauses": [local_clause],
"resolved_clauses": [local_clause],
"unresolved_clauses": [unresolved_clause],
"cloud_input_utterance": unresolved_clause,
}
validated_index = next(
index
for index, event in enumerate(events)
if event["type"] == "validated_control_commands"
)
cloud_progress_index = next(
index
for index, event in enumerate(events)
if event.get("display_text")
== "本地明确子句已处理,正在补充未解析子句……"
)
assert validated_index < cloud_progress_index
assert result["route"] == "agent"
def test_fully_unresolved_control_starts_bounded_cloud_turn_and_fails_closed() -> None:
client = FakeCodexClient()
assistant = CodexAssistantService(client)
instruction = "把遮阳帘调整到适合观影"
preview_result = {
"request_id": "req-unresolved-preview",
"preview_request_id": "req-unresolved-preview",
"status": "no_action",
"execution_mode": "dry_run",
"utterance": instruction,
"provider_used": "local_rules",
"function_configs": {},
"command_plans": {},
"local_control_budget_ms": 1000,
"local_control_elapsed_ms": 4.0,
"local_control_within_budget": True,
"supplement_required": True,
"natural_language_routing": {
"original_utterance": instruction,
"parsed_clauses": [],
"resolved_clauses": [],
"unresolved_clauses": [instruction],
},
}
def preview_control(
text: str,
_progress: Callable[[dict[str, object]], None],
) -> dict[str, object]:
assert text == instruction
return preview_result
def on_start_turn(
text: str,
_request_token: str,
turn: ActiveCodexTurn,
) -> None:
assert text == instruction
# The browser must never receive this unsupported execution claim
# because no guarded vehicle tool call follows it.
_put_answer(turn, "遮阳帘已经调整完成。")
client.on_start_turn = on_start_turn
result, events = _stream(
assistant,
instruction,
preview_control=preview_control,
supplement_control=lambda *_args: pytest.fail(
"a model answer without a tool call must not execute control"
),
)
assert client.turn_calls[0]["text"] == instruction
assert client.turn_calls[0]["application_context"][
"natural_language_routing"
] == {
"original_utterance": instruction,
"parsed_clauses": [],
"resolved_clauses": [],
"unresolved_clauses": [instruction],
"cloud_input_utterance": instruction,
}
assert not any(
event["type"] == "validated_control_commands" for event in events
)
assert result == {
"route": "agent",
"answer": "车辆控制未完成:AI 助手没有调用受控执行链,未向车辆下发任何操作。",
}
assert events[-1]["answer"] == result["answer"]
@pytest.mark.parametrize(
@@ -35,10 +35,10 @@ def test_energy_monitor_assets_are_versioned_and_cached() -> None:
):
assert asset in html
assert f'"{asset}"' in service_worker
assert html.index("/assistant-chat.js?v=codex-assistant-26") < html.index(
assert html.index("/assistant-chat.js?v=codex-assistant-27") < html.index(
"/energy-monitor.js?v=energy-monitor-4"
)
assert 'const CACHE_VERSION = "cognitive-agent-pad-v277"' in service_worker
assert 'const CACHE_VERSION = "cognitive-agent-pad-v282"' in service_worker
def test_energy_monitor_uses_the_fifth_shared_dock_icon_and_starts_closed() -> None:
@@ -160,7 +160,7 @@ def test_energy_monitor_tracks_ai_and_direct_control_activity() -> None:
assert "completeControlActivity" in script
assert 'event.type === "action.progress" && event.payload' in assistant
assert 'new CustomEvent("cognitive-agent:vehicle-control-activity"' in assistant
assert 'new CustomEvent("cognitive-agent:validated-can-commands"' not in assistant
assert '"cognitive-agent:validated-can-commands"' in assistant
def test_energy_monitor_reads_persistent_seat_state_for_both_seats() -> None:
+2 -2
View File
@@ -41,13 +41,13 @@ def test_theme_asset_versions_are_consistent_with_the_pwa_shell() -> None:
for asset in (
"/styles.css?v=dual-seat-23",
"/console-v2.css?v=delivery-state-66",
"/console-v2.css?v=delivery-state-68",
"/self-extension.css?v=gscu-tcp-43",
"/speech-input.css?v=sensevoice-7",
):
assert asset in html
assert f'"{asset}"' in service_worker
assert 'const CACHE_VERSION = "cognitive-agent-pad-v277"' in service_worker
assert 'const CACHE_VERSION = "cognitive-agent-pad-v282"' in service_worker
def test_vehicle_twin_icons_and_nested_overlays_use_semantic_tokens() -> None:
+59
View File
@@ -42,10 +42,22 @@ class _FakeMqttClient:
self.on_message = None
self.subscriptions: list[tuple[str, int]] = []
self.published: list[tuple[str, str, int, bool]] = []
self.websocket_options: list[dict[str, object]] = []
self.credentials: tuple[str, str | None] | None = None
self.tls_configured = False
def reconnect_delay_set(self, **_kwargs) -> None:
return None
def ws_set_options(self, **kwargs: object) -> None:
self.websocket_options.append(kwargs)
def username_pw_set(self, username: str, password: str | None) -> None:
self.credentials = (username, password)
def tls_set(self) -> None:
self.tls_configured = True
def connect_async(self, host: str, port: int, keepalive: int) -> None:
self.connection = (host, port, keepalive)
@@ -89,6 +101,8 @@ def _settings():
"password": None,
"tls_enabled": False,
"feedback_timeout_ms": 100,
"transport": "tcp",
"websocket_path": "/mqtt",
}
values["public_info"] = lambda: {
"enabled": True,
@@ -109,9 +123,31 @@ class MqttCanPayloadTests(unittest.TestCase):
self.assertEqual(settings.condition_topic, "n7/n7-headunit/can/state")
self.assertEqual(settings.control_topic, "n7/n7-headunit/can/command")
self.assertFalse(settings.approval_required)
self.assertEqual(settings.transport, "tcp")
self.assertEqual(settings.websocket_path, "/mqtt")
self.assertEqual(info["state_contract"], "n7-can-signals/v1")
self.assertEqual(info["control_contract"], "n7-headunit-can-command/v1")
def test_websocket_transport_settings_are_loaded_from_environment(self) -> None:
with patch.dict(
"os.environ",
{
"COGNITIVE_AGENT_VEHICLE_MQTT_HOST": "mqtt.example.test",
"COGNITIVE_AGENT_VEHICLE_MQTT_PORT": "443",
"COGNITIVE_AGENT_VEHICLE_MQTT_TRANSPORT": "websocket",
"COGNITIVE_AGENT_VEHICLE_MQTT_WEBSOCKET_PATH": "/mqtt",
"COGNITIVE_AGENT_VEHICLE_MQTT_TLS": "true",
},
clear=True,
):
settings = VehicleMqttSettings.from_env()
self.assertTrue(settings.adapter_configured)
self.assertEqual(settings.transport, "websockets")
self.assertEqual(settings.websocket_path, "/mqtt")
self.assertTrue(settings.tls_enabled)
self.assertEqual(settings.public_info()["transport"], "websockets")
def test_explicit_shared_approval_policy_still_locks_mqtt_control(self) -> None:
with patch.dict(
"os.environ",
@@ -461,6 +497,29 @@ class MqttCanTransportTests(unittest.TestCase):
def tearDown(self) -> None:
self.transport.close()
def test_websocket_client_applies_transport_path_authentication_and_tls(self) -> None:
client = _FakeMqttClient()
settings = _settings()
settings.transport = "websockets"
settings.websocket_path = "/mqtt"
settings.username = "n7web"
settings.password = "secret"
settings.tls_enabled = True
transport = MqttCanTransport(settings)
with patch("paho.mqtt.client.Client", return_value=client) as constructor:
transport.start()
try:
self.assertEqual(
constructor.call_args.kwargs["transport"], "websockets"
)
self.assertEqual(client.websocket_options, [{"path": "/mqtt"}])
self.assertEqual(client.credentials, ("n7web", "secret"))
self.assertTrue(client.tls_configured)
self.assertEqual(client.connection, ("127.0.0.1", 1883, 30))
finally:
transport.close()
def test_condition_frames_use_existing_decoder_and_snapshot_contract(self) -> None:
self.client.emit(
"n7/n7-headunit/can/state",
+3 -2
View File
@@ -137,7 +137,8 @@ def test_main_parsed_control_panel_renders_validated_infrared_operations() -> No
assert 'capability === "infrared.emit"' in console
assert 'channel: "IR"' in console
assert 'frameLabel: "GSCU1B TCP"' in console
assert "已解析控制指令" in console
assert "下发控制指令" in console
assert "已解析控制指令" not in console
assert 'li[data-can-channel="IR"]' in styles
assert "console-v2.js?v=delivery-state-62" in html
assert "console-v2.js?v=local-first-66" in html
assert 'speech_semantic: "语音语义"' in console
@@ -195,9 +195,15 @@ def test_left_sidebar_exposes_collapsed_parsed_can_command_panel() -> None:
assert 'canCommandPanel.id = "v2CanCommandPanel"' in console
assert 'id="v2CanCommandCount">0</strong>' in console
assert 'id="v2CanCommandList"' in console
assert "canCommandPanel.open" not in console
assert 'id="v2CommandTiming"' in console
assert "panel.open = true;" in console
assert "if (ui.sidebarCollapsed) setSidebarCollapsed(false);" in console
assert "function parsedCanCommandForOperation(operation = {})" in console
assert "function parsedCanCommands(result = {})" in console
assert "function semanticControlInstruction(operation = {})" in console
assert "function compiledCommandsForOperation(result, configKey, operation = {})" in console
assert "function parsedCommandTimingRows(result = {}, timingState = {})" in console
assert "result.validated_control_commands" in console
assert "getParsedCanCommands: (result) => parsedCanCommands(result)" in console
assert "const configurations = result.function_configs || {};" in console
assert "if (!configurationEntries.length && result.function_config)" in console
@@ -206,8 +212,18 @@ def test_left_sidebar_exposes_collapsed_parsed_can_command_panel() -> None:
assert 'const command = (category, text, channel = "CAN1", frame = "")' in console
assert 'data-can-channel="${escapeHtml(item.channel)}"' in console
assert 'class="v2-can-command-channel"' in console
assert 'class="v2-can-command-layer v2-can-command-semantic"' in console
assert 'class="v2-can-command-layer v2-can-command-actual"' in console
assert console.index("语义控制指令") < console.index("实际控制指令")
assert 'recordTiming: true' in console
assert 'reveal: true' in console
assert 'window.addEventListener("cognitive-agent:speech-state"' in console
assert 'window.addEventListener("cognitive-agent:speech-result"' in console
assert 'window.addEventListener("cognitive-agent:control-request-start"' in console
assert 'window.addEventListener("cognitive-agent:result"' in console
assert '$("#requestForm")?.addEventListener("submit"' in console
assert "function commandResultMatchesTimedRequest(result = {})" in console
assert "matchesTimedRequest && !validatedCommandsVisible" in console
for command_contract in (
"door rear ${action}",
@@ -246,6 +262,9 @@ def test_left_sidebar_exposes_collapsed_parsed_can_command_panel() -> None:
assert ".v2-can-command-list li code" in styles
assert ".v2-can-command-channel" in styles
assert ".v2-can-command-delivery" in styles
assert ".v2-command-timing" in styles
assert ".v2-can-command-semantic" in styles
assert ".v2-can-command-actual" in styles
assert '.v2-can-command-delivery[data-delivery-state="submitted"]' in styles
assert 'li[data-can-channel="CAN0"]' in styles
assert ".v2-sidebar-collapsed .v2-can-command-panel" in styles
@@ -297,10 +316,12 @@ def test_parsed_can_command_panel_combines_can0_seat_and_can1_vehicle_commands()
"process.stdout.write(JSON.stringify(parsedCanCommands(sample)));"
)
completed = subprocess.run(
[node, "-e", script],
[node, "-"],
input=script,
check=True,
capture_output=True,
text=True,
encoding="utf-8",
)
assert json.loads(completed.stdout) == [
@@ -310,6 +331,8 @@ def test_parsed_can_command_panel_combines_can0_seat_and_can1_vehicle_commands()
"command": "seat passenger backrest position 36",
"frame": "0x300 · 01 12 <SEQ_LO> <SEQ_HI> 10 0E 30 75",
"parseSource": "local_rule",
"semantic": "seat.set_backrest_recline(target=front-right, angle_deg=120, position_pct=36)",
"operationRef": "",
"deliveryState": "not_submitted",
"deliveryLabel": "未下发",
"deliveryTransport": "有线 CAN",
@@ -319,6 +342,8 @@ def test_parsed_can_command_panel_combines_can0_seat_and_can1_vehicle_commands()
"category": "空调温度",
"command": "ac temp driver 24",
"parseSource": "cloud_supplement",
"semantic": "climate.set_temperature(target=driver, temperature_c=24)",
"operationRef": "",
"deliveryState": "not_submitted",
"deliveryLabel": "未下发",
"deliveryTransport": "有线 CAN",
@@ -326,6 +351,335 @@ def test_parsed_can_command_panel_combines_can0_seat_and_can1_vehicle_commands()
]
def test_local_mixed_command_uses_compiled_can_and_ir_details_with_stage_timings() -> None:
node = shutil.which("node")
if node is None:
return
console = source("console-v2.js")
start = console.index(" const normalizedCanTarget")
end = console.index("\n\n function renderParsedCanCommands", start)
helpers = console[start:end]
sample = {
"provider_used": "local_rules",
"function_configs": {
"native_climate": {
"spec": {
"domain": "climate",
"operations": [
{
"id": "climate-temperature-1",
"capability": "climate.set_temperature",
"target": {"zone": "driver"},
"parameters": {"temperature_c": 23},
"parse_source": "local_rule",
}
],
}
},
"self_extension": {
"spec": {
"domain": "self_extension",
"operations": [
{
"id": "infrared-1",
"capability": "infrared.emit",
"target": {"device": "toy-001", "name": "闪灯玩具"},
"parameters": {"slot": 1, "action": "开启"},
"parse_source": "local_rule",
}
],
}
},
},
# Deliberately reverse the plan order and include a distracting command;
# the renderer must join only on the exact operation_ref.
"command_plans": {
"self_extension": {
"spec": {
"domain": "self_extension",
"commands": [
{
"operation_ref": "unrelated-ir",
"message": "infrared.emit",
"signals": {"slot": 9},
},
{
"operation_ref": "infrared-1",
"bus": "TCP",
"message": "infrared.emit",
"signals": {"slot": 1},
},
],
}
},
"native_climate": {
"spec": {
"domain": "climate",
"commands": [
{
"operation_ref": "climate-temperature-1",
"bus": "body_can",
"message": "Native_0x1DE",
"signals": {
"debug_command": "ac temp driver 23",
"frame_id": "0x1DE",
"data_hex": "00 00 00 00 17 00 00 00",
},
}
],
}
},
},
"trace_events": [
{"stage": "local_peripheral_plan", "duration_ms": 1},
{"stage": "nfc_peripheral_sequence", "duration_ms": 0},
{"stage": "intent_parse", "duration_ms": 12},
{"stage": "context_resolve", "duration_ms": 1},
{"stage": "compile", "duration_ms": 2},
{"stage": "execute", "duration_ms": 999},
],
}
timing_state = {
"inputMode": "speech",
"speechStartedAt": 0,
"speechResultAt": 80,
"speechTimings": {
"audio_read_ms": 1,
"vad_ms": 2,
"asr_ms": 30,
"assembly_ms": 1,
"semantic_ms": 3,
"command_resolution_ms": 1,
"recovery_ms": 0,
"total_ms": 37,
},
"requestStartedAt": 100,
"validatedAt": 125,
"displayedAt": 126,
"renderDurationMs": 1,
}
browser_projection = {
"provider_used": "local_rules+infrared_local_rules",
"execution_mode": "dry_run",
"validated_control_commands": [
{
"operation": sample["function_configs"]["native_climate"]["spec"][
"operations"
][0],
"domain": "climate",
"channel": "CAN1",
"actual_command": "ac temp driver 23",
"frames": [
{
"label": "CAN1 帧",
"value": "0x1DE · 00 00 00 00 17 00 00 00",
}
],
},
{
"operation": sample["function_configs"]["self_extension"]["spec"][
"operations"
][0],
"domain": "self_extension",
"channel": "IR",
"actual_command": "infrared emit 1",
"frames": [
{
"label": "GSCU1B TCP",
"value": '{"action":"emit","data":{"no":1},"type":"infrared"}',
}
],
},
],
"trace_events": sample["trace_events"][:3],
}
script = (
f"{helpers}\n"
f"const sample = {json.dumps(sample, ensure_ascii=False)};\n"
f"const browserProjection = {json.dumps(browser_projection, ensure_ascii=False)};\n"
f"const timingState = {json.dumps(timing_state)};\n"
"process.stdout.write(JSON.stringify({"
"commands: parsedCanCommands(sample), "
"projectedCommands: parsedCanCommands(browserProjection), "
"timings: parsedCommandTimingRows(sample, timingState)"
"}));"
)
completed = subprocess.run(
[node, "-"],
input=script,
check=True,
capture_output=True,
text=True,
encoding="utf-8",
)
parsed = json.loads(completed.stdout)
climate, infrared = parsed["commands"]
projected_climate, projected_infrared = parsed["projectedCommands"]
assert climate["semantic"] == (
"climate.set_temperature(target=driver, temperature_c=23)"
)
assert climate["command"] == "ac temp driver 23"
assert climate["frame"] == "0x1DE · 00 00 00 00 17 00 00 00"
assert climate["frameLabel"] == "CAN1 帧"
assert climate["parseSource"] == "local_rule"
assert infrared["semantic"] == (
"infrared.emit(target=toy-001, action=开启, slot=1)"
)
assert infrared["command"] == "infrared emit 1"
assert infrared["frameLabel"] == "GSCU1B TCP"
assert json.loads(infrared["frame"])["data"]["no"] == 1
assert infrared["parseSource"] == "local_rule"
assert projected_climate["semantic"] == climate["semantic"]
assert projected_climate["command"] == climate["command"]
assert projected_climate["frames"] == climate["frames"]
assert projected_infrared["semantic"] == infrared["semantic"]
assert projected_infrared["command"] == infrared["command"]
assert projected_infrared["frames"] == infrared["frames"]
timing_by_key = {item["key"]: item for item in parsed["timings"]}
assert timing_by_key["local_peripheral_plan-0"]["label"] == "红外本地规则编译"
assert timing_by_key["nfc_peripheral_sequence-1"]["label"] == "红外指令序列生成"
assert timing_by_key["intent_parse-2"]["label"] == "本地规则意图解析"
assert timing_by_key["request_to_validated"]["durationMs"] == 25
assert timing_by_key["dom_render"]["durationMs"] == 1
assert timing_by_key["request_to_display"]["durationMs"] == 26
assert timing_by_key["speech_to_display"]["durationMs"] == 126
assert timing_by_key["total_ms"]["durationMs"] == 37
assert all(item["durationMs"] >= 0 for item in parsed["timings"])
assert not any(item["key"].startswith("execute") for item in parsed["timings"])
def test_duplicate_operation_refs_do_not_borrow_another_configs_plan() -> None:
node = shutil.which("node")
if node is None:
return
console = source("console-v2.js")
start = console.index(" const normalizedCanTarget")
end = console.index("\n\n function renderParsedCanCommands", start)
helpers = console[start:end]
sample = {
"function_configs": {
"native_climate": {
"spec": {
"domain": "climate",
"operations": [
{
"id": "op-1",
"capability": "climate.set_temperature",
"target": {"zone": "driver"},
"parameters": {"temperature_c": 23},
}
],
}
},
"native_seat": {
"spec": {
"domain": "seat",
"operations": [
{
"id": "op-1",
"capability": "seat.set_heating",
"target": {"zone": "passenger"},
"parameters": {"level": 2},
}
],
}
},
},
"command_plans": {
"native_seat": {
"spec": {
"domain": "seat",
"commands": [
{
"operation_ref": "op-1",
"bus": "seat_can0",
"signals": {
"debug_command": "seat passenger heat 2",
"frame_id": "0x300",
"data_hex": "01 02 03 04 05 06 07 08",
},
}
],
}
}
},
}
script = (
f"{helpers}\n"
f"const sample = {json.dumps(sample)};\n"
"process.stdout.write(JSON.stringify(parsedCanCommands(sample)));"
)
completed = subprocess.run(
[node, "-"],
input=script,
check=True,
capture_output=True,
text=True,
encoding="utf-8",
)
parsed = json.loads(completed.stdout)
by_semantic = {item["semantic"]: item for item in parsed}
climate = by_semantic[
"climate.set_temperature(target=driver, temperature_c=23)"
]
seat = by_semantic["seat.set_heating(target=passenger, level=2)"]
assert climate["command"] == "ac temp driver 23"
assert "frame" not in climate
assert climate["channel"] == "CAN1"
assert seat["command"] == "seat passenger heat 2"
assert seat["channel"] == "CAN0"
def test_command_timing_rejects_stale_request_events() -> None:
node = shutil.which("node")
if node is None:
return
console = source("console-v2.js")
correlation_start = console.index(" function commandCorrelationIds")
correlation_end = console.index(
"\n\n function rememberValidatedCommandContext",
correlation_start,
)
correlation_helper = console[correlation_start:correlation_end]
start = console.index(" const normalizedCanTarget")
end = console.index("\n\n function renderParsedCanCommands", start)
helpers = console[start:end]
script = (
"let commandTimingState = {requestStartedAt: 1, requestId: '', "
"utterance: '空调温度设置到23度,打开闪烁灯'};\n"
f"{correlation_helper}\n"
f"{helpers}\n"
"const first = commandResultMatchesTimedRequest({request_id: 'req-current', "
"utterance: '空调温度设置到23度,打开闪烁灯'});\n"
"const staleId = commandResultMatchesTimedRequest({request_id: 'req-old', "
"utterance: '空调温度设置到23度,打开闪烁灯'});\n"
"const staleText = commandResultMatchesTimedRequest({request_id: 'req-current', "
"utterance: '打开车窗'});\n"
"process.stdout.write(JSON.stringify({first, staleId, staleText, "
"boundRequestId: commandTimingState.requestId}));"
)
completed = subprocess.run(
[node, "-"],
input=script,
check=True,
capture_output=True,
text=True,
encoding="utf-8",
)
assert json.loads(completed.stdout) == {
"first": True,
"staleId": False,
"staleText": False,
"boundRequestId": "req-current",
}
def test_parsed_command_delivery_is_independent_of_transport_and_target() -> None:
node = shutil.which("node")
if node is None:
@@ -398,10 +752,12 @@ def test_parsed_command_delivery_is_independent_of_transport_and_target() -> Non
"parsedCanCommands(sample))));"
)
completed = subprocess.run(
[node, "-e", script],
[node, "-"],
input=script,
check=True,
capture_output=True,
text=True,
encoding="utf-8",
)
parsed = json.loads(completed.stdout)
@@ -423,6 +779,7 @@ def test_parsed_command_delivery_is_independent_of_transport_and_target() -> Non
def test_mode_editor_renders_readable_commands_with_expandable_mqtt_sequences() -> None:
app = source("app.js")
console = source("console-v2.js")
styles = source("styles.css")
html = source("index.html")
@@ -442,6 +799,10 @@ def test_mode_editor_renders_readable_commands_with_expandable_mqtt_sequences()
assert 'executionMode: "dry_run"' in app
assert 'clientType: "mode_editor_compile"' in app
assert "previewOnly: true" in app
assert 'const nonDispatchTaskSources = new Set(["mode_editor_compile"])' in console
assert "if (isDispatchTask(task))" in console
assert "下发控制指令" in console
assert "已解析控制指令" not in console
assert "function executeSavedModeProfile(mode)" in app
assert "/api/mode-profiles/${encodeURIComponent(mode.id)}/execute" in app
assert '$("#utterance").value = mode.name || mode.id;' in app
@@ -450,6 +811,9 @@ def test_mode_editor_renders_readable_commands_with_expandable_mqtt_sequences()
assert "生成指令" in html
assert ">执行</span></button>" not in html
assert 'id="hardwareNote"' not in html
assert 'id="hardwareAliasNote"' not in html
assert 'id="vehicleTransportNote"' not in html
assert ".mode-editor-command-card" in styles
assert ".mode-editor-command-meta" in styles
@@ -544,10 +908,12 @@ def test_mode_editor_prepares_one_to_many_mqtt_message_sequences() -> None:
"process.stdout.write(JSON.stringify(modeEditorReadableCommands(flow)));"
)
completed = subprocess.run(
[node, "-e", script],
[node, "-"],
input=script,
check=True,
capture_output=True,
text=True,
encoding="utf-8",
)
commands = json.loads(completed.stdout)
@@ -619,10 +985,12 @@ def test_left_sidebar_extracts_speech_and_peripheral_control_commands() -> None:
"}));"
)
completed = subprocess.run(
[node, "-e", script],
[node, "-"],
input=script,
check=True,
capture_output=True,
text=True,
encoding="utf-8",
)
output = json.loads(completed.stdout)
@@ -924,7 +1292,8 @@ def test_assistant_mode_preview_groups_validated_actions_for_the_card() -> None:
"process.stdout.write(JSON.stringify(modeDraftRows(draft, actions)));"
)
completed = subprocess.run(
[node, "-e", script],
[node, "-"],
input=script,
check=True,
capture_output=True,
text=True,
+9 -1
View File
@@ -792,7 +792,15 @@ def test_unknown_mode_uses_one_structured_turn_then_local_dry_run() -> None:
"roof.set_shade_position",
]
call = client.structured_calls[0]
assert call["application_context"] == _scene_context()
expected_context = _scene_context()
expected_context["natural_language_routing"] = {
"original_utterance": "构建一个星夜模式",
"parsed_clauses": [],
"resolved_clauses": [],
"unresolved_clauses": ["构建一个星夜模式"],
"cloud_input_utterance": "构建一个星夜模式",
}
assert call["application_context"] == expected_context
assert call["output_schema"]["type"] == "object"
assert call["output_schema"]["additionalProperties"] is False
assert "requests" in call["output_schema"]["required"]
+476 -2
View File
@@ -1,6 +1,7 @@
from __future__ import annotations
from pathlib import Path
from types import SimpleNamespace
from unittest.mock import patch
from cognitive_agent.application.seat_service import SeatDemoService
@@ -47,6 +48,19 @@ def test_preview_validates_local_controls_without_calling_cloud(tmp_path: Path)
assert result["status"] == "succeeded"
assert result["supplement_required"] is True
assert result["unresolved_clauses"] == ["播放音乐"]
assert result["parsed_clauses"] == [
"空调温度设置为24度",
"风量设置为2档",
]
assert result["natural_language_routing"] == {
"original_utterance": "空调温度设置为24度,风量设置为2档,播放音乐",
"parsed_clauses": ["空调温度设置为24度", "风量设置为2档"],
"resolved_clauses": ["空调温度设置为24度", "风量设置为2档"],
"unresolved_clauses": ["播放音乐"],
}
assert result["local_control_budget_ms"] == 1000
assert result["local_control_elapsed_ms"] < 1000
assert result["local_control_within_budget"] is True
assert [item["capability"] for item in _operations(result)] == [
"climate.set_temperature",
"climate.set_fan_level",
@@ -58,6 +72,8 @@ def test_preview_validates_local_controls_without_calling_cloud(tmp_path: Path)
assert validated_events[0]["phase"] == "deterministic_commands_validated"
assert validated_events[0]["status"] == "processing"
assert validated_events[0]["execution_report"] is None
assert validated_events[0]["local_control_elapsed_ms"] < 1000
assert validated_events[0]["local_control_within_budget"] is True
assert _operations(validated_events[0]) == _operations(result)
assert validated_events[0]["command_plans"]
@@ -119,12 +135,90 @@ def test_fully_local_cabin_request_streams_all_commands_without_supplement(
assert _operations(validated_events[0]) == operations
def test_qualitative_window_position_keeps_adjacent_reading_light_clause(
tmp_path: Path,
) -> None:
service = _service(tmp_path)
cases = (
("空调 29℃、打开阅读灯、副驾车窗开到20%", 20),
("空调 29℃、打开阅读灯、副驾车窗开到一半", 50),
)
for index, (utterance, expected_position) in enumerate(cases):
result = service.preview_native_can(
utterance,
provider="local",
conversation_id=f"conv-reading-window-{index:02d}",
execution_mode="dry_run",
)
operations = _operations(result)
by_capability = {item["capability"]: item for item in operations}
assert result["unresolved_clauses"] == []
assert result["local_control_elapsed_ms"] < 1000
assert result["local_control_within_budget"] is True
assert result["parsed_clauses"] == [
"空调 29℃",
"打开阅读灯",
utterance.rsplit("、", 1)[-1],
]
assert set(by_capability) == {
"climate.set_temperature",
"interior_light.set_reading_power",
"window.set_position",
}
assert (
by_capability["interior_light.set_reading_power"]["target"]["zone"]
== "all"
)
assert by_capability["interior_light.set_reading_power"]["parameters"] == {
"state": "on"
}
assert (
by_capability["window.set_position"]["target"]["zone"]
== "front_right"
)
assert by_capability["window.set_position"]["parameters"] == {
"position_pct": expected_position
}
def test_local_window_parser_accepts_front_and_rear_row_side_aliases(
tmp_path: Path,
) -> None:
service = _service(tmp_path)
cases = (
("左前排车窗开到20%", "front_left", 20),
("右前排窗开到一半", "front_right", 50),
("左后排车窗开到35%", "rear_left", 35),
("右后排窗开到一半", "rear_right", 50),
)
for index, (utterance, expected_target, expected_position) in enumerate(cases):
result = service.preview_native_can(
utterance,
provider="local",
conversation_id=f"conv-window-alias-{index:02d}",
execution_mode="dry_run",
)
operations = _operations(result)
assert result["status"] == "succeeded"
assert result["unresolved_clauses"] == []
assert len(operations) == 1
assert operations[0]["capability"] == "window.set_position"
assert operations[0]["target"]["zone"] == expected_target
assert operations[0]["parameters"] == {
"position_pct": expected_position
}
def test_supplement_receives_only_unresolved_text_and_deduplicates_local_control(
tmp_path: Path,
) -> None:
service = _service(tmp_path)
preview = service.preview_native_can(
"观影时空调温度设置为24度并播放音乐",
"空调温度设置为24度并播放音乐",
provider="openai",
conversation_id="conv-two-stage-deduplicate",
execution_mode="dry_run",
@@ -169,7 +263,15 @@ def test_supplement_receives_only_unresolved_text_and_deduplicates_local_control
provider="openai",
)
assert interpret.call_args.args[0] == "观影时空调温度设置为24度并播放音乐"
assert interpret.call_args.args[0] == "播放音乐"
routing = interpret.call_args.args[1]["natural_language_routing"]
assert routing == {
"original_utterance": "空调温度设置为24度并播放音乐",
"parsed_clauses": ["空调温度设置为24度"],
"resolved_clauses": ["空调温度设置为24度"],
"unresolved_clauses": ["播放音乐"],
"cloud_input_utterance": "播放音乐",
}
operations = _operations(result)
assert [item["capability"] for item in operations].count(
"climate.set_temperature"
@@ -217,6 +319,11 @@ def test_frontend_two_stage_contract_and_source_badges() -> None:
assert 'item.deliveryLabel' in console
assert 'data-delivery-state="${escapeHtml(item.deliveryState)}"' in console
assert '"cognitive-agent:validated-can-commands"' in console
assert "function commandCorrelationIds(result = {})" in console
assert "result.preview_request_id" in console
assert "result.supplement_of" in console
assert '"local_control_elapsed"' in console
assert '"本地规则解析与下发"' in console
assert '.v2-can-command-source[data-parse-source="local_rule"]' in styles
assert '.v2-can-command-delivery[data-delivery-state="submitted"]' in styles
assert '"/api/can/commands"' in server
@@ -287,6 +394,7 @@ def test_unified_result_preserves_cloud_infrared_attribution() -> None:
"command_plans": {},
"operation_results": [],
"decision_summary": [],
"trace_events": [{"stage": "compile", "duration_ms": 3}],
}
infrared_result = {
"status": "succeeded",
@@ -296,6 +404,10 @@ def test_unified_result_preserves_cloud_infrared_attribution() -> None:
"command_plans": {},
"operation_results": [],
"decision_summary": [],
"trace_events": [
{"stage": "local_peripheral_plan", "duration_ms": 1},
{"stage": "nfc_peripheral_sequence", "duration_ms": 0},
],
}
entries = [
{
@@ -321,6 +433,368 @@ def test_unified_result_preserves_cloud_infrared_attribution() -> None:
assert merged["provider_used"] == "local_rules+gpt:gpt-5.6-luna"
assert merged["infrared_actions"][0]["parse_source"] == "cloud_model"
assert [event["stage"] for event in merged["trace_events"]] == [
"local_peripheral_plan",
"nfc_peripheral_sequence",
"compile",
]
def test_pure_infrared_request_publishes_compiled_commands() -> None:
handler = SeatDemoHandler.__new__(SeatDemoHandler)
handler.service = SimpleNamespace(
is_mode_profile_configuration_request=lambda _utterance: False,
conversations=SimpleNamespace(snapshot=lambda _conversation_id: {}),
persist_composed_result=lambda _result: None,
)
handler.extension = SimpleNamespace(list_peripheral_capabilities=lambda: [])
handler.nfc = SimpleNamespace(snapshot=lambda: {})
infrared_result = {
"request_id": "req-ir-only",
"utterance": "打开闪烁灯",
"status": "succeeded",
"function_configs": {
"self_extension": {
"spec": {
"domain": "self_extension",
"operations": [
{
"id": "infrared-1",
"capability": "infrared.emit",
}
],
}
}
},
"command_plans": {
"self_extension": {
"spec": {
"domain": "self_extension",
"commands": [
{
"operation_ref": "infrared-1",
"message": "infrared.emit",
"signals": {"slot": 1},
}
],
}
}
},
"trace_events": [
{"stage": "nfc_peripheral_route", "duration_ms": 0}
],
}
handler._execute_local_peripheral_actions = (
lambda _utterance, _entries, **_kwargs: infrared_result
)
validated_events: list[dict[str, object]] = []
partition = {
"peripheral_actions": [
{
"index": 0,
"clause": "打开闪烁灯",
"capability": {
"id": "toy-001-light-on",
"slot": 1,
},
}
],
"residual_utterance": "",
"unresolved_peripheral_clauses": [],
"denied_peripheral_actions": [],
}
with patch(
"cognitive_agent.server.partition_active_peripheral_intent",
return_value=partition,
):
result = handler._execute_request_with_peripherals(
"/api/requests/preview",
{
"utterance": "打开闪烁灯",
"provider": "local",
"execution_mode": "dry_run",
},
on_commands_validated=validated_events.append,
)
assert result is infrared_result
assert len(validated_events) == 2
assert validated_events[0]["status"] == "processing"
assert validated_events[0]["phase"] == "commands_validated"
assert validated_events[0]["command_plans"] == infrared_result["command_plans"]
assert validated_events[0]["trace_events"] == infrared_result["trace_events"]
assert validated_events[1] is infrared_result
def test_pure_unresolved_peripheral_returns_cloud_ready_local_preview(
tmp_path: Path,
) -> None:
handler = SeatDemoHandler.__new__(SeatDemoHandler)
handler.service = _service(tmp_path)
handler.extension = SimpleNamespace(list_peripheral_capabilities=lambda: [])
handler.nfc = SimpleNamespace(snapshot=lambda: {})
validated_events: list[dict[str, object]] = []
utterance = "让闪烁灯喜庆一点"
partition = {
"peripheral_actions": [],
"residual_utterance": "",
"unresolved_peripheral_clauses": [utterance],
"unresolved_candidates": [
{
"id": "light-festive",
"status": "learned",
"device_id": "toy-001",
"device_name": "闪烁灯",
"action": "喜庆灯效",
"slot": 2,
}
],
"denied_peripheral_actions": [],
}
with patch(
"cognitive_agent.server.partition_active_peripheral_intent",
return_value=partition,
):
result = handler._execute_request_with_peripherals(
"/api/requests/preview",
{
"utterance": utterance,
"provider": "local",
"conversation_id": "conv-unresolved-peripheral-preview",
"execution_mode": "dry_run",
},
on_commands_validated=validated_events.append,
)
assert result is not None
assert result["status"] == "no_action"
assert result["supplement_required"] is True
assert result["function_configs"] == {}
assert result["command_plans"] == {}
assert result["preview_request_id"] == result["request_id"]
assert result["local_control_budget_ms"] == 1000
assert result["local_control_elapsed_ms"] < 1000
assert result["local_control_within_budget"] is True
assert result["natural_language_routing"] == {
"original_utterance": utterance,
"parsed_clauses": [],
"resolved_clauses": [],
"unresolved_clauses": [utterance],
}
assert validated_events == [result]
assert (
handler.service.artifact_root
/ str(result["request_id"])
/ "result.json"
).is_file()
def test_mixed_request_finishes_all_local_phases_before_cloud_ir() -> None:
handler = SeatDemoHandler.__new__(SeatDemoHandler)
order: list[str] = []
validated_events: list[dict[str, object]] = []
local_capability = {
"id": "light-on",
"status": "learned",
"device_id": "toy-001",
"device_name": "闪烁灯",
"card_uid": "A1B2",
"action": "打开",
"slot": 1,
}
cloud_capability = {
**local_capability,
"id": "light-festive",
"action": "喜庆灯效",
"slot": 2,
}
def peripheral_result(
request_id: str,
capability: dict[str, object],
parse_source: str,
) -> dict[str, object]:
operation_id = str(capability["id"])
return {
"request_id": request_id,
"utterance": "空调温度设置到23度,打开闪烁灯,让闪烁灯喜庆一点",
"status": "succeeded",
"success": True,
"provider_used": (
"infrared_local_rules"
if parse_source == "local_rule"
else "gpt:test"
),
"execution_mode": "dry_run",
"control_transport": "gscu_dry_run",
"function_configs": {
"self_extension": {
"spec": {
"domain": "self_extension",
"operations": [
{
"id": operation_id,
"capability": "infrared.emit",
"parse_source": parse_source,
}
],
}
}
},
"command_plans": {
"self_extension": {
"spec": {
"domain": "self_extension",
"commands": [
{
"operation_ref": operation_id,
"message": "infrared.emit",
"signals": {"slot": capability["slot"]},
}
],
}
}
},
"trace_events": [],
"validation": [],
"operation_results": [],
"decision_summary": [],
"execution_report": None,
}
local_result = peripheral_result(
"req-local-ir",
local_capability,
"local_rule",
)
cloud_result = peripheral_result(
"req-cloud-ir",
cloud_capability,
"cloud_model",
)
can_result = {
"request_id": "req-local-can",
"utterance": "空调温度设置到23度",
"status": "succeeded",
"success": True,
"provider_used": "local_rules",
"execution_mode": "dry_run",
"control_transport": "wired_can",
"function_configs": {},
"command_plans": {},
"trace_events": [],
"validation": [],
"operation_results": [],
"decision_summary": [],
"execution_report": None,
"natural_language_routing": {
"original_utterance": "空调温度设置到23度,打开闪烁灯,让闪烁灯喜庆一点",
"parsed_clauses": ["打开闪烁灯", "空调温度设置到23度"],
"resolved_clauses": ["打开闪烁灯", "空调温度设置到23度"],
"unresolved_clauses": ["让闪烁灯喜庆一点"],
},
}
def preview_native_can(**kwargs: object) -> dict[str, object]:
order.append("local_can")
callback = kwargs.get("on_commands_validated")
if callable(callback):
callback(dict(can_result))
return dict(can_result)
handler.service = SimpleNamespace(
is_mode_profile_configuration_request=lambda _utterance: False,
conversations=SimpleNamespace(snapshot=lambda _conversation_id: {}),
preview_native_can=preview_native_can,
persist_composed_result=lambda _result: None,
)
def plan(payload: dict[str, object]) -> dict[str, object]:
order.append("cloud_plan")
assert payload["utterance"] == "让闪烁灯喜庆一点"
routing = payload["natural_language_routing"]
assert isinstance(routing, dict)
assert routing["original_utterance"] == (
"空调温度设置到23度,打开闪烁灯,让闪烁灯喜庆一点"
)
assert routing["parsed_clauses"] == [
"打开闪烁灯",
"空调温度设置到23度",
]
return {
"steps": [
{"kind": "peripheral", "capability_id": "light-festive"}
]
}
def execute_cloud(
_utterance: str,
_plan: dict[str, object],
catalog: list[dict[str, object]],
**_kwargs: object,
) -> dict[str, object]:
order.append("cloud_ir")
assert [item["id"] for item in catalog] == ["light-festive"]
return cloud_result
handler.extension = SimpleNamespace(
list_peripheral_capabilities=lambda: [],
plan=plan,
execute_peripheral_plan_request=execute_cloud,
)
handler.nfc = SimpleNamespace(snapshot=lambda: {})
def execute_local(
_utterance: str,
_entries: list[dict[str, object]],
**kwargs: object,
) -> dict[str, object]:
order.append("local_ir")
callback = kwargs.get("on_commands_validated")
if callable(callback):
callback(local_result)
return local_result
handler._execute_local_peripheral_actions = execute_local
partition = {
"peripheral_actions": [
{"index": 1, "clause": "打开闪烁灯", "capability": local_capability}
],
"residual_utterance": "空调温度设置到23度",
"unresolved_peripheral_clauses": ["让闪烁灯喜庆一点"],
"unresolved_candidates": [cloud_capability],
"denied_peripheral_actions": [],
}
with patch(
"cognitive_agent.server.partition_active_peripheral_intent",
return_value=partition,
):
result = handler._execute_request_with_peripherals(
"/api/requests/preview",
{
"utterance": (
"空调温度设置到23度,打开闪烁灯,让闪烁灯喜庆一点"
),
"provider": "auto",
"conversation_id": "conv-mixed-order",
"execution_mode": "dry_run",
},
on_commands_validated=validated_events.append,
)
assert order == ["local_ir", "local_can", "cloud_plan", "cloud_ir"]
assert result is not None
assert result["status"] == "succeeded"
assert any(event.get("local_control_within_budget") for event in validated_events)
operation_sources = {
operation["parse_source"]
for config in result["function_configs"].values()
for operation in config["spec"].get("operations", [])
}
assert operation_sources == {"local_rule", "cloud_model"}
def test_nfc_command_denial_keeps_allowed_result_and_reports_partial() -> None: