优化后端和前端

This commit is contained in:
minghanchen
2026-08-24 14:15:53 +08:00
parent 8d05815773
commit 33c92d1352
27 changed files with 2328 additions and 234 deletions
@@ -567,14 +567,19 @@ class ConversationStateStore:
device = state["domain_devices"][domain]
if domain == "climate":
# The trusted CAN command vocabulary uses the concise token
# ``rear`` while the conversation state stores that same HVAC
# zone as ``rear_row``. Keep the public command stable and
# normalize only at the state boundary.
zone_target = "rear_row" if target == "rear" else target
if capability == "climate.set_power":
device["zones"][target]["power"] = parameters["state"]
device["zones"][zone_target]["power"] = parameters["state"]
if target == "vehicle":
device["power"] = parameters["state"]
for zone in device["zones"].values():
zone["power"] = parameters["state"]
elif capability == "climate.set_temperature":
device["zones"][target]["temperature_c"] = parameters[
device["zones"][zone_target]["temperature_c"] = parameters[
"temperature_c"
]
if target == "vehicle":
@@ -589,7 +594,7 @@ class ConversationStateStore:
elif capability == "climate.set_front_defrost":
device["front_defrost"] = parameters["state"]
elif capability == "climate.set_outlet_mode":
device["zones"][target]["outlet_mode"] = parameters["mode"]
device["zones"][zone_target]["outlet_mode"] = parameters["mode"]
elif capability == "climate.set_control_mode":
device["control_mode"] = parameters["mode"]
device["feedback_source"] = feedback_source
@@ -78,6 +78,9 @@ class ConfiguredDomainRuntime:
"zone": action["target"],
},
"parameters": dict(action["parameters"]),
"parse_source": str(
action.get("parse_source", "local_rule")
),
"timeout_ms": 8000 if self.domain == "roof" else 4000,
"failure_policy": "abort",
}
@@ -25,6 +25,16 @@ WINDOW_TARGETS = {
"后左": "rear_left",
"右后": "rear_right",
"后右": "rear_right",
# Natural cabin language names the front windows by occupant more often
# than by left/right. These aliases belong only to the window domain:
# driver = left-front, passenger = right-front.
"主驾驶": "front_left",
"驾驶位": "front_left",
"司机位": "front_left",
"主驾": "front_left",
"副驾驶": "front_right",
"乘客位": "front_right",
"副驾": "front_right",
}
WINDOW_GROUP_MEMBERS = {
"all": ["front_left", "front_right", "rear_left", "rear_right"],
@@ -49,6 +59,7 @@ LIGHT_TARGETS = {
"左后": "rear_left",
"右后": "rear_right",
}
CLAUSE_SEPARATOR = re.compile(r"[,,;;。!?!?\n]+")
# Defaults are intentionally scoped to the driver console and verified CH1
# body-CAN vocabulary. A missing target must never become the legacy
@@ -83,7 +94,8 @@ CH1_DEFAULT_TARGETS = {
_EXPLICIT_TARGET_PATTERNS = {
"window": re.compile(
r"(?:全部|所有|四个|全车|前排|后排|左前|前左|右前|前右|左后|后左|右后|后右)"
r"(?:全部|所有|四个|全车|前排|后排|左前|前左|右前|前右|左后|后左|右后|后右|"
r"主驾驶|驾驶位|司机位|主驾|副驾驶|乘客位|副驾)"
r"(?:的)?(?:车窗|窗)|window\s+(?:all|front|rear|front-left|front-right|rear-left|rear-right)",
re.IGNORECASE,
),
@@ -113,6 +125,88 @@ _EXPLICIT_TARGET_PATTERNS = {
}
def _scoped_text(
compact: str,
subject_pattern: str,
continuation_pattern: str | None = None,
) -> str:
"""Keep action words inside clauses that name the controlled device."""
clauses = [clause for clause in CLAUSE_SEPARATOR.split(compact) if clause]
subject_indexes = {
index
for index, clause in enumerate(clauses)
if re.search(subject_pattern, clause, re.IGNORECASE)
}
selected_indexes = set(subject_indexes)
if continuation_pattern:
selected_indexes.update(
index + 1
for index in subject_indexes
if index + 1 < len(clauses)
and re.search(
continuation_pattern,
clauses[index + 1],
re.IGNORECASE,
)
)
return ",".join(
clause
for index, clause in enumerate(clauses)
if index in selected_indexes
)
def _window_targets(text: str) -> tuple[list[str], bool]:
"""Resolve target aliases only when they are attached to a window noun."""
explicit: list[str] = []
broad: str | None = None
target_labels = "|".join(
re.escape(label)
for label in sorted(WINDOW_TARGETS, key=len, reverse=True)
)
for label, target in WINDOW_TARGETS.items():
attached = bool(
re.search(
rf"{re.escape(label)}"
rf"(?:(?:和|与|及|、|以及)(?:{target_labels}))*"
rf"(?:的)?(?:两个|两侧)?(?:车窗|窗)",
text,
)
)
if not attached:
continue
if target in WINDOW_GROUP_MEMBERS:
broad = broad or target
elif target not in explicit:
explicit.append(target)
exclusions = {
target
for label, target in WINDOW_TARGETS.items()
if target not in WINDOW_GROUP_MEMBERS
and re.search(
rf"{re.escape(label)}(?:车窗)?(?:除外|除去|不包括|不含)",
text,
)
}
if broad and exclusions:
return (
[
target
for target in WINDOW_GROUP_MEMBERS[broad]
if target not in exclusions
],
True,
)
if explicit:
return ([target for target in explicit if target not in exclusions], True)
if broad:
return ([broad], True)
return (["front_left"], False)
def _capability_domain(capability: str) -> str:
return "interior_light" if capability.startswith("interior_light.") else capability.split(".", 1)[0]
@@ -156,6 +250,7 @@ def _action(
"operation": "set",
"reason": reason,
"execution_backend": "native_can",
"parse_source": "local_rule",
}
@@ -284,6 +379,8 @@ def _parse_exact_command(text: str) -> list[dict[str, Any]]:
}
if direction not in valid_directions[axis]:
return []
if direction == "stop" and duration_token is not None:
return []
duration = (
500
if direction == "stop"
@@ -381,17 +478,23 @@ def _parse_chinese_driver_seat_commands(compact: str) -> list[dict[str, Any]]:
actions: list[dict[str, Any]] = []
memory = re.search(
r"(?:记忆(?:位置|位)?|记忆座椅(?:位置)?|座椅记忆(?:位置)?|位置记忆)"
r"(?:"
r"(?:记忆(?:位置|位)?|预设(?:位置)?|记忆座椅(?:位置)?|"
r"座椅记忆(?:位置)?|位置记忆)"
r"(?:切换到|切换为|调整到|调到|唤醒|调用|恢复到|恢复为|设为|设置为)?"
r"([1-3])(?:号|档|位)?",
r"(?P<after>[1-3一二三])(?:号|档|位)?"
r"|(?P<before>[1-3一二三])号?(?:记忆|预设)(?:位置|位)?"
r")",
compact,
)
if memory is not None:
slot_token = memory.group("after") or memory.group("before")
slot = {"一": 1, "二": 2, "三": 3}.get(slot_token, slot_token)
actions.append(_action(
"seat",
"seat.recall_driver_memory_native",
"seat.set_driver_position_native",
"driver",
{"slot": int(memory[1])},
{"slot": int(slot)},
(
"用户明确指定了 CH1 主驾座椅记忆位置。"
if explicit_driver
@@ -449,8 +552,10 @@ def _parse_chinese_driver_seat_commands(compact: str) -> list[dict[str, Any]]:
strength = (
1
if re.search(r"弱|低|1档", compact)
else 2
if re.search(r"中等|中档|2档", compact)
else 3
if re.search(r"强|高|3档", compact)
if re.search(r"强(?!度)|高|3档", compact)
else 2
)
massage = {"action": "strength", "strength": strength}
@@ -549,103 +654,149 @@ def _parse_chinese_driver_seat_commands(compact: str) -> list[dict[str, Any]]:
def _parse_chinese_commands(text: str) -> list[dict[str, Any]]:
compact = re.sub(r"\s+", "", text)
actions: list[dict[str, Any]] = _parse_chinese_driver_seat_commands(compact)
seat_text = _scoped_text(
compact,
r"座椅|座位|靠背|椅背|坐盆|坐垫|腿托|脚托|按摩|记忆|预设|"
r"通风|加热|前移|后移|向前移动|向后移动",
)
actions: list[dict[str, Any]] = _parse_chinese_driver_seat_commands(
seat_text
)
climate_text = _scoped_text(
compact,
r"空调|温度|风量|风速|内循环|外循环|循环模式|吹面|吹脸|吹脚|除霜|(?:AC|A/C)",
)
if (
"空调" in compact
"空调" in climate_text
or re.search(r"(?:AC|A/C)", text, re.IGNORECASE)
or re.search(r"温度|风量|风速|内循环|外循环|循环模式", compact)
or re.search(
r"温度|风量|风速|内循环|外循环|循环模式|吹面|吹脸|吹脚|除霜",
climate_text,
)
):
if re.search(r"(?:打开|开启|启动)空调", compact):
if re.search(r"(?:打开|开启|启动)空调", climate_text):
actions.append(_action("climate", "climate.set_power", "vehicle", {"state": "on"}, "用户要求打开空调。"))
elif re.search(r"(?:关闭|关掉|关上)空调", compact):
elif re.search(r"(?:关闭|关掉|关上)空调", climate_text):
actions.append(_action("climate", "climate.set_power", "vehicle", {"state": "off"}, "用户要求关闭空调。"))
temperature = re.search(
r"(?:(主驾|副驾|后排)(?:空调)?(?:温度)?|空调(?:温度)?|温度)(?:调到|设为|设置为|到)?(1[89]|2\d|3[0-2])(?:℃|度)?",
compact,
r"(?:空调(?:温度)?|温度)(?:都)?"
r"(?:调整到|调整为|调到|调为|设为|设置为|调至|到|为)?"
r"(1[89]|2\d|3[0-2])(?:摄氏)?(?:℃|度)?(?![\d.])",
climate_text,
)
if temperature:
target = {"主驾": "driver", "副驾": "passenger", "后排": "rear"}.get(temperature[1], "driver")
if (
re.search(r"全车|整车|前排|主副驾", climate_text)
or ("主驾" in climate_text and "副驾" in climate_text)
):
temperature_targets = ("driver", "passenger")
elif re.search(r"副驾|副驾驶|乘客", climate_text):
temperature_targets = ("passenger",)
elif "后排" in climate_text:
temperature_targets = ("rear",)
else:
temperature_targets = ("driver",)
explicit_temperature_target = bool(
re.search(
r"主驾|副驾|副驾驶|乘客|后排|前排|全车|整车|主副驾",
climate_text,
)
)
reason = (
"用户指定了空调温区和温度。"
if temperature[1]
if explicit_temperature_target
else "用户未指定空调温区,按驾驶台默认选择主驾温区。"
)
actions.append(_action("climate", "climate.set_temperature", target, {"temperature_c": int(temperature[2])}, reason))
actions.extend(
_action(
"climate",
"climate.set_temperature",
target,
{"temperature_c": int(temperature[1])},
reason,
)
for target in temperature_targets
)
fan = re.search(r"(?:风量|风速)(?:调到|设为|设置为|到)?([1-7])(?:档)?", compact)
fan = re.search(
r"(?:风量|风速)(?:调整到|调整为|调到|调为|设为|设置为|到)?"
r"([1-7])(?:档|级)?(?![\d.])",
climate_text,
)
if fan:
actions.append(_action("climate", "climate.set_fan_level", "vehicle", {"level": int(fan[1])}, "用户指定了空调风量。"))
if re.search(r"自动(?:控制|模式)", compact):
if re.search(r"自动(?:控制|模式)", climate_text):
actions.append(_action("climate", "climate.set_control_mode", "vehicle", {"mode": "auto"}, "用户指定空调自动控制。"))
elif re.search(r"手动(?:控制|模式)", compact):
elif re.search(r"手动(?:控制|模式)", climate_text):
actions.append(_action("climate", "climate.set_control_mode", "vehicle", {"mode": "manual"}, "用户指定空调手动控制。"))
if re.search(r"内循环", compact):
if re.search(r"内循环", climate_text):
actions.append(_action("climate", "climate.set_cycle_mode", "vehicle", {"mode": "internal"}, "用户指定空调内循环。"))
elif re.search(r"外循环", compact):
elif re.search(r"外循环", climate_text):
actions.append(_action("climate", "climate.set_cycle_mode", "vehicle", {"mode": "external"}, "用户指定空调外循环。"))
outlet_modes = [
("吹面吹脚除霜", "face_feet_defrost"),
("吹面除霜", "face_defrost"),
("吹脚除霜", "feet_defrost"),
("吹面吹脚", "face_feet"),
("除霜", "defrost"),
("吹脚", "feet"),
("吹面", "face"),
]
for label, mode in outlet_modes:
if label in compact:
actions.append(_action("climate", "climate.set_outlet_mode", "vehicle", {"mode": mode}, "用户指定了空调出风模式。"))
break
if "车窗" in compact:
explicit_targets = []
for label, target_name in WINDOW_TARGETS.items():
if target_name in WINDOW_GROUP_MEMBERS or label not in compact:
continue
if target_name not in explicit_targets:
explicit_targets.append(target_name)
broad_target = next(
(
target_name
for label, target_name in WINDOW_TARGETS.items()
if target_name in WINDOW_GROUP_MEMBERS and label in compact
),
None,
has_face = bool(re.search(r"吹面|吹脸", climate_text))
has_feet = "吹脚" in climate_text
has_defrost = "除霜" in climate_text
outlet_mode = (
"face_feet_defrost"
if has_face and has_feet and has_defrost
else "face_feet"
if has_face and has_feet
else "face_defrost"
if has_face and has_defrost
else "feet_defrost"
if has_feet and has_defrost
else "defrost"
if has_defrost
else "face"
if has_face
else "feet"
if has_feet
else None
)
exclusions = {
target_name
for label, target_name in WINDOW_TARGETS.items()
if target_name not in WINDOW_GROUP_MEMBERS
and re.search(
rf"{re.escape(label)}(?:车窗)?(?:除外|除去|不包括|不含)",
compact,
if outlet_mode:
actions.append(
_action(
"climate",
"climate.set_outlet_mode",
"vehicle",
{"mode": outlet_mode},
"用户指定了空调出风模式。",
)
)
}
if broad_target and exclusions:
targets = [
target_name
for target_name in WINDOW_GROUP_MEMBERS[broad_target]
if target_name not in exclusions
]
elif explicit_targets:
targets = [target_name for target_name in explicit_targets if target_name not in exclusions]
else:
targets = [broad_target or "front_left"]
window_text = _scoped_text(
compact,
r"车窗",
r"^(?:主驾|主驾驶|驾驶位|司机位|副驾|副驾驶|乘客位|"
r"左前|前左|右前|前右|左后|后左|右后|后右)(?:车窗)?(?:除外|除去|不包括|不含)$",
)
if window_text:
targets, explicit_window_target = _window_targets(window_text)
position = re.search(
r"(?:位置|开度|调到|设为|设置为|开启到|打开到)(\d{1,3})%",
compact,
r"(?:位置|开度|调整到|调到|设为|设置为|开启到|打开到|开到)(\d{1,3})%",
window_text,
)
half_position = bool(
re.search(
r"(?:开启|打开|开到|开度)?(?:一半|半开)",
window_text,
)
)
half_position = bool(re.search(r"(?:开启|打开|开到|开度)?(?:一半|半开)", compact))
position_pct = int(position[1]) if position else 50 if half_position else None
if position_pct is not None and all(target not in WINDOW_GROUP_MEMBERS for target in targets) and position_pct <= 100:
if (
position_pct is not None
and all(target not in WINDOW_GROUP_MEMBERS for target in targets)
and position_pct <= 100
):
reason = (
"用户指定了单个车窗的绝对开度。"
if explicit_targets or broad_target
if explicit_window_target
else "用户未指定车窗目标,按驾驶台默认选择主驾左前窗。"
)
actions.extend(
@@ -658,15 +809,15 @@ def _parse_chinese_commands(text: str) -> list[dict[str, Any]]:
pass
else:
motion = (
"stop" if re.search(r"停止|停下|暂停", compact)
else "close" if re.search(r"关闭|关上|升起|关窗", compact)
else "open" if re.search(r"打开|开启|降下|开窗", compact)
"stop" if re.search(r"停止|停下|暂停", window_text)
else "close" if re.search(r"关闭|关上|升起|关窗", window_text)
else "open" if re.search(r"打开|开启|降下|开窗", window_text)
else None
)
if motion:
reason = (
"用户指定了车窗目标和动作。"
if explicit_targets or broad_target
if explicit_window_target
else "用户未指定车窗目标,按驾驶台默认选择主驾左前窗。"
)
actions.extend(
@@ -674,17 +825,18 @@ def _parse_chinese_commands(text: str) -> list[dict[str, Any]]:
for target in targets
)
door_text = _scoped_text(compact, r"门锁|车门|后门|锁车|解锁车辆")
rear_lock_request = bool(
re.search(
r"(?:后门|后排车门|后排门锁|二排车门|二排门锁)", compact
r"(?:后门|后排车门|后排门锁|二排车门|二排门锁)", door_text
)
)
if rear_lock_request:
state = (
"unlock"
if re.search(r"解锁|开锁", compact)
if re.search(r"解锁|开锁", door_text)
else "lock"
if re.search(r"上锁|落锁|锁上", compact)
if re.search(r"上锁|落锁|锁上", door_text)
else None
)
if state:
@@ -695,59 +847,123 @@ def _parse_chinese_commands(text: str) -> list[dict[str, Any]]:
r"整车(?:门锁|车门)|全车(?:门锁|车门)|(?:所有|全部)(?:车门|门锁)|"
r"(?:车门|门锁)(?:上锁|落锁|锁上|解锁|开锁)|"
r"(?:锁车|整车解锁|全车解锁|解锁车辆)",
compact,
door_text,
)
)
if vehicle_lock_request:
state = (
"unlock"
if re.search(r"解锁|开锁", compact)
if re.search(r"解锁|开锁", door_text)
else "lock"
if re.search(r"上锁|落锁|锁上|锁车", compact)
if re.search(r"上锁|落锁|锁上|锁车", door_text)
else None
)
if state:
actions.append(_action("door", "door.set_vehicle_lock", "vehicle", {"state": state}, "用户指定了整车门锁状态。"))
if re.search(r"遮阳帘|天幕帘", compact):
position = re.search(r"(?:位置|开度|调到|设为)(\d{1,3})%", compact)
shade_text = _scoped_text(compact, r"遮阳帘|天幕帘")
if shade_text:
position = re.search(
r"(?:位置|开度|调整到|调到|设为|设置为|开到|打开(?:到)?|开启(?:到)?)(\d{1,3})%",
shade_text,
)
if position and int(position[1]) <= 100:
actions.append(_action("roof", "roof.set_shade_position", "vehicle", {"position_pct": int(position[1])}, "用户指定了遮阳帘开度。"))
else:
motion = (
"stop" if re.search(r"停止|停下|暂停", compact)
else "half" if re.search(r"半开|一半", compact)
else "close" if re.search(r"关闭|关上", compact)
else "open" if re.search(r"打开|开启", compact)
"stop" if re.search(r"停止|停下|暂停", shade_text)
else "half" if re.search(r"半开|一半", shade_text)
else "close" if re.search(r"关闭|关上", shade_text)
else "open" if re.search(r"打开|开启", shade_text)
else None
)
if motion:
actions.append(_action("roof", "roof.set_shade_motion", "vehicle", {"motion": motion}, "用户指定了遮阳帘动作。"))
indoor_text = _scoped_text(compact, r"室内灯|车内灯")
for label, capability in (("室内灯", "interior_light.set_indoor_power"), ("车内灯", "interior_light.set_indoor_power")):
if label in compact:
state = "off" if re.search(r"关闭|关掉|关上", compact) else "on" if re.search(r"打开|开启", compact) else None
if label in indoor_text:
state = "off" if re.search(r"关闭|关掉|关上", indoor_text) else "on" if re.search(r"打开|开启", indoor_text) else None
if state:
actions.append(_action("interior_light", capability, "vehicle", {"state": state}, "用户指定了车内灯状态。"))
break
if "阅读灯" in compact:
target = next((value for label, value in LIGHT_TARGETS.items() if label in compact), "all")
state = "off" if re.search(r"关闭|关掉|关上", compact) else "on" if re.search(r"打开|开启", compact) else None
reading_text = _scoped_text(compact, r"阅读灯")
if reading_text:
target = next((value for label, value in LIGHT_TARGETS.items() if label in reading_text), "all")
state = "off" if re.search(r"关闭|关掉|关上", reading_text) else "on" if re.search(r"打开|开启", reading_text) else None
if state and target == "all":
actions.append(_action("interior_light", "interior_light.set_reading_power", target, {"state": state}, "用户指定了阅读灯区域和状态。"))
if "氛围灯" in compact:
color_match = re.search(r"颜色(?:索引|编号)?(\d{1,3})", compact)
brightness_match = re.search(r"亮度(?:档|级)?([1-5])", compact)
ambient_text = _scoped_text(
compact,
r"氛围灯",
r"^(?:亮度|强度)(?:调整|调节|调|设置|设)?",
)
if ambient_text:
state = (
"off"
if re.search(r"关闭|关掉|熄灭", ambient_text)
else "on"
if re.search(r"打开|开启|点亮", ambient_text)
else None
)
if state:
actions.append(
_action(
"ambient_light",
"ambient_light.set_power",
"vehicle",
{"state": state},
"用户指定了氛围灯总开关状态。",
)
)
color_match = re.search(
r"(?:颜色编号|颜色索引|颜色|色号)"
r"(?:调整到|调整为|调到|调为|设置为|设为|为)?"
r"(\d{1,3})(?![\d.])",
ambient_text,
)
brightness_match = re.search(
r"(?:亮度|强度)(?:调整到|调整为|调到|调为|设置为|设为|为)?"
r"([1-5])(?:档|级)?(?![\d.])",
ambient_text,
)
if color_match and 1 <= int(color_match[1]) <= 127:
actions.append(_action("ambient_light", "ambient_light.set_color_index", "vehicle", {"color_index": int(color_match[1])}, "用户指定了已验证的氛围灯颜色索引。"))
if brightness_match:
actions.append(_action("ambient_light", "ambient_light.set_brightness_level", "vehicle", {"brightness_level": int(brightness_match[1])}, "用户指定了已验证的氛围灯亮度档位。"))
outlet_target = next((value for label, value in OUTLET_TARGETS.items() if f"{label}风口" in compact), None)
outlet_position = re.search(r"(?:左右|水平)(\d{1,3})%?.*?(?:上下|垂直)(\d{1,3})%?", compact)
if "风口" in compact and outlet_position and all(int(value) <= 100 for value in outlet_position.groups()):
outlet_text = _scoped_text(compact, r"风口")
outlet_target = next((value for label, value in OUTLET_TARGETS.items() if f"{label}风口" in outlet_text), None)
calibrated_position = re.search(
r"(?:标定坐标|标定位置).*?(?:左右|水平)"
r"(-?\d{1,3})(?![\d.]).*?(?:上下|垂直)(-?\d{1,3})(?![\d.])",
outlet_text,
)
outlet_position = re.search(
r"(?:左右|水平)(\d{1,3})%?(?![\d.]).*?"
r"(?:上下|垂直)(\d{1,3})%?(?![\d.])",
outlet_text,
)
if (
outlet_target
and calibrated_position
and all(-128 <= int(value) <= 126 for value in calibrated_position.groups())
):
actions.append(
_action(
"outlet",
"outlet.set_calibrated_position",
outlet_target,
{
"horizontal_value": int(calibrated_position[1]),
"vertical_value": int(calibrated_position[2]),
},
"用户指定了已标定风口的双轴原始坐标。",
)
)
elif outlet_text and outlet_position and all(int(value) <= 100 for value in outlet_position.groups()):
target = outlet_target or "driver_left"
reason = (
"用户指定了已标定风口的双轴百分比位置。"
@@ -275,6 +275,7 @@ class NativeCanRelativeResolver:
"target": target,
"operation": "set",
"parameters": parameters,
"parse_source": "local_rule",
"reason": f"用户要求{label}{verb}{delta};基于当前状态 {current} 计算绝对目标 {target_value}{',已按允许范围收敛' if clamped else ''}。",
"relative_resolution": {
"direction": direction,
@@ -183,6 +183,9 @@ class NativeCanRuntime:
"zone": target,
},
"parameters": dict(action.get("parameters", {})),
"parse_source": str(
action.get("parse_source", "local_rule")
),
"timeout_ms": (
20_000
if capability_name
@@ -417,6 +420,8 @@ class NativeCanRuntime:
return f"seat driver massage strength {strength}"
return f"seat driver massage mode {parameters['mode']}"
if capability == "seat.set_driver_axis_native":
if parameters["direction"] == "stop":
return f"seat driver {parameters['axis']} stop"
return (
f"seat driver {parameters['axis']} {parameters['direction']} "
f"{parameters.get('duration_ms', 500)}"
@@ -1129,15 +1134,30 @@ class NativeCanExecutor:
"feedback ID and a target-value match contract"
)
special_handler = command["signals"].get("special_handler")
if special_handler == "window_position":
feedback_required = self.feedback_required
if not feedback_required and special_handler in {
None,
"shade_preset",
}:
# Wireless MQTT uses the state topic as an asynchronous
# source of truth. Fixed frames and self-terminating
# shade presets must not hold the HTTP request open
# while waiting for the same state stream synchronously.
sent_frames = self._send_command(bus, command)
feedback = None
elif special_handler == "window_position":
sent_frames, feedback = self._execute_window_position(bus, command)
elif special_handler == "driver_seat_axis":
sent_frames, feedback = self._execute_driver_seat_axis(
bus, command
bus,
command,
wait_for_feedback=feedback_required,
)
elif special_handler == "driver_seat_memory_recall":
sent_frames, feedback = self._execute_driver_seat_memory(
bus, command
bus,
command,
wait_for_completion=feedback_required,
)
elif special_handler == "shade_preset":
sent_frames, feedback = self._execute_shade_preset(
@@ -1151,7 +1171,6 @@ class NativeCanExecutor:
sent_frames = self._send_command(bus, command)
feedback = self._wait_feedback(bus, command)
feedback_match = self._feedback_matches(command, feedback)
feedback_required = self.feedback_required
verified = feedback is not None and feedback_match is True
delivered = bool(sent_frames)
succeeded = verified or (delivered and not feedback_required)
@@ -1167,17 +1186,26 @@ class NativeCanExecutor:
publication_start:
]
)
delivery_status = "submitted"
if not feedback_required and delivered:
delivery_status = (
"published"
if mqtt_messages
and all(
message.get("delivery_status") == "published"
for message in mqtt_messages
)
else "queued"
)
results.append(
{
"ref": operation["id"],
"domain": function_config["spec"]["domain"],
"status": "succeeded" if succeeded else "failed",
"message": (
"MQTT 白名单控制帧已发布,并收到匹配的车辆状态反馈。"
if not feedback_required and feedback_match is True
else "MQTT 白名单控制帧已发布;已收到状态帧,但目标值尚未确认。"
if not feedback_required and feedback is not None
else "MQTT 白名单控制帧已发布;车辆状态反馈尚未确认。"
"MQTT 白名单控制帧已由 Broker 确认;车辆效果以状态话题回读为准。"
if not feedback_required and delivery_status == "published"
else "MQTT 白名单控制帧已进入客户端发布队列;车辆效果以状态话题回读为准。"
if not feedback_required
else "CAN 盒已提交车端控制帧并观察到匹配反馈。"
if feedback_match is True
@@ -1197,11 +1225,7 @@ class NativeCanExecutor:
"feedback_match": feedback_match,
"feedback_state": self._feedback_state(feedback),
"feedback_required": feedback_required,
"delivery_status": (
"published"
if not feedback_required and delivered
else "submitted"
),
"delivery_status": delivery_status,
"physical_effect_confirmed": verified,
"control_gate": (
"compiled_action_and_frame_whitelist"
@@ -1260,7 +1284,11 @@ class NativeCanExecutor:
return sent
def _execute_driver_seat_axis(
self, bus: CanBus, command: dict[str, Any]
self,
bus: CanBus,
command: dict[str, Any],
*,
wait_for_feedback: bool = True,
) -> tuple[list[CanFrame], CanFrame | None]:
"""Send bounded motion and attempt all three stop frames in a finally path."""
@@ -1313,10 +1341,14 @@ class NativeCanExecutor:
f"{len(stop_errors)}/3 forced-stop frame(s) failed"
)
raise RuntimeError("; ".join(details))
return sent, self._wait_feedback(bus, command)
return sent, self._wait_feedback(bus, command) if wait_for_feedback else None
def _execute_driver_seat_memory(
self, bus: CanBus, command: dict[str, Any]
self,
bus: CanBus,
command: dict[str, Any],
*,
wait_for_completion: bool = True,
) -> tuple[list[CanFrame], CanFrame | None]:
"""Require fresh initialization, then replay the learned 16/15 sequence."""
@@ -1366,8 +1398,12 @@ class NativeCanExecutor:
)
if index + 1 < len(sequence):
time.sleep(int(special["period_ms"]) / 1_000)
feedback = self._wait_for_id(
bus, 0x494, int(special["post_status_timeout_ms"])
feedback = (
self._wait_for_id(
bus, 0x494, int(special["post_status_timeout_ms"])
)
if wait_for_completion
else None
)
return sent, feedback
+518 -17
View File
@@ -8,6 +8,7 @@ import re
import sys
import time
import uuid
from collections.abc import Callable
from concurrent.futures import ThreadPoolExecutor
from copy import deepcopy
from datetime import UTC, datetime
@@ -177,7 +178,7 @@ UPDATE_MODE_PROFILE_PREFIX = re.compile(
UPDATE_MODE_PROFILE_BA = re.compile(
r"^(?:请(?:帮我)?|帮我)?(?:把|将)"
r"(?P<name>[^,。,.!!\s]{1,64}?模式)"
r"(?:的)?(?P<change>.+)$"
r"的(?P<change>.+)$"
)
RESTORE_MODE_PROFILE_PREFIX = re.compile(
r"^(?:请(?:帮我)?|帮我)?(?:恢复|还原|重置|初始化)"
@@ -1066,6 +1067,340 @@ class SeatDemoService:
return MqttOtaSeatExecutor(executor, resolved_bus_factory)
return executor
def preview_native_can(
self,
utterance: str,
provider: str = "auto",
conversation_id: str | None = None,
execution_mode: str = "dry_run",
approval_token: str | None = None,
vehicle_id: str | None = None,
simulation_fault: str = "normal",
*,
hardware_client_id: str | None = None,
hardware_access_bypass: bool = True,
reasoning_effort: str | None = None,
control_transport: str = WIRED_CAN_TRANSPORT,
on_commands_validated: Callable[[dict[str, Any]], None] | None = None,
) -> dict[str, Any]:
"""Validate and execute deterministic controls before hosted parsing."""
utterance = utterance.strip()
if not utterance:
raise ValueError("utterance must not be empty")
if provider not in {"auto", "openai", "local"}:
raise ValueError("provider must be auto, openai, or local")
conversation_id = conversation_id or f"conv-{uuid.uuid4().hex}"
if not CONVERSATION_ID_PATTERN.fullmatch(conversation_id):
raise ValueError(
"conversation_id must start with conv- and contain only lowercase letters, numbers, or hyphens"
)
vehicle_profile = self.vehicle_profiles.get(vehicle_id)
control_plane_request = any(
matcher(utterance) is not None
for matcher in (
self._parse_mode_profile_save_name,
self._parse_mode_profile_create_name,
self._parse_mode_profile_restore_name,
self._parse_mode_profile_update,
)
)
if control_plane_request:
result = self.process(
utterance,
provider=provider,
conversation_id=conversation_id,
execution_mode=execution_mode,
approval_token=approval_token,
vehicle_id=vehicle_profile.id,
simulation_fault=simulation_fault,
hardware_client_id=hardware_client_id,
hardware_access_bypass=hardware_access_bypass,
reasoning_effort=reasoning_effort,
control_transport=control_transport,
_on_commands_validated=on_commands_validated,
)
result.update(
{
"phase": "control_plane_complete",
"preview_request_id": result["request_id"],
"unresolved_clauses": [],
"supplement_required": False,
}
)
self._persist(result)
return result
context = self._context_for_vehicle(
self.conversations.snapshot(conversation_id),
vehicle_profile,
)
clause_results = self._local_clause_results(utterance, context)
unresolved_clauses = [
clause
for clause, _, status in clause_results
if status != "complete"
]
started = time.perf_counter()
def publish_local_commands(validated: dict[str, Any]) -> None:
if on_commands_validated is None:
return
event = deepcopy(validated)
event.update(
{
"phase": "deterministic_commands_validated",
"provider_requested": provider,
"unresolved_clauses": unresolved_clauses,
"supplement_required": bool(
provider != "local" and unresolved_clauses
),
}
)
on_commands_validated(event)
result = self.process(
utterance,
provider="local",
conversation_id=conversation_id,
execution_mode=execution_mode,
approval_token=approval_token,
vehicle_id=vehicle_profile.id,
simulation_fault=simulation_fault,
hardware_client_id=hardware_client_id,
hardware_access_bypass=hardware_access_bypass,
reasoning_effort=reasoning_effort,
control_transport=control_transport,
_local_preview=True,
_allow_empty_control_result=True,
_on_commands_validated=publish_local_commands,
)
elapsed_ms = round((time.perf_counter() - started) * 1000, 3)
supplement_required = bool(
provider != "local" and unresolved_clauses
)
result.update(
{
"phase": "deterministic_preview",
"provider_requested": provider,
"preview_request_id": result["request_id"],
"preview_elapsed_ms": elapsed_ms,
"unresolved_clauses": unresolved_clauses,
"supplement_required": supplement_required,
}
)
result["decision_summary"] = [
*result.get("decision_summary", []),
(
"本地规则阶段已完成白名单校验并先行处理 "
f"{sum(len((config.get('spec') or {}).get('operations', [])) for config in result.get('function_configs', {}).values())} 条 CAN 控制指令。"
),
]
self._persist(result)
manifest = {
"version": 1,
"preview_request_id": result["request_id"],
"utterance": utterance,
"provider": provider,
"conversation_id": conversation_id,
"execution_mode": execution_mode,
"vehicle_id": vehicle_profile.id,
"simulation_fault": simulation_fault,
"hardware_client_id": hardware_client_id,
"reasoning_effort": reasoning_effort,
"control_transport": control_transport,
"unresolved_clauses": unresolved_clauses,
"preview_control_signatures": result.get(
"parsed_control_signatures", []
),
}
self._atomic_write(
self.artifact_root / result["request_id"] / "preview.json",
json.dumps(manifest, ensure_ascii=False, indent=2) + "\n",
)
return result
def supplement_native_can(
self,
preview_request_id: str,
*,
provider: str | None = None,
approval_token: str | None = None,
hardware_client_id: str | None = None,
hardware_access_bypass: bool = True,
reasoning_effort: str | None = None,
on_commands_validated: Callable[[dict[str, Any]], None] | None = None,
) -> dict[str, Any]:
"""Parse only unresolved clauses and merge them with the local result."""
if not MODE_EDITOR_REQUEST_ID_PATTERN.fullmatch(preview_request_id):
raise ValueError("invalid preview request id")
preview_dir = self.artifact_root / preview_request_id
try:
manifest = json.loads(
(preview_dir / "preview.json").read_text(encoding="utf-8")
)
preview = json.loads(
(preview_dir / "result.json").read_text(encoding="utf-8")
)
except (OSError, json.JSONDecodeError) as exc:
raise ValueError("native CAN preview not found") from exc
if (
not isinstance(manifest, dict)
or manifest.get("version") != 1
or manifest.get("preview_request_id") != preview_request_id
or not isinstance(preview, dict)
or preview.get("request_id") != preview_request_id
):
raise ValueError("invalid native CAN preview manifest")
selected_provider = str(manifest.get("provider") or "auto")
if provider is not None and provider != selected_provider:
raise ValueError("supplement provider does not match preview")
selected_effort = manifest.get("reasoning_effort")
if reasoning_effort is not None and reasoning_effort != selected_effort:
raise ValueError("supplement reasoning effort does not match preview")
expected_client_id = manifest.get("hardware_client_id")
if hardware_client_id != expected_client_id:
raise ValueError("supplement hardware client does not match preview")
unresolved_clauses = [
str(item).strip()
for item in manifest.get("unresolved_clauses", [])
if str(item).strip()
]
if not unresolved_clauses:
raise ValueError("native CAN preview has no unresolved clauses")
excluded_signatures = {
tuple(item)
for item in manifest.get("preview_control_signatures", [])
if isinstance(item, list)
}
def publish_cloud_commands(validated: dict[str, Any]) -> None:
if on_commands_validated is None:
return
event = self._merge_native_can_phases(preview, validated)
event.update(
{
"phase": "supplement_commands_validated",
"preview_request_id": preview_request_id,
"supplement_of": preview_request_id,
"supplement_required": False,
}
)
on_commands_validated(event)
supplement = self.process(
",".join(unresolved_clauses),
provider=selected_provider,
conversation_id=str(manifest["conversation_id"]),
execution_mode=str(manifest.get("execution_mode") or "dry_run"),
approval_token=approval_token,
vehicle_id=str(manifest["vehicle_id"]),
simulation_fault=str(manifest.get("simulation_fault") or "normal"),
hardware_client_id=hardware_client_id,
hardware_access_bypass=hardware_access_bypass,
reasoning_effort=(
str(selected_effort) if selected_effort is not None else None
),
control_transport=str(
manifest.get("control_transport") or WIRED_CAN_TRANSPORT
),
_excluded_control_signatures=excluded_signatures,
_model_parse_source="cloud_supplement",
_allow_empty_control_result=True,
_on_commands_validated=publish_cloud_commands,
)
merged = self._merge_native_can_phases(preview, supplement)
merged.update(
{
"phase": "two_stage_complete",
"utterance": str(manifest["utterance"]),
"provider_requested": selected_provider,
"preview_request_id": preview_request_id,
"supplement_of": preview_request_id,
"supplement_request_id": supplement["request_id"],
"supplement_required": False,
"unresolved_clauses": unresolved_clauses,
}
)
self._persist(merged)
return merged
@staticmethod
def _merge_native_can_phases(
preview: dict[str, Any],
supplement: dict[str, Any],
) -> dict[str, Any]:
"""Create one durable result without collapsing same-domain phases."""
merged = deepcopy(supplement)
def phase_documents(
local_documents: dict[str, Any] | None,
cloud_documents: dict[str, Any] | None,
) -> dict[str, Any]:
return {
**{
f"local_{key}": deepcopy(value)
for key, value in (local_documents or {}).items()
},
**{
f"cloud_{key}": deepcopy(value)
for key, value in (cloud_documents or {}).items()
},
}
configs = phase_documents(
preview.get("function_configs"),
supplement.get("function_configs"),
)
plans = phase_documents(
preview.get("command_plans"),
supplement.get("command_plans"),
)
merged["function_configs"] = configs
merged["command_plans"] = plans
merged["function_config"] = next(iter(configs.values()), None)
merged["command_plan"] = next(iter(plans.values()), None)
merged["decision_summary"] = [
*preview.get("decision_summary", []),
*supplement.get("decision_summary", []),
]
merged["trace_events"] = [
*preview.get("trace_events", []),
*supplement.get("trace_events", []),
]
merged["validation"] = [
*preview.get("validation", []),
*supplement.get("validation", []),
]
merged["parsed_control_signatures"] = [
*preview.get("parsed_control_signatures", []),
*supplement.get("parsed_control_signatures", []),
]
merged["phase_execution_reports"] = {
"local_rule": deepcopy(preview.get("execution_report")),
"cloud_supplement": deepcopy(supplement.get("execution_report")),
}
if supplement.get("execution_report") is None:
merged["execution_report"] = deepcopy(preview.get("execution_report"))
successful = {"succeeded", "partially_succeeded", "no_action"}
preview_ok = preview.get("status") in successful
supplement_ok = supplement.get("status") in successful
has_any_commands = bool(configs and plans)
if preview_ok and supplement_ok:
merged["status"] = "succeeded" if has_any_commands else "no_action"
elif preview_ok and preview.get("function_configs"):
merged["status"] = "partially_succeeded"
merged["local_preview"] = {
"request_id": preview.get("request_id"),
"elapsed_ms": preview.get("preview_elapsed_ms"),
"command_count": sum(
len((config.get("spec") or {}).get("operations", []))
for config in preview.get("function_configs", {}).values()
),
}
return merged
def process(
self,
utterance: str,
@@ -1080,6 +1415,11 @@ class SeatDemoService:
hardware_access_bypass: bool = True,
reasoning_effort: str | None = None,
control_transport: str = WIRED_CAN_TRANSPORT,
_local_preview: bool = False,
_excluded_control_signatures: set[tuple[Any, ...]] | None = None,
_model_parse_source: str = "cloud_model",
_allow_empty_control_result: bool = False,
_on_commands_validated: Callable[[dict[str, Any]], None] | None = None,
) -> dict[str, Any]:
control_transport = self._validate_control_transport(control_transport)
# Creating a reusable scene is a configuration workflow. Synthesize
@@ -1099,6 +1439,11 @@ class SeatDemoService:
simulation_fault,
reasoning_effort,
control_transport,
_local_preview,
_excluded_control_signatures,
_model_parse_source,
_allow_empty_control_result,
_on_commands_validated,
)
if hardware_access_bypass or self._driver_only_vehicle_execution_active(
control_transport
@@ -1114,6 +1459,11 @@ class SeatDemoService:
simulation_fault,
reasoning_effort,
control_transport,
_local_preview,
_excluded_control_signatures,
_model_parse_source,
_allow_empty_control_result,
_on_commands_validated,
)
authorized_client_id = self.hardware_access.ensure_authorized(hardware_client_id)
with self.hardware_execution.reserve():
@@ -1128,6 +1478,11 @@ class SeatDemoService:
simulation_fault,
reasoning_effort,
control_transport,
_local_preview,
_excluded_control_signatures,
_model_parse_source,
_allow_empty_control_result,
_on_commands_validated,
)
def _process_once(
@@ -1141,6 +1496,11 @@ class SeatDemoService:
simulation_fault: str = "normal",
reasoning_effort: str | None = None,
control_transport: str = WIRED_CAN_TRANSPORT,
local_preview: bool = False,
excluded_control_signatures: set[tuple[Any, ...]] | None = None,
model_parse_source: str = "cloud_model",
allow_empty_control_result: bool = False,
on_commands_validated: Callable[[dict[str, Any]], None] | None = None,
) -> dict[str, Any]:
control_transport = self._validate_control_transport(control_transport)
utterance = utterance.strip()
@@ -1298,6 +1658,8 @@ class SeatDemoService:
context,
vehicle_profile,
reasoning_effort=reasoning_effort,
local_preview=local_preview,
model_parse_source=model_parse_source,
)
if self._is_dynamic_mode_stop_request(utterance):
parsed = self._apply_dynamic_mode_stop_request(parsed, utterance)
@@ -1305,6 +1667,7 @@ class SeatDemoService:
parsed = self._apply_mode_profiles(
parsed, matched_modes, context, vehicle_profile
)
parsed = self._mark_control_parse_source(parsed, "local_rule")
relative_resolution = (
self.native_can_relative_resolver.resolve(utterance, context)
if self.native_can_relative_resolver.matches(utterance)
@@ -1323,6 +1686,15 @@ class SeatDemoService:
relative_resolution.claimed_controls if relative_resolution else None
),
)
if local_preview:
parsed["ambiguities"] = []
parsed["unresolved_spans"] = []
parsed["model_requests"] = []
if excluded_control_signatures:
parsed = self._filter_excluded_controls(
parsed,
excluded_control_signatures,
)
parsed = apply_language_safety_guards(parsed, utterance)
parsed = self._partition_native_actions(
parsed,
@@ -1335,6 +1707,47 @@ class SeatDemoService:
)
),
)
result["parsed_control_signatures"] = [
list(self._control_signature(action))
for action in [
*parsed.get("actions", []),
*parsed.get("domain_actions", []),
*parsed.get("native_can_actions", []),
]
]
has_control = any(
parsed.get(key)
for key in ("actions", "domain_actions", "native_can_actions")
)
if allow_empty_control_result and not has_control:
result["provider_used"] = provider_used
result["decision_summary"] = [
*parsed.get("decision_summary", []),
"当前阶段没有新增的已验证 CAN 控制指令。",
]
result["deferred_requests"] = parsed.get("deferred_requests", [])
result["model_requests"] = parsed.get("model_requests", [])
result["intent_ir"] = build_intent_ir(
parsed,
utterance,
request_id,
provider_used,
self.semantic_capability_metadata,
)
result["validation"] = self.contracts.validate(result["intent_ir"])
result["status"] = "no_action"
self._trace(
traces,
request_id,
"intent_parse",
"succeeded",
"当前阶段未产生新的白名单控制动作。",
started,
{"provider": provider_used, "control_count": 0},
domain="orchestration",
)
self._persist(result)
return result
if hardware_request and not control_plane_request:
if (
control_transport == WIRELESS_MQTT_TRANSPORT
@@ -1505,6 +1918,7 @@ class SeatDemoService:
simulation_fault=simulation_fault,
vehicle_profile=vehicle_profile,
control_transport=control_transport,
on_commands_validated=on_commands_validated,
)
intent_ir = build_intent_ir(
@@ -1776,6 +2190,8 @@ class SeatDemoService:
self._persist(result)
return result
self._emit_validated_commands(result, on_commands_validated)
safety_started = time.perf_counter()
self._trace(
traces,
@@ -3395,6 +3811,21 @@ class SeatDemoService:
result["deferred_requests"] = deferred_requests
return result
@staticmethod
def _emit_validated_commands(
result: dict[str, Any],
callback: Callable[[dict[str, Any]], None] | None,
) -> None:
"""Expose fully compiled commands before physical execution begins."""
if callback is None:
return
event = deepcopy(result)
event["status"] = "processing"
event["phase"] = "commands_validated"
event["execution_report"] = None
callback(event)
def _process_orchestrated(
self,
utterance: str,
@@ -3411,6 +3842,7 @@ class SeatDemoService:
simulation_fault: str = "normal",
vehicle_profile: VehicleProfile | None = None,
control_transport: str = WIRED_CAN_TRANSPORT,
on_commands_validated: Callable[[dict[str, Any]], None] | None = None,
) -> dict[str, Any]:
"""Run isolated adapters under the selected physical-bus policy."""
vehicle_profile = vehicle_profile or self.vehicle_profiles.default()
@@ -3669,6 +4101,8 @@ class SeatDemoService:
self._persist(result)
return result
self._emit_validated_commands(result, on_commands_validated)
self._trace(
traces,
request_id,
@@ -3842,7 +4276,7 @@ class SeatDemoService:
"execute",
"succeeded" if execution_succeeded and not report_issues else "failed",
(
"无线 MQTT 白名单控制帧已发布;车辆实际状态以状态话题回读为准。"
"无线 MQTT 白名单控制帧已提交;车辆实际状态以状态话题回读为准。"
if (
hardware_request
and control_transport == WIRELESS_MQTT_TRANSPORT
@@ -4385,16 +4819,29 @@ class SeatDemoService:
)
return merged
def _interpret(
@staticmethod
def _mark_control_parse_source(
parsed: dict[str, Any],
source: str,
*,
overwrite: bool = False,
) -> dict[str, Any]:
"""Attach per-control provenance without changing semantic content."""
marked = deepcopy(parsed)
for key in ("actions", "domain_actions", "native_can_actions"):
for action in marked.get(key, []):
if overwrite or not action.get("parse_source"):
action["parse_source"] = source
return marked
def _local_clause_results(
self,
utterance: str,
provider: str,
context: dict[str, Any],
vehicle_profile: VehicleProfile | None = None,
*,
reasoning_effort: str | None = None,
) -> tuple[dict[str, Any], str, str | None]:
vehicle_profile = vehicle_profile or self.vehicle_profiles.default()
) -> list[tuple[str, dict[str, Any], str]]:
"""Parse each clause deterministically and preserve inherited seat scope."""
clause_results: list[tuple[str, dict[str, Any], str]] = []
def parse_clause(clause: str) -> dict[str, Any]:
@@ -4413,7 +4860,7 @@ class SeatDemoService:
relative.claimed_controls if relative else None
),
)
return local
return self._mark_control_parse_source(local, "local_rule")
def has_seat_domain(parsed: dict[str, Any]) -> bool:
if parsed.get("actions") or "seat" in parsed.get("detected_domains", []):
@@ -4441,12 +4888,6 @@ class SeatDemoService:
if clause_has_seat and explicit_driver != explicit_passenger:
inherited_seat_scope = "driver" if explicit_driver else "passenger"
elif clause_has_seat and inherited_seat_scope:
# Chinese compound controls commonly state the seat once and
# then list axes or thermal functions after commas. Carry the
# last explicit seat scope only into later seat clauses; this
# prevents a passenger request from silently falling through
# to the CH1 driver default while leaving climate/door/window
# clauses untouched.
scope_label = (
"主驾座椅"
if inherited_seat_scope == "driver"
@@ -4457,10 +4898,59 @@ class SeatDemoService:
clause_results.append(
(clause, local, self._local_clause_status(local, clause))
)
return clause_results
@classmethod
def _trusted_local_preview(
cls,
clause_results: list[tuple[str, dict[str, Any], str]],
) -> dict[str, Any]:
"""Keep deterministic controls while deferring incomplete text to GPT."""
trusted = cls._merge_parsed_results(
[parsed for _, parsed, status in clause_results if status != "unparsed"]
)
# Ambiguities and unresolved spans belong to the supplement phase. A
# deterministic action still goes through the normal schema, compiler,
# whitelist and physical execution gates before it can be sent.
trusted["ambiguities"] = []
trusted["unresolved_spans"] = []
trusted["model_requests"] = []
return trusted
@classmethod
def _filter_excluded_controls(
cls,
parsed: dict[str, Any],
excluded: set[tuple[Any, ...]],
) -> dict[str, Any]:
filtered = deepcopy(parsed)
for key in ("actions", "domain_actions", "native_can_actions"):
filtered[key] = [
action
for action in filtered.get(key, [])
if cls._control_signature(action) not in excluded
]
return filtered
def _interpret(
self,
utterance: str,
provider: str,
context: dict[str, Any],
vehicle_profile: VehicleProfile | None = None,
*,
reasoning_effort: str | None = None,
local_preview: bool = False,
model_parse_source: str = "cloud_model",
) -> tuple[dict[str, Any], str, str | None]:
vehicle_profile = vehicle_profile or self.vehicle_profiles.default()
clause_results = self._local_clause_results(utterance, context)
trusted_local = self._merge_parsed_results(
[parsed for _, parsed, status in clause_results if status != "unparsed"]
)
preview_local = self._trusted_local_preview(clause_results)
all_local = self._merge_parsed_results(
[parsed for _, parsed, _ in clause_results]
)
@@ -4474,7 +4964,13 @@ class SeatDemoService:
safe_fallback = trusted_local if trusted_controls else all_local
if provider == "local" or not fallback_clauses:
return (
all_local if provider == "local" else trusted_local,
(
preview_local
if provider == "local" and local_preview
else all_local
if provider == "local"
else trusted_local
),
"local_rules",
None,
)
@@ -4505,6 +5001,11 @@ class SeatDemoService:
vehicle_profile,
utterance=fallback_utterance,
)
model = self._mark_control_parse_source(
model,
model_parse_source,
overwrite=True,
)
return (
self._merge_local_and_model_intent(trusted_local, model),
f"local_rules+openai:{self.openai.model}",