124 lines
6.5 KiB
Python
124 lines
6.5 KiB
Python
from __future__ import annotations
|
|
|
|
from copy import deepcopy
|
|
|
|
import pytest
|
|
|
|
from cognitive_agent.application.codex_assistant import (
|
|
CodexAssistantService,
|
|
_authorize_vehicle_request,
|
|
_validated_mode_profile_draft_context,
|
|
)
|
|
from cognitive_agent.application.mode_profile_intent import (
|
|
is_mode_profile_update_shape,
|
|
parse_mode_profile_update,
|
|
)
|
|
|
|
DRAFT_REFERENCE = {
|
|
"name": "躺平模式", "source_request_id": "req-augmentation-source-123456", "vehicle_id": "denza_n7",
|
|
}
|
|
CONTROL_TERMS = ("空调", "氛围灯", "阅读灯", "车窗", "座椅")
|
|
|
|
|
|
@pytest.mark.parametrize("prefix", ["在", "给", "为", "往", "向", ""])
|
|
@pytest.mark.parametrize("verb", ["加入", "添加", "增加", "补充", "加上", "追加", "新增"])
|
|
def test_named_mode_device_augmentation_is_recognized(prefix, verb):
|
|
message = f"{prefix}躺平模式中{verb}空调、氛围灯、阅读灯、车窗等设备动作"
|
|
assert parse_mode_profile_update(message) == ("躺平模式", f"{verb}空调、氛围灯、阅读灯、车窗等设备动作")
|
|
assert _validated_mode_profile_draft_context(DRAFT_REFERENCE, message=message) == DRAFT_REFERENCE
|
|
authorization = _authorize_vehicle_request(message, CONTROL_TERMS, DRAFT_REFERENCE)
|
|
assert authorization.instruction == message
|
|
assert authorization.reason == "explicit_mode_profile_draft_update"
|
|
|
|
|
|
@pytest.mark.parametrize(("message", "change"), [
|
|
("请在躺平模式里补充空调", "补充空调"),
|
|
("帮我给躺平模式添加空调", "添加空调"),
|
|
("为躺平模式新增阅读灯", "新增阅读灯"),
|
|
("躺平模式再补充车窗", "补充车窗"),
|
|
("躺平模式中再加入阅读灯", "再加入阅读灯"),
|
|
("在躺平模式中加入空调24度、阅读灯打开", "加入空调24度、阅读灯打开"),
|
|
("把空调24度、阅读灯打开加入到躺平模式中", "加入空调24度、阅读灯打开"),
|
|
("将车窗、氛围灯追加进躺平模式", "追加车窗、氛围灯"),
|
|
("请把空调加入躺平模式吧", "加入空调"),
|
|
("把副驾空调温度25.5度补充到躺平模式", "补充副驾空调温度25.5度"),
|
|
("在“躺平模式”中添加空调", "添加空调"),
|
|
])
|
|
def test_append_word_order_preserves_device_parameters_and_scoped_name(message, change):
|
|
parsed = parse_mode_profile_update(message)
|
|
assert parsed is not None and parsed[0] == "躺平模式"
|
|
# A leading 再 modifier is harmless; neither parameters nor target words
|
|
# may be lost when the reverse-order request is normalized.
|
|
assert parsed[1].removeprefix("再") == change.removeprefix("再")
|
|
|
|
|
|
@pytest.mark.parametrize("message", [
|
|
"不要在躺平模式中加入空调", "在躺平模式中不要加入空调",
|
|
"躺平模式中先不要补充阅读灯", "给躺平模式添加空调,但不要执行",
|
|
"给躺平模式添加空调,算了", "给躺平模式添加空调,撤销",
|
|
"躺平模式中取消加入空调", "如何在躺平模式中加入空调?",
|
|
"在躺平模式中加入空调好吗?", "在躺平模式中能否加入空调",
|
|
"把空调加入到躺平模式会怎样?", "解释在躺平模式中加入空调这句话",
|
|
"在躺平模式中加入哪些设备?", "引用在躺平模式中加入空调",
|
|
"在空调出风模式中加入阅读灯", "在副驾按摩模式中添加车窗",
|
|
"在躺平模式中加入", "给躺平模式补充",
|
|
])
|
|
def test_negated_read_only_or_device_property_requests_do_not_authorize_augmentation(message):
|
|
assert parse_mode_profile_update(message) is None
|
|
assert _validated_mode_profile_draft_context(DRAFT_REFERENCE, message=message) is None
|
|
authorization = _authorize_vehicle_request(message, CONTROL_TERMS, DRAFT_REFERENCE)
|
|
assert authorization.reason not in {"explicit_mode_profile_draft_update", "explicit_mode_profile_update"}
|
|
|
|
|
|
def test_other_named_mode_never_borrows_the_selected_draft_reference():
|
|
message = "在阅读模式中加入空调"
|
|
assert parse_mode_profile_update(message) == ("阅读模式", "加入空调")
|
|
assert _validated_mode_profile_draft_context(DRAFT_REFERENCE, message=message) is None
|
|
assert _authorize_vehicle_request(message, CONTROL_TERMS, DRAFT_REFERENCE).reason == "explicit_mode_profile_update"
|
|
|
|
|
|
def test_augmentation_reference_cannot_include_browser_supplied_device_actions():
|
|
reference = {**DRAFT_REFERENCE, "operations": [{"capability": "seat.anything"}]}
|
|
with pytest.raises(ValueError, match="fields are invalid"):
|
|
_validated_mode_profile_draft_context(reference, message="在躺平模式中加入空调")
|
|
|
|
|
|
def test_speech_update_shape_preserves_named_augmentation_even_when_negated():
|
|
assert is_mode_profile_update_shape("在躺平模式中加入空调,但不要执行")
|
|
assert parse_mode_profile_update("在躺平模式中加入空调,但不要执行") is None
|
|
|
|
|
|
def test_device_augmentation_bypasses_general_agent_and_partial_control_paths():
|
|
client = type("UnavailableClient", (), {"available": False})()
|
|
assistant = CodexAssistantService(client)
|
|
message = "在躺平模式中加入空调、氛围灯、阅读灯、车窗等设备动作"
|
|
calls = []
|
|
events = []
|
|
|
|
def execute_control(instruction, _progress):
|
|
calls.append(instruction)
|
|
return {
|
|
"request_id": "req-revised-123456", "status": "succeeded", "execution_mode": "dry_run",
|
|
"orchestration_route": "mode_profile_create_preview",
|
|
"intent_ir": {"spec": {"actions": [{"domain": "climate"}]}},
|
|
"mode_profile_draft": {
|
|
"name": "躺平模式", "source_request_id": "req-revised-123456", "vehicle_id": "denza_n7",
|
|
"instruction": "进入躺平模式", "operation_count": 1,
|
|
"generation_source": "draft_revision", "draft_revision": 2,
|
|
},
|
|
}
|
|
|
|
reference = deepcopy(DRAFT_REFERENCE)
|
|
result = assistant.stream(
|
|
message, conversation_id="conv-augmentation-intent-123456", principal="test",
|
|
history=[], emit=events.append, execute_control=execute_control,
|
|
preview_control=lambda *_args: pytest.fail("augmentation must not run partial hardware controls"),
|
|
supplement_control=lambda *_args: pytest.fail("augmentation must not become a vehicle supplement"),
|
|
mode_profile_draft_context=reference, control_terms=CONTROL_TERMS,
|
|
)
|
|
assert calls == [message]
|
|
assert result["success"] is True
|
|
assert result["result"]["mode_profile_draft"]["name"] == "躺平模式"
|
|
assert reference == DRAFT_REFERENCE
|
|
assert events[0]["route"] == "vehicle_control"
|