Merge branch 'feature/N7Web' of https://git.kendeji.fun/vee/Cognitive_Agent into feature/N7Web
# Conflicts: # apps/web-console/app.js # apps/web-console/index.html # apps/web-console/service-worker.js # src/cognitive_agent/server.py # tests/e2e/test_seat_web_api.py
This commit is contained in:
@@ -349,11 +349,11 @@ operations:
|
||||
) as response:
|
||||
console_styles = response.read().decode("utf-8")
|
||||
with self.opener.open(
|
||||
f"{self.base_url}/self-extension.js?v=sync-layout-26", timeout=3
|
||||
f"{self.base_url}/self-extension.js?v=nfc-toy-33", timeout=3
|
||||
) as response:
|
||||
extension_script = response.read().decode("utf-8")
|
||||
with self.opener.open(
|
||||
f"{self.base_url}/self-extension.css?v=sync-layout-26", timeout=3
|
||||
f"{self.base_url}/self-extension.css?v=nfc-toy-33", timeout=3
|
||||
) as response:
|
||||
extension_styles = response.read().decode("utf-8").replace("\r\n", "\n")
|
||||
with self.opener.open(
|
||||
@@ -364,6 +364,10 @@ operations:
|
||||
f"{self.base_url}/api/extension/health", timeout=3
|
||||
) as response:
|
||||
extension_health = json.loads(response.read().decode("utf-8"))
|
||||
with self.opener.open(
|
||||
f"{self.base_url}/api/extension/nfc/status", timeout=3
|
||||
) as response:
|
||||
nfc_status = json.loads(response.read().decode("utf-8"))
|
||||
with self.opener.open(f"{self.base_url}/api/runtime", timeout=3) as response:
|
||||
runtime = json.loads(response.read().decode("utf-8"))
|
||||
|
||||
@@ -389,8 +393,8 @@ operations:
|
||||
self.assertIn('<body class="console-v2">', html)
|
||||
self.assertIn('id="v2BootScreen"', html)
|
||||
self.assertIn('正在加载车机控制与实时状态', html)
|
||||
self.assertIn('src="/self-extension.js?v=sync-layout-26"', html)
|
||||
self.assertIn('href="/self-extension.css?v=sync-layout-26"', html)
|
||||
self.assertIn('src="/self-extension.js?v=nfc-toy-33"', html)
|
||||
self.assertIn('href="/self-extension.css?v=nfc-toy-33"', html)
|
||||
self.assertNotIn("self-calibration", html)
|
||||
self.assertNotIn("self-repair", html)
|
||||
self.assertNotIn("self-evolution", html)
|
||||
@@ -606,6 +610,12 @@ operations:
|
||||
self.assertIn('grid-template-columns: repeat(3, minmax(0, 1fr))', extension_styles)
|
||||
self.assertTrue(extension_health["success"])
|
||||
self.assertIn(extension_health["transport"], {"tcp", "mqtt", "simulation"})
|
||||
self.assertIn("nfc", extension_health)
|
||||
self.assertTrue(nfc_status["success"])
|
||||
self.assertIn("reader", nfc_status)
|
||||
self.assertIn("card", nfc_status)
|
||||
self.assertIn("legacy_profiles", nfc_status)
|
||||
self.assertIn("legacy_bridge_addresses", nfc_status)
|
||||
self.assertIn("function emphasizeReferenceFrontLampMeshes", app_script)
|
||||
self.assertIn("function createReferenceFrontLampHighlights", app_script)
|
||||
self.assertIn("referenceFrontLampMaterials", app_script)
|
||||
@@ -1027,6 +1037,170 @@ operations:
|
||||
self.assertEqual(result["transport"], "simulation")
|
||||
self.assertFalse(result["physical_state_confirmed"])
|
||||
|
||||
def test_main_request_routes_live_nfc_peripheral_to_gscu(self) -> None:
|
||||
original_extension = SeatDemoHandler.extension
|
||||
original_nfc = SeatDemoHandler.nfc
|
||||
SeatDemoHandler.extension = SelfExtensionService(SimulationGateway())
|
||||
SeatDemoHandler.nfc = SimpleNamespace(
|
||||
snapshot=lambda: {
|
||||
"revision": 1,
|
||||
"reader": {"online": True},
|
||||
"card": {"action": "present", "uid": "1A53DD95"},
|
||||
}
|
||||
)
|
||||
catalog_request = urllib.request.Request(
|
||||
f"{self.base_url}/api/extension/display-sync",
|
||||
method="POST",
|
||||
headers={"Content-Type": "application/json"},
|
||||
data=json.dumps(
|
||||
{
|
||||
"peripherals": [
|
||||
{
|
||||
"id": "cap-third-party-light-on",
|
||||
"status": "learned",
|
||||
"device_id": "third-party-light",
|
||||
"device_name": "第三方氛围灯",
|
||||
"card_uid": "1A53DD95",
|
||||
"action": "开启",
|
||||
"aliases": ["开灯"],
|
||||
"slot": 9,
|
||||
}
|
||||
]
|
||||
},
|
||||
ensure_ascii=False,
|
||||
).encode("utf-8"),
|
||||
)
|
||||
request = urllib.request.Request(
|
||||
f"{self.base_url}/api/requests",
|
||||
method="POST",
|
||||
headers={"Content-Type": "application/json"},
|
||||
data=json.dumps(
|
||||
{
|
||||
"utterance": "打开第三方氛围灯",
|
||||
"provider": "local",
|
||||
"conversation_id": "conv-main-nfc-extension",
|
||||
"execution_mode": "hardware",
|
||||
"client_type": "web_console",
|
||||
},
|
||||
ensure_ascii=False,
|
||||
).encode("utf-8"),
|
||||
)
|
||||
try:
|
||||
with self.opener.open(catalog_request, timeout=3) as response:
|
||||
self.assertEqual(response.status, 200)
|
||||
with patch.object(
|
||||
self.service,
|
||||
"process",
|
||||
side_effect=AssertionError("vehicle orchestration must not run"),
|
||||
):
|
||||
with self.opener.open(request, timeout=3) as response:
|
||||
result = json.loads(response.read().decode("utf-8"))
|
||||
finally:
|
||||
SeatDemoHandler.extension = original_extension
|
||||
SeatDemoHandler.nfc = original_nfc
|
||||
|
||||
self.assertEqual(result["status"], "succeeded")
|
||||
self.assertEqual(result["orchestration_route"], "nfc_peripheral")
|
||||
self.assertEqual(result["peripheral"]["name"], "第三方氛围灯")
|
||||
self.assertEqual(result["peripheral"]["slot"], 9)
|
||||
operation = result["execution_report"]["spec"]["results"][0]
|
||||
self.assertEqual(operation["domain"], "self_extension")
|
||||
self.assertTrue(operation["telemetry"]["gateway_acknowledged"])
|
||||
|
||||
def test_main_request_executes_gpt_nfc_sequence_without_vehicle_can(self) -> None:
|
||||
original_extension = SeatDemoHandler.extension
|
||||
original_nfc = SeatDemoHandler.nfc
|
||||
extension = SelfExtensionService(SimulationGateway())
|
||||
SeatDemoHandler.extension = extension
|
||||
SeatDemoHandler.nfc = SimpleNamespace(
|
||||
snapshot=lambda: {
|
||||
"revision": 1,
|
||||
"reader": {"online": True},
|
||||
"card": {"action": "present", "uid": "1A53DD95"},
|
||||
}
|
||||
)
|
||||
capabilities = [
|
||||
{
|
||||
"id": capability_id,
|
||||
"status": "learned",
|
||||
"device_id": "third-party-light",
|
||||
"device_name": "第三方氛围灯",
|
||||
"card_uid": "1A53DD95",
|
||||
"action": action,
|
||||
"slot": slot,
|
||||
}
|
||||
for capability_id, action, slot in (
|
||||
("on", "开启灯光", 1),
|
||||
("red", "红色灯光", 2),
|
||||
("off", "关闭灯光", 3),
|
||||
)
|
||||
]
|
||||
catalog_request = urllib.request.Request(
|
||||
f"{self.base_url}/api/extension/display-sync",
|
||||
method="POST",
|
||||
headers={"Content-Type": "application/json"},
|
||||
data=json.dumps(
|
||||
{"peripherals": capabilities}, ensure_ascii=False
|
||||
).encode("utf-8"),
|
||||
)
|
||||
utterance = "打开第三方氛围灯,红灯闪烁5s后,最后关闭第三方氛围灯"
|
||||
request = urllib.request.Request(
|
||||
f"{self.base_url}/api/requests",
|
||||
method="POST",
|
||||
headers={"Content-Type": "application/json"},
|
||||
data=json.dumps(
|
||||
{
|
||||
"utterance": utterance,
|
||||
"provider": "local",
|
||||
"conversation_id": "conv-main-nfc-sequence",
|
||||
"execution_mode": "hardware",
|
||||
"client_type": "web_console",
|
||||
},
|
||||
ensure_ascii=False,
|
||||
).encode("utf-8"),
|
||||
)
|
||||
plan_result = {
|
||||
"model": "gpt-5.6-sol",
|
||||
"summary": "按顺序切换第三方氛围灯",
|
||||
"needs_confirmation": False,
|
||||
"steps": [
|
||||
{"kind": "peripheral", "capability_id": "on"},
|
||||
{"kind": "peripheral", "capability_id": "red"},
|
||||
{"kind": "delay", "delay_ms": 5000},
|
||||
{"kind": "peripheral", "capability_id": "off"},
|
||||
],
|
||||
}
|
||||
try:
|
||||
with self.opener.open(catalog_request, timeout=3) as response:
|
||||
self.assertEqual(response.status, 200)
|
||||
with patch.object(
|
||||
self.service,
|
||||
"process",
|
||||
side_effect=AssertionError("vehicle orchestration must not run"),
|
||||
), patch.object(
|
||||
extension, "plan", return_value=plan_result
|
||||
) as planner, patch(
|
||||
"cognitive_agent.application.self_extension.time.sleep"
|
||||
):
|
||||
with self.opener.open(request, timeout=3) as response:
|
||||
result = json.loads(response.read().decode("utf-8"))
|
||||
finally:
|
||||
SeatDemoHandler.extension = original_extension
|
||||
SeatDemoHandler.nfc = original_nfc
|
||||
|
||||
self.assertEqual(result["status"], "succeeded")
|
||||
self.assertEqual(
|
||||
result["orchestration_route"], "nfc_peripheral_sequence"
|
||||
)
|
||||
self.assertEqual(result["sequence"]["step_count"], 4)
|
||||
self.assertEqual(result["sequence"]["action_count"], 3)
|
||||
self.assertEqual(result["sequence"]["emit_count"], 3)
|
||||
self.assertEqual(result["sequence"]["steady_blink_fallbacks"], 1)
|
||||
self.assertEqual(result["sequence"]["total_delay_ms"], 5000)
|
||||
self.assertEqual(len(result["operation_results"]), 4)
|
||||
planner.assert_called_once()
|
||||
self.assertEqual(planner.call_args.args[0]["utterance"], utterance)
|
||||
|
||||
def test_post_request_returns_complete_dry_run(self) -> None:
|
||||
request = urllib.request.Request(
|
||||
f"{self.base_url}/api/requests",
|
||||
|
||||
Reference in New Issue
Block a user