fix: harden conversational mode draft revisions

This commit is contained in:
vee
2026-09-08 00:55:45 +08:00
parent f0a248ede3
commit 5e228e12ae
16 changed files with 1862 additions and 23 deletions
@@ -983,6 +983,22 @@ class CodexAssistantService:
if draft.get("existing_mode_id")
else "确认后可保存为功能模式。"
)
requested_operation_count = draft.get("requested_operation_count")
changed_operation_count = draft.get("changed_operation_count")
no_effective_change = (
isinstance(requested_operation_count, int)
and not isinstance(requested_operation_count, bool)
and requested_operation_count > 0
and isinstance(changed_operation_count, int)
and not isinstance(changed_operation_count, bool)
and changed_operation_count == 0
)
if no_effective_change:
return (
f"待保存的“{name}”模式中,该项原本已是目标状态,"
f"已保持不变并重新校验全部{operation_count}项组合动作。"
f"原草稿未被保存或执行,可以继续调整;{persistence_hint}"
)
return (
f"已调整待保存的“{name}”模式,并重新校验全部"
f"{operation_count}项组合动作。原草稿未被保存或执行,"
@@ -1839,6 +1855,8 @@ class CodexAssistantService:
"generation_source",
"draft_revision",
"revises_source_request_id",
"requested_operation_count",
"changed_operation_count",
)
if key in mode_profile_draft
}
@@ -29,7 +29,7 @@ _CONTEXTUAL_NAME_FIRST_UPDATE = re.compile(
r"(?P<change>.+?)[。.!!]?$"
)
MODE_PROFILE_CHANGE_PREDICATE = re.compile(
r"修改|改成|改为|更改|更新|变更|变为|换成|"
r"修改|改成|改为|更改|更新|变更|变成|变为|换成|"
r"调整|调节|调高|调低|调到|调成|调为|调至|"
r"设置|设为|设成|设到|设至|"
r"打开|开启|启动|关闭|关掉|关上|全关|停止|停用|切换|进入|退出|"
@@ -40,7 +40,7 @@ MODE_PROFILE_QUERY_CUE = re.compile(
r"什么|哪些|多少|几(?:度|档|级|个|项)?|是否|是不是|"
r"合适(?:吗)?|合理(?:吗)?|会(?:怎么样|怎样|如何)|"
r"怎么|如何|列(?:出|一下)|介绍(?:一下)?|说明(?:一下)?|"
r"告诉我|查询|查看|有什么作用|是何作用|还是|[??]"
r"告诉我|查询|查看|有什么作用|是何作用|还是|[??]|(?:吗|么|呢)$"
)
@@ -1349,7 +1349,8 @@ def _parse_chinese_commands(text: str) -> list[dict[str, Any]]:
shade_text = _scoped_text(compact, r"遮阳帘|天幕帘")
if shade_text and _allows_vehicle_wide_target(shade_text):
position = re.search(
r"(?:位置|开度|调整到|调到|设为|设置为|开到|打开(?:到)?|开启(?:到)?)(\d{1,3})%",
r"(?:位置|开度|调整到|调整为|调到|调为|改为|改成|变为|变成|"
r"换成|设为|设置为|开到|打开(?:到)?|开启(?:到)?)(\d{1,3})%",
shade_text,
)
if position and int(position[1]) <= 100:
@@ -4309,7 +4309,7 @@ class SeatDemoService:
if not change_text:
raise ValueError("请说明要调整的设备和明确目标值。")
parseable_change_text = re.sub(
r"改为|改成|变更为|换成|变为",
r"改为|改成|变更为|换成|变成|变为",
"调整为",
change_text,
)
@@ -4387,6 +4387,22 @@ class SeatDemoService:
): item
for item in normalized_changes
}
base_by_key = {
(
str(item["domain"]),
str(item["capability"]),
str(item["target_scope"]),
): item
for item in base_operations
}
requested_operation_count = len(change_by_key)
changed_operation_count = sum(
1
for key, item in change_by_key.items()
if key not in base_by_key
or dict(base_by_key[key].get("parameters") or {})
!= dict(item.get("parameters") or {})
)
merged: list[dict[str, Any]] = []
for operation in base_operations:
key = (
@@ -4397,7 +4413,7 @@ class SeatDemoService:
merged.append(deepcopy(change_by_key.pop(key, operation)))
merged.extend(deepcopy(list(change_by_key.values())))
return self._process_mode_profile_create(
revised_result = self._process_mode_profile_create(
utterance,
"local",
conversation_id,
@@ -4410,6 +4426,26 @@ class SeatDemoService:
assistant_plan=self._mode_profile_draft_plan(merged),
source_draft=source_draft,
)
revised_draft = revised_result.get("mode_profile_draft")
if (
revised_result.get("status") == "succeeded"
and isinstance(revised_draft, dict)
and revised_draft.get("generation_source") == "draft_revision"
):
revised_draft["requested_operation_count"] = (
requested_operation_count
)
revised_draft["changed_operation_count"] = changed_operation_count
mode_creation = revised_result.get("mode_creation")
if isinstance(mode_creation, dict):
mode_creation["requested_operation_count"] = (
requested_operation_count
)
mode_creation["changed_operation_count"] = (
changed_operation_count
)
self._persist(revised_result)
return revised_result
except ValueError as exc:
result["status"] = "needs_confirmation"
result["execution_mode"] = "dry_run"
@@ -7180,7 +7216,21 @@ class SeatDemoService:
allow_scene_composition: bool = False,
) -> tuple[dict[str, Any], str, str | None]:
vehicle_profile = vehicle_profile or self.vehicle_profiles.default()
clause_results = self._local_clause_results(utterance, context)
if assistant_plan is not None and allow_scene_composition:
# ``utterance`` is a synthesized ``进入<mode name>`` carrier for a
# name-only scene (and for a server-owned draft revision). Words
# inside that label are not user-authored atomic instructions:
# e.g. ``外循环通风模式`` previously leaked ``通风`` into the seat
# parser and added an AMBIGUOUS_INTENT beside an otherwise valid
# climate plan. In this narrow path the complete assistant plan is
# the candidate source of actions; it still passes the scene
# allowlist, registered capability contract, ModelPlanAdapter,
# schema/safety validation and native compiler below.
clause_results = [
(utterance, self._merge_parsed_results([]), "unparsed")
]
else:
clause_results = self._local_clause_results(utterance, context)
trusted_local = self._merge_parsed_results(
[parsed for _, parsed, status in clause_results if status != "unparsed"]