feat: integrate production cockpit AI assistant

This commit is contained in:
vee
2026-09-08 17:46:07 +08:00
parent 5e228e12ae
commit 93103c2579
66 changed files with 11046 additions and 374 deletions
+306 -3
View File
@@ -3,6 +3,7 @@ from __future__ import annotations
import json
from collections import Counter
from collections.abc import Callable, Mapping
from dataclasses import replace
import pytest
@@ -64,6 +65,46 @@ def test_catalog_covers_every_authoritative_definition_exactly_once(
)
def test_snapshot_resolves_explicit_vehicle_and_keeps_no_argument_default(
vehicle_service: SeatDemoService,
monkeypatch: pytest.MonkeyPatch,
) -> None:
catalog = _catalog(vehicle_service)
registry = vehicle_service.vehicle_profiles
original_get = registry.get
default_vehicle = original_get(None)
alternate_vehicle = replace(
default_vehicle,
id="alternate_vehicle",
name="另一测试车型",
)
requested_ids: list[str | None] = []
def get(vehicle_id: str | None): # type: ignore[no-untyped-def]
requested_ids.append(vehicle_id)
if vehicle_id == alternate_vehicle.id:
return alternate_vehicle
return original_get(vehicle_id)
monkeypatch.setattr(registry, "get", get)
default_snapshot = catalog.snapshot()
alternate_snapshot = catalog.snapshot(vehicle_id=alternate_vehicle.id)
assert requested_ids == [None, alternate_vehicle.id]
assert default_snapshot["vehicle"] == {
"id": default_vehicle.id,
"name": default_vehicle.name,
}
assert alternate_snapshot["vehicle"] == {
"id": alternate_vehicle.id,
"name": alternate_vehicle.name,
}
assert catalog.scene_composition_context(alternate_snapshot)["vehicle"] == (
alternate_snapshot["vehicle"]
)
def test_all_44_capabilities_preserve_authoritative_metadata(
vehicle_service: SeatDemoService,
) -> None:
@@ -217,7 +258,9 @@ def test_all_modes_are_discoverable_and_reference_callable_capabilities(
}
modes = snapshot["modes"]
assert len(modes) == 13
# Runtime user modes are intentionally discovered without a restart, so
# the repository's 13 presets are a lower bound rather than a fixed total.
assert len(modes) >= 13
assert {item["id"] for item in modes} >= {
"baby_nap",
"dynamic_cradle",
@@ -235,6 +278,116 @@ def test_all_modes_are_discoverable_and_reference_callable_capabilities(
)
def test_mode_catalog_projects_bounded_action_details_without_free_form_reasons(
vehicle_service: SeatDemoService,
) -> None:
snapshot = _catalog(vehicle_service).snapshot()
office = next(item for item in snapshot["modes"] if item["id"] == "office")
assert len(office["operations"]) == office["operation_count"] == 10
temperature = next(
item
for item in office["operations"]
if item["capability"] == "climate.set_temperature"
)
assert temperature == {
"domain": "climate",
"capability": "climate.set_temperature",
"target_scope": "passenger",
"operation": "set",
"parameters": [{"name": "temperature_c", "value": 25}],
}
assert all("reason" not in operation for operation in office["operations"])
@pytest.mark.parametrize(
"question",
(
"办公模式下会控制哪些车端设备,执行哪些动作?",
"办公模式包含什么配置",
"请展示一下办公模式的设备和参数",
"请告诉我办公模式下执行哪些动作",
),
)
def test_saved_mode_detail_fast_answer_is_exact_read_only_and_natural(
vehicle_service: SeatDemoService,
question: str,
) -> None:
catalog = _catalog(vehicle_service)
answer = catalog.fast_answer(question, catalog.snapshot())
assert answer is not None
assert answer.topic == "specific_mode_detail"
assert all(
fragment in answer.answer
for fragment in (
"办公模式",
"座椅",
"空调",
"遮阳帘",
"氛围灯",
"副驾空调温度设为25℃",
"共10项动作",
"本次仅查询,未下发车辆控制",
)
)
assert "Matched mode profile" not in answer.answer
assert "climate.set_temperature" not in answer.answer
def test_unknown_mode_detail_fast_answer_fails_closed_without_inventing_actions(
vehicle_service: SeatDemoService,
) -> None:
catalog = _catalog(vehicle_service)
answer = catalog.fast_answer(
"火星模式中包含哪些设备和动作?",
catalog.snapshot(),
)
assert answer is not None
assert answer.topic == "specific_mode_detail_not_found"
assert "火星模式" in answer.answer
assert "未找到" in answer.answer
assert "未下发车辆控制" in answer.answer
def test_common_scene_templates_are_read_only_callable_catalog_entries(
vehicle_service: SeatDemoService,
) -> None:
snapshot = _catalog(vehicle_service).snapshot()
templates = snapshot["scene_templates"]
assert snapshot["source_status"]["scene_templates"] == "ready"
assert len(templates) == 12
assert {item["name"] for item in templates} >= {
"观影模式",
"露营模式",
"阅读模式",
"放松模式",
"雨天除雾模式",
"夏季清凉模式",
"冬季温暖模式",
}
assert all(item["read_only"] is True for item in templates)
assert all(item["assistant_callable"] is True for item in templates)
assert all(item["plan"]["metadata"]["template_id"] == item["id"] for item in templates)
assert all("source" not in item for item in templates)
def test_scene_template_provider_failure_is_explicit_and_empty(
vehicle_service: SeatDemoService,
monkeypatch: pytest.MonkeyPatch,
) -> None:
def unavailable(*_args: object, **_kwargs: object) -> list[dict[str, object]]:
raise OSError("template directory unavailable")
monkeypatch.setattr(vehicle_service.scene_templates, "public_templates", unavailable)
snapshot = _catalog(vehicle_service).snapshot()
assert snapshot["source_status"]["scene_templates"] == "unavailable"
assert snapshot["scene_templates"] == []
def test_user_mode_is_discovered_on_the_next_snapshot_without_restart(
vehicle_service: SeatDemoService,
monkeypatch: pytest.MonkeyPatch,
@@ -272,8 +425,7 @@ def test_user_mode_is_discovered_on_the_next_snapshot_without_restart(
mode = next(item for item in after["modes"] if item["id"] == "user_focus")
answer = catalog.fast_answer("当前有哪些模式", after)
assert len(before["modes"]) == 13
assert len(after["modes"]) == 14
assert len(after["modes"]) == len(before["modes"]) + 1
assert after["catalog_revision"] != before["catalog_revision"]
assert mode["assistant_callable"] is True
assert answer is not None
@@ -571,6 +723,157 @@ def test_fast_answer_never_consumes_a_direct_control_request(
assert catalog.fast_answer(instruction, catalog.snapshot()) is None
@pytest.mark.parametrize(
"instruction",
[
"打开后备箱",
"请开启后备箱门",
"帮我把尾门打开一下",
"打开尾门盖",
"开启后备箱盖",
"麻烦帮忙打开电动尾门",
"关闭行李箱",
"把行李厢盖关上",
"打开物理天窗",
"开启近光灯",
"打开前大灯",
"关闭后雾灯",
"请锁车",
"关闭墙壁灯",
"打开方向盘加热",
"调节方向盘",
"打开雨刮",
"请鸣笛",
"按喇叭",
"可以帮我打开后备箱吗",
"能不能打开尾门",
"后备箱能开吗",
"支持后备箱控制吗",
"是否可以打开后备箱",
"是否能打开后备箱",
"请问后备箱是否能打开",
],
)
def test_known_unsupported_controls_return_catalog_backed_quick_suggestions(
vehicle_service: SeatDemoService,
instruction: str,
) -> None:
catalog = _catalog(vehicle_service)
snapshot = catalog.snapshot()
callable_ids = {
item["id"] for item in snapshot["capabilities"] if item["assistant_callable"] is True
}
native_ids = {
item["id"] for item in snapshot["capabilities"] if item["native_can_verified"] is True
}
answer = catalog.fast_answer(instruction, snapshot)
assert answer is not None
assert answer.topic == "unsupported_vehicle_control"
assert answer.answer.startswith(
"当前车端暂不支持该功能控制哦,您可以尝试以下控制:"
)
assert 1 <= len(answer.suggestions) <= 3
assert all(suggestion.label in answer.answer for suggestion in answer.suggestions)
assert all(
set(suggestion.capability_ids) <= callable_ids for suggestion in answer.suggestions
)
assert all(set(suggestion.capability_ids) <= native_ids for suggestion in answer.suggestions)
@pytest.mark.parametrize(
"message",
[
"后备箱怎么打开?",
"不要打开后备箱",
"先别打开后备箱",
"如果打开后备箱会怎么样?",
"后备箱打不开",
"打开后备箱失败了",
"解释一下“打开后备箱”",
"帮我写一个打开后备箱的故事",
"今天聊聊后备箱",
"把后备箱打开到一半",
"可以介绍一下后备箱吗",
"你支持回答后备箱相关问题吗",
"哪些车型支持打开后备箱",
"支持后备箱保养知识问答吗",
],
)
def test_unsupported_control_fast_path_does_not_consume_non_commands(
vehicle_service: SeatDemoService,
message: str,
) -> None:
catalog = _catalog(vehicle_service)
assert catalog.fast_answer(message, catalog.snapshot()) is None
def test_unsupported_control_fast_path_leaves_mixed_supported_control_for_full_route(
vehicle_service: SeatDemoService,
) -> None:
catalog = _catalog(vehicle_service)
assert catalog.fast_answer("打开后备箱并关闭所有车窗", catalog.snapshot()) is None
assert catalog.fast_answer("打开天窗遮阳帘", catalog.snapshot()) is None
def test_unsupported_control_fast_path_stands_down_when_runtime_adds_capability(
vehicle_service: SeatDemoService,
) -> None:
catalog = _catalog(vehicle_service)
snapshot = json.loads(json.dumps(catalog.snapshot(), ensure_ascii=False))
snapshot["capabilities"].append(
{
"id": "body.toggle_tailgate",
"domain": "door",
"description": "Toggle the vehicle tailgate.",
"target_devices": ["tailgate"],
"assistant_callable": True,
"target_scopes": ["vehicle"],
}
)
assert catalog.fast_answer("打开后备箱", snapshot) is None
def test_quick_control_suggestions_are_filtered_by_current_callable_catalog(
vehicle_service: SeatDemoService,
) -> None:
catalog = _catalog(vehicle_service)
snapshot = json.loads(json.dumps(catalog.snapshot(), ensure_ascii=False))
for capability in snapshot["capabilities"]:
if capability["id"] in {"window.set_motion", "roof.set_shade_motion"}:
capability["assistant_callable"] = False
answer = catalog.fast_answer("打开后备箱", snapshot)
assert answer is not None
assert [suggestion.id for suggestion in answer.suggestions] == ["set-climate-24c"]
assert "关闭所有车窗" not in answer.answer
assert "关闭遮阳帘" not in answer.answer
def test_quick_control_suggestions_require_native_can_verification(
vehicle_service: SeatDemoService,
) -> None:
catalog = _catalog(vehicle_service)
snapshot = json.loads(json.dumps(catalog.snapshot(), ensure_ascii=False))
for capability in snapshot["capabilities"]:
if capability["id"] == "climate.set_temperature":
capability["native_can_verified"] = False
answer = catalog.fast_answer("打开后备箱", snapshot)
assert answer is not None
assert "set-climate-24c" not in {item.id for item in answer.suggestions}
assert all(
capability_id != "climate.set_temperature"
for item in answer.suggestions
for capability_id in item.capability_ids
)
def test_control_terms_do_not_authorize_declared_but_unexecutable_controls(
vehicle_service: SeatDemoService,
) -> None: