feat: harden seat intent and expand HIL coverage

This commit is contained in:
vee
2026-08-02 05:20:38 +08:00
parent 4aded5c21d
commit 55d0d400cb
62 changed files with 53112 additions and 1748 deletions
+123
View File
@@ -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",
+423
View File
@@ -0,0 +1,423 @@
{
"schema_version": 2,
"suite": "t2g-seat-frontend-comprehensive-hil",
"vehicle_id": "denza_n7",
"execution_policy": {
"mode": "hardware",
"provider": "local",
"continue_after_failure": true,
"require_physical_feedback": true,
"passenger_only": true,
"position_tolerance_pct": 2,
"known_safety_blocks": {}
},
"coverage_dimensions": {
"targets": ["副驾", "副驾驶", "副驾位", "右前座", "乘客座", "省略座位", "后排/左前/全部座椅安全拒绝"],
"verbs": ["打开", "开启", "启动", "调到", "设置为", "进入", "切换到", "执行", "关闭", "停止", "归零", "复位"],
"values": ["阿拉伯数字", "中文数字", "全角百分号", "百分之表达", "高/中/低档", "正常秒数/半秒/毫秒", "时长上下限钳制", "绝对角度", "相对角度", "相对百分比", "逐轴机械端点", "超量程备用端点", "小数/负数安全拒绝"],
"functions": ["加热/取暖", "通风/吹风", "水平", "坐盆", "靠背", "腿托", "四轴组合", "相对移动", "按时长移动", "预设模式", "GPT模糊舒适意图"],
"safety": ["主驾", "前排范围", "越界容错", "缺少状态", "缺少当前位置", "矛盾指令", "未知能力", "多模式冲突", "非端点堵转不容错"]
},
"cases": [
{
"group": "thermal",
"expected": {"heating": 1},
"variants": [
{"id": "THERM-H-LOW-NUMERIC", "utterance": "副驾座椅加热开到1档"},
{"id": "THERM-H-LOW-CHINESE", "utterance": "把副驾驶座椅加热调到一档"},
{"id": "THERM-H-LOW-WORD", "utterance": "副驾位座椅加热最低档"},
{"id": "THERM-H-LOW-DEFAULT", "utterance": "打开座椅加热"}
]
},
{
"group": "thermal",
"expected": {"heating": 2},
"variants": [
{"id": "THERM-H-MID-NUMERIC", "utterance": "副驾座椅加热开到2档"},
{"id": "THERM-H-MID-CHINESE", "utterance": "乘客座加热调到二档"},
{"id": "THERM-H-MID-WORD", "utterance": "座椅加热设置为中档"}
]
},
{
"group": "thermal",
"expected": {"heating": 3},
"variants": [
{"id": "THERM-H-HIGH-NUMERIC", "utterance": "副驾座椅加热开到3档"},
{"id": "THERM-H-HIGH-PREFIX", "utterance": "右前座椅开启3档加热"},
{"id": "THERM-H-HIGH-WORD", "utterance": "乘客座椅加热调到最高档"}
]
},
{
"group": "thermal",
"expected": {"heating": 0},
"variants": [
{"id": "THERM-H-OFF-CLOSE", "utterance": "关闭副驾座椅加热"},
{"id": "THERM-H-OFF-DROP", "utterance": "关掉副驾驶座椅加热"},
{"id": "THERM-H-OFF-STOP", "utterance": "停止乘客座加热"},
{"id": "THERM-H-OFF-DEFAULT", "utterance": "座椅加热关闭"}
]
},
{"id": "THERM-H-COMFORT-MILD", "group": "thermal", "utterance": "副驾驶有点冷", "expected": {"heating": 1}},
{"id": "THERM-H-COMFORT-STRONG", "group": "thermal", "utterance": "右前座椅太冷了", "expected": {"heating": 3}},
{"id": "THERM-H-COMFORT-OFF", "group": "thermal", "utterance": "把右前座加热关掉", "expected": {"heating": 0}},
{
"group": "thermal",
"expected": {"ventilation": 1},
"variants": [
{"id": "THERM-V-LOW-NUMERIC", "utterance": "副驾座椅通风开到1档"},
{"id": "THERM-V-LOW-CHINESE", "utterance": "副驾驶座椅通风调到一档"},
{"id": "THERM-V-LOW-WORD", "utterance": "乘客座通风最低档"}
]
},
{
"group": "thermal",
"expected": {"ventilation": 2},
"variants": [
{"id": "THERM-V-MID-NUMERIC", "utterance": "副驾座椅通风开到2档"},
{"id": "THERM-V-MID-PREFIX", "utterance": "右前座椅开启2档通风"},
{"id": "THERM-V-MID-WORD", "utterance": "打开座椅通风中档"}
]
},
{
"group": "thermal",
"expected": {"ventilation": 3},
"variants": [
{"id": "THERM-V-HIGH-NUMERIC", "utterance": "副驾座椅通风开到3档"},
{"id": "THERM-V-HIGH-CHINESE", "utterance": "乘客座椅通风调到三档"},
{"id": "THERM-V-HIGH-WORD", "utterance": "右前座通风最高档"}
]
},
{
"group": "thermal",
"expected": {"ventilation": 0},
"variants": [
{"id": "THERM-V-OFF-CLOSE", "utterance": "关闭副驾座椅通风"},
{"id": "THERM-V-OFF-DROP", "utterance": "关掉副驾驶座椅通风"},
{"id": "THERM-V-OFF-STOP", "utterance": "停止右前座通风"}
]
},
{"id": "THERM-V-COMFORT-MILD", "group": "thermal", "utterance": "副驾驶有点热", "expected": {"ventilation": 1}},
{"id": "THERM-V-COMFORT-STRONG", "group": "thermal", "utterance": "右前座椅太闷了", "expected": {"ventilation": 3}},
{"id": "THERM-V-COMFORT-OFF", "group": "thermal", "utterance": "把乘客座通风关掉", "expected": {"ventilation": 0}},
{"id": "THERM-MIXED-COMFORT", "group": "thermal", "utterance": "副驾驶有点冷又有点闷", "expected": {"heating": 1, "ventilation": 1}},
{"id": "THERM-MIXED-OFF", "group": "thermal", "utterance": "关闭右前座的加热和通风", "expected": {"heating": 0, "ventilation": 0}},
{"id": "THERM-AIRFLOW-SETUP", "group": "thermal", "utterance": "右前座通风调到2档", "expected": {"ventilation": 2}},
{"id": "THERM-AIRFLOW-COLD", "group": "thermal", "utterance": "副驾被风吹得有点冷", "expected": {"ventilation": 1}},
{"id": "THERM-AIRFLOW-OFF", "group": "thermal", "utterance": "关闭座椅通风", "expected": {"ventilation": 0}},
{
"group": "motion",
"expected": {"longitudinal": 25},
"variants": [
{"id": "MOTION-M0-25-HORIZONTAL", "utterance": "把副驾座椅水平位置调到25%"},
{"id": "MOTION-M0-25-FRONTBACK", "utterance": "副驾驶座椅前后位置设置为25%"},
{"id": "MOTION-M0-25-RAIL", "utterance": "右前座滑轨移到25%"}
]
},
{
"group": "motion",
"expected": {"longitudinal": 75},
"variants": [
{"id": "MOTION-M0-75-HORIZONTAL", "utterance": "副驾座椅水平位置调到75%"},
{"id": "MOTION-M0-75-LONGITUDINAL", "utterance": "乘客座纵向移动至75%"},
{"id": "MOTION-M0-75-RAIL", "utterance": "把副驾驶滑轨设为75%"}
]
},
{
"group": "motion",
"expected": {"longitudinal": 50},
"variants": [
{"id": "MOTION-M0-50-CN-PERCENT", "utterance": "座椅前后移动到百分之五十"},
{"id": "MOTION-M0-50-FULLWIDTH", "utterance": "右前座水平位置调到50%"}
]
},
{
"group": "motion",
"expected": {"longitudinal": 0},
"variants": [
{"id": "MOTION-M0-FRONTMOST", "utterance": "把副驾座椅推到最前面"},
{"id": "MOTION-M0-HOME", "utterance": "副驾驶座椅前后复位"},
{"id": "MOTION-M0-ZERO", "utterance": "右前座滑轨归零"}
]
},
{
"group": "motion",
"expected": {"longitudinal": 100},
"variants": [
{"id": "MOTION-M0-REARMOST", "utterance": "副驾驶座椅移动到最后面"},
{"id": "MOTION-M0-BACK-END", "utterance": "把右前座滑轨向后移到底"}
]
},
{"id": "MOTION-M0-RELATIVE-FORWARD", "group": "motion", "utterance": "副驾滑轨向前移动10%", "expected": {"longitudinal": 90}, "expected_motion": {"axis": "longitudinal", "direction": "decrease", "delta_position_pct": 10}},
{"id": "MOTION-M0-RELATIVE-BACKWARD", "group": "motion", "utterance": "座椅前后向后移动10%", "expected": {"longitudinal": 100}, "expected_motion": {"axis": "longitudinal", "direction": "increase", "delta_position_pct": 10}},
{"id": "MOTION-M0-TIMED-FORWARD-CLAMP", "group": "motion", "utterance": "座椅向前移动60秒", "expected": {"longitudinal": 0}, "expected_motion": {"axis": "longitudinal", "mode": "timed", "direction": "decrease", "requested_duration_ms": 60000, "applied_duration_ms": 30000, "max_duration_ms": 30000, "duration_clamped": true, "endpoint_expected": "near"}, "expected_notice_contains": ["最大允许时长为30秒", "到达最前方"]},
{"id": "MOTION-M0-TIMED-BACK-MINUTE", "group": "motion", "utterance": "副驾座椅向后移动1分钟", "expected": {"longitudinal": 100}, "expected_motion": {"axis": "longitudinal", "mode": "timed", "direction": "increase", "requested_duration_ms": 60000, "applied_duration_ms": 30000, "max_duration_ms": 30000, "duration_clamped": true, "endpoint_expected": "far"}, "expected_notice_contains": ["最大允许时长为30秒", "到达最后方"]},
{"id": "MOTION-M0-ABS-OVERFLOW", "group": "motion", "utterance": "副驾座椅水平位置调到101%", "expected": {"longitudinal": 100}, "expected_notice_contains": ["备用目标", "机械末端"]},
{
"group": "motion",
"expected": {"cushion_tilt": 20},
"variants": [
{"id": "MOTION-M1-20-PAN", "utterance": "把副驾坐盆调到20%"},
{"id": "MOTION-M1-20-CUSHION", "utterance": "副驾驶坐垫位置设置为20%"}
]
},
{
"group": "motion",
"expected": {"cushion_tilt": 60},
"variants": [
{"id": "MOTION-M1-60-PAN", "utterance": "副驾坐盆调到60%"},
{"id": "MOTION-M1-60-TILT", "utterance": "右前座坐垫倾角调到60%"}
]
},
{"id": "MOTION-M1-RELATIVE-UP", "group": "motion", "utterance": "副驾坐垫向上调整10%", "expected": {"cushion_tilt": 50}, "expected_motion": {"axis": "cushion_tilt", "direction": "decrease", "delta_position_pct": 10}},
{"id": "MOTION-M1-RELATIVE-DOWN", "group": "motion", "utterance": "坐盆向下移动10%", "expected": {"cushion_tilt": 60}, "expected_motion": {"axis": "cushion_tilt", "direction": "increase", "delta_position_pct": 10}},
{
"group": "motion",
"expected": {"cushion_tilt": 50},
"variants": [
{"id": "MOTION-M1-50-CN-PERCENT", "utterance": "乘客座坐盆调到百分之五十"},
{"id": "MOTION-M1-50-FULLWIDTH", "utterance": "副驾驶坐垫设置为50%"}
]
},
{
"group": "motion",
"expected": {"cushion_tilt": 0},
"variants": [
{"id": "MOTION-M1-HOME", "utterance": "副驾坐盆归零"},
{"id": "MOTION-M1-RESET", "utterance": "右前座坐垫复位"}
]
},
{"id": "MOTION-M1-TIMED-DOWN-CLAMP", "group": "motion", "utterance": "坐垫向下移动1分钟", "expected": {"cushion_tilt": 100}, "expected_motion": {"axis": "cushion_tilt", "mode": "timed", "direction": "increase", "requested_duration_ms": 60000, "applied_duration_ms": 20000, "max_duration_ms": 20000, "duration_clamped": true, "endpoint_expected": "far"}, "expected_notice_contains": ["最大允许时长为20秒", "到达最下方"]},
{"id": "MOTION-M1-TIMED-UP-CLAMP", "group": "motion", "utterance": "坐盆向上移动60秒", "expected": {"cushion_tilt": 0}, "expected_motion": {"axis": "cushion_tilt", "mode": "timed", "direction": "decrease", "requested_duration_ms": 60000, "applied_duration_ms": 20000, "max_duration_ms": 20000, "duration_clamped": true, "endpoint_expected": "near"}, "expected_notice_contains": ["最大允许时长为20秒", "到达最上方"]},
{
"group": "motion",
"expected": {"backrest_recline": 25},
"variants": [
{"id": "MOTION-M2-25-BACKREST", "utterance": "副驾靠背调到25%"},
{"id": "MOTION-M2-25-SEATBACK", "utterance": "副驾驶椅背设置为25%"}
]
},
{
"group": "motion",
"expected": {"backrest_recline": 60},
"variants": [
{"id": "MOTION-M2-60-BACKREST", "utterance": "把副驾靠背调到60%"},
{"id": "MOTION-M2-60-SEATBACK", "utterance": "右前座椅背调至60%"}
]
},
{
"group": "motion",
"expected": {"backrest_recline": 51},
"variants": [
{"id": "MOTION-M2-ANGLE-130", "utterance": "副驾靠背角度调到130度"},
{"id": "MOTION-M2-ANGLE-130-SEATBACK", "utterance": "把副驾驶椅背设为130°"},
{"id": "MOTION-M2-ANGLE-130-RIGHT", "utterance": "右前座靠背夹角调至130度"}
]
},
{"id": "MOTION-M2-RELATIVE-FORWARD", "group": "motion", "utterance": "靠背向前移动10度", "expected": {"backrest_recline": 36}, "expected_motion": {"axis": "backrest_recline", "direction": "decrease", "delta_position_pct": 15, "delta_angle_deg": 10.0}},
{"id": "MOTION-M2-RELATIVE-BACKWARD", "group": "motion", "utterance": "副驾椅背向后移动10度", "expected": {"backrest_recline": 51}, "expected_motion": {"axis": "backrest_recline", "direction": "increase", "delta_position_pct": 15, "delta_angle_deg": 10.0}},
{"id": "MOTION-M2-RELATIVE-FORWARD-CN", "group": "motion", "utterance": "椅背往前调十度", "expected": {"backrest_recline": 36}, "expected_motion": {"axis": "backrest_recline", "direction": "decrease", "delta_position_pct": 15, "delta_angle_deg": 10.0}},
{"id": "MOTION-M2-RELATIVE-BACKWARD-CN", "group": "motion", "utterance": "靠背后仰十度", "expected": {"backrest_recline": 51}, "expected_motion": {"axis": "backrest_recline", "direction": "increase", "delta_position_pct": 15, "delta_angle_deg": 10.0}},
{
"group": "motion",
"expected": {"backrest_recline": 81},
"variants": [
{"id": "MOTION-M2-ANGLE-150", "utterance": "副驾靠背放倒到150度"},
{"id": "MOTION-M2-ANGLE-150-SEATBACK", "utterance": "乘客座椅背调到150°"}
]
},
{
"group": "motion",
"expected": {"backrest_recline": 0},
"variants": [
{"id": "MOTION-M2-UPRIGHT", "utterance": "把副驾靠背调直"},
{"id": "MOTION-M2-HOME", "utterance": "副驾驶椅背归零"},
{"id": "MOTION-M2-RESET", "utterance": "右前座靠背复位"}
]
},
{"id": "MOTION-M2-RELATIVE-OVERFLOW-BACK", "group": "motion", "utterance": "靠背向后移动70度", "expected": {"backrest_recline": 100}, "expected_motion": {"axis": "backrest_recline", "mode": "relative", "direction": "increase", "delta_position_pct": 100, "delta_angle_deg": 68.0, "requested_delta_angle_deg": 70.0, "range_clamped": true}, "expected_notice_contains": ["备用目标", "最后方"]},
{"id": "MOTION-M2-RELATIVE-OVERFLOW-FORWARD", "group": "motion", "utterance": "靠背向前移动70度", "expected": {"backrest_recline": 0}, "expected_motion": {"axis": "backrest_recline", "mode": "relative", "direction": "decrease", "delta_position_pct": 100, "delta_angle_deg": 68.0, "requested_delta_angle_deg": 70.0, "range_clamped": true}, "expected_notice_contains": ["备用目标", "最前方"]},
{"id": "MOTION-M2-TIMED-BACK-CLAMP", "group": "motion", "utterance": "靠背向后移动70秒", "expected": {"backrest_recline": 100}, "expected_motion": {"axis": "backrest_recline", "mode": "timed", "direction": "increase", "requested_duration_ms": 70000, "applied_duration_ms": 30000, "max_duration_ms": 30000, "duration_clamped": true, "endpoint_expected": "far"}, "expected_notice_contains": ["最大允许时长为30秒", "到达最后方"]},
{"id": "MOTION-M2-TIMED-FORWARD-MINUTE", "group": "motion", "utterance": "椅背向前移动1分钟", "expected": {"backrest_recline": 0}, "expected_motion": {"axis": "backrest_recline", "mode": "timed", "direction": "decrease", "requested_duration_ms": 60000, "applied_duration_ms": 30000, "max_duration_ms": 30000, "duration_clamped": true, "endpoint_expected": "near"}, "expected_notice_contains": ["最大允许时长为30秒", "到达最前方"]},
{"id": "MOTION-M2-ABS-ANGLE-OVERFLOW", "group": "motion", "utterance": "副驾靠背调到200度", "expected": {"backrest_recline": 100}, "expected_notice_contains": ["备用目标", "163度", "最后方"]},
{"id": "MOTION-M2-POST-OVERFLOW-HOME", "group": "motion", "utterance": "副驾靠背归零", "expected": {"backrest_recline": 0}},
{
"group": "motion",
"expected": {"legrest_height": 20},
"variants": [
{"id": "MOTION-M3-20-LEGREST", "utterance": "副驾腿托调到20%"},
{"id": "MOTION-M3-20-FOOTREST", "utterance": "副驾驶脚托设置为20%"}
]
},
{
"group": "motion",
"expected": {"legrest_height": 60},
"variants": [
{"id": "MOTION-M3-60-LEGREST", "utterance": "把副驾腿托调到60%"},
{"id": "MOTION-M3-60-SUPPORT", "utterance": "右前座腿部支撑调至60%"}
]
},
{
"group": "motion",
"expected": {"legrest_height": 69},
"variants": [
{"id": "MOTION-M3-ANGLE-145", "utterance": "副驾腿托抬到145度"},
{"id": "MOTION-M3-ANGLE-145-FOOTREST", "utterance": "把副驾驶脚托设为145°"}
]
},
{"id": "MOTION-M3-RELATIVE-DOWN", "group": "motion", "utterance": "脚托往下调10度", "expected": {"legrest_height": 55}, "expected_motion": {"axis": "legrest_height", "direction": "decrease", "delta_position_pct": 14, "delta_angle_deg": 10.0}},
{"id": "MOTION-M3-RELATIVE-UP", "group": "motion", "utterance": "腿托向上抬十度", "expected": {"legrest_height": 69}, "expected_motion": {"axis": "legrest_height", "direction": "increase", "delta_position_pct": 14, "delta_angle_deg": 10.0}},
{
"group": "motion",
"expected": {"legrest_height": 90},
"variants": [
{"id": "MOTION-M3-ANGLE-160", "utterance": "右前座小腿托调到160度"},
{"id": "MOTION-M3-ANGLE-160-SUPPORT", "utterance": "乘客座腿部支撑夹角设为160°"}
]
},
{
"group": "motion",
"expected": {"legrest_height": 0},
"variants": [
{"id": "MOTION-M3-RETRACT", "utterance": "把副驾腿托收起"},
{"id": "MOTION-M3-HOME", "utterance": "副驾驶脚托归零"},
{"id": "MOTION-M3-RESET", "utterance": "右前座腿部支撑复位"}
]
},
{"id": "MOTION-M3-TIMED-UP-CLAMP", "group": "motion", "utterance": "腿托向上移动1分钟", "expected": {"legrest_height": 100}, "expected_motion": {"axis": "legrest_height", "mode": "timed", "direction": "increase", "requested_duration_ms": 60000, "applied_duration_ms": 25000, "max_duration_ms": 25000, "duration_clamped": true, "endpoint_expected": "far"}, "expected_notice_contains": ["最大允许时长为25秒", "到达最上方"]},
{"id": "MOTION-M3-TIMED-DOWN-CLAMP", "group": "motion", "utterance": "脚托向下移动60秒", "expected": {"legrest_height": 0}, "expected_motion": {"axis": "legrest_height", "mode": "timed", "direction": "decrease", "requested_duration_ms": 60000, "applied_duration_ms": 25000, "max_duration_ms": 25000, "duration_clamped": true, "endpoint_expected": "near"}, "expected_notice_contains": ["最大允许时长为25秒", "到达最下方"]},
{"id": "MOTION-ALL-40", "group": "motion", "utterance": "副驾驶水平、坐垫、椅背、脚托都调到40%", "expected": {"longitudinal": 40, "cushion_tilt": 40, "backrest_recline": 40, "legrest_height": 40}},
{"id": "MOTION-ALL-40-FULLWIDTH", "group": "motion", "utterance": "右前座前后、坐盆、靠背、腿托全部设置为40%", "expected": {"longitudinal": 40, "cushion_tilt": 40, "backrest_recline": 40, "legrest_height": 40}},
{"id": "MOTION-ALL-ZERO", "group": "motion", "utterance": "把副驾水平、坐盆、靠背、腿托四轴归零", "expected": {"longitudinal": 0, "cushion_tilt": 0, "backrest_recline": 0, "legrest_height": 0}},
{"id": "MOTION-ALL-RESET", "group": "motion", "utterance": "右前座椅四轴全部复位", "expected": {"longitudinal": 0, "cushion_tilt": 0, "backrest_recline": 0, "legrest_height": 0}},
{"id": "COMBINED-ALL", "group": "combined", "utterance": "打开副驾加热1档、通风1档,座椅水平到30%,坐盆到40%,靠背到37%,腿托到42%", "expected": {"heating": 1, "ventilation": 1, "longitudinal": 30, "cushion_tilt": 40, "backrest_recline": 37, "legrest_height": 42}},
{"id": "COMBINED-ALL-OFF", "group": "combined", "utterance": "关闭副驾驶座椅加热和通风", "expected": {"heating": 0, "ventilation": 0}},
{"id": "COMBINED-HEAT-MOTION", "group": "combined", "utterance": "副驾驶加热调到二档,前后位置50%,椅背130度", "expected": {"heating": 2, "longitudinal": 50, "backrest_recline": 51}},
{"id": "COMBINED-VENT-MOTION", "group": "combined", "utterance": "右前座通风最高档 坐垫40% 脚托145度", "expected": {"ventilation": 3, "cushion_tilt": 40, "legrest_height": 69}},
{"id": "COMBINED-FULLWIDTH", "group": "combined", "utterance": "乘客座加热1档,通风2档,前后30%,坐盆40%,靠背130度,腿托145度", "expected": {"heating": 1, "ventilation": 2, "longitudinal": 30, "cushion_tilt": 40, "backrest_recline": 51, "legrest_height": 69}},
{"id": "COMBINED-CLEANUP", "group": "combined", "utterance": "关闭右前座加热和通风,并把水平、坐盆、靠背、腿托四轴归零", "expected": {"heating": 0, "ventilation": 0, "longitudinal": 0, "cushion_tilt": 0, "backrest_recline": 0, "legrest_height": 0}},
{"id": "THERM-H-SYNONYM-WARM", "group": "thermal", "utterance": "副驾座椅取暖开到2档", "expected": {"heating": 2}},
{"id": "THERM-H-SYNONYM-BRIEF", "group": "thermal", "utterance": "副驾座椅暖一下", "expected": {"heating": 1}},
{"id": "THERM-V-SYNONYM-AIR", "group": "thermal", "utterance": "副驾座椅吹风开到2档", "expected": {"ventilation": 2}},
{"id": "MOTION-M0-TIMED-WITHIN-RANGE", "group": "motion", "utterance": "副驾滑轨向后移动2秒", "expected": {}, "expected_motion": {"axis": "longitudinal", "mode": "timed", "direction": "increase", "requested_duration_ms": 2000, "applied_duration_ms": 2000, "max_duration_ms": 30000, "min_duration_ms": 300, "duration_clamped": false}},
{"id": "MOTION-M1-TIMED-ENGLISH-UNIT", "group": "motion", "utterance": "坐垫向下移动2s", "expected": {}, "expected_motion": {"axis": "cushion_tilt", "mode": "timed", "direction": "increase", "requested_duration_ms": 2000, "applied_duration_ms": 2000, "max_duration_ms": 20000, "min_duration_ms": 300, "duration_clamped": false}},
{"id": "MOTION-M2-TIMED-HALF-SECOND", "group": "motion", "utterance": "靠背向后移动半秒", "expected": {}, "expected_motion": {"axis": "backrest_recline", "mode": "timed", "direction": "increase", "requested_duration_ms": 500, "applied_duration_ms": 500, "max_duration_ms": 30000, "min_duration_ms": 300, "duration_clamped": false}},
{"id": "MOTION-M3-TIMED-MIN-CLAMP", "group": "motion", "utterance": "腿托向上移动200毫秒", "expected": {}, "expected_motion": {"axis": "legrest_height", "mode": "timed", "direction": "increase", "requested_duration_ms": 200, "applied_duration_ms": 300, "max_duration_ms": 25000, "min_duration_ms": 300, "duration_clamped": true}, "expected_notice_contains": ["最小允许时长为0.3秒", "已按0.3秒"]},
{"id": "MOTION-M1-ENDPOINT-UPPER", "group": "motion", "utterance": "坐盆抬到最上方", "expected": {"cushion_tilt": 0}},
{"id": "MOTION-M1-ENDPOINT-LOWER", "group": "motion", "utterance": "坐盆降到最下方", "expected": {"cushion_tilt": 100}},
{"id": "MOTION-M2-ENDPOINT-RECLINED", "group": "motion", "utterance": "靠背放倒到最后", "expected": {"backrest_recline": 100}},
{"id": "MOTION-M3-ENDPOINT-UPPER", "group": "motion", "utterance": "腿托升到最高", "expected": {"legrest_height": 100}},
{"id": "MOTION-M3-ENDPOINT-LOWER", "group": "motion", "utterance": "腿托降到最低", "expected": {"legrest_height": 0}},
{"id": "MOTION-M1-ABS-OVERFLOW", "group": "motion", "utterance": "坐垫位置调到150%", "expected": {"cushion_tilt": 100}, "expected_notice_contains": ["备用目标", "机械末端"]},
{"id": "MOTION-M2-ABS-ANGLE-UNDERFLOW", "group": "motion", "utterance": "副驾靠背调到80度", "expected": {"backrest_recline": 0}, "expected_notice_contains": ["备用目标", "95度", "最前方"]},
{"id": "MOTION-M3-ABS-ANGLE-OVERFLOW", "group": "motion", "utterance": "腿托调到200度", "expected": {"legrest_height": 100}, "expected_notice_contains": ["备用目标", "167度", "最上方"]}
],
"mode_cases": [
{"mode_id": "easy_entry_exit", "expected": {"longitudinal": 100, "cushion_tilt": 0, "backrest_recline": 0, "legrest_height": 0}, "variants": [
{"id": "MODE-EASY-OPEN", "utterance": "打开轻松进出模式"}, {"id": "MODE-EASY-ENTER", "utterance": "副驾进入轻松进出"}, {"id": "MODE-EASY-SWITCH", "utterance": "切换到轻松进出模式"}
]},
{"mode_id": "office", "expected": {"longitudinal": 72, "cushion_tilt": 22, "backrest_recline": 22, "legrest_height": 21}, "variants": [
{"id": "MODE-OFFICE-OPEN", "utterance": "打开办公模式"}, {"id": "MODE-OFFICE-ENTER", "utterance": "副驾进入办公"}, {"id": "MODE-OFFICE-START", "utterance": "启动办公模式"}
]},
{"mode_id": "queen_passenger_driving", "expected": {"longitudinal": 72, "cushion_tilt": 38, "backrest_recline": 44, "legrest_height": 31}, "variants": [
{"id": "MODE-QUEEN-OPEN", "utterance": "打开女王副驾模式"}, {"id": "MODE-QUEEN-ENTER", "utterance": "进入女王副驾"}, {"id": "MODE-QUEEN-SWITCH", "utterance": "切换到女王副驾行驶模式"}
]},
{"mode_id": "baby_nap", "expected": {"longitudinal": 81, "cushion_tilt": 30, "backrest_recline": 41, "legrest_height": 0}, "variants": [
{"id": "MODE-BABY-OPEN", "utterance": "打开宝宝午睡模式"}, {"id": "MODE-BABY-ENTER", "utterance": "副驾进入宝宝午睡"}, {"id": "MODE-BABY-START", "utterance": "启动宝宝午睡模式"}
]},
{"mode_id": "cruise_sofa", "expected": {"longitudinal": 51, "cushion_tilt": 30, "backrest_recline": 44, "legrest_height": 53}, "variants": [
{"id": "MODE-CRUISE-OPEN", "utterance": "打开巡航沙发模式"}, {"id": "MODE-CRUISE-ENTER", "utterance": "进入巡航沙发"}, {"id": "MODE-CRUISE-SWITCH", "utterance": "切换到巡航沙发模式"}
]},
{"mode_id": "meditation_space", "expected": {"longitudinal": 100, "cushion_tilt": 44, "backrest_recline": 44, "legrest_height": 58}, "variants": [
{"id": "MODE-MEDITATION-OPEN", "utterance": "打开冥想空间模式"}, {"id": "MODE-MEDITATION-ENTER", "utterance": "副驾进入冥想"}, {"id": "MODE-MEDITATION-START", "utterance": "启动冥想空间模式"}
]},
{"mode_id": "leisure_lounger", "expected": {"longitudinal": 51, "cushion_tilt": 30, "backrest_recline": 66, "legrest_height": 76}, "variants": [
{"id": "MODE-LEISURE-OPEN", "utterance": "打开休闲躺椅模式"}, {"id": "MODE-LEISURE-ENTER", "utterance": "进入休闲躺椅"}, {"id": "MODE-LEISURE-SWITCH", "utterance": "切换到休闲躺椅模式"}
]},
{"mode_id": "calf_decompression_float", "expected": {"longitudinal": 51, "cushion_tilt": 75, "backrest_recline": 0, "legrest_height": 100}, "variants": [
{"id": "MODE-CALF-OPEN", "utterance": "打开小腿解压悬浮模式"}, {"id": "MODE-CALF-ENTER", "utterance": "进入小腿解压"}, {"id": "MODE-CALF-START", "utterance": "启动小腿悬浮模式"}
]},
{"mode_id": "electric_beach_chair", "expected": {"longitudinal": 100, "cushion_tilt": 0, "backrest_recline": 100, "legrest_height": 60}, "variants": [
{"id": "MODE-BEACH-OPEN", "utterance": "打开电感沙滩椅"}, {"id": "MODE-BEACH-ENTER", "utterance": "进入沙滩椅模式"}, {"id": "MODE-BEACH-SWITCH", "utterance": "切换到电感沙滩椅模式"}
]},
{"mode_id": "nap", "expected": {"longitudinal": 100, "cushion_tilt": 100, "backrest_recline": 81, "legrest_height": 94}, "variants": [
{"id": "MODE-NAP-OPEN", "utterance": "打开小憩模式"}, {"id": "MODE-NAP-ENTER", "utterance": "副驾进入小憩"}, {"id": "MODE-NAP-START", "utterance": "启动小憩模式"}
]},
{"mode_id": "zero_gravity", "expected": {"longitudinal": 100, "cushion_tilt": 100, "backrest_recline": 81, "legrest_height": 94}, "variants": [
{"id": "MODE-ZERO-GRAVITY-OPEN", "utterance": "打开零重力模式"}, {"id": "MODE-ZERO-GRAVITY-ENTER", "utterance": "副驾进入零重力"}, {"id": "MODE-ZERO-GRAVITY-ENGLISH", "utterance": "开启 zero-gravity 模式"}
]}
],
"model_cases": [
{
"id": "MODEL-FUZZY-CRAMPED-COMFORT",
"utterance": "前舱空间太挤了,把座椅调整到舒服的位置",
"provider": "openai",
"expected_provider_prefix": "openai:gpt-5.6-sol",
"expected": {
"longitudinal": 80,
"cushion_tilt": 30,
"backrest_recline": 35,
"legrest_height": 10
}
},
{
"id": "MODEL-FUZZY-LEGROOM-COMFORT",
"utterance": "副驾腿部空间不够,把座椅调到舒适位置",
"provider": "openai",
"expected_provider_prefix": "openai:gpt-5.6-sol",
"expected": {"longitudinal": 80, "cushion_tilt": 30, "backrest_recline": 35, "legrest_height": 10}
},
{
"id": "MODEL-FUZZY-FRONT-CABIN-PARAPHRASE",
"utterance": "感觉前舱有点挤,帮我把座椅调整舒服些",
"provider": "openai",
"expected_provider_prefix": "openai:gpt-5.6-sol",
"expected": {"longitudinal": 80, "cushion_tilt": 30, "backrest_recline": 35, "legrest_height": 10}
},
{
"id": "MODEL-FUZZY-FIRST-PERSON-COMFORT",
"utterance": "我在副驾坐着太挤了,给我调整一个舒服的坐姿",
"provider": "openai",
"expected_provider_prefix": "openai:gpt-5.6-sol",
"expected": {"longitudinal": 80, "cushion_tilt": 30, "backrest_recline": 35, "legrest_height": 10}
}
],
"rejection_cases": [
{"id": "SAFE-DRIVER-THERMAL", "utterance": "打开主驾座椅通风", "expected_status": "needs_confirmation"},
{"id": "SAFE-DRIVER-MOTION", "utterance": "把驾驶位靠背调到50%", "expected_status": "needs_confirmation"},
{"id": "SAFE-DRIVER-MODE", "utterance": "主驾进入零重力模式", "expected_status": "needs_confirmation"},
{"id": "SAFE-FRONT-ROW", "utterance": "打开前排座椅加热", "expected_status": "needs_confirmation"},
{"id": "SAFE-LEVEL-UNSUPPORTED", "utterance": "副驾座椅加热开到4档", "expected_status": "needs_confirmation"},
{"id": "SAFE-MISSING-STATE", "utterance": "副驾座椅通风", "expected_status": "needs_confirmation"},
{"id": "SAFE-CONTRADICTORY", "utterance": "打开副驾加热然后关闭副驾加热", "expected_status": "needs_confirmation"},
{"id": "SAFE-UNKNOWN-CAPABILITY", "utterance": "把副驾座椅按摩打开", "expected_status": "needs_confirmation"},
{"id": "SAFE-UNKNOWN-MODE", "utterance": "打开老板模式", "expected_status": "needs_confirmation"},
{"id": "SAFE-MULTIPLE-MODES", "utterance": "进入办公模式和零重力模式", "expected_status": "needs_confirmation"},
{"id": "SAFE-LEGREST-EXTENSION", "utterance": "把副驾腿托伸长到50%", "expected_status": "needs_confirmation"},
{"id": "SAFE-RELATIVE-WRONG-UNIT", "utterance": "坐垫向前移动10度", "expected_status": "needs_confirmation"},
{"id": "SAFE-RELATIVE-MISSING-AXIS", "utterance": "向前移动10度", "expected_status": "needs_confirmation"},
{"id": "SAFE-VAGUE", "utterance": "把座椅调舒服一点", "expected_status": "needs_confirmation"},
{"id": "SAFE-UNSUPPORTED-REAR-ROW", "utterance": "打开后排座椅加热", "expected_status": "needs_confirmation"},
{"id": "SAFE-UNSUPPORTED-FRONT-LEFT", "utterance": "打开左前座椅通风", "expected_status": "needs_confirmation"},
{"id": "SAFE-UNSUPPORTED-ALL-SEATS", "utterance": "打开所有座椅加热", "expected_status": "needs_confirmation"},
{"id": "SAFE-DECIMAL-POSITION", "utterance": "副驾座椅水平位置调到50.5%", "expected_status": "needs_confirmation"},
{"id": "SAFE-NEGATIVE-POSITION", "utterance": "副驾座椅水平位置调到-10%", "expected_status": "needs_confirmation"},
{"id": "SAFE-ZERO-DURATION", "utterance": "副驾靠背向后移动0秒", "expected_status": "needs_confirmation"}
],
"final_state": {
"utterance": "关闭副驾座椅加热和通风,并把水平、坐盆、靠背、腿托四轴归零",
"expected": {
"heating": 0,
"ventilation": 0,
"longitudinal": 0,
"cushion_tilt": 0,
"backrest_recline": 0,
"legrest_height": 0
}
}
}
@@ -0,0 +1,171 @@
from __future__ import annotations
import json
import os
import tempfile
import unittest
from pathlib import Path
from unittest.mock import patch
from cognitive_agent.application.seat_service import REPOSITORY_ROOT, SeatDemoService
SUITE_PATH = REPOSITORY_ROOT / "tests" / "fixtures" / "seat_frontend_hil_suite.json"
EXPECTED_CAPABILITIES = {
"heating": "seat.set_heating",
"ventilation": "seat.set_ventilation",
"longitudinal": "seat.set_longitudinal_position",
"cushion_tilt": "seat.set_cushion_tilt",
"backrest_recline": "seat.set_backrest_recline",
"legrest_height": "seat.set_legrest_height",
}
def expand_entries(entries: list[dict], group: str | None = None) -> list[dict]:
expanded = []
for entry in entries:
base = {key: value for key, value in entry.items() if key != "variants"}
if group is not None:
base["group"] = group
variants = entry.get("variants")
if not variants:
expanded.append(base)
continue
expanded.extend({**base, **variant} for variant in variants)
return expanded
class SeatFrontendInputCoverageTests(unittest.TestCase):
@classmethod
def setUpClass(cls) -> None:
cls.temporary_directory = tempfile.TemporaryDirectory()
with patch.dict(
os.environ,
{"COGNITIVE_AGENT_HARDWARE_EXECUTION_ENABLED": "false"},
clear=False,
):
cls.service = SeatDemoService(REPOSITORY_ROOT)
cls.service.artifact_root = Path(cls.temporary_directory.name)
cls.suite = json.loads(SUITE_PATH.read_text(encoding="utf-8"))
cls.positive_cases = expand_entries(cls.suite["cases"]) + expand_entries(
cls.suite["mode_cases"], "mode"
)
cls.model_cases = expand_entries(cls.suite.get("model_cases", []), "model")
cls.rejection_cases = expand_entries(cls.suite["rejection_cases"], "safety")
@classmethod
def tearDownClass(cls) -> None:
cls.temporary_directory.cleanup()
def test_suite_has_broad_equivalence_and_safety_coverage(self) -> None:
self.assertGreaterEqual(len(self.positive_cases), 160)
self.assertGreaterEqual(len(self.model_cases), 4)
self.assertGreaterEqual(len(self.rejection_cases), 20)
self.assertEqual(
{case["group"] for case in self.positive_cases},
{"thermal", "motion", "combined", "mode"},
)
self.assertEqual(
{case["mode_id"] for case in self.positive_cases if case["group"] == "mode"},
{
"easy_entry_exit",
"office",
"queen_passenger_driving",
"baby_nap",
"cruise_sofa",
"meditation_space",
"leisure_lounger",
"calf_decompression_float",
"electric_beach_chair",
"nap",
"zero_gravity",
},
)
def test_all_expanded_case_ids_and_utterances_are_unique(self) -> None:
cases = [*self.positive_cases, *self.model_cases, *self.rejection_cases]
self.assertEqual(len({case["id"] for case in cases}), len(cases))
self.assertEqual(len({case["utterance"] for case in cases}), len(cases))
def test_model_cases_require_gpt56_and_define_physical_targets(self) -> None:
for case in self.model_cases:
with self.subTest(case=case["id"]):
self.assertEqual(case["provider"], "openai")
self.assertEqual(
case["expected_provider_prefix"], "openai:gpt-5.6-sol"
)
self.assertEqual(
set(case["expected"]),
{
"longitudinal",
"cushion_tilt",
"backrest_recline",
"legrest_height",
},
)
def test_every_positive_utterance_generates_the_expected_dry_run(self) -> None:
for case in self.positive_cases:
with self.subTest(case=case["id"], utterance=case["utterance"]):
result = self.service.process(
case["utterance"],
provider=case.get("provider", "local"),
conversation_id="conv-coverage-positive-sequence",
execution_mode="dry_run",
vehicle_id=self.suite["vehicle_id"],
)
self.assertEqual(result["status"], "succeeded")
if case.get("mode_id"):
self.assertIn(
case["mode_id"],
{item["id"] for item in result["matched_modes"]},
)
seat_config = result["function_configs"].get("seat") or result[
"function_config"
]
self.assertIsNotNone(seat_config)
operations = seat_config["spec"]["operations"]
by_capability = {item["capability"]: item for item in operations}
passenger = result["actuator_state"]["passenger"]
for field, expected in case["expected"].items():
capability = EXPECTED_CAPABILITIES[field]
self.assertIn(capability, by_capability)
operation = by_capability[capability]
self.assertEqual(operation["target"]["instance"], "passenger")
self.assertEqual(operation["target"]["zone"], "front_right")
if field in {"heating", "ventilation"}:
if expected == 0:
self.assertEqual(operation["parameters"]["state"], "off")
else:
self.assertEqual(passenger[field]["level"], expected)
else:
self.assertEqual(operation["parameters"]["position_pct"], expected)
self.assertEqual(
passenger["position"][field]["position_pct"], expected
)
expected_motion = case.get("expected_motion")
if expected_motion:
capability = EXPECTED_CAPABILITIES[expected_motion["axis"]]
operation = by_capability[capability]
self.assertIn("motion", operation)
for field, expected in expected_motion.items():
if field != "axis":
self.assertEqual(operation["motion"][field], expected)
def test_every_rejection_utterance_stops_before_execution(self) -> None:
for case in self.rejection_cases:
with self.subTest(case=case["id"], utterance=case["utterance"]):
result = self.service.process(
case["utterance"],
provider=case.get("provider", "local"),
conversation_id=f"conv-coverage-{case['id'].lower()}",
execution_mode="dry_run",
vehicle_id=self.suite["vehicle_id"],
)
self.assertEqual(result["status"], case["expected_status"])
self.assertIsNone(result["function_config"])
self.assertEqual(result["function_configs"], {})
self.assertIsNone(result["execution_report"])
if __name__ == "__main__":
unittest.main()
+46
View File
@@ -0,0 +1,46 @@
from __future__ import annotations
import os
import tempfile
import unittest
from pathlib import Path
from unittest.mock import patch
from cognitive_agent.config_loader import load_local_env
class LocalEnvironmentLoaderTests(unittest.TestCase):
def test_file_replaces_blank_inherited_value_but_preserves_non_blank_override(self) -> None:
with tempfile.TemporaryDirectory() as directory:
path = Path(directory) / ".env"
path.write_text(
"OPENAI_MODEL=gpt-5.6-sol\nOPENAI_BASE_URL=https://configured.example/v1\n",
encoding="utf-8",
)
with patch.dict(
os.environ,
{
"OPENAI_MODEL": "",
"OPENAI_BASE_URL": "https://explicit.example/v1",
},
clear=True,
):
load_local_env(path)
self.assertEqual(os.environ["OPENAI_MODEL"], "gpt-5.6-sol")
self.assertEqual(
os.environ["OPENAI_BASE_URL"], "https://explicit.example/v1"
)
def test_explicit_override_replaces_stale_non_blank_launcher_value(self) -> None:
with tempfile.TemporaryDirectory() as directory:
path = Path(directory) / ".env"
path.write_text("OPENAI_MODEL=gpt-5.6-sol\n", encoding="utf-8")
with patch.dict(
os.environ, {"OPENAI_MODEL": "gpt-5-mini"}, clear=True
):
load_local_env(path, override=True)
self.assertEqual(os.environ["OPENAI_MODEL"], "gpt-5.6-sol")
if __name__ == "__main__":
unittest.main()
+36 -1
View File
@@ -6,7 +6,10 @@ import unittest
import urllib.error
from unittest.mock import patch
from cognitive_agent.infrastructure.llm.openai_responses import OpenAIIntentInterpreter
from cognitive_agent.infrastructure.llm.openai_responses import (
SYSTEM_PROMPT,
OpenAIIntentInterpreter,
)
INTENT = {
@@ -45,10 +48,37 @@ class FakeResponse:
class OpenAIResponsesTests(unittest.TestCase):
def test_blank_model_and_base_url_use_documented_defaults(self) -> None:
with patch.dict(
os.environ,
{
"OPENAI_API_KEY": " test-key ",
"OPENAI_MODEL": " ",
"OPENAI_BASE_URL": "",
"OPENAI_REASONING_EFFORT": " ",
},
clear=True,
):
interpreter = OpenAIIntentInterpreter()
self.assertEqual(interpreter.api_key, "test-key")
self.assertEqual(interpreter.model, "gpt-5.6-sol")
self.assertEqual(interpreter.responses_endpoint, "https://api.openai.com/v1/responses")
self.assertEqual(interpreter.reasoning_effort, "medium")
def test_fuzzy_front_cabin_goal_has_deterministic_ergonomic_policy(self) -> None:
normalized = " ".join(SYSTEM_PROMPT.split())
self.assertIn("front cabin is cramped", normalized)
self.assertIn("longitudinal 80%", normalized)
self.assertIn("backrest recline 35%", normalized)
self.assertIn("do not manufacture a driver-seat target", normalized)
def test_non_streaming_structured_output(self) -> None:
response_body = json.dumps({"output_text": json.dumps(INTENT)})
environment = {
"OPENAI_API_KEY": "test-key",
"OPENAI_MODEL": "gpt-5.6-sol",
"OPENAI_BASE_URL": "https://api.openai.com/v1",
"OPENAI_STREAM": "false",
}
@@ -73,6 +103,11 @@ class OpenAIResponsesTests(unittest.TestCase):
request_payload = json.loads(urlopen.call_args.args[0].data.decode("utf-8"))
self.assertEqual(result, INTENT)
self.assertNotIn("stream", request_payload)
self.assertEqual(request_payload["model"], "gpt-5.6-sol")
self.assertEqual(
urlopen.call_args.args[0].full_url,
"https://api.openai.com/v1/responses",
)
self.assertIn("capability registry", request_payload["input"][1]["content"][0]["text"])
self.assertIn('"level": 1', request_payload["input"][2]["content"][0]["text"])