优化后端和前端
This commit is contained in:
@@ -332,18 +332,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-101", timeout=3) as response:
|
||||
with self.opener.open(f"{self.base_url}/app.js?v=dual-seat-103", 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=sync-layout-41", timeout=3
|
||||
f"{self.base_url}/console-v2.js?v=sync-layout-43", timeout=3
|
||||
) as response:
|
||||
console_script = response.read().decode("utf-8")
|
||||
with self.opener.open(
|
||||
f"{self.base_url}/console-v2.css?v=sync-layout-40", timeout=3
|
||||
f"{self.base_url}/console-v2.css?v=sync-layout-41", timeout=3
|
||||
) as response:
|
||||
console_styles = response.read().decode("utf-8")
|
||||
with self.opener.open(
|
||||
@@ -378,12 +378,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-101"', html)
|
||||
self.assertIn('src="/app.js?v=dual-seat-103"', html)
|
||||
self.assertIn('href="/styles.css?v=dual-seat-16"', 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=sync-layout-41"', html)
|
||||
self.assertIn('href="/console-v2.css?v=sync-layout-40"', html)
|
||||
self.assertIn('src="/console-v2.js?v=sync-layout-43"', html)
|
||||
self.assertIn('href="/console-v2.css?v=sync-layout-41"', html)
|
||||
self.assertIn('<body class="console-v2">', html)
|
||||
self.assertIn('id="v2BootScreen"', html)
|
||||
self.assertIn('正在加载车机控制与实时状态', html)
|
||||
@@ -612,7 +612,7 @@ operations:
|
||||
self.assertIn('client_type: "web_console"', app_script)
|
||||
self.assertIn('canvas.addEventListener("webglcontextlost"', app_script)
|
||||
self.assertIn('"cognitive-agent:module-change"', app_script)
|
||||
self.assertIn('const CACHE_VERSION = "cognitive-agent-pad-v202"', service_worker)
|
||||
self.assertIn('const CACHE_VERSION = "cognitive-agent-pad-v204"', service_worker)
|
||||
self.assertIn('"/assets/pets/self-extension-cat.png?v=3"', service_worker)
|
||||
self.assertIn(".then(() => self.skipWaiting())", service_worker)
|
||||
self.assertNotIn("const cached = await caches.match(request)", service_worker)
|
||||
@@ -852,7 +852,7 @@ operations:
|
||||
) as response:
|
||||
extension_script = response.read().decode("utf-8")
|
||||
with self.opener.open(
|
||||
f"{self.base_url}/app.js?v=dual-seat-101", timeout=3
|
||||
f"{self.base_url}/app.js?v=dual-seat-103", timeout=3
|
||||
) as response:
|
||||
app_script = response.read().decode("utf-8")
|
||||
|
||||
@@ -1314,6 +1314,134 @@ operations:
|
||||
self.assertEqual(result["provider_used"], "local_rules_fallback")
|
||||
self.assertEqual(result["execution_report"]["spec"]["mode"], "dry_run")
|
||||
|
||||
def test_two_stage_http_api_returns_local_commands_before_cloud_supplement(
|
||||
self,
|
||||
) -> None:
|
||||
original_api_key = self.service.openai.api_key
|
||||
self.service.openai.api_key = "two-stage-http-test-key"
|
||||
preview_request = urllib.request.Request(
|
||||
f"{self.base_url}/api/requests/preview",
|
||||
method="POST",
|
||||
headers={"Content-Type": "application/json"},
|
||||
data=json.dumps(
|
||||
{
|
||||
"utterance": "空调温度设置为24度,播放音乐",
|
||||
"provider": "openai",
|
||||
"conversation_id": "conv-e2e-two-stage-http",
|
||||
"execution_mode": "dry_run",
|
||||
"reasoning_effort": "medium",
|
||||
"client_type": "web_console",
|
||||
}
|
||||
).encode("utf-8"),
|
||||
)
|
||||
model_output = {
|
||||
"detected_domains": ["ambient_light"],
|
||||
"requests": [
|
||||
{
|
||||
"domain": "ambient_light",
|
||||
"capability": "ambient_light.set_power",
|
||||
"target_scope": "vehicle",
|
||||
"operation": "set",
|
||||
"duration_ms": None,
|
||||
"parameters": [{"name": "state", "value": "on"}],
|
||||
"reason": "补充播放场景氛围灯。",
|
||||
}
|
||||
],
|
||||
"ambiguities": [],
|
||||
"decision_summary": ["仅补充本地未解析片段。"],
|
||||
"confidence": 0.9,
|
||||
}
|
||||
try:
|
||||
with patch.object(
|
||||
self.service.openai,
|
||||
"interpret",
|
||||
side_effect=AssertionError("preview must not invoke GPT"),
|
||||
):
|
||||
with self.opener.open(preview_request, timeout=3) as response:
|
||||
preview = json.loads(response.read().decode("utf-8"))
|
||||
self.assertEqual(response.status, 201)
|
||||
local_operation = next(
|
||||
operation
|
||||
for config in preview["function_configs"].values()
|
||||
for operation in config["spec"]["operations"]
|
||||
)
|
||||
self.assertEqual(local_operation["parse_source"], "local_rule")
|
||||
self.assertTrue(preview["supplement_required"])
|
||||
|
||||
supplement_request = urllib.request.Request(
|
||||
f"{self.base_url}/api/requests/{preview['request_id']}/supplement",
|
||||
method="POST",
|
||||
headers={"Content-Type": "application/json"},
|
||||
data=json.dumps(
|
||||
{
|
||||
"provider": "openai",
|
||||
"reasoning_effort": "medium",
|
||||
"client_type": "web_console",
|
||||
}
|
||||
).encode("utf-8"),
|
||||
)
|
||||
with patch.object(
|
||||
self.service.openai,
|
||||
"interpret",
|
||||
return_value=model_output,
|
||||
) as interpret:
|
||||
with self.opener.open(supplement_request, timeout=3) as response:
|
||||
result = json.loads(response.read().decode("utf-8"))
|
||||
self.assertEqual(response.status, 201)
|
||||
self.assertEqual(interpret.call_args.args[0], "播放音乐")
|
||||
finally:
|
||||
self.service.openai.api_key = original_api_key
|
||||
|
||||
self.assertEqual(result["phase"], "two_stage_complete")
|
||||
sources = {
|
||||
operation["capability"]: operation["parse_source"]
|
||||
for config in result["function_configs"].values()
|
||||
for operation in config["spec"]["operations"]
|
||||
}
|
||||
self.assertEqual(sources["climate.set_temperature"], "local_rule")
|
||||
self.assertEqual(sources["ambient_light.set_power"], "cloud_supplement")
|
||||
|
||||
def test_preview_streams_validated_commands_before_terminal_result(self) -> None:
|
||||
request = urllib.request.Request(
|
||||
f"{self.base_url}/api/requests/preview",
|
||||
method="POST",
|
||||
headers={
|
||||
"Content-Type": "application/json",
|
||||
"Accept": "application/x-ndjson",
|
||||
},
|
||||
data=json.dumps(
|
||||
{
|
||||
"utterance": "空调24度、风量2档",
|
||||
"provider": "local",
|
||||
"conversation_id": "conv-e2e-command-stream",
|
||||
"execution_mode": "dry_run",
|
||||
"client_type": "web_console",
|
||||
},
|
||||
ensure_ascii=False,
|
||||
).encode("utf-8"),
|
||||
)
|
||||
|
||||
with self.opener.open(request, timeout=3) as response:
|
||||
events = [
|
||||
json.loads(line)
|
||||
for line in response.read().decode("utf-8").splitlines()
|
||||
if line.strip()
|
||||
]
|
||||
|
||||
self.assertEqual(
|
||||
response.headers.get_content_type(),
|
||||
"application/x-ndjson",
|
||||
)
|
||||
self.assertEqual(
|
||||
[event["type"] for event in events],
|
||||
["validated_commands", "result"],
|
||||
)
|
||||
validated = events[0]["payload"]
|
||||
self.assertEqual(validated["status"], "processing")
|
||||
self.assertIsNone(validated["execution_report"])
|
||||
self.assertTrue(validated["command_plans"])
|
||||
self.assertEqual(events[1]["payload"]["status"], "succeeded")
|
||||
|
||||
def test_frontend_suite_covers_every_physical_seat_capability(self) -> None:
|
||||
suite_path = (
|
||||
REPOSITORY_ROOT
|
||||
|
||||
Reference in New Issue
Block a user