feat: integrate production cockpit AI assistant
This commit is contained in:
@@ -18,19 +18,18 @@ from typing import Any
|
||||
|
||||
import yaml
|
||||
|
||||
|
||||
REPOSITORY_ROOT = Path(__file__).resolve().parents[3]
|
||||
SEAT_SOURCE = REPOSITORY_ROOT / "domains" / "seat" / "src"
|
||||
if str(SEAT_SOURCE) not in sys.path:
|
||||
sys.path.insert(0, str(SEAT_SOURCE))
|
||||
|
||||
from seat_domain import ( # noqa: E402
|
||||
HardwareSeatExecutor,
|
||||
SIMULATED_HARDWARE_FAULTS,
|
||||
HardwareSeatExecutor,
|
||||
SeatCanMatrix,
|
||||
SeatCompiler,
|
||||
T2GSeatCompiler,
|
||||
SimulatedHardwareSeatExecutor,
|
||||
T2GSeatCompiler,
|
||||
VirtualSeatExecutor,
|
||||
build_function_config,
|
||||
build_function_preset,
|
||||
@@ -44,41 +43,32 @@ from cognitive_agent.application.assistant_plan_guard import ( # noqa: E402
|
||||
)
|
||||
from cognitive_agent.application.capability_registry import CapabilityRegistry # noqa: E402
|
||||
from cognitive_agent.application.conversation_state import ConversationStateStore # noqa: E402
|
||||
from cognitive_agent.application.direct_can_control import ( # noqa: E402
|
||||
build_direct_can_intent,
|
||||
)
|
||||
from cognitive_agent.application.domain_runtime import ConfiguredDomainRuntime # noqa: E402
|
||||
from cognitive_agent.application.mode_profile import ( # noqa: E402
|
||||
ModeProfile,
|
||||
ModeProfileRegistry,
|
||||
)
|
||||
from cognitive_agent.application.mode_profile_intent import ( # noqa: E402
|
||||
is_mode_profile_query,
|
||||
normalize_mode_profile_name,
|
||||
parse_mode_profile_create,
|
||||
parse_mode_profile_delete,
|
||||
parse_mode_profile_update,
|
||||
)
|
||||
from cognitive_agent.application.model_plan_adapter import ( # noqa: E402
|
||||
ModelPlanAdapter,
|
||||
APPLY_PRESET_CAPABILITY,
|
||||
DYNAMIC_MODE_STOP_CAPABILITY,
|
||||
SAVE_PRESET_CAPABILITY,
|
||||
SEAT_CAPABILITIES,
|
||||
SEAT_MOTION_CAPABILITIES,
|
||||
ModelPlanAdapter,
|
||||
)
|
||||
from cognitive_agent.application.mqtt_ota_seat_executor import ( # noqa: E402
|
||||
MqttNativeSeatExecutor,
|
||||
)
|
||||
from cognitive_agent.application.native_can_intent import ( # noqa: E402
|
||||
DRIVER_SEAT_MASSAGE_CONTINUATION_PATTERN,
|
||||
merge_native_can_intent,
|
||||
)
|
||||
from cognitive_agent.application.direct_can_control import ( # noqa: E402
|
||||
build_direct_can_intent,
|
||||
)
|
||||
from cognitive_agent.application.native_can_relative import ( # noqa: E402
|
||||
NativeCanRelativeResolver,
|
||||
)
|
||||
from cognitive_agent.application.semantic_ir import ( # noqa: E402
|
||||
apply_language_safety_guards,
|
||||
resolve_explicit_seat_scopes,
|
||||
)
|
||||
from cognitive_agent.application.mode_profile_intent import ( # noqa: E402
|
||||
is_mode_profile_query,
|
||||
normalize_mode_profile_name,
|
||||
parse_mode_profile_update,
|
||||
)
|
||||
from cognitive_agent.application.native_can_feedback import ( # noqa: E402
|
||||
VehicleSafetyPreflightError,
|
||||
authoritative_physical_ch1_feedback_state,
|
||||
@@ -86,13 +76,25 @@ from cognitive_agent.application.native_can_feedback import ( # noqa: E402
|
||||
observe_vehicle_safety,
|
||||
require_stationary_vehicle,
|
||||
)
|
||||
from cognitive_agent.application.native_can_intent import ( # noqa: E402
|
||||
DRIVER_SEAT_MASSAGE_CONTINUATION_PATTERN,
|
||||
merge_native_can_intent,
|
||||
)
|
||||
from cognitive_agent.application.native_can_relative import ( # noqa: E402
|
||||
NativeCanRelativeResolver,
|
||||
)
|
||||
from cognitive_agent.application.native_can_runtime import ( # noqa: E402
|
||||
UNVERIFIED_NATIVE_CAPABILITIES,
|
||||
NativeCanCompileError,
|
||||
NativeCanExecutor,
|
||||
NativeCanRuntime,
|
||||
UNVERIFIED_NATIVE_CAPABILITIES,
|
||||
)
|
||||
from cognitive_agent.application.scene_templates import SceneTemplateRegistry # noqa: E402
|
||||
from cognitive_agent.application.seat_calibration import position_pct_to_angle # noqa: E402
|
||||
from cognitive_agent.application.semantic_ir import ( # noqa: E402
|
||||
apply_language_safety_guards,
|
||||
resolve_explicit_seat_scopes,
|
||||
)
|
||||
from cognitive_agent.application.vehicle_profile import ( # noqa: E402
|
||||
VehicleProfile,
|
||||
VehicleProfileRegistry,
|
||||
@@ -106,27 +108,27 @@ from cognitive_agent.application.vehicle_state_monitor import ( # noqa: E402
|
||||
from cognitive_agent.application.zcan_seat_executor import ( # noqa: E402
|
||||
ZcanBridgeSeatExecutor,
|
||||
)
|
||||
from cognitive_agent.infrastructure.llm.openai_responses import ( # noqa: E402
|
||||
OpenAIIntentError,
|
||||
OpenAIIntentInterpreter,
|
||||
REASONING_EFFORTS,
|
||||
)
|
||||
from cognitive_agent.infrastructure.can.hardware import ( # noqa: E402
|
||||
HardwareSafetyError,
|
||||
PHYSICAL_BENCH_COORDINATOR,
|
||||
SeatHardwareSettings,
|
||||
VehicleCanSettings,
|
||||
VehicleMqttSettings,
|
||||
from cognitive_agent.infrastructure.can.access import ( # noqa: E402
|
||||
PHYSICAL_BENCH_ACCESS,
|
||||
)
|
||||
from cognitive_agent.infrastructure.can.gateway import ( # noqa: E402
|
||||
ZlgCanBridgeProcess,
|
||||
ZlgChannelConfig,
|
||||
)
|
||||
from cognitive_agent.infrastructure.can.hardware import ( # noqa: E402
|
||||
PHYSICAL_BENCH_COORDINATOR,
|
||||
HardwareSafetyError,
|
||||
SeatHardwareSettings,
|
||||
VehicleCanSettings,
|
||||
VehicleMqttSettings,
|
||||
)
|
||||
from cognitive_agent.infrastructure.can.mqtt_transport import ( # noqa: E402
|
||||
MqttCanTransport,
|
||||
)
|
||||
from cognitive_agent.infrastructure.can.access import ( # noqa: E402
|
||||
PHYSICAL_BENCH_ACCESS,
|
||||
from cognitive_agent.infrastructure.llm.openai_responses import ( # noqa: E402
|
||||
REASONING_EFFORTS,
|
||||
OpenAIIntentError,
|
||||
OpenAIIntentInterpreter,
|
||||
)
|
||||
from cognitive_agent.runtime.contracts import ContractValidator # noqa: E402
|
||||
|
||||
@@ -154,6 +156,13 @@ PASSENGER_SEAT_SCOPE_PATTERN = re.compile(
|
||||
r"\bpassenger\b|\bfront[-_ ]?right\b|\bright[-_ ]?front\b|副驾|乘客|乘员|右前",
|
||||
re.IGNORECASE,
|
||||
)
|
||||
MODE_PROFILE_UPDATE_EXPLICIT_TARGET_PATTERN = re.compile(
|
||||
r"\b(?:vehicle|driver|passenger|front[-_ ]?(?:left|right)|rear)\b|"
|
||||
r"主驾|主驾驶|驾驶位|驾驶员|驾驶席|司机位|副驾|副驾驶|乘客|乘员|"
|
||||
r"全车|整车|所有|全部|前排|后排|二排|三排|左前|右前|左后|右后|"
|
||||
r"主驾左|主驾右|副驾左|副驾右",
|
||||
re.IGNORECASE,
|
||||
)
|
||||
WIRED_CAN_TRANSPORT = "wired_can"
|
||||
WIRELESS_MQTT_TRANSPORT = "wireless_mqtt"
|
||||
VEHICLE_CONTROL_TRANSPORTS = frozenset(
|
||||
@@ -269,18 +278,6 @@ SAVE_MODE_PROFILE_NAME = re.compile(
|
||||
r"(?:固化为|保存为长期|保存成长期|长期保存为|记为长期|命名为)"
|
||||
r"(?P<name>[^,。,.!!\s]{1,64}?模式)"
|
||||
)
|
||||
CREATE_MODE_PROFILE_NAME = re.compile(
|
||||
r"^\s*(?:(?:请|麻烦)\s*(?:帮我|帮忙|给我)?\s*|"
|
||||
r"(?:帮我|帮忙|给我)\s*)?"
|
||||
r"(?:构建|创建|新建|新增|生成|添加)\s*(?:一下)?\s*"
|
||||
r"(?:一个|个|一套|套)?\s*"
|
||||
r"(?:名为|叫做|叫)?\s*[“\"'「『]?"
|
||||
r"(?P<name>[^,。,.!!::\s“”\"'「」『』]{1,64}?模式)"
|
||||
r"[”\"'」』]?"
|
||||
r"\s*(?:配置)?(?:并(?:添加|加入)(?:到)?功能模式(?:栏)?)?"
|
||||
r"(?:[,,::]\s*(?P<instruction>[\s\S]{1,800}))?[。.!!]?\s*$"
|
||||
)
|
||||
|
||||
# ``、`` is used both as a command separator (``车窗关闭、空调24度``)
|
||||
# and as an enumeration separator (``主驾、副驾车窗关闭``). Treat it as a
|
||||
# boundary only when both sides already look like self-contained controls;
|
||||
@@ -417,6 +414,9 @@ class SeatDemoService:
|
||||
repository_root / "config" / "modes",
|
||||
user_root=self.mode_profile_user_root,
|
||||
)
|
||||
self.scene_templates = SceneTemplateRegistry(
|
||||
repository_root / "config" / "scene_templates"
|
||||
)
|
||||
self._artifact_root = repository_root / "artifacts" / "runs"
|
||||
self.conversations = ConversationStateStore(
|
||||
preset_root=repository_root / "artifacts" / "user-presets"
|
||||
@@ -585,6 +585,7 @@ class SeatDemoService:
|
||||
"vehicle": default_vehicle.public_dict(),
|
||||
"vehicles": self.vehicle_profiles.public_profiles(),
|
||||
"modes": self.mode_profiles.public_modes(default_vehicle.id),
|
||||
"scene_templates": self.scene_templates.public_templates(),
|
||||
"mode_profile_storage": "persistent_yaml",
|
||||
"mode_profile_user_root": str(self.mode_profile_user_root),
|
||||
"preset_storage": "conversation_durable",
|
||||
@@ -2063,19 +2064,15 @@ class SeatDemoService:
|
||||
utterance.strip(),
|
||||
expected_mode_name=draft_reference_name or None,
|
||||
)
|
||||
mode_delete_name = parse_mode_profile_delete(utterance.strip())
|
||||
mode_profile_query = is_mode_profile_query(
|
||||
utterance,
|
||||
expected_mode_name=draft_reference_name or None,
|
||||
)
|
||||
draft_update_requested = bool(
|
||||
draft_update is not None
|
||||
and draft_reference_name
|
||||
and normalize_mode_profile_name(draft_update[0])
|
||||
== normalize_mode_profile_name(draft_reference_name)
|
||||
)
|
||||
if (
|
||||
self._parse_mode_profile_create_name(utterance.strip()) is not None
|
||||
or draft_update_requested
|
||||
or draft_update is not None
|
||||
or mode_delete_name is not None
|
||||
or mode_profile_query
|
||||
):
|
||||
execution_mode = "dry_run"
|
||||
@@ -2231,6 +2228,7 @@ class SeatDemoService:
|
||||
mode_save_name = self._parse_mode_profile_save_name(utterance)
|
||||
mode_create_name = self._parse_mode_profile_create_name(utterance)
|
||||
mode_restore_name = self._parse_mode_profile_restore_name(utterance)
|
||||
mode_delete_name = parse_mode_profile_delete(utterance)
|
||||
draft_reference_name = (
|
||||
str(mode_profile_draft_context.get("name") or "").strip()
|
||||
if isinstance(mode_profile_draft_context, dict)
|
||||
@@ -2247,6 +2245,7 @@ class SeatDemoService:
|
||||
control_plane_request = (
|
||||
mode_save_name is not None
|
||||
or mode_restore_name is not None
|
||||
or mode_delete_name is not None
|
||||
or mode_update is not None
|
||||
or mode_profile_query
|
||||
)
|
||||
@@ -2383,6 +2382,16 @@ class SeatDemoService:
|
||||
]
|
||||
self._persist(result)
|
||||
return result
|
||||
if mode_delete_name is not None:
|
||||
return self._process_mode_profile_delete(
|
||||
utterance,
|
||||
request_id,
|
||||
conversation_id,
|
||||
mode_delete_name,
|
||||
vehicle_profile,
|
||||
result,
|
||||
traces,
|
||||
)
|
||||
if mode_save_name is not None:
|
||||
return self._process_mode_profile_save(
|
||||
utterance,
|
||||
@@ -3619,20 +3628,26 @@ class SeatDemoService:
|
||||
"""Identify mode configuration text that must never reach live executors."""
|
||||
|
||||
clean_utterance = str(utterance).strip()
|
||||
if is_mode_profile_query(clean_utterance):
|
||||
# Named-mode questions may contain words such as “控制/执行” and
|
||||
# concrete device names. They are read-only control-plane turns,
|
||||
# so learned-peripheral and live CAN routers must not claim them.
|
||||
return True
|
||||
return any(
|
||||
matcher(clean_utterance) is not None
|
||||
for matcher in (
|
||||
cls._parse_mode_profile_save_name,
|
||||
cls._parse_mode_profile_create_name,
|
||||
cls._parse_mode_profile_restore_name,
|
||||
parse_mode_profile_delete,
|
||||
cls._parse_mode_profile_update,
|
||||
)
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _parse_mode_profile_create_name(utterance: str) -> str | None:
|
||||
explicit = CREATE_MODE_PROFILE_NAME.search(utterance)
|
||||
return explicit.group("name").strip() if explicit is not None else None
|
||||
explicit = parse_mode_profile_create(utterance)
|
||||
return explicit[0] if explicit is not None else None
|
||||
|
||||
@staticmethod
|
||||
def _parse_mode_profile_update(utterance: str) -> tuple[str, str] | None:
|
||||
@@ -4472,6 +4487,146 @@ class SeatDemoService:
|
||||
self._persist(result)
|
||||
return result
|
||||
|
||||
def _process_mode_profile_delete(
|
||||
self,
|
||||
utterance: str,
|
||||
request_id: str,
|
||||
conversation_id: str,
|
||||
mode_name: str,
|
||||
vehicle_profile: VehicleProfile,
|
||||
result: dict[str, Any],
|
||||
traces: list[dict[str, Any]],
|
||||
) -> dict[str, Any]:
|
||||
"""Delete one exact user-owned mode without entering a vehicle executor."""
|
||||
|
||||
started = time.perf_counter()
|
||||
result["execution_mode"] = "dry_run"
|
||||
result["hardware_execution_allowed"] = False
|
||||
result["orchestration_route"] = "mode_profile_delete"
|
||||
result["provider_used"] = "mode_profile_registry"
|
||||
result["decision_summary"] = [
|
||||
f"识别为自定义模式删除请求,目标模式为“{mode_name}”。"
|
||||
]
|
||||
self._trace(
|
||||
traces,
|
||||
request_id,
|
||||
"intent_parse",
|
||||
"succeeded",
|
||||
"识别到功能模式删除请求;本次不调用模型、CAN 编译或硬件路径。",
|
||||
started,
|
||||
{"mode_name": mode_name},
|
||||
domain="mode",
|
||||
)
|
||||
|
||||
profile = self._resolve_single_mode_profile(
|
||||
mode_name,
|
||||
vehicle_profile,
|
||||
result,
|
||||
traces,
|
||||
request_id,
|
||||
)
|
||||
if profile is None:
|
||||
self._persist(result)
|
||||
return result
|
||||
if not profile.user_defined:
|
||||
result["status"] = "rejected"
|
||||
result["validation"] = [
|
||||
{
|
||||
"severity": "error",
|
||||
"code": "MODE_PROFILE_DELETE_PROTECTED",
|
||||
"path": "utterance.mode_profile",
|
||||
"message": (
|
||||
f"“{profile.name}”是工程预设模式,不能删除;"
|
||||
"仅可删除用户创建或调整后形成的自定义模式。"
|
||||
),
|
||||
}
|
||||
]
|
||||
result["decision_summary"].append(
|
||||
f"未删除工程预设模式“{profile.name}”。"
|
||||
)
|
||||
self._trace(
|
||||
traces,
|
||||
request_id,
|
||||
"execute",
|
||||
"failed",
|
||||
result["validation"][0]["message"],
|
||||
time.perf_counter(),
|
||||
{"mode_profile_id": profile.id, "can_command_emitted": False},
|
||||
domain="mode",
|
||||
)
|
||||
self._persist(result)
|
||||
return result
|
||||
|
||||
try:
|
||||
deleted = self.mode_profiles.delete_user_mode(
|
||||
profile.id,
|
||||
vehicle_profile.id,
|
||||
)
|
||||
except KeyError:
|
||||
# A concurrent delete is reported as not found instead of leaking
|
||||
# an exception into the assistant's generic vehicle-failure text.
|
||||
result["status"] = "needs_confirmation"
|
||||
result["validation"] = [
|
||||
{
|
||||
"severity": "error",
|
||||
"code": "MODE_PROFILE_NOT_FOUND",
|
||||
"path": "utterance.mode_profile",
|
||||
"message": f"未找到可删除的自定义模式“{mode_name}”。",
|
||||
}
|
||||
]
|
||||
self._persist(result)
|
||||
return result
|
||||
|
||||
deleted_public = deleted.public_dict()
|
||||
result["deleted_mode_profile"] = deleted_public
|
||||
result["matched_modes"] = []
|
||||
result["decision_summary"].append(
|
||||
f"已删除自定义模式“{deleted.name}”;未生成或发送任何 CAN 帧。"
|
||||
)
|
||||
execution_report = self._mode_profile_control_report(
|
||||
request_id,
|
||||
"delete_mode_profile",
|
||||
deleted,
|
||||
f"已删除自定义模式“{deleted.name}”。",
|
||||
{"deleted": True, "can_command_emitted": False},
|
||||
)
|
||||
validation = self.contracts.validate(execution_report)
|
||||
result["validation"] = validation
|
||||
result["execution_report"] = execution_report
|
||||
if validation:
|
||||
result["status"] = "execution_report_invalid"
|
||||
self._persist(result)
|
||||
return result
|
||||
|
||||
updated_context = self.conversations.record_mode_profile_event(
|
||||
conversation_id,
|
||||
utterance,
|
||||
deleted_public,
|
||||
[],
|
||||
result["decision_summary"],
|
||||
"mode_profile_delete",
|
||||
)
|
||||
result["status"] = "succeeded"
|
||||
result["conversation_turn"] = updated_context["turn_count"]
|
||||
result["actuator_state"] = updated_context["devices"]
|
||||
result["domain_state"] = updated_context["domain_devices"]
|
||||
self._trace(
|
||||
traces,
|
||||
request_id,
|
||||
"execute",
|
||||
"succeeded",
|
||||
"自定义模式删除属于控制平面操作,未生成或发送 CAN 帧。",
|
||||
time.perf_counter(),
|
||||
{
|
||||
"mode": "control_plane",
|
||||
"mode_profile_id": deleted.id,
|
||||
"can_command_emitted": False,
|
||||
},
|
||||
domain="mode",
|
||||
)
|
||||
self._persist(result)
|
||||
return result
|
||||
|
||||
def _process_mode_profile_update(
|
||||
self,
|
||||
utterance: str,
|
||||
@@ -4486,6 +4641,9 @@ class SeatDemoService:
|
||||
traces: list[dict[str, Any]],
|
||||
) -> dict[str, Any]:
|
||||
started = time.perf_counter()
|
||||
result["execution_mode"] = "dry_run"
|
||||
result["hardware_execution_allowed"] = False
|
||||
result["orchestration_route"] = "mode_profile_update"
|
||||
result["provider_used"] = "mode_profile_registry"
|
||||
result["decision_summary"] = [
|
||||
f"识别为长期模式修改请求,目标模式为“{mode_name}”。"
|
||||
@@ -4530,6 +4688,8 @@ class SeatDemoService:
|
||||
context,
|
||||
vehicle_profile,
|
||||
)
|
||||
parsed = apply_language_safety_guards(parsed, change_text)
|
||||
parsed = self._partition_native_actions(parsed)
|
||||
result["provider_used"] = f"mode_profile_registry+{provider_used}"
|
||||
result["decision_summary"].extend(parsed.get("decision_summary", []))
|
||||
if provider_notice:
|
||||
@@ -4540,8 +4700,30 @@ class SeatDemoService:
|
||||
source_actions = [
|
||||
*parsed.get("actions", []),
|
||||
*parsed.get("domain_actions", []),
|
||||
*parsed.get("native_can_actions", []),
|
||||
]
|
||||
mode_operations = self._mode_operations_from_actions(source_actions)
|
||||
mode_operations, ambiguous_targets = self._inherit_mode_update_targets(
|
||||
profile,
|
||||
mode_operations,
|
||||
change_text,
|
||||
)
|
||||
if ambiguous_targets:
|
||||
result["validation"] = [
|
||||
{
|
||||
"severity": "error",
|
||||
"code": "MODE_PROFILE_UPDATE_TARGET_AMBIGUOUS",
|
||||
"path": "utterance.target_scope",
|
||||
"message": (
|
||||
"模式中同一能力包含多个区域,请明确要修改的区域:"
|
||||
+ "、".join(ambiguous_targets)
|
||||
+ "。"
|
||||
),
|
||||
}
|
||||
]
|
||||
result["status"] = "needs_confirmation"
|
||||
self._persist(result)
|
||||
return result
|
||||
if not mode_operations:
|
||||
validation = [
|
||||
{
|
||||
@@ -4569,8 +4751,12 @@ class SeatDemoService:
|
||||
self._persist(result)
|
||||
return result
|
||||
|
||||
result["mode_profile_changes"] = deepcopy(mode_operations)
|
||||
updated_profile = self.mode_profiles.update_mode(profile, mode_operations)
|
||||
profile_public = updated_profile.public_dict()
|
||||
if not profile.user_defined and updated_profile.user_defined:
|
||||
profile_public["origin"] = "customized"
|
||||
profile_public["preset_override"] = True
|
||||
source_path = self._display_path(Path(updated_profile.source))
|
||||
backup_path = self._display_path(
|
||||
Path(updated_profile.source).with_suffix(".yaml.initial.bak")
|
||||
@@ -4669,6 +4855,34 @@ class SeatDemoService:
|
||||
self._persist(result)
|
||||
return result
|
||||
|
||||
@staticmethod
|
||||
def _inherit_mode_update_targets(
|
||||
profile: ModeProfile,
|
||||
operations: list[dict[str, Any]],
|
||||
change_text: str,
|
||||
) -> tuple[list[dict[str, Any]], list[str]]:
|
||||
"""Bind unscoped edits to the unique matching target already in a mode."""
|
||||
|
||||
if MODE_PROFILE_UPDATE_EXPLICIT_TARGET_PATTERN.search(change_text):
|
||||
return deepcopy(operations), []
|
||||
|
||||
aligned: list[dict[str, Any]] = []
|
||||
ambiguous: list[str] = []
|
||||
for raw_operation in operations:
|
||||
operation = deepcopy(raw_operation)
|
||||
candidates = [
|
||||
existing
|
||||
for existing in profile.operations
|
||||
if existing.domain == str(operation.get("domain") or "")
|
||||
and existing.capability == str(operation.get("capability") or "")
|
||||
]
|
||||
if len(candidates) == 1:
|
||||
operation["target_scope"] = candidates[0].target_scope
|
||||
elif len(candidates) > 1:
|
||||
ambiguous.append(str(operation.get("capability") or "设备动作"))
|
||||
aligned.append(operation)
|
||||
return aligned, list(dict.fromkeys(ambiguous))
|
||||
|
||||
def _process_mode_profile_restore(
|
||||
self,
|
||||
utterance: str,
|
||||
@@ -5144,12 +5358,47 @@ class SeatDemoService:
|
||||
the assistant can show the exact actions before the user saves them.
|
||||
"""
|
||||
|
||||
create_match = CREATE_MODE_PROFILE_NAME.fullmatch(utterance)
|
||||
requested_instruction = (
|
||||
str(create_match.group("instruction") or "").strip(" ,,::。.!!")
|
||||
if create_match is not None
|
||||
else ""
|
||||
create_parts = (
|
||||
parse_mode_profile_create(utterance) if source_draft is None else None
|
||||
)
|
||||
if source_draft is None and create_parts is None:
|
||||
raise ValueError("未识别到安全、明确的功能模式创建请求。")
|
||||
if (
|
||||
create_parts is not None
|
||||
and normalize_mode_profile_name(create_parts[0])
|
||||
!= normalize_mode_profile_name(mode_name)
|
||||
):
|
||||
raise ValueError("功能模式名称与创建请求不一致。")
|
||||
requested_instruction = create_parts[1] if create_parts is not None else ""
|
||||
raw_plan_metadata = (
|
||||
assistant_plan.get("metadata")
|
||||
if isinstance(assistant_plan, dict)
|
||||
else None
|
||||
)
|
||||
scene_template = None
|
||||
if (
|
||||
isinstance(raw_plan_metadata, dict)
|
||||
and raw_plan_metadata.get("source") == "scene_template"
|
||||
):
|
||||
template_id = str(raw_plan_metadata.get("template_id") or "").strip()
|
||||
template_revision = str(
|
||||
raw_plan_metadata.get("template_revision") or ""
|
||||
).strip()
|
||||
try:
|
||||
candidate_template = self.scene_templates.get_template(template_id)
|
||||
except KeyError as exc:
|
||||
raise ValueError("内置常用模式模板已不存在,请重新提交。") from exc
|
||||
exact_template = self.scene_templates.resolve_exact_name(mode_name)
|
||||
if (
|
||||
candidate_template.revision != template_revision
|
||||
or exact_template is None
|
||||
or exact_template.id != candidate_template.id
|
||||
or requested_instruction
|
||||
or assistant_plan.get("requests")
|
||||
!= candidate_template.plan().get("requests")
|
||||
):
|
||||
raise ValueError("内置常用模式模板已更新,请重新提交。")
|
||||
scene_template = candidate_template
|
||||
source_existing_mode_id = (
|
||||
str(source_draft.get("existing_mode_id") or "").strip()
|
||||
if isinstance(source_draft, dict)
|
||||
@@ -5170,6 +5419,8 @@ class SeatDemoService:
|
||||
mode_name,
|
||||
vehicle_id=vehicle_profile.id,
|
||||
)
|
||||
if scene_template is not None and existing_profile is not None:
|
||||
raise ValueError("同名功能模式已创建,请重新提交以载入最新模式。")
|
||||
source_profile = (
|
||||
existing_profile
|
||||
if not requested_instruction and source_draft is None
|
||||
@@ -5185,6 +5436,8 @@ class SeatDemoService:
|
||||
generation_source = (
|
||||
"draft_revision"
|
||||
if source_draft is not None
|
||||
else "scene_template_copy"
|
||||
if scene_template is not None
|
||||
else "registered_mode_profile"
|
||||
if source_profile is not None
|
||||
else "codex_scene_composition"
|
||||
@@ -5353,7 +5606,12 @@ class SeatDemoService:
|
||||
] if isinstance(source_actions, list) else []
|
||||
if (
|
||||
source_result.get("status") == "succeeded"
|
||||
and generation_source in {"codex_scene_composition", "draft_revision"}
|
||||
and generation_source
|
||||
in {
|
||||
"codex_scene_composition",
|
||||
"draft_revision",
|
||||
"scene_template_copy",
|
||||
}
|
||||
and assistant_plan is not None
|
||||
and (
|
||||
self._scene_plan_signatures(assistant_plan)
|
||||
@@ -5372,14 +5630,18 @@ class SeatDemoService:
|
||||
"code": "MODE_PROFILE_SCENE_PLAN_INCOMPLETE",
|
||||
"path": "intent_ir.spec.actions",
|
||||
"message": (
|
||||
"Codex 场景候选动作未全部进入当前车型的可复用执行链,"
|
||||
"小迪生成的场景候选动作未全部进入当前车型的可复用执行链,"
|
||||
"已拒绝生成部分功能模式。"
|
||||
),
|
||||
},
|
||||
]
|
||||
|
||||
if source_result.get("status") != "succeeded":
|
||||
if generation_source in {"codex_scene_composition", "draft_revision"}:
|
||||
if generation_source in {
|
||||
"codex_scene_composition",
|
||||
"draft_revision",
|
||||
"scene_template_copy",
|
||||
}:
|
||||
# Final fail-closed barrier for errors discovered after the
|
||||
# plan guard (adapter, schema, compiler, or safety validation).
|
||||
# A failed reusable scene must not expose a trial-able subset.
|
||||
@@ -5462,6 +5724,25 @@ class SeatDemoService:
|
||||
if existing_profile is not None
|
||||
else {}
|
||||
)
|
||||
source_template_binding = (
|
||||
{
|
||||
"source_template_id": scene_template.id,
|
||||
"source_template_revision": scene_template.revision,
|
||||
}
|
||||
if scene_template is not None
|
||||
else {
|
||||
"source_template_id": str(
|
||||
source_draft.get("source_template_id") or ""
|
||||
).strip(),
|
||||
"source_template_revision": str(
|
||||
source_draft.get("source_template_revision") or ""
|
||||
).strip(),
|
||||
}
|
||||
if isinstance(source_draft, dict)
|
||||
and source_draft.get("source_template_id")
|
||||
and source_draft.get("source_template_revision")
|
||||
else {}
|
||||
)
|
||||
raw_source_revision = (
|
||||
source_draft.get("draft_revision")
|
||||
if isinstance(source_draft, dict)
|
||||
@@ -5484,6 +5765,7 @@ class SeatDemoService:
|
||||
"domains": domains,
|
||||
"operation_count": len(reusable_actions),
|
||||
**existing_binding,
|
||||
**source_template_binding,
|
||||
"generation_source": generation_source,
|
||||
"draft_revision": source_revision + 1,
|
||||
**(
|
||||
@@ -5504,6 +5786,7 @@ class SeatDemoService:
|
||||
"source_provider": source_result.get("provider_used"),
|
||||
"operation_count": len(reusable_actions),
|
||||
**existing_binding,
|
||||
**source_template_binding,
|
||||
"generation_source": generation_source,
|
||||
"draft_revision": source_revision + 1,
|
||||
}
|
||||
@@ -5513,7 +5796,10 @@ class SeatDemoService:
|
||||
f"已从当前车型的工程模式目录载入并验证“{source_profile.name}”的"
|
||||
f" {len(reusable_actions)} 项设备动作。"
|
||||
if source_profile is not None
|
||||
else f"Codex 已根据场景语义组合并验证“{draft_mode_name}”的工程能力。"
|
||||
else f"已从内置常用模式模板载入并验证“{draft_mode_name}”的"
|
||||
f" {len(reusable_actions)} 项设备动作。"
|
||||
if generation_source == "scene_template_copy"
|
||||
else f"小迪已根据场景语义组合并验证“{draft_mode_name}”的工程能力。"
|
||||
if generation_source == "codex_scene_composition"
|
||||
else f"已调整并重新校验待保存的“{draft_mode_name}”模式草稿。"
|
||||
if generation_source == "draft_revision"
|
||||
@@ -5719,7 +6005,7 @@ class SeatDemoService:
|
||||
action: dict[str, Any],
|
||||
) -> dict[str, Any] | None:
|
||||
domain = action.get("domain")
|
||||
if domain in self.domain_runtimes:
|
||||
if domain in self.domain_runtimes or self.native_can_runtime.supports(action):
|
||||
return {
|
||||
"domain": domain,
|
||||
"capability": action["capability"],
|
||||
@@ -7304,7 +7590,7 @@ class SeatDemoService:
|
||||
),
|
||||
"operation": str(deferred.get("desired_change") or "set"),
|
||||
"parameters": dict(deferred.get("parameters") or {}),
|
||||
"reason": "Codex 场景候选动作已通过工程能力契约校验。",
|
||||
"reason": "小迪生成的场景候选动作已通过工程能力契约校验。",
|
||||
"execution_backend": "native_can",
|
||||
}
|
||||
if self.native_can_runtime.supports(candidate):
|
||||
@@ -7349,7 +7635,7 @@ class SeatDemoService:
|
||||
return (
|
||||
self._merge_local_and_model_intent(trusted_local, model),
|
||||
"local_rules+codex_app_server",
|
||||
"本地规则结果优先;Codex 结构化计划仅补足未解析子句,并已重新通过能力注册表校验。",
|
||||
"本地规则结果优先;小迪生成的结构化计划仅补足未解析子句,并已重新通过能力注册表校验。",
|
||||
)
|
||||
if provider == "local" or not fallback_clauses:
|
||||
return (
|
||||
|
||||
Reference in New Issue
Block a user