from __future__ import annotations from copy import deepcopy from pathlib import Path import pytest from cognitive_agent.application.codex_assistant import ( CodexAssistantService, _catalog_exact_mode_match_count, _catalog_exact_scene_template_plan, ) from cognitive_agent.application.mode_profile_intent import ( mode_creation_source_name, parse_mode_profile_create, ) from cognitive_agent.application.scene_composer import ( SCENE_COMPOSER_INSTRUCTIONS, SCHEDULE_COMPOSER_INSTRUCTIONS, scene_context_for_mode_creation, ) from cognitive_agent.application.scene_templates import SceneTemplateRegistry ROOT = Path(__file__).resolve().parents[2] @pytest.mark.parametrize(("message", "name"), [ ("构建主驾阅读模式", "主驾阅读模式"), ("为主驾构建一个阅读模式", "主驾阅读模式"), ("帮我为副驾驶构建一个阅读模式", "副驾阅读模式"), ("给驾驶席创建一个阅读模式", "主驾阅读模式"), ("可以帮我为主驾构建一个阅读模式吗?", "主驾阅读模式"), ("构建一个阅读模式,针对主驾", "主驾阅读模式"), ("构建一个阅读模式,仅联动副驾座椅", "副驾阅读模式"), ("构建一个阅读模式,给主驾使用,先看看", "主驾阅读模式"), ("构建一个主驾驶座椅专属的阅读模式", "主驾阅读模式"), ("构建一个主驾适合看书的功能模式", "主驾看书模式"), ("构建一个适合主驾看书的功能模式", "主驾看书模式"), ("为主驾和副驾创建一个阅读模式", "主副驾阅读模式"), ("构建一个双座阅读模式", "主副驾阅读模式"), ("构建一个阅读模式,针对双座", "主副驾阅读模式"), ]) def test_scoped_creation_keeps_distinct_name_and_fast_template_source(message, name): assert parse_mode_profile_create(message) == (name, "") registry = SceneTemplateRegistry(ROOT / "config" / "scene_templates") template = registry.resolve_exact_name(name) assert template is not None and template.id == "reading" @pytest.mark.parametrize(("message", "expected"), [ ("为主驾构建一个阅读模式,空调调整到26度", ("主驾阅读模式", "空调调整到26度")), ("构建一个阅读模式,针对主驾,空调调整到26度", ("阅读模式", "针对主驾,空调调整到26度")), ("构建一个主驾阅读模式,靠背向后移动3s", ("主驾阅读模式", "靠背向后移动3s")), ("构建一个名为主驾驶阅读模式", ("主驾驶阅读模式", "")), ("构建一个“阅读模式”,针对主驾", ("阅读模式", "")), ]) def test_explicit_names_and_actual_device_instructions_are_preserved(message, expected): assert parse_mode_profile_create(message) == expected @pytest.mark.parametrize("message", [ "不要为主驾构建一个阅读模式", "如何为主驾构建一个阅读模式?", "为主驾构建一个阅读模式,但不要执行", "引用为主驾构建一个阅读模式", ]) def test_scope_grammar_does_not_bypass_authorization(message): assert parse_mode_profile_create(message) is None @pytest.mark.parametrize("name", ["后排阅读模式", "左后排阅读模式", "第三排阅读模式", "未知座位阅读模式"]) def test_unsupported_seats_never_alias_to_front_seat_templates(name): assert mode_creation_source_name(name) == name assert SceneTemplateRegistry(ROOT / "config" / "scene_templates").resolve_exact_name(name) is None @pytest.mark.parametrize(("name", "source", "template_id"), [ ("主驾座椅舒适模式", "座椅舒适模式", "seat-comfort"), ("主驾和副驾座椅舒适模式", "座椅舒适模式", "seat-comfort"), ("主副驾座椅动态模式", "座椅动态模式", "seat-dynamic"), ("副驾座椅的动态模式", "座椅的动态模式", "seat-dynamic"), ]) def test_seat_word_is_preserved_when_it_is_the_template_subject(name, source, template_id): assert mode_creation_source_name(name) == source template = SceneTemplateRegistry(ROOT / "config" / "scene_templates").resolve_exact_name(name) assert template is not None and template.id == template_id def test_catalog_seed_fallback_is_exact_and_preserves_explicit_match_priority(): template = SceneTemplateRegistry(ROOT / "config" / "scene_templates").get_template("reading").public_dict(include_plan=True) template["assistant_callable"] = True catalog = { "source_status": {"scene_templates": "ready", "modes": "ready"}, "scene_templates": [template], "modes": [{"id": "office", "name": "办公模式"}], } assert _catalog_exact_scene_template_plan(catalog, "主驾阅读模式") == template["plan"] assert _catalog_exact_scene_template_plan(catalog, "后排阅读模式") is None assert _catalog_exact_mode_match_count(catalog, "主驾办公模式") == 1 catalog["modes"].extend([ {"id": "driver-office-a", "name": "主驾办公模式"}, {"id": "driver-office-b", "name": "主驾办公模式"}, ]) assert _catalog_exact_mode_match_count(catalog, "主驾办公模式") == 2 @pytest.mark.parametrize(("message", "expected"), [ ("构建一个神游模式", {"passenger"}), ("构建一个主驾神游模式", {"driver", "front_left"}), ("构建一个主副驾神游模式", {"driver", "front_left", "passenger"}), ("构建一个神游模式,主驾空调26度", {"passenger"}), ("构建一个后排神游模式", set()), ]) def test_model_context_scopes_only_seat_capabilities_and_does_not_mutate_source(message, expected): context = { "composition_revision": "revision", "vehicle": {"id": "denza_n7"}, "capabilities": [ {"id": "seat.driver", "domain": "seat", "target_scopes": ["driver", "front_left"]}, {"id": "seat.passenger", "domain": "seat", "target_scopes": ["passenger"]}, {"id": "climate.zone", "domain": "climate", "target_scopes": ["driver", "passenger"]}, {"id": "roof.shade", "domain": "roof", "target_scopes": ["vehicle"]}, ], } original = deepcopy(context) projected = scene_context_for_mode_creation(context, message) scopes = {scope for capability in projected["capabilities"] if capability["domain"] == "seat" for scope in capability["target_scopes"]} assert scopes == expected assert [capability for capability in projected["capabilities"] if capability["domain"] != "seat"] == original["capabilities"][2:] assert context == original assert projected["seat_scope_policy"]["default_scope"] == "passenger" def test_static_and_dynamic_prompts_use_one_selected_seat(): assert "defaults to the passenger seat only" in SCENE_COMPOSER_INSTRUCTIONS assert "retarget a passenger capability to the driver" in SCENE_COMPOSER_INSTRUCTIONS assert "selected seat scope in every phase" in SCHEDULE_COMPOSER_INSTRUCTIONS def test_identical_explicit_mode_names_do_not_share_cached_plans_across_seats(): context = {"composition_revision": "revision", "vehicle": {"id": "denza_n7"}, "capabilities": []} # Explicitly named drafts may intentionally keep the same display name even # when the outer request selects a different seat. driver = scene_context_for_mode_creation(context, "为主驾构建一个名为神游模式") passenger = scene_context_for_mode_creation(context, "为副驾构建一个名为神游模式") driver_key = CodexAssistantService._scene_cache_key(None, mode_name="神游模式", scene_context=driver) passenger_key = CodexAssistantService._scene_cache_key(None, mode_name="神游模式", scene_context=passenger) assert driver_key != passenger_key @pytest.mark.parametrize("seat", [ "两个座椅", "两张座椅", "两座椅", "两座", "两个座位", "前排两座", "前排两座椅", "前排两个座椅", "前排两张座椅", "双座", "双座椅", ]) @pytest.mark.parametrize("pattern", ["构建一个{}阅读模式", "为{}构建一个阅读模式", "构建一个阅读模式,针对{}"]) def test_all_two_seat_wordings_have_one_canonical_name(seat, pattern): message = pattern.format(seat) assert parse_mode_profile_create(message) == ("主副驾阅读模式", "") assert mode_creation_source_name(f"{seat}阅读模式") == "阅读模式" @pytest.mark.parametrize("message", [ "构建一个主驾阅读模式,针对副驾", "构建一个副驾阅读模式,针对主驾", "为主驾构建一个副驾阅读模式", "为副驾构建一个主驾阅读模式", "构建一个主副驾阅读模式,只针对主驾", "构建一个主驾阅读模式,针对双座", "为主驾构建一个名为副驾阅读模式", "构建一个“副驾阅读模式”,针对主驾", ]) def test_conflicting_seat_selection_cannot_silently_become_a_two_seat_creation(message): assert parse_mode_profile_create(message) is None @pytest.mark.parametrize(("name", "source"), [ ("副驾驶座椅阅读模式", "阅读模式"), ("主驾座椅的阅读模式", "阅读模式"), ("主驾座椅办公模式", "办公模式"), ("副驾座位的观影模式", "观影模式"), ("副驾座椅看书模式", "看书模式"), ("主驾座椅露营模式", "露营模式"), ("主驾座椅舒适模式", "座椅舒适模式"), ("副驾座椅动态模式", "座椅动态模式"), ("乘客座椅舒适模式", "座椅舒适模式"), ("两个座椅的动态模式", "座椅动态模式"), ("两张座椅舒适模式", "座椅舒适模式"), ("前排两座动态模式", "座椅动态模式"), ]) def test_seat_location_wrappers_do_not_hide_generic_scene_sources_or_actual_seat_topics(name, source): assert mode_creation_source_name(name) == source @pytest.mark.parametrize("message", [ "构建一个两个座椅的动态模式", "构建一个两张座椅的动态模式", "构建一个前排两座动态模式", ]) def test_double_seat_dynamic_topic_is_not_lost_with_the_scope_qualifier(message): assert parse_mode_profile_create(message) == ("主副驾座椅动态模式", "")