1087 lines
35 KiB
Python
1087 lines
35 KiB
Python
from __future__ import annotations
|
|
|
|
import json
|
|
from concurrent.futures import ThreadPoolExecutor
|
|
from pathlib import Path
|
|
from threading import Barrier
|
|
from unittest.mock import MagicMock, patch
|
|
|
|
import pytest
|
|
|
|
from cognitive_agent.application.codex_assistant import CodexAssistantService
|
|
from cognitive_agent.application.assistant_capabilities import AssistantCapabilityCatalog
|
|
from cognitive_agent.application.mode_profile import ModeProfileRegistry
|
|
from cognitive_agent.application.seat_service import (
|
|
REPOSITORY_ROOT,
|
|
SeatDemoService,
|
|
)
|
|
|
|
NAP_REQUEST = "构建一个小憩模式"
|
|
INTERNAL_SCENE_PLACEHOLDER = "请根据该场景名称使用当前可控原子能力构建并执行场景"
|
|
EXPECTED_NAP_CAPABILITIES = {
|
|
"seat.set_longitudinal_position",
|
|
"seat.set_cushion_tilt",
|
|
"seat.set_backrest_recline",
|
|
"seat.set_legrest_height",
|
|
"climate.set_temperature",
|
|
"climate.set_fan_level",
|
|
"climate.set_cycle_mode",
|
|
"roof.set_shade_position",
|
|
"ambient_light.set_power",
|
|
}
|
|
|
|
|
|
def _scene_request(
|
|
domain: str,
|
|
capability: str,
|
|
target_scope: str,
|
|
parameters: dict[str, object],
|
|
) -> dict[str, object]:
|
|
return {
|
|
"domain": domain,
|
|
"capability": capability,
|
|
"target_scope": target_scope,
|
|
"operation": "set",
|
|
"duration_ms": None,
|
|
"parameters": [
|
|
{"name": name, "value": value}
|
|
for name, value in parameters.items()
|
|
],
|
|
"reason": "根据午休场景组合当前工程已登记能力。",
|
|
}
|
|
|
|
|
|
LUNCH_BREAK_SCENE_PLAN = {
|
|
"detected_domains": ["seat", "climate", "roof", "ambient_light"],
|
|
"requests": [
|
|
_scene_request(
|
|
"seat",
|
|
"seat.set_backrest_recline",
|
|
"passenger",
|
|
{"position_pct": 70},
|
|
),
|
|
_scene_request(
|
|
"climate",
|
|
"climate.set_temperature",
|
|
"vehicle",
|
|
{"temperature_c": 23},
|
|
),
|
|
_scene_request(
|
|
"climate",
|
|
"climate.set_fan_level",
|
|
"vehicle",
|
|
{"level": 1},
|
|
),
|
|
_scene_request(
|
|
"roof",
|
|
"roof.set_shade_position",
|
|
"vehicle",
|
|
{"position_pct": 0},
|
|
),
|
|
_scene_request(
|
|
"ambient_light",
|
|
"ambient_light.set_power",
|
|
"vehicle",
|
|
{"state": "off"},
|
|
),
|
|
],
|
|
"ambiguities": [],
|
|
"decision_summary": ["组合安静、遮光且温度适宜的午休场景。"],
|
|
"confidence": 0.92,
|
|
}
|
|
|
|
|
|
@pytest.fixture
|
|
def vehicle_service(tmp_path: Path) -> SeatDemoService:
|
|
service = SeatDemoService(REPOSITORY_ROOT)
|
|
service.artifact_root = tmp_path / "runs"
|
|
service.mode_profiles = ModeProfileRegistry(
|
|
REPOSITORY_ROOT / "config" / "modes",
|
|
user_root=tmp_path / "user-modes",
|
|
)
|
|
return service
|
|
|
|
|
|
def _build_nap_mode(service: SeatDemoService, conversation_id: str) -> dict[str, object]:
|
|
with patch.object(
|
|
service.openai,
|
|
"interpret",
|
|
side_effect=AssertionError(
|
|
"a repository-backed named mode must not depend on a cloud model"
|
|
),
|
|
):
|
|
return service.preview_native_can(
|
|
NAP_REQUEST,
|
|
provider="local",
|
|
conversation_id=conversation_id,
|
|
execution_mode="hardware",
|
|
vehicle_id="denza_n7",
|
|
)
|
|
|
|
|
|
def _intent_actions(result: dict[str, object]) -> list[dict[str, object]]:
|
|
intent_ir = result.get("intent_ir")
|
|
assert isinstance(intent_ir, dict)
|
|
spec = intent_ir.get("spec")
|
|
assert isinstance(spec, dict)
|
|
actions = spec.get("actions")
|
|
assert isinstance(actions, list)
|
|
assert all(isinstance(action, dict) for action in actions)
|
|
return actions
|
|
|
|
|
|
def test_name_only_nap_request_reuses_the_verified_repository_profile(
|
|
vehicle_service: SeatDemoService,
|
|
tmp_path: Path,
|
|
) -> None:
|
|
preset = vehicle_service.mode_profiles.get_profile("nap", "denza_n7")
|
|
|
|
result = _build_nap_mode(vehicle_service, "conv-nap-name-inference")
|
|
|
|
assert result["status"] == "succeeded"
|
|
assert result["execution_mode"] == "dry_run"
|
|
assert result["hardware_execution_allowed"] is False
|
|
assert result["orchestration_route"] == "mode_profile_create_preview"
|
|
assert result["mode_profile"] is None
|
|
draft = result["mode_profile_draft"]
|
|
assert isinstance(draft, dict)
|
|
assert draft["name"] == "小憩模式"
|
|
assert draft["operation_count"] == len(preset.operations) == 9
|
|
assert set(draft["domains"]) == {"seat", "climate", "roof", "ambient_light"}
|
|
assert draft["existing_mode_id"] == "nap"
|
|
assert isinstance(draft["existing_mode_revision"], str)
|
|
assert len(draft["existing_mode_revision"]) == 16
|
|
assert draft["generation_source"] == "registered_mode_profile"
|
|
|
|
actions = _intent_actions(result)
|
|
assert {action["capability"] for action in actions} == {
|
|
operation.capability for operation in preset.operations
|
|
} == EXPECTED_NAP_CAPABILITIES
|
|
expected_targets_and_parameters = {
|
|
operation.capability: (
|
|
"passenger"
|
|
if operation.domain == "seat" and operation.target_scope == "front_right"
|
|
else operation.target_scope,
|
|
operation.parameters,
|
|
)
|
|
for operation in preset.operations
|
|
}
|
|
actual_targets_and_parameters = {
|
|
action["capability"]: (
|
|
action["target"].get("instance") or action["target"].get("zone"),
|
|
action["parameters"],
|
|
)
|
|
for action in actions
|
|
}
|
|
assert actual_targets_and_parameters == expected_targets_and_parameters
|
|
assert not list((tmp_path / "user-modes").rglob("*.yaml"))
|
|
|
|
public_result = json.dumps(result, ensure_ascii=False, default=str)
|
|
assert INTERNAL_SCENE_PLACEHOLDER not in public_result
|
|
|
|
|
|
def test_inferred_nap_actions_are_bounded_by_the_runtime_capability_catalog(
|
|
vehicle_service: SeatDemoService,
|
|
) -> None:
|
|
result = _build_nap_mode(vehicle_service, "conv-nap-capability-boundary")
|
|
actions = _intent_actions(result)
|
|
catalog = {
|
|
capability["name"]: capability
|
|
for capability in vehicle_service.runtime_info()["capabilities"]
|
|
}
|
|
|
|
for action in actions:
|
|
capability = catalog[action["capability"]]
|
|
assert capability.get("execution_support") != "observational_only"
|
|
assert capability.get("physical_control_supported", True) is not False
|
|
assert action["domain"] in {"seat", "climate", "roof", "ambient_light"}
|
|
|
|
assert "door.set_vehicle_lock" not in {
|
|
action["capability"] for action in actions
|
|
}
|
|
assert all("media" not in str(action["capability"]) for action in actions)
|
|
|
|
|
|
def test_codex_composes_an_unregistered_name_only_scene_from_runtime_capabilities(
|
|
vehicle_service: SeatDemoService,
|
|
tmp_path: Path,
|
|
) -> None:
|
|
with patch.object(
|
|
vehicle_service.openai,
|
|
"interpret",
|
|
side_effect=AssertionError("the embedded Codex plan must not invoke legacy GPT"),
|
|
):
|
|
result = vehicle_service.preview_native_can(
|
|
"构建一个午休模式",
|
|
provider="local",
|
|
conversation_id="conv-codex-scene-composition",
|
|
execution_mode="hardware",
|
|
vehicle_id="denza_n7",
|
|
assistant_plan=LUNCH_BREAK_SCENE_PLAN,
|
|
)
|
|
|
|
draft = result["mode_profile_draft"]
|
|
assert result["status"] == "succeeded"
|
|
assert result["execution_mode"] == "dry_run"
|
|
assert result["hardware_execution_allowed"] is False
|
|
assert result["provider_used"] == "local_rules+codex_app_server"
|
|
assert result["orchestration_route"] == "mode_profile_create_preview"
|
|
assert draft["name"] == "午休模式"
|
|
assert draft["operation_count"] == 5
|
|
assert draft["generation_source"] == "codex_scene_composition"
|
|
assert "existing_mode_id" not in draft
|
|
assert {action["capability"] for action in _intent_actions(result)} == {
|
|
"seat.set_backrest_recline",
|
|
"climate.set_temperature",
|
|
"climate.set_fan_level",
|
|
"roof.set_shade_position",
|
|
"ambient_light.set_power",
|
|
}
|
|
assert not list((tmp_path / "user-modes").rglob("*.yaml"))
|
|
|
|
trial = vehicle_service.trial_mode_profile_draft(
|
|
str(draft["name"]),
|
|
str(draft["instruction"]),
|
|
str(draft["source_request_id"]),
|
|
str(draft["vehicle_id"]),
|
|
)
|
|
assert trial["status"] == "succeeded"
|
|
assert trial["execution_mode"] == "dry_run"
|
|
assert trial["hardware_execution_allowed"] is False
|
|
assert trial["orchestration_route"] == "mode_profile_draft_trial"
|
|
assert trial["trial_source"] == "server_validated_preview_artifact"
|
|
assert _intent_actions(trial) == _intent_actions(result)
|
|
|
|
with pytest.raises(ValueError, match="名称不匹配"):
|
|
vehicle_service.trial_mode_profile_draft(
|
|
"另一个模式",
|
|
str(draft["instruction"]),
|
|
str(draft["source_request_id"]),
|
|
str(draft["vehicle_id"]),
|
|
)
|
|
|
|
result_path = vehicle_service.artifact_root / str(draft["source_request_id"]) / "result.json"
|
|
tampered = json.loads(result_path.read_text(encoding="utf-8"))
|
|
tampered["mode_profile_draft"]["source_request_id"] = "req-another-draft"
|
|
result_path.write_text(json.dumps(tampered, ensure_ascii=False), encoding="utf-8")
|
|
with pytest.raises(ValueError, match="来源请求编号不匹配"):
|
|
vehicle_service.trial_mode_profile_draft(
|
|
str(draft["name"]),
|
|
str(draft["instruction"]),
|
|
str(draft["source_request_id"]),
|
|
str(draft["vehicle_id"]),
|
|
)
|
|
|
|
|
|
def test_codex_scene_composition_still_rejects_capabilities_outside_the_registry(
|
|
vehicle_service: SeatDemoService,
|
|
) -> None:
|
|
invalid_plan = {
|
|
**LUNCH_BREAK_SCENE_PLAN,
|
|
"requests": [
|
|
_scene_request(
|
|
"flight",
|
|
"vehicle.fly",
|
|
"vehicle",
|
|
{"altitude_m": 100},
|
|
)
|
|
],
|
|
}
|
|
|
|
result = vehicle_service.preview_native_can(
|
|
"构建一个午休模式",
|
|
provider="local",
|
|
conversation_id="conv-codex-scene-unknown-capability",
|
|
execution_mode="hardware",
|
|
vehicle_id="denza_n7",
|
|
assistant_plan=invalid_plan,
|
|
)
|
|
|
|
assert result["status"] != "succeeded"
|
|
assert result.get("mode_profile_draft") is None
|
|
assert not _intent_actions(result)
|
|
|
|
|
|
def test_codex_scene_composition_rejects_out_of_range_parameters_atomically(
|
|
vehicle_service: SeatDemoService,
|
|
) -> None:
|
|
invalid_plan = {
|
|
**LUNCH_BREAK_SCENE_PLAN,
|
|
"requests": [
|
|
_scene_request(
|
|
"climate",
|
|
"climate.set_temperature",
|
|
"vehicle",
|
|
{"temperature_c": 99},
|
|
),
|
|
_scene_request(
|
|
"ambient_light",
|
|
"ambient_light.set_power",
|
|
"vehicle",
|
|
{"state": "off"},
|
|
),
|
|
],
|
|
}
|
|
|
|
result = vehicle_service.preview_native_can(
|
|
"构建一个午休模式",
|
|
provider="local",
|
|
conversation_id="conv-codex-scene-range-rejected",
|
|
execution_mode="hardware",
|
|
vehicle_id="denza_n7",
|
|
assistant_plan=invalid_plan,
|
|
)
|
|
|
|
assert result["status"] != "succeeded"
|
|
assert result.get("mode_profile_draft") is None
|
|
assert not _intent_actions(result)
|
|
assert any("32" in str(issue.get("message") or "") for issue in result["validation"])
|
|
|
|
|
|
def test_codex_scene_rejects_fan_zero_before_native_compile_and_clears_all_evidence(
|
|
vehicle_service: SeatDemoService,
|
|
) -> None:
|
|
invalid_plan = {
|
|
**LUNCH_BREAK_SCENE_PLAN,
|
|
"detected_domains": ["climate", "ambient_light"],
|
|
"requests": [
|
|
_scene_request(
|
|
"climate",
|
|
"climate.set_fan_level",
|
|
"vehicle",
|
|
{"level": 0},
|
|
),
|
|
_scene_request(
|
|
"ambient_light",
|
|
"ambient_light.set_power",
|
|
"vehicle",
|
|
{"state": "off"},
|
|
),
|
|
],
|
|
}
|
|
|
|
result = vehicle_service.preview_native_can(
|
|
"构建一个静享模式",
|
|
provider="local",
|
|
conversation_id="conv-codex-scene-fan-zero",
|
|
execution_mode="hardware",
|
|
vehicle_id="denza_n7",
|
|
assistant_plan=invalid_plan,
|
|
)
|
|
|
|
assert result["status"] != "succeeded"
|
|
assert result["execution_mode"] == "dry_run"
|
|
assert result["hardware_execution_allowed"] is False
|
|
assert result.get("mode_profile_draft") is None
|
|
assert not _intent_actions(result)
|
|
assert result["function_config"] is None
|
|
assert result["function_configs"] == {}
|
|
assert result["command_plan"] is None
|
|
assert result["command_plans"] == {}
|
|
assert result["scene_config"] is None
|
|
assert result["execution_report"] is None
|
|
assert not result["deferred_requests"]
|
|
assert any("1-7" in str(issue.get("message") or "") for issue in result["validation"])
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
("case_id", "invalid_request"),
|
|
[
|
|
(
|
|
"wrong-type",
|
|
_scene_request(
|
|
"climate",
|
|
"climate.set_temperature",
|
|
"vehicle",
|
|
{"temperature_c": "温暖"},
|
|
),
|
|
),
|
|
(
|
|
"unknown-parameter",
|
|
_scene_request(
|
|
"climate",
|
|
"climate.set_temperature",
|
|
"vehicle",
|
|
{"temperature_c": 24, "invented": 1},
|
|
),
|
|
),
|
|
(
|
|
"missing-required",
|
|
_scene_request(
|
|
"climate",
|
|
"climate.set_temperature",
|
|
"vehicle",
|
|
{},
|
|
),
|
|
),
|
|
(
|
|
"invalid-target",
|
|
_scene_request(
|
|
"climate",
|
|
"climate.set_temperature",
|
|
"trunk",
|
|
{"temperature_c": 24},
|
|
),
|
|
),
|
|
(
|
|
"relative-seat-delta",
|
|
_scene_request(
|
|
"seat",
|
|
"seat.set_backrest_recline",
|
|
"passenger",
|
|
{"angle_delta_deg": 3},
|
|
),
|
|
),
|
|
(
|
|
"native-missing-required-axis",
|
|
_scene_request(
|
|
"outlet",
|
|
"outlet.set_position_percent",
|
|
"driver_left",
|
|
{"horizontal_pct": 50},
|
|
),
|
|
),
|
|
(
|
|
"conflicting-seat-representations",
|
|
_scene_request(
|
|
"seat",
|
|
"seat.set_backrest_recline",
|
|
"passenger",
|
|
{"position_pct": 0, "angle_deg": 163},
|
|
),
|
|
),
|
|
],
|
|
)
|
|
def test_codex_scene_adapter_rejects_one_invalid_action_without_partial_draft(
|
|
vehicle_service: SeatDemoService,
|
|
case_id: str,
|
|
invalid_request: dict[str, object],
|
|
) -> None:
|
|
plan = {
|
|
**LUNCH_BREAK_SCENE_PLAN,
|
|
"requests": [
|
|
invalid_request,
|
|
_scene_request(
|
|
"ambient_light",
|
|
"ambient_light.set_power",
|
|
"vehicle",
|
|
{"state": "off"},
|
|
),
|
|
],
|
|
}
|
|
|
|
result = vehicle_service.preview_native_can(
|
|
"构建一个午休模式",
|
|
provider="local",
|
|
conversation_id=f"conv-codex-scene-adapter-{case_id}",
|
|
execution_mode="hardware",
|
|
vehicle_id="denza_n7",
|
|
assistant_plan=plan,
|
|
)
|
|
|
|
assert result["status"] != "succeeded"
|
|
assert result.get("mode_profile_draft") is None
|
|
assert not _intent_actions(result)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"native_request",
|
|
[
|
|
_scene_request(
|
|
"interior_light",
|
|
"interior_light.set_reading_power",
|
|
"all",
|
|
{"state": "off"},
|
|
),
|
|
_scene_request(
|
|
"outlet",
|
|
"outlet.set_switch_state",
|
|
"driver_left",
|
|
{"state": "close"},
|
|
),
|
|
_scene_request(
|
|
"climate",
|
|
"climate.set_control_mode",
|
|
"vehicle",
|
|
{"mode": "auto"},
|
|
),
|
|
],
|
|
)
|
|
def test_codex_scene_keeps_valid_native_can_actions_in_the_complete_draft(
|
|
vehicle_service: SeatDemoService,
|
|
native_request: dict[str, object],
|
|
) -> None:
|
|
plan = {
|
|
**LUNCH_BREAK_SCENE_PLAN,
|
|
"requests": [
|
|
native_request,
|
|
_scene_request(
|
|
"climate",
|
|
"climate.set_temperature",
|
|
"vehicle",
|
|
{"temperature_c": 24},
|
|
),
|
|
],
|
|
}
|
|
|
|
result = vehicle_service.preview_native_can(
|
|
"构建一个静享模式",
|
|
provider="local",
|
|
conversation_id=(
|
|
"conv-codex-scene-native-"
|
|
+ str(native_request["capability"]).replace(".", "-").replace("_", "-")
|
|
),
|
|
execution_mode="hardware",
|
|
vehicle_id="denza_n7",
|
|
assistant_plan=plan,
|
|
)
|
|
|
|
draft = result["mode_profile_draft"]
|
|
assert result["status"] == "succeeded"
|
|
assert draft["operation_count"] == 2
|
|
assert {action["capability"] for action in _intent_actions(result)} == {
|
|
str(native_request["capability"]),
|
|
"climate.set_temperature",
|
|
}
|
|
assert not result["deferred_requests"]
|
|
|
|
|
|
def test_codex_scene_promotes_rear_temperature_to_verified_native_can(
|
|
vehicle_service: SeatDemoService,
|
|
) -> None:
|
|
plan = {
|
|
**LUNCH_BREAK_SCENE_PLAN,
|
|
"detected_domains": ["climate"],
|
|
"requests": [
|
|
_scene_request(
|
|
"climate",
|
|
"climate.set_temperature",
|
|
"rear",
|
|
{"temperature_c": 24},
|
|
)
|
|
],
|
|
}
|
|
|
|
result = vehicle_service.preview_native_can(
|
|
"构建一个后排舒适模式",
|
|
provider="local",
|
|
conversation_id="conv-codex-scene-rear-climate",
|
|
execution_mode="hardware",
|
|
vehicle_id="denza_n7",
|
|
assistant_plan=plan,
|
|
)
|
|
|
|
draft = result["mode_profile_draft"]
|
|
assert result["status"] == "succeeded"
|
|
assert result["execution_mode"] == "dry_run"
|
|
assert result["hardware_execution_allowed"] is False
|
|
assert draft["operation_count"] == 1
|
|
assert result["deferred_requests"] == []
|
|
assert set(result["function_configs"]) == {"native_climate"}
|
|
actions = _intent_actions(result)
|
|
assert [(action["capability"], action["target"]["zone"]) for action in actions] == [
|
|
("climate.set_temperature", "rear")
|
|
]
|
|
|
|
|
|
def test_codex_composed_scene_can_be_saved_and_replayed_without_reinterpretation(
|
|
vehicle_service: SeatDemoService,
|
|
tmp_path: Path,
|
|
) -> None:
|
|
built = vehicle_service.preview_native_can(
|
|
"构建一个午休模式",
|
|
provider="local",
|
|
conversation_id="conv-codex-scene-save",
|
|
execution_mode="hardware",
|
|
vehicle_id="denza_n7",
|
|
assistant_plan=LUNCH_BREAK_SCENE_PLAN,
|
|
)
|
|
draft = built["mode_profile_draft"]
|
|
|
|
saved = vehicle_service.create_mode_profile_card(
|
|
str(draft["name"]),
|
|
str(draft["instruction"]),
|
|
str(draft["source_request_id"]),
|
|
str(draft["vehicle_id"]),
|
|
)
|
|
mode_id = str(saved["mode_profile"]["id"])
|
|
|
|
assert saved["status"] == "succeeded"
|
|
assert saved["mode_profile"]["name"] == "午休模式"
|
|
assert saved["mode_profile"]["operation_count"] == 5
|
|
assert list((tmp_path / "user-modes").rglob(f"{mode_id}.yaml"))
|
|
|
|
with patch.object(
|
|
vehicle_service,
|
|
"_interpret",
|
|
side_effect=AssertionError("saved scene must replay trusted command flow"),
|
|
):
|
|
replayed = vehicle_service.execute_mode_profile_card(
|
|
mode_id,
|
|
conversation_id="conv-codex-scene-replay",
|
|
execution_mode="dry_run",
|
|
vehicle_id="denza_n7",
|
|
)
|
|
|
|
assert replayed["status"] == "succeeded"
|
|
assert replayed["provider_used"] == "saved_mode_command_flow"
|
|
assert {action["capability"] for action in _intent_actions(replayed)} == {
|
|
request["capability"] for request in LUNCH_BREAK_SCENE_PLAN["requests"]
|
|
}
|
|
|
|
|
|
def test_inferred_nap_draft_can_be_confirmed_saved_and_replayed_without_reinterpretation(
|
|
vehicle_service: SeatDemoService,
|
|
tmp_path: Path,
|
|
) -> None:
|
|
built = _build_nap_mode(vehicle_service, "conv-nap-build-save")
|
|
draft = built["mode_profile_draft"]
|
|
assert isinstance(draft, dict)
|
|
|
|
saved = vehicle_service.save_mode_profile_card(
|
|
"nap",
|
|
str(draft["name"]),
|
|
str(draft["instruction"]),
|
|
str(draft["source_request_id"]),
|
|
str(draft["vehicle_id"]),
|
|
)
|
|
|
|
profile = saved["mode_profile"]
|
|
assert saved["status"] == "succeeded"
|
|
assert profile["id"] == "nap"
|
|
assert profile["name"] == "小憩模式"
|
|
assert profile["origin"] == "customized"
|
|
assert profile["operation_count"] == 9
|
|
assert (
|
|
tmp_path / "user-modes" / "vehicles" / "denza_n7" / "user" / "nap.yaml"
|
|
).is_file()
|
|
|
|
with patch.object(
|
|
vehicle_service,
|
|
"_interpret",
|
|
side_effect=AssertionError("saved mode must replay its trusted command flow"),
|
|
):
|
|
executed = vehicle_service.execute_mode_profile_card(
|
|
"nap",
|
|
conversation_id="conv-nap-saved-execution",
|
|
execution_mode="dry_run",
|
|
vehicle_id="denza_n7",
|
|
)
|
|
|
|
assert executed["status"] == "succeeded"
|
|
assert executed["provider_used"] == "saved_mode_command_flow"
|
|
assert executed["orchestration_route"] == "saved_mode_command_flow"
|
|
assert executed["execution_report"]["spec"]["mode"] == "dry_run"
|
|
replayed_capabilities = {
|
|
operation["capability"]
|
|
for config in executed["function_configs"].values()
|
|
for operation in config["spec"]["operations"]
|
|
}
|
|
assert replayed_capabilities == EXPECTED_NAP_CAPABILITIES
|
|
|
|
|
|
def test_assistant_name_only_nap_request_stays_useful_without_codex(
|
|
vehicle_service: SeatDemoService,
|
|
tmp_path: Path,
|
|
) -> None:
|
|
codex_client = MagicMock()
|
|
codex_client.available = False
|
|
assistant = CodexAssistantService(codex_client)
|
|
events: list[dict[str, object]] = []
|
|
|
|
def execute_control(
|
|
instruction: str,
|
|
progress: object,
|
|
) -> dict[str, object]:
|
|
callback = progress if callable(progress) else None
|
|
return vehicle_service.preview_native_can(
|
|
instruction,
|
|
provider="local",
|
|
conversation_id="conv-nap-assistant-route",
|
|
execution_mode="hardware",
|
|
vehicle_id="denza_n7",
|
|
on_commands_validated=callback,
|
|
)
|
|
|
|
response = assistant.stream(
|
|
NAP_REQUEST,
|
|
conversation_id="conv-nap-assistant-route",
|
|
principal="mode-regression-test",
|
|
history=[],
|
|
emit=lambda event: events.append(event) or True,
|
|
execute_control=execute_control,
|
|
capability_snapshot=AssistantCapabilityCatalog(vehicle_service).snapshot(),
|
|
)
|
|
|
|
assert response["route"] == "vehicle_control"
|
|
assert response["success"] is True
|
|
assert response["result"]["mode_profile_draft"]["operation_count"] == 9
|
|
assert response["result"]["mode_profile_draft"]["existing_mode_id"] == "nap"
|
|
assert (
|
|
response["result"]["mode_profile_draft"]["generation_source"]
|
|
== "registered_mode_profile"
|
|
)
|
|
assert "小憩模式" in response["answer"]
|
|
assert "预览" in response["answer"]
|
|
assert "更新现有模式" in response["answer"]
|
|
assert "功能模式未生成" not in response["answer"]
|
|
assert INTERNAL_SCENE_PLACEHOLDER not in json.dumps(
|
|
events,
|
|
ensure_ascii=False,
|
|
default=str,
|
|
)
|
|
assert not list((tmp_path / "user-modes").rglob("*.yaml"))
|
|
codex_client.start_turn.assert_not_called()
|
|
|
|
|
|
def test_existing_mode_draft_is_bound_to_its_update_endpoint(
|
|
vehicle_service: SeatDemoService,
|
|
) -> None:
|
|
built = _build_nap_mode(vehicle_service, "conv-nap-update-binding")
|
|
draft = built["mode_profile_draft"]
|
|
assert isinstance(draft, dict)
|
|
|
|
with pytest.raises(ValueError):
|
|
vehicle_service.create_mode_profile_card(
|
|
str(draft["name"]),
|
|
str(draft["instruction"]),
|
|
str(draft["source_request_id"]),
|
|
str(draft["vehicle_id"]),
|
|
)
|
|
|
|
with pytest.raises(ValueError):
|
|
vehicle_service.save_mode_profile_card(
|
|
"office",
|
|
str(draft["name"]),
|
|
str(draft["instruction"]),
|
|
str(draft["source_request_id"]),
|
|
str(draft["vehicle_id"]),
|
|
)
|
|
|
|
|
|
def test_concurrent_existing_mode_saves_use_revision_compare_and_swap(
|
|
vehicle_service: SeatDemoService,
|
|
) -> None:
|
|
drafts = []
|
|
for index, temperature in enumerate((24, 25), start=1):
|
|
result = vehicle_service.preview_native_can(
|
|
f"创建一个小憩模式,空调调整到{temperature}度",
|
|
provider="local",
|
|
conversation_id=f"conv-mode-cas-update-{index}",
|
|
execution_mode="hardware",
|
|
vehicle_id="denza_n7",
|
|
)
|
|
drafts.append(result["mode_profile_draft"])
|
|
|
|
assert drafts[0]["existing_mode_revision"] == drafts[1]["existing_mode_revision"]
|
|
barrier = Barrier(2)
|
|
original_save = vehicle_service.mode_profiles.save_mode
|
|
|
|
def synchronized_save(*args: object, **kwargs: object) -> object:
|
|
barrier.wait(timeout=5)
|
|
return original_save(*args, **kwargs)
|
|
|
|
def persist(draft: dict[str, object]) -> tuple[str, str]:
|
|
try:
|
|
result = vehicle_service.save_mode_profile_card(
|
|
"nap",
|
|
str(draft["name"]),
|
|
str(draft["instruction"]),
|
|
str(draft["source_request_id"]),
|
|
str(draft["vehicle_id"]),
|
|
)
|
|
return "saved", str(result["mode_profile"]["id"])
|
|
except ValueError as exc:
|
|
return "rejected", str(exc)
|
|
|
|
with patch.object(
|
|
vehicle_service.mode_profiles,
|
|
"save_mode",
|
|
side_effect=synchronized_save,
|
|
):
|
|
with ThreadPoolExecutor(max_workers=2) as executor:
|
|
outcomes = list(executor.map(persist, drafts))
|
|
|
|
assert [status for status, _detail in outcomes].count("saved") == 1
|
|
assert [status for status, _detail in outcomes].count("rejected") == 1
|
|
assert any("已被其他会话更新" in detail for _status, detail in outcomes)
|
|
|
|
|
|
def test_concurrent_same_name_creates_are_create_if_absent(
|
|
vehicle_service: SeatDemoService,
|
|
) -> None:
|
|
drafts = []
|
|
for index, temperature in enumerate((24, 25), start=1):
|
|
plan = {
|
|
**LUNCH_BREAK_SCENE_PLAN,
|
|
"requests": [
|
|
_scene_request(
|
|
"climate",
|
|
"climate.set_temperature",
|
|
"vehicle",
|
|
{"temperature_c": temperature},
|
|
)
|
|
],
|
|
}
|
|
result = vehicle_service.preview_native_can(
|
|
"构建一个午休模式",
|
|
provider="local",
|
|
conversation_id=f"conv-mode-cas-create-{index}",
|
|
execution_mode="hardware",
|
|
vehicle_id="denza_n7",
|
|
assistant_plan=plan,
|
|
)
|
|
drafts.append(result["mode_profile_draft"])
|
|
|
|
barrier = Barrier(2)
|
|
original_save = vehicle_service.mode_profiles.save_mode
|
|
|
|
def synchronized_save(*args: object, **kwargs: object) -> object:
|
|
barrier.wait(timeout=5)
|
|
return original_save(*args, **kwargs)
|
|
|
|
def persist(draft: dict[str, object]) -> tuple[str, str]:
|
|
try:
|
|
result = vehicle_service.create_mode_profile_card(
|
|
str(draft["name"]),
|
|
str(draft["instruction"]),
|
|
str(draft["source_request_id"]),
|
|
str(draft["vehicle_id"]),
|
|
)
|
|
return "saved", str(result["mode_profile"]["id"])
|
|
except ValueError as exc:
|
|
return "rejected", str(exc)
|
|
|
|
with patch.object(
|
|
vehicle_service.mode_profiles,
|
|
"save_mode",
|
|
side_effect=synchronized_save,
|
|
):
|
|
with ThreadPoolExecutor(max_workers=2) as executor:
|
|
outcomes = list(executor.map(persist, drafts))
|
|
|
|
assert [status for status, _detail in outcomes].count("saved") == 1
|
|
assert [status for status, _detail in outcomes].count("rejected") == 1
|
|
assert any("已由其他会话创建" in detail for _status, detail in outcomes)
|
|
|
|
|
|
def test_name_only_mode_does_not_bypass_exact_resolution_with_substring_match(
|
|
vehicle_service: SeatDemoService,
|
|
) -> None:
|
|
result = vehicle_service.preview_native_can(
|
|
"构建一个我的小憩模式",
|
|
provider="local",
|
|
conversation_id="conv-mode-substring-rejected",
|
|
execution_mode="hardware",
|
|
vehicle_id="denza_n7",
|
|
)
|
|
|
|
assert result["status"] == "needs_confirmation"
|
|
assert result["matched_modes"] == []
|
|
assert result.get("mode_profile_draft") is None
|
|
assert not _intent_actions(result)
|
|
|
|
|
|
def test_name_only_mode_does_not_combine_profiles_when_exact_name_is_ambiguous(
|
|
vehicle_service: SeatDemoService,
|
|
) -> None:
|
|
nap = vehicle_service.mode_profiles.get_profile("nap", "denza_n7")
|
|
vehicle_service.mode_profiles.save_mode(
|
|
"午休模式",
|
|
list(nap.operations),
|
|
mode_id="duplicate-nap-name",
|
|
aliases=["小憩模式"],
|
|
vehicle_id="denza_n7",
|
|
)
|
|
|
|
result = vehicle_service.preview_native_can(
|
|
NAP_REQUEST,
|
|
provider="local",
|
|
conversation_id="conv-mode-ambiguous-rejected",
|
|
execution_mode="hardware",
|
|
vehicle_id="denza_n7",
|
|
)
|
|
|
|
assert result["status"] == "needs_confirmation"
|
|
assert result["matched_modes"] == []
|
|
assert result.get("mode_profile_draft") is None
|
|
assert not _intent_actions(result)
|
|
|
|
|
|
def test_explicit_actions_for_existing_name_are_bound_to_that_mode_update(
|
|
vehicle_service: SeatDemoService,
|
|
) -> None:
|
|
result = vehicle_service.preview_native_can(
|
|
"创建一个小憩模式,空调调整到24度",
|
|
provider="local",
|
|
conversation_id="conv-existing-mode-explicit-action",
|
|
execution_mode="hardware",
|
|
vehicle_id="denza_n7",
|
|
)
|
|
|
|
draft = result["mode_profile_draft"]
|
|
assert result["status"] == "succeeded"
|
|
assert draft["existing_mode_id"] == "nap"
|
|
assert draft["generation_source"] == "explicit_scene_requirements"
|
|
assert draft["operation_count"] == 1
|
|
answer = CodexAssistantService._mode_profile_preview_answer(result, True)
|
|
assert "按你提供的设置" in answer
|
|
assert "载入预览" not in answer
|
|
|
|
saved = vehicle_service.save_mode_profile_card(
|
|
"nap",
|
|
str(draft["name"]),
|
|
str(draft["instruction"]),
|
|
str(draft["source_request_id"]),
|
|
str(draft["vehicle_id"]),
|
|
)
|
|
assert saved["status"] == "succeeded"
|
|
assert saved["mode_profile"]["id"] == "nap"
|
|
assert saved["mode_profile"]["operation_count"] == 1
|
|
|
|
|
|
def test_new_mode_draft_cannot_overwrite_an_unrelated_existing_mode(
|
|
vehicle_service: SeatDemoService,
|
|
) -> None:
|
|
result = vehicle_service.preview_native_can(
|
|
"创建一个清凉测试模式,空调调整到24度",
|
|
provider="local",
|
|
conversation_id="conv-new-mode-update-rejected",
|
|
execution_mode="hardware",
|
|
vehicle_id="denza_n7",
|
|
)
|
|
draft = result["mode_profile_draft"]
|
|
assert result["status"] == "succeeded"
|
|
assert "existing_mode_id" not in draft
|
|
|
|
with pytest.raises(ValueError, match="新功能模式预览不能"):
|
|
vehicle_service.save_mode_profile_card(
|
|
"nap",
|
|
str(draft["name"]),
|
|
str(draft["instruction"]),
|
|
str(draft["source_request_id"]),
|
|
str(draft["vehicle_id"]),
|
|
)
|
|
|
|
|
|
def test_exact_mode_resolver_supports_aliases_but_rejects_substrings_and_ambiguity(
|
|
vehicle_service: SeatDemoService,
|
|
) -> None:
|
|
registry = vehicle_service.mode_profiles
|
|
nap = registry.get_profile("nap", "denza_n7")
|
|
|
|
assert registry.resolve_exact_name("小憩模式", "denza_n7") == nap
|
|
assert registry.resolve_exact_name("小憩", "denza_n7") == nap
|
|
assert registry.resolve_exact_name("NAP", "denza_n7") == nap
|
|
assert registry.resolve_exact_name("我的小憩模式", "denza_n7") is None
|
|
assert registry.resolve_exact_name("小憩模式", "unsupported_vehicle") is None
|
|
|
|
registry.save_mode(
|
|
"午休模式",
|
|
list(nap.operations),
|
|
mode_id="duplicate-nap-alias",
|
|
aliases=["小憩"],
|
|
vehicle_id="denza_n7",
|
|
)
|
|
|
|
assert registry.resolve_exact_name("小憩", "denza_n7") is None
|
|
assert registry.resolve_exact_name("小憩模式", "denza_n7").id == "nap"
|
|
|
|
|
|
def test_existing_mode_alias_keeps_the_canonical_name_when_updated(
|
|
vehicle_service: SeatDemoService,
|
|
) -> None:
|
|
result = vehicle_service.preview_native_can(
|
|
"构建一个摇篮模式",
|
|
provider="local",
|
|
conversation_id="conv-mode-alias-canonical-name",
|
|
execution_mode="hardware",
|
|
vehicle_id="denza_n7",
|
|
)
|
|
|
|
draft = result["mode_profile_draft"]
|
|
assert result["status"] == "succeeded"
|
|
assert draft["existing_mode_id"] == "dynamic_cradle"
|
|
assert draft["name"] == "动态摇篮模式"
|
|
|
|
saved = vehicle_service.save_mode_profile_card(
|
|
"dynamic_cradle",
|
|
str(draft["name"]),
|
|
str(draft["instruction"]),
|
|
str(draft["source_request_id"]),
|
|
str(draft["vehicle_id"]),
|
|
)
|
|
assert saved["mode_profile"]["name"] == "动态摇篮模式"
|
|
|
|
|
|
def test_internal_scene_builder_placeholder_is_never_returned_to_the_user() -> None:
|
|
result = {
|
|
"status": "needs_confirmation",
|
|
"mode_creation_failure": {
|
|
"code": "MODE_PROFILE_DRAFT_INCOMPLETE",
|
|
"reason": (
|
|
f"设置“{INTERNAL_SCENE_PLACEHOLDER}”未被识别为当前支持的座舱控制动作"
|
|
),
|
|
"next_step": "请按设备、动作和目标值重新说明",
|
|
"unresolved_clauses": [INTERNAL_SCENE_PLACEHOLDER],
|
|
},
|
|
"unresolved_clauses": [INTERNAL_SCENE_PLACEHOLDER],
|
|
}
|
|
|
|
answer = CodexAssistantService._mode_profile_preview_answer(result, False)
|
|
safe_result = CodexAssistantService._safe_vehicle_result(result)
|
|
|
|
assert INTERNAL_SCENE_PLACEHOLDER not in answer
|
|
assert INTERNAL_SCENE_PLACEHOLDER not in json.dumps(
|
|
safe_result,
|
|
ensure_ascii=False,
|
|
)
|
|
assert "内部" not in answer
|
|
assert "失败原因" in answer
|
|
assert "下一步" in answer
|
|
|
|
|
|
def test_model_cannot_create_a_mode_with_an_unregistered_capability(
|
|
vehicle_service: SeatDemoService,
|
|
tmp_path: Path,
|
|
) -> None:
|
|
hallucinated_plan = {
|
|
"detected_domains": ["flight"],
|
|
"requests": [
|
|
{
|
|
"domain": "flight",
|
|
"capability": "vehicle.fly",
|
|
"target_scope": "vehicle",
|
|
"operation": "set",
|
|
"parameters": [{"name": "altitude_m", "value": 100}],
|
|
"reason": "模型臆造的飞行能力。",
|
|
}
|
|
],
|
|
"ambiguities": [],
|
|
"decision_summary": ["切换飞行模式。"],
|
|
"confidence": 0.99,
|
|
}
|
|
|
|
with patch.object(
|
|
vehicle_service.openai,
|
|
"interpret",
|
|
return_value=hallucinated_plan,
|
|
):
|
|
result = vehicle_service.process(
|
|
"构建一个飞行模式",
|
|
"openai",
|
|
"conv-hallucinated-mode",
|
|
execution_mode="dry_run",
|
|
vehicle_id="denza_n7",
|
|
)
|
|
|
|
assert result["status"] != "succeeded"
|
|
assert result.get("mode_profile_draft") is None
|
|
assert result.get("mode_profile") is None
|
|
assert not _intent_actions(result)
|
|
assert any(
|
|
issue["severity"] == "error"
|
|
for issue in result.get("validation", [])
|
|
)
|
|
assert not list((tmp_path / "user-modes").rglob("*.yaml"))
|