feat: tolerate recoverable seat intent inputs

This commit is contained in:
vee
2026-08-02 06:12:36 +08:00
parent 55d0d400cb
commit a6d974cb29
9 changed files with 942 additions and 31 deletions
+58 -17
View File
@@ -2,6 +2,7 @@
from __future__ import annotations
import math
import re
import unicodedata
from copy import deepcopy
@@ -20,7 +21,7 @@ STATE_PATTERN = re.compile(
LEVEL_PATTERN = re.compile(r"(最高|最低|[1-5一二三四五高中低])\s*档")
POSITION_PATTERN_TEMPLATE = (
r"{alias}(?:位置)?(?:调到|移动到|移到|设为|设置为|调至|移动至|到|为)?"
r"(?P<position>\d{{1,3}})%"
r"(?P<position>[+-]?\d{{1,3}}(?:\.\d+)?)%"
)
ANGLE_PATTERN_TEMPLATE = (
r"{alias}(?:夹角|角度)?(?:调到|放倒到|抬到|移动到|移到|设为|设置为|"
@@ -300,6 +301,16 @@ def _format_seconds(duration_ms: int) -> str:
return f"{seconds:g}"
def _round_half_away_from_zero(value: float) -> int:
"""Use the integer a user normally expects from a .5 position request."""
return math.floor(value + 0.5) if value >= 0 else math.ceil(value - 0.5)
def _format_numeric_request(value: float) -> str:
return f"{value:g}"
def _timed_motion_action(
axis: str,
direction_token: str,
@@ -309,8 +320,8 @@ def _timed_motion_action(
operation = _relative_direction(axis, direction_token)
if operation is None:
return None, f"{direction_token}不适用于该座椅轴,请确认移动方向。"
if requested_duration_ms <= 0:
return None, "座椅按时长移动的时长必须大于0。"
if requested_duration_ms < 0:
return None, "座椅按时长移动的时长不能为负数。"
state = _context_motion_state(context, axis)
if state is None:
return None, "缺少当前座椅位置,无法安全计算按时长移动的方向和备用端点。"
@@ -894,12 +905,18 @@ def parse_local_intent(
for axis, aliases in MOTION_AXIS_ALIASES.items()
if any(alias in clause for alias in aliases)
]
percentages = list(re.finditer(r"(?P<position>\d{1,3})%", clause))
percentages = list(
re.finditer(r"(?P<position>[+-]?\d{1,3}(?:\.\d+)?)%", clause)
)
shared_position = None
if len(clause_axes) > 1 and len(percentages) == 1 and re.search(
r"都|全部|均", clause
):
shared_position = int(percentages[0].group("position"))
requested_shared_value = float(percentages[0].group("position"))
rounded_shared_position = _round_half_away_from_zero(
requested_shared_value
)
shared_position = max(0, min(100, rounded_shared_position))
target_zh = (
_nearest_preceding_target(clause, percentages[0].start()) or "副驾"
)
@@ -908,8 +925,6 @@ def parse_local_intent(
{"field": "target", "question": PASSENGER_ONLY_QUESTION}
)
else:
requested_shared_position = shared_position
shared_position = min(100, shared_position)
motion_actions.extend(
{
"target": "passenger",
@@ -920,11 +935,22 @@ def parse_local_intent(
{
"user_notice": (
f"{MOTION_AXIS_LABELS[axis]}请求位置"
f"{requested_shared_position}%超过最大行程;"
"已采用备用目标并移动到对应机械末端。"
f"{_format_numeric_request(requested_shared_value)}%;"
+ (
f"已取整为{rounded_shared_position}%。"
if requested_shared_value
!= rounded_shared_position
else ""
)
+ (
f" 取整后超过0到100%行程,已采用"
f"{shared_position}%备用目标并移动到对应机械末端。"
if rounded_shared_position != shared_position
else ""
)
)
}
if requested_shared_position > 100
if requested_shared_value != shared_position
else {}
),
}
@@ -1104,8 +1130,13 @@ def parse_local_intent(
break
absolute_angle = None
if position_match is not None:
requested_position_pct = int(position_match.group("position"))
position_pct = min(100, requested_position_pct)
requested_position_value = float(
position_match.group("position")
)
requested_position_pct = _round_half_away_from_zero(
requested_position_value
)
position_pct = max(0, min(100, requested_position_pct))
else:
requested_angle = int(angle_match.group("angle"))
minimum, maximum = MOTION_ANGLE_RANGES[axis]
@@ -1133,11 +1164,21 @@ def parse_local_intent(
f"{minimum}到{maximum}度行程;已采用备用目标"
f"{absolute_angle}度并移动到{endpoint_label}。"
)
elif requested_position_pct != position_pct:
motion_action["user_notice"] = (
f"{MOTION_AXIS_LABELS[axis]}请求位置{requested_position_pct}%超过"
"0到100%行程;已采用100%的备用目标并移动到机械末端。"
)
else:
notices: list[str] = []
if requested_position_value != requested_position_pct:
notices.append(
f"{MOTION_AXIS_LABELS[axis]}请求位置"
f"{_format_numeric_request(requested_position_value)}%;"
f"已取整为{requested_position_pct}%。"
)
if requested_position_pct != position_pct:
notices.append(
f"取整后超过0到100%行程;已采用{position_pct}%的备用目标"
"并移动到对应机械末端。"
)
if notices:
motion_action["user_notice"] = " ".join(notices)
motion_actions.append(motion_action)
break