from __future__ import annotations 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 from cognitive_agent.infrastructure.llm.openai_responses import OpenAIIntentError class SeatServiceIntegrationTests(unittest.TestCase): def setUp(self) -> None: self.memory_environment = patch.dict( os.environ, { "COGNITIVE_AGENT_DRIVER_SEAT_MEMORY_PATH": str( REPOSITORY_ROOT / "tests" / "fixtures" / "driver_seat_memory.json" ) }, ) self.memory_environment.start() self.addCleanup(self.memory_environment.stop) self.service = SeatDemoService(REPOSITORY_ROOT) self.temporary_directory = tempfile.TemporaryDirectory() self.addCleanup(self.temporary_directory.cleanup) self.service.artifact_root = Path(self.temporary_directory.name) def test_supported_request_completes_full_dry_run(self) -> None: result = self.service.process( "打开副驾座椅通风,同时关闭副驾座椅加热", provider="local" ) self.assertEqual(result["status"], "succeeded") self.assertEqual(len(result["function_config"]["spec"]["operations"]), 2) telemetry = result["execution_report"]["spec"]["results"][0]["telemetry"] self.assertEqual(telemetry["command_frame"]["data_hex"], "00 90") self.assertTrue(Path(result["artifact_path"]).is_absolute()) self.assertTrue( (self.service.artifact_root / result["request_id"] / "result.json").is_file() ) for event in result["trace_events"]: self.assertEqual(self.service.contracts.validate(event), []) def test_explicit_legrest_height_does_not_execute_the_seat_height_axis(self) -> None: result = self.service.preview_native_can( "副驾腿托高度调到50%", provider="local", execution_mode="dry_run", conversation_id="conv-legrest-height-regression", ) self.assertEqual(result["status"], "succeeded") actions = result["intent_ir"]["spec"]["actions"] self.assertEqual(len(actions), 1) self.assertEqual(actions[0]["capability"], "seat.set_legrest_height") self.assertEqual(actions[0]["parameters"], {"position_pct": 50}) self.assertEqual( result["actuator_state"]["passenger"]["position"]["legrest_height"][ "position_pct" ], 50, ) def test_dynamic_mode_runs_until_explicit_natural_language_stop(self) -> None: conversation_id = "conv-dynamic-stop" started = self.service.process( "\u5f00\u542f\u52a8\u6001\u6447\u7bee\u6a21\u5f0f", provider="local", conversation_id=conversation_id, vehicle_id="denza_n7", ) stopped = self.service.process( "\u5173\u95ed\u52a8\u6001\u6447\u7bee\u6a21\u5f0f", provider="local", conversation_id=conversation_id, vehicle_id="denza_n7", ) self.assertEqual(started["status"], "succeeded") self.assertEqual( started["actuator_state"]["passenger"]["dynamic_mode"], { "mode_id": 2, "name": "\u52a8\u6001\u6447\u7bee\u6a21\u5f0f", "state": "running", "repeat": "until_stopped_or_timeout", "max_runtime_ms": 600_000, }, ) self.assertEqual(stopped["status"], "succeeded") operation = stopped["function_config"]["spec"]["operations"][0] self.assertEqual(operation["capability"], "seat.stop_dynamic_mode") self.assertEqual(operation["parameters"], {}) self.assertEqual( stopped["actuator_state"]["passenger"]["dynamic_mode"]["state"], "idle", ) def test_gear_request_is_preserved_and_simulated(self) -> None: result = self.service.process("副驾座椅加热开到三档", provider="local") self.assertEqual(result["status"], "succeeded") operation = result["function_config"]["spec"]["operations"][0] self.assertEqual(operation["parameters"]["level"], 3) telemetry = result["execution_report"]["spec"]["results"][0]["telemetry"] self.assertEqual(telemetry["effective_level"], 3) self.assertTrue(telemetry["level_command_simulated"]) self.assertIn( "LEVEL_COMMAND_SIMULATED", {issue["code"] for issue in result["validation"]} ) def test_consecutive_feedback_uses_previous_successful_state(self) -> None: conversation_id = "conv-test-context" first = self.service.process( "副驾有点热,有点闷", provider="local", conversation_id=conversation_id, ) second = self.service.process( "副驾还有点闷", provider="local", conversation_id=conversation_id, ) self.assertEqual(first["status"], "succeeded") self.assertEqual(first["actuator_state"]["passenger"]["ventilation"]["level"], 1) self.assertEqual(second["status"], "succeeded") self.assertEqual(second["conversation_turn"], 2) self.assertEqual(second["actuator_state"]["passenger"]["ventilation"]["level"], 2) operation = second["function_config"]["spec"]["operations"][0] self.assertEqual(operation["parameters"], {"state": "on", "level": 2}) self.assertIn("提升到2档", "".join(second["decision_summary"])) def test_four_turn_causal_feedback_sequence(self) -> None: conversation_id = "conv-causal-sequence" utterances = [ "我有点热", "我有点冷", "我还有点冷", "副驾吹的我有点冷", ] first, second, third, fourth = [ self.service.process(text, "local", conversation_id) for text in utterances ] self.assertEqual( first["function_config"]["spec"]["operations"][0]["parameters"], {"state": "on", "level": 1}, ) second_operation = second["function_config"]["spec"]["operations"][0] self.assertEqual(second_operation["capability"], "seat.set_ventilation") self.assertEqual(second_operation["parameters"], {"state": "off"}) third_operation = third["function_config"]["spec"]["operations"][0] self.assertEqual(third_operation["capability"], "seat.set_heating") self.assertEqual(third_operation["parameters"], {"state": "on", "level": 1}) fourth_operation = fourth["function_config"]["spec"]["operations"][0] self.assertEqual(fourth_operation["target"]["instance"], "passenger") self.assertEqual(fourth_operation["capability"], "seat.set_ventilation") self.assertEqual(fourth_operation["parameters"], {"state": "off"}) self.assertEqual( fourth["actuator_state"]["passenger"]["heating"], {"state": "on", "level": 1}, ) self.assertEqual(fourth["conversation_turn"], 4) def test_compound_cold_and_stuffy_feedback_generates_balanced_dry_run(self) -> None: result = self.service.process( "副驾有点冷、有点闷", provider="local", conversation_id="conv-compound-comfort", ) self.assertEqual(result["status"], "succeeded") operations = result["function_config"]["spec"]["operations"] self.assertEqual( {operation["capability"] for operation in operations}, {"seat.set_heating", "seat.set_ventilation"}, ) command = result["command_plan"]["spec"]["commands"][0] self.assertEqual( command["signals"], { "Front_Passenger_Seat_Heating_S": 1, "Front_Passenger_Seat_Ventilation_S": 1, }, ) self.assertIn( "COMBINED_THERMAL_SIMULATED", {issue["code"] for issue in result["validation"]}, ) def test_vehicle_scope_is_routed_away_from_seat_domain(self) -> None: result = self.service.process( "车上太闷了", provider="local", conversation_id="conv-vehicle-scope", ) self.assertEqual(result["status"], "needs_confirmation") self.assertEqual(result["intent_ir"]["spec"]["detected_domains"], ["climate"]) self.assertIsNone(result["function_config"]) self.assertIsNone(result["command_plan"]) self.assertEqual(result["actuator_state"]["passenger"]["ventilation"]["state"], "off") route = next(event for event in result["trace_events"] if event["stage"] == "domain_route") self.assertEqual(route["details"]["domains"], ["climate"]) def test_local_rules_precede_openai_for_unscoped_driver_console_command(self) -> None: model_output = { "detected_domains": ["seat"], "requests": [ { "domain": "seat", "capability": "seat.set_ventilation", "target_scope": "passenger", "operation": "deactivate", "parameters": [{"name": "state", "value": "off"}], "reason": "model-owned-plan", } ], "ambiguities": [], "decision_summary": ["model-owned-plan"], "confidence": 0.91, } context = self.service.conversations.snapshot("conv-model-owned-plan") self.service.openai.api_key = "test-key" with patch.object(self.service.openai, "interpret", return_value=model_output) as interpret: parsed, provider, notice = self.service._interpret( "关闭座椅通风", "openai", context ) self.assertTrue( any("默认选择主驾座椅" in item for item in parsed["decision_summary"]) ) self.assertEqual(len(parsed["model_requests"]), 1) self.assertEqual( parsed["model_requests"][0]["capability"], "seat.set_driver_ventilation_native", ) self.assertEqual(parsed["actions"], []) self.assertEqual(len(parsed["deferred_requests"]), 1) deferred = parsed["deferred_requests"][0] self.assertEqual(deferred["capability"], "seat.set_driver_ventilation_native") self.assertEqual(deferred["target_scope"], "driver") self.assertEqual(deferred["parameters"], {"state": "off"}) self.assertEqual(deferred["execution_backend"], "native_can") self.assertEqual(provider, "local_rules") self.assertIsNone(notice) interpret.assert_not_called() def test_explicit_gpt_failure_falls_back_to_local_rules(self) -> None: self.service.openai.api_key = "test-key" with patch.object( self.service.openai, "interpret", side_effect=OpenAIIntentError("invalid_api_key"), ): result = self.service.process( "打开副驾座椅通风,同时播放音乐", "openai", "conv-explicit-gpt-fallback", ) self.assertEqual(result["status"], "succeeded") self.assertEqual(result["provider_used"], "local_rules_fallback") self.assertEqual( result["function_config"]["spec"]["operations"][0]["target"]["instance"], "passenger", ) self.assertTrue( any("已降级到本地规则解析" in item for item in result["decision_summary"]) ) def test_local_first_calls_model_with_only_uncovered_clauses(self) -> None: self.service.openai.api_key = "test-key" model_output = { "detected_domains": ["ambient_light"], "requests": [ { "domain": "ambient_light", "capability": "ambient_light.set_power", "target_scope": "vehicle", "operation": "set", "parameters": [{"name": "state", "value": "on"}], "reason": "播放场景需要氛围灯。", } ], "ambiguities": [], "decision_summary": ["仅补充本地规则未覆盖的播放场景。"], "confidence": 0.9, } context = self.service.conversations.snapshot("conv-clause-fallback") with patch.object( self.service.openai, "interpret", return_value=model_output ) as interpret: parsed, provider, notice = self.service._interpret( "空调温度设置为25度,播放音乐", "openai", context, ) interpret.assert_called_once_with( "播放音乐", { **context, "natural_language_routing": { "original_utterance": "空调温度设置为25度,播放音乐", "parsed_clauses": ["空调温度设置为25度"], "resolved_clauses": ["空调温度设置为25度"], "unresolved_clauses": ["播放音乐"], "cloud_input_utterance": "播放音乐", }, }, self.service.capability_context, ) self.assertNotIn("natural_language_routing", context) self.assertEqual( provider, f"local_rules+openai:{self.service.openai.model}" ) self.assertIn("仅未覆盖", str(notice)) self.assertEqual( [item["capability"] for item in parsed["native_can_actions"]], ["climate.set_temperature"], ) self.assertEqual( [item["capability"] for item in parsed["domain_actions"]], ["ambient_light.set_power"], ) def test_local_first_compiles_memory_temperature_and_fan_without_model(self) -> None: self.service.openai.api_key = "test-key" with patch.object(self.service.openai, "interpret") as interpret: result = self.service.process( "主驾座椅调整到记忆位置3,空调温度设置为25度,风速设置为1档", "openai", "conv-local-memory-climate", ) interpret.assert_not_called() self.assertEqual(result["status"], "succeeded") self.assertEqual(result["provider_used"], "local_rules") self.assertEqual( set(result["function_configs"]), {"native_seat", "native_climate"} ) commands = [ command["signals"]["debug_command"] for plan in result["command_plans"].values() for command in plan["spec"]["commands"] ] self.assertEqual( commands, ["seat driver position 3", "ac temp driver 25", "ac fan 1"], ) def test_model_climate_request_completes_domain_loop(self) -> None: model_output = { "detected_domains": ["climate"], "requests": [ { "domain": "climate", "capability": "climate.set_fan_level", "target_scope": "vehicle", "operation": "increase", "parameters": [{"name": "level", "value": 3}], "reason": "Increase cabin airflow.", } ], "ambiguities": [], "decision_summary": ["整车舱风量增加需求。"], "confidence": 0.94, } self.service.openai.api_key = "test-key" with patch.object(self.service.openai, "interpret", return_value=model_output): result = self.service.process( "车上太闷了", "openai", "conv-model-deferred", ) self.assertEqual(result["status"], "succeeded") self.assertEqual(result["model_requests"], model_output["requests"]) self.assertEqual(result["deferred_requests"], []) self.assertIn("climate", result["function_configs"]) request = result["intent_ir"]["spec"]["requests"][0] self.assertEqual(request["domain"], "climate") self.assertEqual(request["parameters"]["capability"], "climate.set_fan_level") def test_climate_and_seat_requests_generate_scene(self) -> None: model_output = { "detected_domains": ["seat", "climate"], "requests": [ { "domain": "seat", "capability": "seat.set_heating", "target_scope": "passenger", "operation": "activate", "parameters": [ {"name": "state", "value": "on"}, {"name": "level", "value": 1}, ], "reason": "Warm the passenger seat.", }, { "domain": "climate", "capability": "climate.set_fan_level", "target_scope": "vehicle", "operation": "set", "parameters": [{"name": "level", "value": 2}], "reason": "Improve cabin airflow.", }, ], "ambiguities": [], "decision_summary": ["座椅加热与整车空调风量并行执行。"], "confidence": 0.9, } self.service.openai.api_key = "test-key" with patch.object(self.service.openai, "interpret", return_value=model_output): result = self.service.process( "打开副驾座椅加热,播放音乐并把车内风量调舒适", "openai", "conv-model-partial", ) self.assertEqual(result["status"], "succeeded") self.assertIsNotNone(result["execution_report"]) self.assertEqual( result["actuator_state"]["passenger"]["heating"], {"state": "on", "level": 1}, ) self.assertEqual(result["domain_state"]["climate"]["fan_level"], 2) self.assertEqual( result["provider_used"], f"local_rules+openai:{self.service.openai.model}", ) self.assertEqual(set(result["function_configs"]), {"seat", "climate"}) self.assertEqual(result["scene_config"]["kind"], "SceneConfig") def test_model_motion_requests_complete_control_and_feedback_loop(self) -> None: model_output = { "detected_domains": ["seat"], "requests": [ { "domain": "seat", "capability": "seat.set_longitudinal_position", "target_scope": "passenger", "operation": "set", "parameters": [{"name": "position_pct", "value": 35}], "reason": "Move the passenger seat backward from its current midpoint.", }, { "domain": "seat", "capability": "seat.set_backrest_recline", "target_scope": "passenger", "operation": "set", "parameters": [{"name": "position_pct", "value": 60}], "reason": "Recline the passenger backrest.", }, { "domain": "seat", "capability": "seat.set_legrest_height", "target_scope": "passenger", "operation": "set", "parameters": [{"name": "position_pct", "value": 65}], "reason": "Raise the passenger leg rest.", }, { "domain": "seat", "capability": "seat.set_legrest_extension", "target_scope": "passenger", "operation": "set", "parameters": [{"name": "position_pct", "value": 70}], "reason": "Extend the passenger leg rest.", }, ], "ambiguities": [], "decision_summary": ["四个电机轴使用绝对位置目标。"], "confidence": 0.93, } self.service.openai.api_key = "test-key" with patch.object(self.service.openai, "interpret", return_value=model_output): result = self.service.process( "副驾往后一点并把靠背躺下,腿托升起并伸长", "openai", "conv-model-motion", ) self.assertEqual(result["status"], "succeeded") self.assertEqual(len(result["function_config"]["spec"]["operations"]), 4) self.assertEqual(len(result["command_plan"]["spec"]["commands"]), 4) self.assertEqual(len(result["execution_report"]["spec"]["results"]), 4) passenger = result["actuator_state"]["passenger"]["position"] self.assertEqual(passenger["longitudinal"]["position_pct"], 35) self.assertEqual(passenger["backrest_recline"]["position_pct"], 60) self.assertEqual(passenger["legrest_height"]["position_pct"], 65) self.assertEqual(passenger["legrest_extension"]["position_pct"], 70) telemetry = result["execution_report"]["spec"]["results"][0]["telemetry"] self.assertEqual(telemetry["start_position_pct"], 50) self.assertEqual(telemetry["effective_position_pct"], 35) self.assertEqual(telemetry["feedback_frame"]["frame_id"], "0x559") self.assertEqual(telemetry["fault_mask"], 0) codes = {issue["code"] for issue in result["validation"]} self.assertIn("SYNTHETIC_MOTION_CAN", codes) def test_motion_follow_up_state_is_supplied_to_model(self) -> None: def output(position: int) -> dict[str, object]: return { "detected_domains": ["seat"], "requests": [ { "domain": "seat", "capability": "seat.set_longitudinal_position", "target_scope": "passenger", "operation": "increase", "parameters": [{"name": "position_pct", "value": position}], "reason": "Model resolved the relative request.", } ], "ambiguities": [], "decision_summary": [f"Move to {position}%."], "confidence": 0.9, } self.service.openai.api_key = "test-key" with patch.object( self.service.openai, "interpret", side_effect=[output(60), output(70)], ) as interpret: first = self.service.process( "副驾往后一点", "openai", "conv-motion-follow-up" ) second = self.service.process( "还是靠前,再往后一点", "openai", "conv-motion-follow-up" ) self.assertEqual( first["actuator_state"]["passenger"]["position"]["longitudinal"][ "position_pct" ], 60, ) self.assertEqual( second["actuator_state"]["passenger"]["position"]["longitudinal"][ "position_pct" ], 70, ) second_context = interpret.call_args_list[1].args[1] self.assertEqual( second_context["devices"]["passenger"]["position"]["longitudinal"][ "position_pct" ], 60, ) def test_model_relative_physical_motion_uses_vehicle_profile(self) -> None: model_output = { "detected_domains": ["seat"], "requests": [ { "domain": "seat", "capability": "seat.set_longitudinal_position", "target_scope": "passenger", "operation": "decrease", "parameters": [{"name": "distance_mm", "value": 50}], "reason": "Move forward by a physical distance.", }, { "domain": "seat", "capability": "seat.set_backrest_recline", "target_scope": "passenger", "operation": "increase", "parameters": [{"name": "angle_delta_deg", "value": 10}], "reason": "Recline by a relative physical angle.", }, ], "ambiguities": [], "decision_summary": ["使用车型量程把相对物理量换算为绝对百分比。"], "confidence": 0.94, } self.service.openai.api_key = "test-key" with patch.object(self.service.openai, "interpret", return_value=model_output) as interpret: result = self.service.process( "副驾水平向前移动50毫米,靠背往后10度", "openai", "conv-vehicle-profile-relative", vehicle_id="denza_n7", ) self.assertEqual(result["status"], "succeeded") self.assertEqual(result["vehicle"]["id"], "denza_n7") operations = result["function_config"]["spec"]["operations"] self.assertEqual(operations[0]["parameters"], {"position_pct": 27}) self.assertEqual( operations[1]["parameters"], {"position_pct": 35, "angle_deg": 118.6}, ) backrest_motion = operations[1]["motion"] self.assertEqual(backrest_motion["mode"], "relative") self.assertEqual(backrest_motion["direction"], "increase") self.assertEqual(backrest_motion["start_position_pct"], 20) self.assertEqual(backrest_motion["delta_position_pct"], 15) self.assertEqual(backrest_motion["start_angle_deg"], 108.6) self.assertEqual(backrest_motion["delta_angle_deg"], 10) context_sent_to_model = interpret.call_args.args[1] self.assertEqual(context_sent_to_model["vehicle"]["id"], "denza_n7") self.assertEqual( context_sent_to_model["devices"]["passenger"]["position"][ "backrest_recline" ]["angle_deg"], 108.6, ) def test_model_timed_motion_survives_endpoint_resolution_and_reaches_compiler(self) -> None: self.service.process( "副驾座椅水平位置调到100%", provider="local", conversation_id="conv-model-timed", ) model_output = { "detected_domains": ["seat"], "requests": [ { "domain": "seat", "capability": "seat.set_longitudinal_position", "target_scope": "passenger", "operation": "increase", "duration_ms": 3_000, "parameters": [{"name": "position_pct", "value": 100}], "reason": "Continue moving the rail backward for three seconds.", }, { "domain": "seat", "capability": "seat.set_legrest_height", "target_scope": "passenger", "operation": "increase", "duration_ms": 3_000, "parameters": [], "reason": "Raise the leg rest for three seconds.", }, ], "ambiguities": [], "decision_summary": ["保留两个动作各自三秒的定时运动语义。"], "confidence": 0.99, } self.service.openai.api_key = "test-key" with patch.object(self.service.openai, "interpret", return_value=model_output): result = self.service.process( "副驾座椅水平再后移动3秒,副驾腿托向上调3秒", "openai", "conv-model-timed", vehicle_id="denza_n7", ) self.assertEqual(result["status"], "succeeded") operations = result["function_config"]["spec"]["operations"] self.assertEqual(len(operations), 2) by_capability = {item["capability"]: item for item in operations} rail_motion = by_capability["seat.set_longitudinal_position"]["motion"] self.assertEqual(rail_motion["mode"], "timed") self.assertEqual(rail_motion["direction"], "increase") self.assertEqual(rail_motion["start_position_pct"], 100) self.assertEqual(rail_motion["delta_position_pct"], 0) self.assertEqual(rail_motion["applied_duration_ms"], 3_000) legrest_motion = by_capability["seat.set_legrest_height"]["motion"] self.assertEqual(legrest_motion["mode"], "timed") self.assertEqual(legrest_motion["direction"], "increase") self.assertEqual(legrest_motion["applied_duration_ms"], 3_000) def test_vehicle_profile_angle_limits_are_accepted(self) -> None: model_output = { "detected_domains": ["seat"], "requests": [ { "domain": "seat", "capability": "seat.set_backrest_recline", "target_scope": "passenger", "operation": "set", "parameters": [{"name": "angle_deg", "value": 163}], "reason": "Use the vehicle-specific maximum backrest angle.", }, { "domain": "seat", "capability": "seat.set_legrest_height", "target_scope": "passenger", "operation": "set", "parameters": [{"name": "angle_deg", "value": 167}], "reason": "Use the vehicle-specific maximum legrest angle.", }, ], "ambiguities": [], "decision_summary": ["车型角度边界按腾势 N7 配置执行。"], "confidence": 0.95, } self.service.openai.api_key = "test-key" with patch.object(self.service.openai, "interpret", return_value=model_output): result = self.service.process( "副驾靠背到163度,腿托到167度", "openai", "conv-vehicle-angle-limits", vehicle_id="denza_n7", ) self.assertEqual(result["status"], "succeeded") operations = result["function_config"]["spec"]["operations"] self.assertEqual(operations[0]["parameters"], {"position_pct": 100, "angle_deg": 163}) self.assertEqual(operations[1]["parameters"], {"position_pct": 100, "angle_deg": 167}) def test_motion_adapter_does_not_resolve_missing_absolute_target(self) -> None: model_output = { "detected_domains": ["seat"], "requests": [ { "domain": "seat", "capability": "seat.set_longitudinal_position", "target_scope": "passenger", "operation": "decrease", "parameters": [], "reason": "No absolute target was produced.", } ], "ambiguities": [], "decision_summary": ["Unresolved relative request."], "confidence": 0.6, } self.service.openai.api_key = "test-key" with patch.object(self.service.openai, "interpret", return_value=model_output): result = self.service.process( "副驾往后一点", "openai", "conv-motion-missing-target" ) self.assertEqual(result["status"], "needs_confirmation") self.assertIsNone(result["function_config"]) self.assertIn( "AMBIGUOUS_INTENT", {issue["code"] for issue in result["validation"]} ) self.assertNotIn( "NO_EXECUTABLE_OPERATION", {issue["code"] for issue in result["validation"]}, ) self.assertNotIn( "PUBLIC_SCHEMA", {issue["code"] for issue in result["validation"]} ) def test_exact_angles_and_named_preset_complete_conversation_loop(self) -> None: def plan(requests: list[dict[str, object]], summary: str) -> dict[str, object]: return { "detected_domains": ["seat"], "requests": requests, "ambiguities": [], "decision_summary": [summary], "confidence": 0.96, } def request( capability: str, operation: str, parameters: list[dict[str, object]], ) -> dict[str, object]: return { "domain": "seat", "capability": capability, "target_scope": "passenger", "operation": operation, "parameters": parameters, "reason": "Model resolved the user's posture goal against runtime context.", } first_plan = plan( [ request( "seat.set_backrest_recline", "set", [{"name": "angle_deg", "value": 128}], ), request( "seat.set_legrest_height", "set", [{"name": "angle_deg", "value": 133}], ), ], "副驾靠背/坐垫夹角设为128度,坐垫/腿托夹角设为133度。", ) recline_plan = plan( [ request( "seat.set_backrest_recline", "increase", [{"name": "angle_deg", "value": 133}], ) ], "根据当前128度姿态,将副驾靠背继续后仰至133度。", ) save_plan = plan( [ request( "seat.save_posture_preset", "save", [{"name": "preset_name", "value": "零重力模式"}], ) ], "将副驾当前五轴姿态保存为零重力模式。", ) leave_preset_plan = plan( [ request( "seat.set_backrest_recline", "set", [{"name": "angle_deg", "value": 105}], ) ], "将副驾靠背恢复到105度用于验证预设回调。", ) apply_plan = plan( [ request( "seat.apply_posture_preset", "apply", [{"name": "preset_name", "value": "零重力模式"}], ) ], "调用已保存的副驾零重力模式。", ) self.service.openai.api_key = "test-key" conversation_id = "conv-angle-preset-loop" with patch.object( self.service.openai, "interpret", side_effect=[ first_plan, recline_plan, save_plan, leave_preset_plan, apply_plan, ], ) as interpret: first = self.service.process( "副驾调整为零重力模式,要求靠背座椅夹角128度,坐垫脚托夹角133度", "openai", conversation_id, ) second = self.service.process( "副驾靠背再向后一点", "openai", conversation_id ) saved = self.service.process( "将当前状态保存为零重力模式", "openai", conversation_id ) changed = self.service.process( "副驾靠背恢复直立一些", "openai", conversation_id ) applied = self.service.process( "副驾恢复到零重力模式", "openai", conversation_id ) first_operations = first["function_config"]["spec"]["operations"] self.assertEqual( first_operations[0]["parameters"], {"position_pct": 49, "angle_deg": 128}, ) self.assertEqual( first_operations[1]["parameters"], {"position_pct": 53, "angle_deg": 133}, ) self.assertEqual( first["actuator_state"]["passenger"]["position"]["backrest_recline"][ "angle_deg" ], 128, ) first_telemetry = first["execution_report"]["spec"]["results"][0]["telemetry"] self.assertEqual(first_telemetry["requested_angle_deg"], 128) self.assertEqual(first_telemetry["effective_position_pct"], 49) self.assertEqual( second["actuator_state"]["passenger"]["position"]["backrest_recline"][ "angle_deg" ], 133, ) relative_motion = second["function_config"]["spec"]["operations"][0][ "motion" ] self.assertEqual(relative_motion["mode"], "relative") self.assertEqual(relative_motion["direction"], "increase") self.assertEqual(relative_motion["start_position_pct"], 49) self.assertEqual(relative_motion["delta_position_pct"], 7) self.assertEqual(relative_motion["delta_angle_deg"], 5) second_context = interpret.call_args_list[1].args[1] self.assertEqual( second_context["devices"]["passenger"]["position"]["backrest_recline"][ "angle_deg" ], 128, ) self.assertEqual(saved["status"], "succeeded") self.assertIsNone(saved["function_config"]) self.assertEqual(saved["preset_config"]["kind"], "FunctionPreset") self.assertEqual(saved["preset_config"]["metadata"]["name"], "零重力模式") self.assertFalse( saved["execution_report"]["spec"]["results"][0]["telemetry"][ "can_command_emitted" ] ) self.assertEqual( saved["saved_presets"], [{"name": "零重力模式", "target": "passenger"}], ) preset_path = ( self.service.artifact_root / "user-presets" / f"{conversation_id}.json" ) self.assertTrue(preset_path.is_file()) self.assertEqual( changed["actuator_state"]["passenger"]["position"]["backrest_recline"][ "angle_deg" ], 105, ) self.assertEqual(applied["status"], "succeeded") restored = applied["actuator_state"]["passenger"]["position"] self.assertEqual(restored["backrest_recline"]["angle_deg"], 133) self.assertEqual(restored["legrest_height"]["angle_deg"], 133) self.assertEqual(len(applied["function_config"]["spec"]["operations"]), 5) apply_context = interpret.call_args_list[4].args[1] self.assertEqual(apply_context["presets"][0]["name"], "零重力模式") def test_missing_target_defaults_to_driver_ch1(self) -> None: result = self.service.process("打开座椅通风", provider="local") self.assertEqual(result["status"], "succeeded") operation = result["function_config"]["spec"]["operations"][0] self.assertEqual(operation["target"]["zone"], "driver") self.assertEqual( operation["capability"], "seat.set_driver_ventilation_native" ) self.assertEqual( result["command_plan"]["spec"]["commands"][0]["bus"], "body_can", ) def test_explicit_driver_target_uses_ch1_native_can(self) -> None: result = self.service.process("打开主驾座椅通风", provider="local") self.assertEqual(result["status"], "succeeded") operation = result["function_config"]["spec"]["operations"][0] self.assertEqual(operation["target"]["zone"], "driver") self.assertEqual( operation["capability"], "seat.set_driver_ventilation_native" ) command = result["command_plan"]["spec"]["commands"][0] self.assertEqual(command["bus"], "body_can") self.assertEqual(command["signals"]["frame_id"], "0x431") self.assertEqual(command["signals"]["feedback_ids"], "0x4FA") def test_model_driver_target_is_replaced_by_ch1_native_can(self) -> None: model_output = { "detected_domains": ["seat"], "requests": [ { "domain": "seat", "capability": "seat.set_ventilation", "target_scope": "driver", "operation": "activate", "parameters": [{"name": "state", "value": "on"}], "reason": "Ventilate the explicitly requested driver seat.", } ], "ambiguities": [], "decision_summary": ["Explicit driver-seat request."], "confidence": 0.95, } self.service.openai.api_key = "test-key" with patch.object(self.service.openai, "interpret", return_value=model_output): result = self.service.process( "打开主驾座椅通风", "openai", "conv-driver-rejected" ) self.assertEqual(result["status"], "succeeded") operation = result["function_config"]["spec"]["operations"][0] self.assertEqual(operation["target"]["zone"], "driver") self.assertEqual( operation["capability"], "seat.set_driver_ventilation_native" ) self.assertFalse(result["deferred_requests"]) if __name__ == "__main__": unittest.main()