feat: synchronize CH1 vehicle control and feedback

This commit is contained in:
vee
2026-08-11 10:42:13 +08:00
parent 3701192a11
commit 32379a1573
50 changed files with 13636 additions and 736 deletions
@@ -6,6 +6,7 @@ from copy import deepcopy
import re
from typing import Any
from cognitive_agent.application.native_can_intent import resolve_ch1_default_target
from cognitive_agent.application.seat_calibration import (
ANGLE_CALIBRATIONS,
angle_to_position_pct,
@@ -35,6 +36,20 @@ NATIVE_CAN_ONLY_CAPABILITIES = {
"climate.set_control_mode",
"roof.set_shade_motion",
}
DEFAULT_DRIVER_NATIVE_SEAT_CAPABILITIES = {
"seat.set_ventilation": "seat.set_driver_ventilation_native",
"seat.set_heating": "seat.set_driver_heating_native",
}
EXPLICIT_DRIVER_SEAT_PATTERN = re.compile(
r"主驾|驾驶位|司机位|左前(?:的)?(?:座椅|座位)|"
r"seat\s+(?:driver|front-left)",
re.IGNORECASE,
)
EXPLICIT_NON_DRIVER_SEAT_PATTERN = re.compile(
r"副驾|乘客|右前|后排|前排|所有座椅|全部座椅|"
r"seat\s+(?:passenger|front-right)",
re.IGNORECASE,
)
class ModelPlanAdapter:
@@ -55,6 +70,7 @@ class ModelPlanAdapter:
plan: dict[str, Any],
context: dict[str, Any] | None = None,
vehicle_profile: VehicleProfile | None = None,
utterance: str = "",
) -> dict[str, Any]:
actions: list[dict[str, Any]] = []
domain_actions: list[dict[str, Any]] = []
@@ -64,7 +80,8 @@ class ModelPlanAdapter:
ambiguities = list(plan.get("ambiguities", []))
context = context or {}
for index, request in enumerate(plan.get("requests", [])):
for index, raw_request in enumerate(plan.get("requests", [])):
request = self._apply_ch1_default_target(raw_request, utterance)
capability_name = str(request.get("capability"))
registered = self.capabilities.get(capability_name)
if registered is None or registered[0] != request.get("domain"):
@@ -246,6 +263,36 @@ class ModelPlanAdapter:
"model_requests": deepcopy(plan.get("requests", [])),
}
@staticmethod
def _apply_ch1_default_target(
raw_request: dict[str, Any], utterance: str
) -> dict[str, Any]:
"""Normalize omitted model targets to driver-console CH1 defaults."""
request = deepcopy(raw_request)
capability = str(request.get("capability", ""))
native_seat_capability = DEFAULT_DRIVER_NATIVE_SEAT_CAPABILITIES.get(
capability
)
if native_seat_capability is not None:
explicit_non_driver = bool(
EXPLICIT_NON_DRIVER_SEAT_PATTERN.search(utterance)
)
explicit_driver = bool(EXPLICIT_DRIVER_SEAT_PATTERN.search(utterance))
implicit_driver = (
resolve_ch1_default_target(native_seat_capability, utterance)
== "driver"
)
if not explicit_non_driver and (explicit_driver or implicit_driver):
request["capability"] = native_seat_capability
request["target_scope"] = "driver"
capability = native_seat_capability
default_target = resolve_ch1_default_target(capability, utterance)
if default_target is not None:
request["target_scope"] = default_target
return request
@staticmethod
def _generic_action(
index: int,