feat: harden seat intent and expand HIL coverage
This commit is contained in:
@@ -95,6 +95,9 @@ class SeatWebApiTests(unittest.TestCase):
|
||||
|
||||
self.assertIn("/login", final_url)
|
||||
self.assertIn("体验账号登录", login_html)
|
||||
with unauthenticated.open(f"{self.base_url}/network.js", timeout=3) as response:
|
||||
network_script = response.read().decode("utf-8")
|
||||
self.assertIn("CognitiveAgentNetwork", network_script)
|
||||
with self.assertRaises(urllib.error.HTTPError) as caught:
|
||||
unauthenticated.open(f"{self.base_url}/api/runtime", timeout=3)
|
||||
self.assertEqual(caught.exception.code, 401)
|
||||
@@ -102,6 +105,8 @@ class SeatWebApiTests(unittest.TestCase):
|
||||
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=seat-hil-7", timeout=3) as response:
|
||||
app_script = 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"))
|
||||
|
||||
@@ -110,6 +115,11 @@ class SeatWebApiTests(unittest.TestCase):
|
||||
self.assertIn('data-vehicle-model="byd-tang-2024-reference"', html)
|
||||
self.assertIn("整车孪生界面", html)
|
||||
self.assertIn("透明车体 · 实体设备", html)
|
||||
self.assertIn('src="/app.js?v=seat-hil-7"', html)
|
||||
self.assertIn("HARDWARE_REQUEST_TIMEOUT_MS = 15 * 60 * 1000", app_script)
|
||||
self.assertIn(
|
||||
"requestTimeoutForExecutionMode(state.executionMode)", app_script
|
||||
)
|
||||
self.assertIn("FBXLoader.js", html)
|
||||
self.assertIn('data-seat-layout="five-seat"', html)
|
||||
self.assertIn('data-active-seat="front-right"', html)
|
||||
@@ -202,6 +212,119 @@ class SeatWebApiTests(unittest.TestCase):
|
||||
self.assertEqual(result["vehicle"]["id"], "denza_n7")
|
||||
self.assertEqual(result["actuator_state"]["passenger"]["ventilation"]["level"], 1)
|
||||
|
||||
def test_relative_backrest_phrase_uses_current_conversation_position(self) -> None:
|
||||
conversation_id = "conv-e2e-relative-backrest"
|
||||
for utterance in ("副驾靠背角度调到130度", "靠背向前移动10度"):
|
||||
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": conversation_id,
|
||||
"vehicle_id": "denza_n7",
|
||||
"execution_mode": "dry_run",
|
||||
},
|
||||
ensure_ascii=False,
|
||||
).encode("utf-8"),
|
||||
)
|
||||
with self.opener.open(request, timeout=3) as response:
|
||||
result = json.loads(response.read().decode("utf-8"))
|
||||
|
||||
self.assertEqual(result["status"], "succeeded")
|
||||
operation = result["function_config"]["spec"]["operations"][0]
|
||||
self.assertEqual(operation["capability"], "seat.set_backrest_recline")
|
||||
self.assertEqual(operation["parameters"]["position_pct"], 36)
|
||||
self.assertEqual(operation["parameters"]["angle_deg"], 120)
|
||||
self.assertEqual(operation["motion"]["direction"], "decrease")
|
||||
self.assertEqual(operation["motion"]["delta_angle_deg"], 10.0)
|
||||
|
||||
def test_timed_and_oversized_backrest_requests_use_endpoint_fallbacks(self) -> None:
|
||||
conversation_id = "conv-e2e-timed-endpoint-fallbacks"
|
||||
|
||||
def submit(utterance: str) -> dict:
|
||||
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": conversation_id,
|
||||
"vehicle_id": "denza_n7",
|
||||
"execution_mode": "dry_run",
|
||||
},
|
||||
ensure_ascii=False,
|
||||
).encode("utf-8"),
|
||||
)
|
||||
with self.opener.open(request, timeout=3) as response:
|
||||
return json.loads(response.read().decode("utf-8"))
|
||||
|
||||
submit("副驾靠背归零")
|
||||
timed = submit("靠背向后移动70秒")
|
||||
|
||||
self.assertEqual(timed["status"], "succeeded")
|
||||
timed_operation = timed["function_config"]["spec"]["operations"][0]
|
||||
self.assertEqual(timed_operation["parameters"]["position_pct"], 100)
|
||||
self.assertEqual(timed_operation["motion"]["mode"], "timed")
|
||||
self.assertEqual(timed_operation["motion"]["requested_duration_ms"], 70_000)
|
||||
self.assertEqual(timed_operation["motion"]["applied_duration_ms"], 30_000)
|
||||
self.assertTrue(timed_operation["motion"]["duration_clamped"])
|
||||
self.assertIn("最大允许时长为30秒", "\n".join(timed["decision_summary"]))
|
||||
|
||||
submit("副驾靠背角度调到130度")
|
||||
overflow = submit("靠背向后移动70度")
|
||||
|
||||
self.assertEqual(overflow["status"], "succeeded")
|
||||
overflow_operation = overflow["function_config"]["spec"]["operations"][0]
|
||||
self.assertEqual(overflow_operation["parameters"]["position_pct"], 100)
|
||||
self.assertEqual(overflow_operation["parameters"]["angle_deg"], 163)
|
||||
self.assertTrue(overflow_operation["motion"]["range_clamped"])
|
||||
self.assertEqual(
|
||||
overflow_operation["motion"]["requested_delta_angle_deg"], 70
|
||||
)
|
||||
self.assertIn("备用目标", "\n".join(overflow["decision_summary"]))
|
||||
|
||||
def test_open_zero_gravity_phrase_matches_complete_mode(self) -> None:
|
||||
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-e2e-open-zero-gravity",
|
||||
"vehicle_id": "denza_n7",
|
||||
"execution_mode": "dry_run",
|
||||
},
|
||||
ensure_ascii=False,
|
||||
).encode("utf-8"),
|
||||
)
|
||||
with self.opener.open(request, timeout=3) as response:
|
||||
result = json.loads(response.read().decode("utf-8"))
|
||||
|
||||
self.assertEqual(result["status"], "succeeded")
|
||||
self.assertIn("zero_gravity", {item["id"] for item in result["matched_modes"]})
|
||||
operations = result["function_config"]["spec"]["operations"]
|
||||
self.assertEqual(len(operations), 4)
|
||||
self.assertEqual(
|
||||
{
|
||||
operation["capability"]: operation["parameters"]["position_pct"]
|
||||
for operation in operations
|
||||
},
|
||||
{
|
||||
"seat.set_longitudinal_position": 100,
|
||||
"seat.set_cushion_tilt": 100,
|
||||
"seat.set_backrest_recline": 81,
|
||||
"seat.set_legrest_height": 94,
|
||||
},
|
||||
)
|
||||
self.assertEqual(result["execution_report"]["spec"]["mode"], "dry_run")
|
||||
|
||||
def test_hardware_state_endpoint_returns_physical_passenger_snapshot(self) -> None:
|
||||
synchronized = {
|
||||
"status": "succeeded",
|
||||
|
||||
Reference in New Issue
Block a user