Files
Cognitive_Agent/src/cognitive_agent/application/seat_service.py
T
2026-09-18 12:52:36 +08:00

8966 lines
371 KiB
Python
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""Application service for the stage-one seat single-domain closed loop."""
from __future__ import annotations
import hashlib
import json
import os
import re
import sys
import threading
import time
import uuid
from collections import Counter
from collections.abc import Callable, Mapping
from concurrent.futures import ThreadPoolExecutor
from copy import deepcopy
from datetime import UTC, datetime
from pathlib import Path
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
SIMULATED_HARDWARE_FAULTS,
HardwareSeatExecutor,
SeatCanMatrix,
SeatCompiler,
SimulatedHardwareSeatExecutor,
T2GSeatCompiler,
VirtualSeatExecutor,
build_function_config,
build_function_preset,
build_intent_ir,
parse_local_intent,
validate_semantics,
)
from cognitive_agent.application.assistant_plan_guard import ( # noqa: E402
guard_assistant_plan,
)
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_creation_seat_policy import ( # noqa: E402
ModeCreationSeatPolicyError,
creation_seat_scopes,
project_seed_operations,
project_seed_plan,
project_seed_schedule,
)
from cognitive_agent.application.mode_device_additions import ( # noqa: E402
mode_device_additions,
)
from cognitive_agent.application.mode_profile import ( # noqa: E402
ModeProfile,
ModeProfileRegistry,
ModeTimeline,
)
from cognitive_agent.application.mode_scene_ingress import ( # noqa: E402
ModeSceneIngressConsumer,
)
from cognitive_agent.application.mode_profile_intent import ( # noqa: E402
is_mode_profile_query,
mode_creation_source_name,
normalize_mode_profile_name,
parse_mode_profile_create,
parse_mode_profile_delete,
parse_mode_profile_update,
)
from cognitive_agent.application.mode_scene_catalog import ( # noqa: E402
DEFAULT_N7_SCENE_SERVER_ID,
DEFAULT_N7_SCENE_SERVER_NAME,
ModeSceneCatalogPublisher,
ModeSceneMqttBurstPublisher,
build_mode_scene_catalog,
)
from cognitive_agent.application.mode_schedule import ModeSchedule, ModeScheduleRunner # noqa: E402
from cognitive_agent.application.mode_schedule_service import ModeScheduleServiceMixin # noqa: E402
from cognitive_agent.application.model_plan_adapter import ( # noqa: E402
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_feedback import ( # noqa: E402
VehicleSafetyPreflightError,
authoritative_physical_ch1_feedback_state,
decode_feedback,
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,
)
from cognitive_agent.application.scene_templates import SceneTemplateRegistry # noqa: E402
from cognitive_agent.application.schedule_intent import ( # noqa: E402
is_schedule_stop,
needs_mode_schedule,
)
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,
)
from cognitive_agent.application.vehicle_state_monitor import ( # noqa: E402
OBSERVABLE_IDS,
REQUIRED_BODY_IDS,
REQUIRED_SEAT_IDS,
VehicleStateMonitor,
)
from cognitive_agent.application.zcan_seat_executor import ( # noqa: E402
ZcanBridgeSeatExecutor,
)
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
MQTT_MODE_SCENE_INGRESS_TOPIC,
MQTT_MODE_SCENE_TOPIC,
MQTT_N7_CONTROL_TRANSFER_TOPIC,
MqttCanTransport,
)
from cognitive_agent.infrastructure.llm.openai_responses import ( # noqa: E402
REASONING_EFFORTS,
OpenAIIntentError,
OpenAIIntentInterpreter,
)
from cognitive_agent.runtime.contracts import ContractValidator # noqa: E402
def _now() -> str:
return datetime.now(UTC).isoformat().replace("+00:00", "Z")
def _clean_vehicle_number(value: float | None) -> int | float | None:
if value is None:
return None
return int(value) if float(value).is_integer() else round(float(value), 1)
CONVERSATION_ID_PATTERN = re.compile(r"^conv-[a-z0-9-]{6,80}$")
MODE_EDITOR_REQUEST_ID_PATTERN = re.compile(r"^req-[a-z0-9-]{6,100}$")
SEAT_SCOPE_PATTERN = re.compile(r"\bseats?\b|座椅|坐椅", re.IGNORECASE)
DRIVER_SEAT_SCOPE_PATTERN = re.compile(
r"\bdriver\b|\bfront[-_ ]?left\b|\bleft[-_ ]?front\b|"
r"主驾|主驾驶(?:座|座椅|席)?|驾驶位|驾驶员|驾驶席|驾驶座(?:椅)?|"
r"司机位|司机座(?:椅)?|左前",
re.IGNORECASE,
)
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"
LOCAL_CONTROL_BUDGET_MS = 1000
VEHICLE_CONTROL_TRANSPORTS = frozenset(
{WIRED_CAN_TRANSPORT, WIRELESS_MQTT_TRANSPORT}
)
def _wireless_mqtt_publish_preflight() -> dict[str, Any]:
"""Describe the non-physical gate used before an approved MQTT publish."""
return {
"transport": WIRELESS_MQTT_TRANSPORT,
"status": "delegated",
"policy": "mqtt_whitelist_publish",
"control_gate": "capability_parameter_and_frame_whitelist",
"required_vehicle_frames": [],
}
EXECUTION_VERIFICATION_STATUSES = frozenset(
{"verified_success", "submitted_unverified", "failed_or_blocked"}
)
def _operation_verification_status(item: dict[str, Any]) -> str:
"""Classify one operation without conflating Tx and physical feedback."""
telemetry = item.get("telemetry", {})
explicit = str(telemetry.get("verification_status") or "")
if explicit in EXECUTION_VERIFICATION_STATUSES:
return explicit
if item.get("status") != "succeeded":
return "failed_or_blocked"
if (
telemetry.get("physical_effect_confirmed") is True
or telemetry.get("feedback_frame") is not None
or telemetry.get("hardware_feedback") is True
or telemetry.get("firmware_verified") is True
):
return "verified_success"
if (
telemetry.get("command_frame") is not None
or telemetry.get("delivery_status") in {"submitted", "queued", "published"}
):
return "submitted_unverified"
# Dry-run and control-plane operations are complete at their own evidence
# level and do not claim that a physical actuator moved.
return "verified_success"
def _annotate_execution_report(report: dict[str, Any]) -> tuple[str, bool]:
"""Add task-level evidence and normalize submission success.
Returns ``(verification_status, partially_succeeded)``. Support steps such
as firmware identity checks do not turn a failed requested operation into a
partially successful user task.
"""
spec = report.setdefault("spec", {})
results = [item for item in spec.get("results", []) if isinstance(item, dict)]
operation_results = [
item
for item in results
if item.get("ref") not in {"seat-controller-firmware"}
] or results
statuses_by_ref: dict[str, list[str]] = {}
for item in operation_results:
statuses_by_ref.setdefault(str(item.get("ref") or "operation"), []).append(
_operation_verification_status(item)
)
statuses = [
(
"failed_or_blocked"
if "failed_or_blocked" in values
else "submitted_unverified"
if "submitted_unverified" in values
else "verified_success"
)
for values in statuses_by_ref.values()
]
failed = sum(status == "failed_or_blocked" for status in statuses)
accepted = len(statuses) - failed
partially_succeeded = bool(failed and accepted)
verification_status = (
"failed_or_blocked"
if failed
else "submitted_unverified"
if any(status == "submitted_unverified" for status in statuses)
else "verified_success"
)
spec["verification_status"] = verification_status
spec["status"] = (
"partially_succeeded"
if partially_succeeded
else "failed"
if failed
else "succeeded"
)
return verification_status, partially_succeeded
def _has_mixed_driver_passenger_seat_scope(utterance: str) -> bool:
"""Detect one request that crosses the CH1-driver and CH0-passenger boundary."""
return bool(
SEAT_SCOPE_PATTERN.search(utterance)
and DRIVER_SEAT_SCOPE_PATTERN.search(utterance)
and PASSENGER_SEAT_SCOPE_PATTERN.search(utterance)
)
SAVE_MODE_PROFILE_NAME = re.compile(
r"(?:固化为|保存为长期|保存成长期|长期保存为|记为长期|命名为)"
r"(?P<name>[^,。,.!!\s]{1,64}?模式)"
)
# ``、`` 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;
# otherwise keep it intact for the target/feature parsers below.
CONTROL_CLAUSE_SUBJECT_PATTERN = re.compile(
r"座椅|座位|靠背|椅背|坐盆|坐垫|腿托|脚托|滑轨|"
r"通风|加热|按摩|零重力|半躺|女王副驾|休闲躺椅|"
r"遮阳帘|天幕帘|天幕|车窗|窗户|"
r"(?:左前|前左|右前|前右|左后|后左|右后|后右)(?:排)?窗|"
r"灯光|室内灯|车内灯|阅读灯|氛围灯|"
r"空调|压缩机|温度|风量|风速|内循环|外循环|"
r"车门|门锁|后备箱|前大灯|近光灯|远光灯|雾灯|转向灯|双闪|"
r"出风口"
)
CONTROL_CLAUSE_PREDICATE_PATTERN = re.compile(
r"打开|开启|启动|关闭|关掉|关上|停止|暂停|取消|"
r"调整|调节|调到|调成|调为|调至|设置|设为|设到|切换|"
r"移动|移到|移至|前移|后移|上移|下移|升高|降低|抬起|放倒|"
r"调直|立直|竖直|归零|复位|锁上|上锁|落锁|解锁|"
r"零重力|半躺|一半|半开|半关|最大|最小|最高|最低|最前|最后|到底|"
r"(?:\d+(?:\.\d+)?|[零一二三四五六七八九十百两半]+)"
r"(?:%|%|度|℃|档|级|秒|毫秒)"
)
# These verbs identify an independent request that may be outside the local
# vehicle registry. They are used only to split mixed sentences so an
# already deterministic clause never has to be repeated to a hosted model.
CLOUD_REQUEST_CLAUSE_PATTERN = re.compile(
r"播放|暂停播放|停止播放|导航|搜索|查找|查询|拨打|呼叫|发送|"
r"提醒|推荐|讲解|介绍|告诉|规划"
)
RESTORE_MODE_PROFILE_PREFIX = re.compile(
r"^(?:请(?:帮我)?|帮我)?(?:恢复|还原|重置|初始化)"
r"(?P<name>[^,。,.!!\s]{1,64}?模式)"
r"(?:的)?(?:初始配置|初始参数|默认配置|原始配置|出厂配置)?$"
)
RESTORE_MODE_PROFILE_SUFFIX = re.compile(
r"^(?:请(?:帮我)?|帮我)?(?:把|将)?"
r"(?P<name>[^,。,.!!\s]{1,64}?模式)"
r"(?:恢复|还原|重置|初始化)(?:到|为)?"
r"(?:初始配置|初始参数|默认配置|原始配置|出厂配置)?$"
)
SEAT_AXIS_TO_CAPABILITY = {
axis: capability for capability, axis in SEAT_MOTION_CAPABILITIES.items()
}
SEAT_FUNCTION_TO_CAPABILITY = {
function: capability for capability, function in SEAT_CAPABILITIES.items()
}
DYNAMIC_MODE_STOP_WORDS = ("停止", "关闭", "结束", "取消", "停下", "退出")
DYNAMIC_MODE_WORDS = (
"动态模式",
"动态座椅",
"木马模式",
"木马",
"摇摇马",
"骑木马",
"rocking horse",
"动态摇篮",
"摇篮模式",
)
class SeatDemoService(ModeScheduleServiceMixin):
def __init__(self, repository_root: Path = REPOSITORY_ROOT):
self.mode_schedule_runner = ModeScheduleRunner()
self.root = repository_root
self.domain_root = repository_root / "domains" / "seat"
self.contracts = ContractValidator(repository_root)
self.matrix = SeatCanMatrix(
[
self.domain_root / "can" / "seat-thermal-matrix.yaml",
self.domain_root / "can" / "seat-motion-virtual-matrix.yaml",
]
)
self.compiler = SeatCompiler(self.domain_root / "can" / "signal-mapping.yaml")
self.executor = VirtualSeatExecutor(self.matrix)
self.simulated_hardware_executor = SimulatedHardwareSeatExecutor(self.matrix)
self.simulated_hardware_enabled = os.getenv(
"COGNITIVE_AGENT_SIMULATED_HARDWARE_ENABLED", "false"
).strip().lower() in {"1", "true", "yes", "on"}
self.hardware_matrix = SeatCanMatrix(
[
self.domain_root / "can" / "seat-thermal-hardware-matrix.yaml",
self.domain_root / "can" / "seat-motion-hardware-matrix.yaml",
]
)
self.hardware_compiler = T2GSeatCompiler(
self.domain_root / "can" / "signal-mapping.hardware.yaml",
repository_root / "platforms" / "t2g_seat_bench" / "calibration" / "seat.yaml",
)
self.hardware_settings = SeatHardwareSettings.from_env(repository_root)
self.vehicle_can_settings = VehicleCanSettings.from_env(repository_root)
self.vehicle_mqtt_settings = VehicleMqttSettings.from_env()
self._vehicle_state_monitor = (
VehicleStateMonitor(self.vehicle_can_settings)
if self.vehicle_can_settings.backend == "zlgcan-linux"
and self.vehicle_can_settings.state_configured
and not self.vehicle_can_settings.configured
else None
)
self._vehicle_mqtt_transport = MqttCanTransport(
self.vehicle_mqtt_settings
)
self.native_can_runtime = NativeCanRuntime(repository_root)
self.native_can_relative_resolver = NativeCanRelativeResolver(repository_root)
self.hardware_execution = PHYSICAL_BENCH_COORDINATOR
self.hardware_access = PHYSICAL_BENCH_ACCESS
# Shared by the short-lived hardware executor instances. A process must
# establish a fresh physical four-axis reference before trusting stored
# Hall counts, and a firmware replacement invalidates it again.
self._seat_position_memory_state = {"synchronized": False}
self.openai = OpenAIIntentInterpreter()
self.domain_runtimes = {
domain: ConfiguredDomainRuntime(repository_root, domain)
for domain in ("climate", "roof", "ambient_light")
}
self.capability_registry = CapabilityRegistry(
repository_root, {"seat", *self.domain_runtimes}
)
self.capability_context = self.capability_registry.snapshot()
self.semantic_capability_metadata = {
capability["name"]: {
"risk_level": capability.get("risk_level", "high"),
"confirmation_policy": capability.get(
"confirmation_policy", "on_ambiguity"
),
}
for domain in self.capability_context["domains"]
for capability in domain["capabilities"]
}
self.model_plan_adapter = ModelPlanAdapter(self.capability_context)
self.vehicle_profiles = VehicleProfileRegistry(repository_root / "config" / "vehicles")
configured_mode_root = os.getenv(
"COGNITIVE_AGENT_MODE_PROFILE_ROOT", ""
).strip()
self.mode_profile_user_root = (
Path(configured_mode_root).expanduser()
if configured_mode_root
else repository_root / "artifacts" / "mode-profiles"
)
if not self.mode_profile_user_root.is_absolute():
self.mode_profile_user_root = repository_root / self.mode_profile_user_root
self.mode_profiles = ModeProfileRegistry(
repository_root / "config" / "modes",
user_root=self.mode_profile_user_root,
)
self._mode_scene_catalog_publisher: ModeSceneCatalogPublisher | None = None
self._mode_scene_catalog_status: dict[str, Any] = {
"enabled": False,
"state": "disabled",
"path": None,
"vehicle_id": self.vehicle_profiles.default().id,
"server_id": DEFAULT_N7_SCENE_SERVER_ID,
"catalog_revision": None,
"scene_count": 0,
"last_error": None,
}
self._mode_scene_mqtt_publisher = ModeSceneMqttBurstPublisher(
lambda scene: self._vehicle_mqtt_transport.publish_mode_scene(scene),
topic=MQTT_MODE_SCENE_TOPIC,
)
self._mode_scene_ingress = ModeSceneIngressConsumer(
self.import_n7_mode_scene,
)
self._vehicle_mqtt_transport.set_mode_scene_ingress_handler(
self._mode_scene_ingress.submit
)
self._n7_transfer_lock = threading.RLock()
self._n7_transfer_publications: dict[str, float] = {}
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"
)
@property
def artifact_root(self) -> Path:
return self._artifact_root
@artifact_root.setter
def artifact_root(self, value: Path) -> None:
self._artifact_root = Path(value)
if hasattr(self, "conversations"):
self.conversations.preset_root = self._artifact_root / "user-presets"
def configure_mode_scene_catalog(
self,
path: Path,
*,
server_id: str = DEFAULT_N7_SCENE_SERVER_ID,
name: str = DEFAULT_N7_SCENE_SERVER_NAME,
) -> dict[str, Any]:
"""Enable the derived JSON catalog without changing YAML persistence."""
catalog_path = Path(path).expanduser()
if not catalog_path.is_absolute():
catalog_path = self.root / catalog_path
vehicle_id = self.vehicle_profiles.default().id
self._mode_scene_catalog_publisher = ModeSceneCatalogPublisher(
catalog_path,
vehicle_id,
server_id=server_id,
name=name,
)
self._mode_scene_catalog_status = {
"enabled": True,
"state": "initializing",
"path": str(catalog_path),
"vehicle_id": vehicle_id,
"server_id": self._mode_scene_catalog_publisher.server_id,
"catalog_revision": None,
"scene_count": 0,
"last_error": None,
}
# The registry notifies after create, update, delete, restore, and any
# explicit reload. The callback only updates a derived projection; YAML
# remains the authoritative store used by every existing execution path.
self.mode_profiles.add_change_listener(self._synchronize_mode_scene_catalog)
return self.mode_scene_catalog_status()
def _synchronize_mode_scene_catalog(self, registry: ModeProfileRegistry) -> None:
publisher = self._mode_scene_catalog_publisher
if publisher is None:
return
try:
catalog = publisher.publish(registry)
except Exception as exc: # noqa: BLE001 - derived export must not block controls
self._mode_scene_catalog_status.update(
{
"state": "error",
"last_error": str(exc),
}
)
return
self._mode_scene_catalog_status.update(
{
"state": "ready",
"catalog_revision": catalog["catalog_revision"],
"scene_count": catalog["scene_count"],
"last_error": None,
}
)
def mode_scene_catalog(self, vehicle_id: str | None = None) -> dict[str, Any]:
"""Return the current read-only mode-card catalog for an N7 client."""
resolved_vehicle_id = vehicle_id or self.vehicle_profiles.default().id
publisher = self._mode_scene_catalog_publisher
if publisher is not None and resolved_vehicle_id == publisher.vehicle_id:
self._synchronize_mode_scene_catalog(self.mode_profiles)
snapshot = publisher.snapshot()
if snapshot is not None and self._mode_scene_catalog_status["state"] == "ready":
return snapshot
return build_mode_scene_catalog(
self.mode_profiles,
resolved_vehicle_id,
server_id=(
publisher.server_id
if publisher is not None
else DEFAULT_N7_SCENE_SERVER_ID
),
name=(publisher.name if publisher is not None else DEFAULT_N7_SCENE_SERVER_NAME),
)
def mode_scene_catalog_status(self) -> dict[str, Any]:
return deepcopy(self._mode_scene_catalog_status)
def mode_profile_sync_status(self) -> dict[str, Any]:
"""Return the lightweight state used by the browser's scene watcher."""
catalog = self.mode_scene_catalog_status()
return {
"catalog_revision": catalog.get("catalog_revision"),
"scene_count": int(catalog.get("scene_count") or 0),
"catalog_state": str(catalog.get("state") or "unknown"),
"catalog_error": catalog.get("last_error"),
"mode_scene_ingress": self._mode_scene_ingress.snapshot(),
}
def publish_saved_mode_scene(
self,
mode_id: str,
vehicle_id: str | None = None,
) -> dict[str, Any]:
"""Queue the just-saved card from the canonical scene projection.
Both a new user mode and a user override of a repository preset are
projected by the same registry. Reading the card back from that
projection guarantees that MQTT and ``/n7/scenes`` expose exactly the
same post-save JSON object.
"""
clean_mode_id = str(mode_id).strip()
if not clean_mode_id:
raise ValueError("mode scene id must not be empty")
resolved_vehicle_id = vehicle_id or self.vehicle_profiles.default().id
catalog = self.mode_scene_catalog(resolved_vehicle_id)
catalog_publisher = self._mode_scene_catalog_publisher
if catalog_publisher is not None and (
catalog_publisher.vehicle_id != resolved_vehicle_id
or self._mode_scene_catalog_status.get("state") != "ready"
):
raise RuntimeError("mode scene catalog is not synchronized for this vehicle")
scene = next(
(
item
for item in catalog.get("scenes", [])
if isinstance(item, dict) and item.get("id") == clean_mode_id
),
None,
)
if scene is None:
raise KeyError(clean_mode_id)
return self._mode_scene_mqtt_publisher.queue(scene)
def publish_created_mode_scene(
self,
mode_id: str,
vehicle_id: str | None = None,
) -> dict[str, Any]:
"""Backward-compatible alias for callers predating update publishing."""
return self.publish_saved_mode_scene(mode_id, vehicle_id)
@staticmethod
def _imported_scene_function_configs(
scene: dict[str, Any],
) -> dict[str, dict[str, Any]]:
"""Retain only local peripheral metadata needed for later authorization."""
extension_operations: list[dict[str, Any]] = []
for index, action in enumerate(scene.get("actions", [])):
if (
not isinstance(action, dict)
or action.get("domain") != "self_extension"
or action.get("capability") != "infrared.emit"
):
continue
target = action.get("target")
target_document = (
deepcopy(target)
if isinstance(target, dict)
else {"device": str(action.get("target_scope") or "")}
)
extension_operations.append(
{
"id": str(action.get("operation_ref") or f"n7-ir-{index + 1}"),
"domain": "self_extension",
"capability": "infrared.emit",
"target_scope": str(action.get("target_scope") or ""),
"target": target_document,
"operation": str(action.get("operation") or "emit"),
"parameters": deepcopy(dict(action.get("parameters") or {})),
"parse_source": "from_n7_scene",
}
)
if not extension_operations:
return {}
return {
"self_extension": {
"apiVersion": "cognitive-agent.io/v1alpha1",
"kind": "FunctionConfig",
"metadata": {
"id": f"from-n7-{scene['id']}-self-extension",
"name": f"{scene['name']} imported infrared actions",
"labels": {"source_topic": MQTT_MODE_SCENE_INGRESS_TOPIC},
},
"spec": {
"domain": "self_extension",
"operations": extension_operations,
},
}
}
def import_n7_mode_scene(
self,
scene: Mapping[str, Any],
) -> dict[str, Any]:
"""Upsert one validated N7 card without publishing it back to the broker."""
document = deepcopy(dict(scene))
actions = document.get("actions")
if not isinstance(actions, list) or not actions:
raise ValueError("from_n7/scenes card must contain executable actions")
operations = [
{
"domain": str(action["domain"]),
"capability": str(action["capability"]),
"target_scope": str(action["target_scope"]),
"operation": str(action["operation"]),
"parameters": deepcopy(dict(action["parameters"])),
"reason": (
f"由 {MQTT_MODE_SCENE_INGRESS_TOPIC} 导入的模式动作。"
),
}
for action in actions
]
timeline = None
raw_timeline = document.get("timeline")
if raw_timeline is not None:
if not isinstance(raw_timeline, dict):
raise ValueError("from_n7/scenes timeline must be an object")
timeline = ModeTimeline.from_dict(raw_timeline)
command_flow = {
"schema": "cognitive-agent-mode-command-flow/v1",
"source_topic": MQTT_MODE_SCENE_INGRESS_TOPIC,
"imported_at": _now(),
"source_scene": document,
"function_configs": self._imported_scene_function_configs(document),
# The execution path recompiles the canonical ModeOperation list
# against this PC's capability registry. This marker makes the
# card directly executable while avoiding trust in foreign CAN bytes.
"plans": {
"from_n7_scene": {
"spec": {
"commands": [
{
"operation_ref": str(
action.get("operation_ref") or f"action-{index + 1}"
),
"at_ms": int(action.get("at_ms", 0)),
}
for index, action in enumerate(actions)
]
}
}
},
"execution_steps": [],
}
scope = document.get("scope") or {}
vehicle_ids = scope.get("vehicle_ids", []) if isinstance(scope, dict) else []
common = bool(scope.get("common", not vehicle_ids)) if isinstance(scope, dict) else False
resolved_vehicle_id = (
None
if common and not vehicle_ids
else self.vehicle_profiles.default().id
)
saved = self.mode_profiles.save_mode(
str(document["name"]),
operations,
mode_id=str(document["id"]),
aliases=[str(alias) for alias in document.get("aliases", [])],
vehicle_id=resolved_vehicle_id,
description=str(document.get("description") or "由 N7 同步的模式卡片。"),
icon=str(document.get("icon") or "radio-tower"),
instruction=f"进入{document['name']}",
command_flow=command_flow,
timeline=timeline,
sync_origin=MQTT_MODE_SCENE_INGRESS_TOPIC,
upstream_origin=str(document.get("origin") or ""),
)
return {
"status": "imported",
"scene_id": saved.id,
"scene_name": saved.name,
"source_topic": MQTT_MODE_SCENE_INGRESS_TOPIC,
"profile_revision": self.mode_profiles.profile_revision(saved),
"storage": "persistent_yaml",
}
@property
def driver_only_ch1_execution_active(self) -> bool:
"""Return whether deployment policy authorizes only the CH1 vehicle path."""
return bool(
not self.hardware_settings.enabled
and self.vehicle_can_settings.configured
and self.vehicle_can_settings.channel_is_allowed
and self.vehicle_can_settings.channel == 1
)
def _driver_only_vehicle_execution_active(self, transport: str) -> bool:
if self.hardware_settings.enabled:
return False
if transport == WIRELESS_MQTT_TRANSPORT:
return self.vehicle_mqtt_settings.configured
return self.driver_only_ch1_execution_active
@staticmethod
def _validate_control_transport(transport: str) -> str:
normalized = str(transport).strip().lower()
if normalized not in VEHICLE_CONTROL_TRANSPORTS:
raise ValueError(
"control_transport must be wired_can or wireless_mqtt"
)
return normalized
def runtime_info(self) -> dict[str, Any]:
domain_runtime_info = [
runtime.runtime_info() for runtime in self.domain_runtimes.values()
]
default_vehicle = self.vehicle_profiles.default()
hardware_info = self.hardware_settings.public_info()
hardware_info.update(self.hardware_execution.public_info())
hardware_info.update(self.hardware_access.public_info())
vehicle_can_info = self.vehicle_can_settings.public_info()
if self._vehicle_state_monitor is not None:
state_sync_info = self._vehicle_state_monitor.public_info()
if self.hardware_settings.configured:
# Keep the slower legacy cadence when the same page also polls
# the physical passenger-seat path through the shared adapter.
state_sync_info["poll_interval_ms"] = 5_000
vehicle_can_info["state_sync"] = state_sync_info
elif self.vehicle_can_settings.state_configured:
vehicle_can_info["state_sync"] = {
"mode": "on_demand_shared_adapter",
"state": "ready",
"read_only": True,
"poll_interval_ms": 5_000,
"snapshot_freshness_ms": 5_000,
"capture_sequence": None,
"observed_frame_ids": [],
"last_error": None,
}
vehicle_mqtt_info = self._vehicle_mqtt_transport.public_info()
vehicle_mqtt_info["mode_scene_import"] = self._mode_scene_ingress.snapshot()
return {
"service": "cabin-cognitive-orchestrator",
"domain": "cabin",
"platform": "reference_vehicle",
"execution_mode": os.getenv(
"COGNITIVE_AGENT_EXECUTION_MODE", "dry_run"
).strip(),
"execution_modes": ["dry_run", "simulated_hardware", "hardware"],
"hardware_execution_allowed": (
self.hardware_settings.configured
or self.vehicle_can_settings.configured
or self.vehicle_mqtt_settings.configured
),
"hardware": hardware_info,
"vehicle_can": vehicle_can_info,
"vehicle_mqtt": vehicle_mqtt_info,
"vehicle_transports": {
"default": WIRED_CAN_TRANSPORT,
"selection_scope": "browser_request",
"options": [
{
"id": WIRED_CAN_TRANSPORT,
"label": "有线控制",
**vehicle_can_info,
"target_policy": (
"dual_channel_wired_can0_passenger_can1_vehicle"
if self.hardware_settings.configured
and self.vehicle_can_settings.configured
else vehicle_can_info.get("target_policy")
),
"channels": {
"seat": 0,
"vehicle": self.vehicle_can_settings.channel,
},
},
{
"id": WIRELESS_MQTT_TRANSPORT,
"label": "无线控制",
**vehicle_mqtt_info,
},
],
},
"vehicle_safety_policy": {
"required_channel": 1,
"required_gear": "P",
"maximum_speed_kmh": 0.05,
"fresh_feedback_ids": ["0x342", "0x4EB"],
"preflight": "read_only_before_every_physical_request",
},
"multi_channel_can": {
"adapter_ownership": "single_process",
"seat_channel": (
self.hardware_settings.channel
if self.hardware_settings.enabled
else None
),
"vehicle_channel": self.vehicle_can_settings.channel,
"channels_must_be_distinct": bool(
self.hardware_settings.enabled
and self.vehicle_can_settings.enabled
),
"active_policy": (
"dual_channel_wired_can0_passenger_can1_vehicle"
if self.hardware_settings.configured
and self.vehicle_can_settings.configured
else "ch1_body_can_driver_only"
if self.vehicle_can_settings.configured
else "ch1_body_can_read_only"
if self.vehicle_can_settings.state_configured
else "ch0_passenger_only"
if self.hardware_settings.configured
else "virtual_only"
),
"seat_roles": {
"passenger": {"zone": "front_right", "channel": 0},
"driver": {"zone": "front_left", "channel": 1},
},
},
"seat_position_memory_synchronized": bool(
self._seat_position_memory_state.get("synchronized", False)
),
"simulated_hardware": {
"enabled": self.simulated_hardware_enabled,
"default_fault": "normal",
"fault_scenarios": list(SIMULATED_HARDWARE_FAULTS),
"physical_can_opened": False,
},
"openai_available": self.openai.available,
"openai_model": self.openai.model,
"openai_models": self.openai.model_catalog,
"openai_reasoning_effort_default": self.openai.reasoning_effort,
"openai_reasoning_efforts": list(REASONING_EFFORTS),
"openai_responses_endpoint": self.openai.responses_endpoint,
"conversation_memory": True,
"default_vehicle_id": default_vehicle.id,
"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),
"mode_scene_catalog": self.mode_scene_catalog_status(),
"mode_scene_mqtt": self._mode_scene_mqtt_publisher.snapshot(),
"preset_storage": "conversation_durable",
"inferred_level_range": [1, 3],
"motion_position_range_pct": [0, 100],
"capability_domains": [
{
"domain": item["domain"],
"availability": item["availability"],
}
for item in self.capability_context["domains"]
],
"capabilities": [
capability
for domain in self.capability_context["domains"]
for capability in domain["capabilities"]
],
"matrix": {
"messages": len(self.matrix.data["messages"])
+ sum(item["matrix"]["messages"] for item in domain_runtime_info),
"signals": sum(len(item["signals"]) for item in self.matrix.data["messages"])
+ sum(item["matrix"]["signals"] for item in domain_runtime_info),
"source": {
"seat": self.matrix.data["source"],
**{
item["domain"]: item["matrix"]["source"]
for item in domain_runtime_info
},
},
"limitations": self.matrix.data["limitations"]
+ [
limitation
for item in domain_runtime_info
for limitation in item["matrix"]["limitations"]
],
},
"domain_runtimes": domain_runtime_info,
}
def delete_user_mode_profile(
self,
mode_id: str,
vehicle_id: str | None = None,
) -> dict[str, Any]:
resolved_vehicle_id = vehicle_id or self.vehicle_profiles.default().id
profile = self.mode_profiles.delete_user_mode(mode_id, resolved_vehicle_id)
return {
"status": "succeeded",
"deleted_mode_profile": profile.public_dict(),
"vehicle_id": resolved_vehicle_id,
}
def get_mode_profile_card(
self,
mode_id: str,
vehicle_id: str | None = None,
) -> ModeProfile:
resolved_vehicle_id = vehicle_id or self.vehicle_profiles.default().id
return self.mode_profiles.get_profile(mode_id, resolved_vehicle_id)
@staticmethod
def _fallback_scene_action(
operation: Any,
*,
index: int,
) -> dict[str, Any]:
return {
"type": "control",
"at_ms": 0,
"domain": operation.domain,
"capability": operation.capability,
"target_scope": operation.target_scope,
"operation": operation.operation,
"parameters": deepcopy(operation.parameters),
"operation_ref": f"action-{index + 1}",
}
def _mode_requests_and_n7_transfers(
self,
profile: ModeProfile,
) -> tuple[list[dict[str, Any]], list[dict[str, Any]]]:
"""Separate locally understood operations from N7-only capabilities."""
requests = self.mode_profiles.requests_for_profiles([profile])
if profile.sync_origin != MQTT_MODE_SCENE_INGRESS_TOPIC:
return requests, []
source_scene = (
profile.command_flow.get("source_scene")
if isinstance(profile.command_flow, dict)
else None
)
source_actions = (
source_scene.get("actions")
if isinstance(source_scene, dict)
and isinstance(source_scene.get("actions"), list)
else []
)
local_requests: list[dict[str, Any]] = []
transfer_actions: list[dict[str, Any]] = []
for index, (operation, request) in enumerate(
zip(profile.operations, requests, strict=True)
):
if (
operation.domain == "self_extension"
and operation.capability == "infrared.emit"
):
# The server's durable NFC allowlist owns this local TCP path.
# Authorization failures must not be converted into N7 retries.
continue
registered = self.model_plan_adapter.capabilities.get(
operation.capability
)
native_candidate = {
"domain": operation.domain,
"capability": operation.capability,
"target": operation.target_scope,
"parameters": deepcopy(operation.parameters),
"operation": operation.operation,
}
if registered is not None or self.native_can_runtime.supports(
native_candidate
):
local_requests.append(request)
continue
raw_action = (
source_actions[index]
if index < len(source_actions)
and isinstance(source_actions[index], dict)
else None
)
action = (
deepcopy(raw_action)
if raw_action is not None
else self._fallback_scene_action(operation, index=index)
)
transfer_actions.append(action)
return local_requests, transfer_actions
@staticmethod
def _has_local_mode_peripheral(profile: ModeProfile) -> bool:
return any(
operation.domain == "self_extension"
and operation.capability == "infrared.emit"
for operation in profile.operations
)
@staticmethod
def _n7_transfer_deferred_requests(
actions: list[dict[str, Any]],
) -> list[dict[str, Any]]:
return [
{
"domain": str(action.get("domain") or ""),
"capability": str(action.get("capability") or ""),
"target_scope": str(action.get("target_scope") or ""),
"desired_change": str(action.get("operation") or "set"),
"parameters": deepcopy(dict(action.get("parameters") or {})),
"reason": "当前 PC 未注册该能力,将由 N7 端继续处理。",
"execution_backend": "n7_vehicle_transfer",
"rejection_code": "PC_CAPABILITY_UNAVAILABLE",
"transport_submitted": False,
}
for action in actions
]
def _finalize_n7_control_transfer(
self,
result: dict[str, Any],
*,
execution_mode: str,
local_preview: bool,
) -> dict[str, Any]:
"""Forward only N7-originated actions never submitted by this PC."""
actions = result.get("n7_transfer_candidates")
scene = result.get("n7_source_scene")
if (
execution_mode != "hardware"
or local_preview
or not isinstance(actions, list)
or not actions
or not isinstance(scene, dict)
):
return result
if str(result.get("status") or "") not in {
"succeeded",
"partially_succeeded",
"capability_unavailable",
}:
return result
if result.get("execution_outcome") == "failed_or_blocked":
return result
request_id = str(result.get("request_id") or "").strip()
scene_id = str(scene.get("id") or "").strip()
scene_name = str(scene.get("name") or "").strip()
if not request_id or not scene_id or not scene_name:
return result
transfer_actions = [deepcopy(action) for action in actions if isinstance(action, dict)]
if not transfer_actions:
return result
payload = {
"scene_id": scene_id,
"scene_name": scene_name,
"request_id": request_id,
"operation_count": len(transfer_actions),
"actions": transfer_actions,
}
digest = json.dumps(
payload,
ensure_ascii=False,
sort_keys=True,
separators=(",", ":"),
)
publication_key = f"{request_id}:{hashlib.sha256(digest.encode('utf-8')).hexdigest()}"
now = time.monotonic()
with self._n7_transfer_lock:
cutoff = now - 300.0
self._n7_transfer_publications = {
key: published_at
for key, published_at in self._n7_transfer_publications.items()
if published_at >= cutoff
}
if publication_key in self._n7_transfer_publications:
result["n7_control_transfer"] = {
"state": "duplicate_ignored",
"topic": MQTT_N7_CONTROL_TRANSFER_TOPIC,
"scene_id": scene_id,
"operation_count": len(transfer_actions),
}
return result
try:
publication = self._vehicle_mqtt_transport.publish_n7_control_transfer(
payload
)
except Exception as exc: # noqa: BLE001 - local execution result remains valid
result["n7_control_transfer"] = {
"state": "publish_failed",
"topic": MQTT_N7_CONTROL_TRANSFER_TOPIC,
"scene_id": scene_id,
"operation_count": len(transfer_actions),
"last_error": str(exc)[:240],
}
return result
with self._n7_transfer_lock:
self._n7_transfer_publications[publication_key] = now
result["n7_control_transfer"] = {
"state": "published",
"scene_id": scene_id,
"operation_count": len(transfer_actions),
"payload": payload,
**dict(publication),
}
result["decision_summary"] = [
*list(result.get("decision_summary") or []),
(
f"当前 PC 未注册的 {len(transfer_actions)} 条 N7 来源动作已发送至 "
f"{MQTT_N7_CONTROL_TRANSFER_TOPIC};PC 已提交或被安全门禁拦截的动作未转发。"
),
]
return result
def execute_mode_profile_card(
self,
mode_id: str,
*,
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,
control_transport: str = WIRED_CAN_TRANSPORT,
) -> dict[str, Any]:
"""Execute a persisted mode by ID without interpreting natural language."""
resolved_vehicle_id = vehicle_id or self.vehicle_profiles.default().id
profile = self.mode_profiles.get_profile(mode_id, resolved_vehicle_id)
if profile.schedule is None and (not profile.command_flow or not profile.command_flow.get("plans")):
raise ValueError("该模式尚未生成可直接执行的控制指令流。")
result = self.process(
profile.instruction or f"进入{profile.name}",
provider="local",
conversation_id=conversation_id,
execution_mode=execution_mode,
approval_token=approval_token,
vehicle_id=resolved_vehicle_id,
simulation_fault=simulation_fault,
hardware_client_id=hardware_client_id,
hardware_access_bypass=hardware_access_bypass,
control_transport=control_transport,
_direct_mode_profile_id=profile.id,
)
if profile.schedule is not None:
return result
public_profile = profile.public_dict()
result["provider_used"] = "saved_mode_command_flow"
result["utterance"] = profile.name
result["matched_modes"] = [public_profile]
result["mode_profile"] = public_profile
result["orchestration_route"] = "saved_mode_command_flow"
result["decision_summary"] = [
f"直接载入已保存的模式“{profile.name}”,未再次解析自然语言。",
*list(result.get("decision_summary") or []),
]
self._persist(result)
return result
def save_mode_profile_card(
self,
mode_id: str,
name: str,
instruction: str,
source_request_id: str,
vehicle_id: str | None = None,
) -> dict[str, Any]:
"""Persist one edited mode from a server-generated control flow."""
return self._persist_mode_profile_card(
mode_id,
name,
instruction,
source_request_id,
vehicle_id,
)
def create_mode_profile_card(
self,
name: str,
instruction: str,
source_request_id: str,
vehicle_id: str | None = None,
) -> dict[str, Any]:
"""Create one user mode from a server-generated control flow."""
return self._persist_mode_profile_card(
None,
name,
instruction,
source_request_id,
vehicle_id,
)
def trial_mode_profile_draft(
self,
name: str,
instruction: str,
source_request_id: str,
vehicle_id: str | None = None,
) -> dict[str, Any]:
"""Replay the server-owned dry-run evidence for one assistant draft."""
clean_name = str(name).strip()
clean_instruction = str(instruction).strip()
clean_request_id = str(source_request_id).strip()
resolved_vehicle_id = vehicle_id or self.vehicle_profiles.default().id
if not clean_name or not clean_instruction:
raise ValueError("功能模式草稿名称和指令不能为空。")
if not MODE_EDITOR_REQUEST_ID_PATTERN.fullmatch(clean_request_id):
raise ValueError("功能模式草稿来源请求编号无效。")
result_path = self.artifact_root / clean_request_id / "result.json"
if not result_path.is_file():
raise ValueError("未找到功能模式草稿,请重新生成。")
try:
source_result = json.loads(result_path.read_text(encoding="utf-8"))
except (OSError, json.JSONDecodeError) as exc:
raise ValueError("功能模式草稿结果无法读取,请重新生成。") from exc
if not isinstance(source_result, dict) or source_result.get(
"request_id"
) != clean_request_id:
raise ValueError("功能模式草稿来源结果不完整,请重新生成。")
if (
source_result.get("status") != "succeeded"
or source_result.get("execution_mode") != "dry_run"
):
raise ValueError("功能模式草稿没有通过完整的模拟校验。")
draft = source_result.get("mode_profile_draft")
if not isinstance(draft, dict):
raise ValueError("来源请求不是可试运行的功能模式草稿。")
if str(draft.get("source_request_id") or "").strip() != clean_request_id:
raise ValueError("功能模式草稿来源请求编号不匹配,请重新生成。")
if str(draft.get("name") or "").strip() != clean_name:
raise ValueError("功能模式草稿名称不匹配,请重新生成。")
if str(draft.get("instruction") or "").strip() != clean_instruction:
raise ValueError("功能模式草稿指令不匹配,请重新生成。")
if str(draft.get("vehicle_id") or "").strip() != resolved_vehicle_id:
raise ValueError("功能模式草稿不属于当前车型。")
intent_ir = source_result.get("intent_ir")
spec = intent_ir.get("spec") if isinstance(intent_ir, dict) else None
actions = spec.get("actions") if isinstance(spec, dict) else None
function_configs = source_result.get("function_configs")
operation_count = draft.get("operation_count")
if (
not isinstance(actions, list)
or not actions
or not isinstance(function_configs, dict)
or not function_configs
or not isinstance(operation_count, int)
or isinstance(operation_count, bool)
or len(actions) != operation_count
):
raise ValueError("功能模式草稿动作证据不完整,请重新生成。")
validation = source_result.get("validation", [])
validation = validation if isinstance(validation, list) else []
if any(
isinstance(issue, dict) and issue.get("severity") == "error"
for issue in validation
):
raise ValueError("功能模式草稿未通过安全校验,请重新生成。")
trial = {
"request_id": clean_request_id,
"status": "succeeded",
"execution_mode": "dry_run",
"hardware_execution_allowed": False,
"orchestration_route": "mode_profile_draft_trial",
"mode_profile_draft": deepcopy(draft),
"intent_ir": {"spec": {"actions": deepcopy(actions)}},
"function_configs": deepcopy(function_configs),
"execution_report": deepcopy(source_result.get("execution_report")),
"validation": deepcopy(validation),
"trial_source": "server_validated_preview_artifact",
}
schedule = self._validated_schedule_artifact(source_result)
if schedule is not None:
trial["schedule"] = schedule.to_dict()
trial["schedule_execution"] = self.mode_schedule_runner.simulate(
schedule, lambda phase: {"status": "succeeded", "phase_id": phase.id,
"operations": deepcopy(list(phase.operations))},
)
return trial
def _persist_mode_profile_card(
self,
mode_id: str | None,
name: str,
instruction: str,
source_request_id: str,
vehicle_id: str | None = None,
) -> dict[str, Any]:
"""Validate and persist a created or edited mode command flow."""
resolved_vehicle_id = vehicle_id or self.vehicle_profiles.default().id
clean_mode_id = str(mode_id or "").strip()
clean_name = str(name).strip()
clean_instruction = str(instruction).strip()
clean_request_id = str(source_request_id).strip()
if not clean_name:
raise ValueError("模式名称不能为空。")
if len(clean_name) > 64:
raise ValueError("模式名称不能超过 64 个字符。")
if not clean_instruction:
raise ValueError("自然语言指令不能为空。")
if len(clean_instruction) > 1000:
raise ValueError("自然语言指令不能超过 1000 个字符。")
if not MODE_EDITOR_REQUEST_ID_PATTERN.fullmatch(clean_request_id):
raise ValueError("控制指令流来源请求编号无效。")
visible_profiles = self.mode_profiles._profiles_for_vehicle( # noqa: SLF001
resolved_vehicle_id
)
profile = (
next(
(item for item in visible_profiles if item.id == clean_mode_id),
None,
)
if clean_mode_id
else None
)
if clean_mode_id and profile is None:
raise KeyError(clean_mode_id)
normalized_name = re.sub(r"\s+", "", clean_name).casefold()
if any(
(profile is None or item.id != profile.id)
and re.sub(r"\s+", "", item.name).casefold() == normalized_name
for item in visible_profiles
):
raise ValueError("该模式名称已被其他模式使用。")
result_path = self.artifact_root / clean_request_id / "result.json"
if not result_path.is_file():
raise ValueError("未找到“生成指令”按钮产生的控制指令流,请重新生成。")
try:
source_result = json.loads(result_path.read_text(encoding="utf-8"))
except (OSError, json.JSONDecodeError) as exc:
raise ValueError("控制指令流来源结果无法读取,请重新生成。") from exc
if not isinstance(source_result, dict) or source_result.get(
"request_id"
) != clean_request_id:
raise ValueError("控制指令流来源结果不完整,请重新生成。")
if source_result.get("status") != "succeeded":
raise ValueError("控制指令流没有完整通过校验,请重新生成。")
source_vehicle = source_result.get("vehicle")
source_vehicle_id = (
str(source_vehicle.get("id") or "").strip()
if isinstance(source_vehicle, dict)
else ""
)
if source_vehicle_id and source_vehicle_id != resolved_vehicle_id:
raise ValueError("控制指令流不属于当前车型,请重新生成。")
source_draft = source_result.get("mode_profile_draft")
draft_existing_mode_revision = ""
if isinstance(source_draft, dict):
draft_request_id = str(source_draft.get("source_request_id") or "").strip()
draft_vehicle_id = str(source_draft.get("vehicle_id") or "").strip()
draft_instruction = str(source_draft.get("instruction") or "").strip()
draft_name = str(source_draft.get("name") or "").strip()
draft_existing_mode_id = str(
source_draft.get("existing_mode_id") or ""
).strip()
draft_existing_mode_revision = str(
source_draft.get("existing_mode_revision") or ""
).strip()
if draft_request_id != clean_request_id:
raise ValueError("功能模式草稿与控制指令流不匹配,请重新生成。")
if draft_vehicle_id and draft_vehicle_id != resolved_vehicle_id:
raise ValueError("功能模式草稿不属于当前车型,请重新生成。")
if draft_instruction and draft_instruction != clean_instruction:
raise ValueError("自然语言指令已修改,请重新生成模式预览。")
if draft_name and draft_name != clean_name:
raise ValueError("功能模式名称已修改,请重新生成模式预览。")
if clean_mode_id and not draft_existing_mode_id:
raise ValueError("新功能模式预览不能通过已有模式的更新接口保存。")
if draft_existing_mode_id and draft_existing_mode_id != clean_mode_id:
raise ValueError("该预览属于已有功能模式,请通过对应模式的更新接口保存。")
if draft_existing_mode_id and not draft_existing_mode_revision:
raise ValueError("功能模式预览版本过旧,请重新生成后再更新。")
if (
draft_existing_mode_revision
and profile is not None
and draft_existing_mode_revision
!= self.mode_profiles.profile_revision(profile)
):
raise ValueError("功能模式已被其他会话更新,请重新生成预览后再保存。")
recorded_instruction = str(source_result.get("utterance", "")).strip()
if not recorded_instruction:
source_spans = {
str(item.get("source_span", "")).strip()
for item in (
(source_result.get("intent_ir") or {}).get("spec", {}).get(
"requests", []
)
)
if isinstance(item, dict) and str(item.get("source_span", "")).strip()
}
if len(source_spans) == 1:
recorded_instruction = next(iter(source_spans))
if recorded_instruction and recorded_instruction != clean_instruction:
raise ValueError("自然语言指令已修改,请重新点击“生成指令”。")
plans = source_result.get("command_plans") or {}
configs = source_result.get("function_configs") or {}
if not isinstance(plans, dict) or not isinstance(configs, dict):
raise ValueError("解析结果中没有可永久化的控制指令流。")
executable_plan_keys = {
key
for key, plan in plans.items()
if isinstance(plan, dict)
and isinstance(plan.get("spec"), dict)
and isinstance(plan["spec"].get("commands"), list)
and plan["spec"]["commands"]
}
if not executable_plan_keys:
raise ValueError("请先点击“生成指令”并成功生成控制指令流。")
operations: list[dict[str, Any]] = []
signatures: set[tuple[str, str, str, str]] = set()
for key in plans:
if key not in executable_plan_keys:
continue
config = configs.get(key)
if not isinstance(config, dict):
continue
spec = config.get("spec") or {}
domain = str(spec.get("domain", "")).strip()
for operation in spec.get("operations", []):
if not isinstance(operation, dict):
continue
capability = str(operation.get("capability", "")).strip()
operation_target = operation.get("target") or {}
target = str(
operation_target.get("instance")
or operation_target.get("zone")
or ""
).strip()
parameters = operation.get("parameters") or {}
if not domain or not capability or not target or not isinstance(
parameters, dict
):
continue
signature = (
domain,
capability,
target,
json.dumps(parameters, ensure_ascii=False, sort_keys=True),
)
if signature in signatures:
continue
signatures.add(signature)
operations.append(
{
"domain": domain,
"capability": capability,
"target_scope": target,
"operation": "set",
"parameters": deepcopy(parameters),
"reason": f"由模式编辑器请求 {clean_request_id} 固化。",
}
)
if not operations:
raise ValueError("控制指令流没有关联到可复用的模式操作。")
command_flow = {
"schema": "cognitive-agent-mode-command-flow/v1",
"source_request_id": clean_request_id,
"generated_at": _now(),
"execution_mode": source_result.get("execution_mode"),
"control_transport": source_result.get("control_transport"),
"function_configs": {
key: deepcopy(configs[key])
for key in plans
if key in executable_plan_keys and key in configs
},
"plans": {key: deepcopy(plans[key]) for key in plans if key in executable_plan_keys},
# A mode card stores only the validated, deterministic plan. Live
# delivery evidence belongs to each later execution, not to the
# generation workflow.
"execution_steps": [],
}
schedule = self._validated_schedule_artifact(source_result)
if profile is not None and profile.schedule is not None and schedule is None:
raise ValueError("动态模式更新必须保留完整时间轴,请按阶段重新生成预览。")
if schedule is not None:
operations = [deepcopy(op) for phase in schedule.phases for op in phase.operations]
saved = self.mode_profiles.save_mode(
clean_name,
operations,
mode_id=profile.id if profile is not None else None,
aliases=list(profile.aliases) if profile is not None else [],
vehicle_id=resolved_vehicle_id,
description=(
profile.description
if profile is not None
else "由自然语言生成的自定义模式。"
),
icon=profile.icon if profile is not None else "bookmark-plus",
instruction=clean_instruction,
command_flow=command_flow,
timeline=profile.timeline if profile is not None else None,
sync_origin=profile.sync_origin if profile is not None else "",
upstream_origin=(
profile.upstream_origin if profile is not None else ""
),
schedule=schedule,
expected_revision=draft_existing_mode_revision or None,
create_only=not bool(clean_mode_id),
)
public_profile = next(
(
item
for item in self.mode_profiles.public_modes(resolved_vehicle_id)
if item["id"] == saved.id
),
saved.public_dict(),
)
return {
"status": "succeeded",
"mode_profile": public_profile,
"vehicle_id": resolved_vehicle_id,
"storage": "persistent_yaml",
"source_request_id": clean_request_id,
}
def synchronize_hardware_state(
self,
conversation_id: str,
vehicle_id: str | None = None,
*,
control_transport: str = WIRED_CAN_TRANSPORT,
hardware_client_id: str | None = None,
hardware_access_bypass: bool = True,
) -> dict[str, Any]:
"""Read the bench only when no physical execution currently owns it."""
control_transport = self._validate_control_transport(control_transport)
mqtt_selected = control_transport == WIRELESS_MQTT_TRANSPORT
if hardware_access_bypass:
# Wireless CAN0 is a thread-safe view over the persistent MQTT
# subscription. HardwareSeatExecutor still serializes this seat
# read with seat actuation through its adapter lock, while an
# absent CAN0 stream must not block the independent cached CAN1
# dashboard or make control requests fail with hardware_busy.
if mqtt_selected:
return self._synchronize_hardware_state_once(
conversation_id,
vehicle_id,
control_transport=control_transport,
)
with self.hardware_execution.reserve():
return self._synchronize_hardware_state_once(
conversation_id,
vehicle_id,
control_transport=control_transport,
)
authorized_client_id = self.hardware_access.ensure_authorized(hardware_client_id)
if mqtt_selected:
with self.hardware_access.use(authorized_client_id):
return self._synchronize_hardware_state_once(
conversation_id,
vehicle_id,
control_transport=control_transport,
)
with self.hardware_execution.reserve():
with self.hardware_access.use(authorized_client_id):
return self._synchronize_hardware_state_once(
conversation_id,
vehicle_id,
control_transport=control_transport,
)
def _synchronize_hardware_state_once(
self,
conversation_id: str,
vehicle_id: str | None = None,
*,
control_transport: str = WIRED_CAN_TRANSPORT,
) -> dict[str, Any]:
"""Read the bench and project it into the current passenger conversation."""
if not CONVERSATION_ID_PATTERN.fullmatch(conversation_id):
raise ValueError(
"conversation_id must start with conv- and contain only lowercase letters, numbers, or hyphens"
)
control_transport = self._validate_control_transport(control_transport)
mqtt_selected = control_transport == WIRELESS_MQTT_TRANSPORT
wired_state_bridge = None
if mqtt_selected:
if not self.vehicle_mqtt_settings.state_configured:
raise HardwareSafetyError("the MQTT CAN0 seat relay is not configured")
bus_factory = lambda: self._vehicle_mqtt_transport.bus(
channel=0,
replay_latest=True,
)
else:
if not self.hardware_settings.configured:
raise HardwareSafetyError(
"the passenger-seat CAN0 hardware runtime is not configured"
)
if (
self.hardware_settings.native_passenger_can0
and self.vehicle_can_settings.adapter_configured
):
wired_state_bridge = self.vehicle_can_settings.bridge(
[
ZlgChannelConfig(
channel=0,
frame_format="classic_can",
arbitration_bitrate=self.hardware_settings.bitrate,
data_bitrate=self.vehicle_can_settings.data_bitrate,
can_fd_iso=True,
termination_enabled=(
self.hardware_settings.termination_enabled
),
)
],
monitor_only=True,
)
bus_factory = lambda: wired_state_bridge.channel_bus(0)
else:
bus_factory = None
vehicle_profile = self.vehicle_profiles.get(vehicle_id)
try:
physical_state = self._new_hardware_executor(
bus_factory,
remote_transport=mqtt_selected,
).read_physical_state()
finally:
if wired_state_bridge is not None:
wired_state_bridge.close()
if mqtt_selected:
physical_state["source"] = "wireless_mqtt_ch0"
context = self.conversations.synchronize_physical_passenger(
conversation_id,
physical_state,
)
context = self._context_for_vehicle(context, vehicle_profile)
return {
"status": "succeeded",
"conversation_id": conversation_id,
"conversation_turn": context["turn_count"],
"source": "wireless_mqtt_ch0" if mqtt_selected else "physical_can",
"control_transport": control_transport,
"captured_at": physical_state.get("captured_at"),
"complete": bool(physical_state.get("complete")),
"missing": list(physical_state.get("missing", [])),
"physical_state": physical_state,
"actuator_state": context["devices"],
}
def synchronize_vehicle_state(
self,
conversation_id: str,
vehicle_id: str | None = None,
*,
control_transport: str = WIRED_CAN_TRANSPORT,
hardware_client_id: str | None = None,
hardware_access_bypass: bool = True,
) -> dict[str, Any]:
"""Passively receive CH1 driver-seat feedback without transmitting CAN."""
control_transport = self._validate_control_transport(control_transport)
mqtt_selected = control_transport == WIRELESS_MQTT_TRANSPORT
if hardware_access_bypass:
# The MQTT snapshot is already decoded and protected by the
# transport condition lock. It is independent from physical
# execution ownership and should remain readable during CAN0
# waits or an in-flight command.
if mqtt_selected:
return self._synchronize_vehicle_state_once(
conversation_id,
vehicle_id,
control_transport=control_transport,
)
with self.hardware_execution.reserve():
return self._synchronize_vehicle_state_once(
conversation_id,
vehicle_id,
control_transport=control_transport,
)
authorized_client_id = self.hardware_access.ensure_authorized(hardware_client_id)
if mqtt_selected:
with self.hardware_access.use(authorized_client_id):
return self._synchronize_vehicle_state_once(
conversation_id,
vehicle_id,
control_transport=control_transport,
)
with self.hardware_execution.reserve():
with self.hardware_access.use(authorized_client_id):
return self._synchronize_vehicle_state_once(
conversation_id,
vehicle_id,
control_transport=control_transport,
)
def start(self) -> None:
if self._vehicle_state_monitor is not None:
self._vehicle_state_monitor.start()
self._mode_scene_mqtt_publisher.start()
self._mode_scene_ingress.start()
self._vehicle_mqtt_transport.start()
def close(self) -> None:
self.mode_schedule_runner.close()
if self._vehicle_state_monitor is not None:
self._vehicle_state_monitor.close()
self._mode_scene_mqtt_publisher.close()
self._vehicle_mqtt_transport.close()
self._mode_scene_ingress.close()
def publish_pet_message(self, payload: dict[str, str]) -> dict[str, Any]:
"""Publish one NFC-authorized pet card through the shared vehicle broker."""
return self._vehicle_mqtt_transport.publish_pet_message(payload)
def publish_extension_toy_message(
self,
payload: dict[str, Any],
) -> dict[str, Any]:
"""Publish one NFC-detected extension capability announcement."""
return self._vehicle_mqtt_transport.publish_extension_toy_message(payload)
def publish_firmware_notification(
self,
payload: Mapping[str, Any],
) -> dict[str, Any]:
"""Publish one firmware release notification through the vehicle broker."""
return self._vehicle_mqtt_transport.publish_firmware_notification(payload)
def set_extension_control_handler(
self,
handler: Callable[[bytes], None] | None,
) -> None:
"""Route ``extension/ctl`` payloads from the shared broker connection."""
self._vehicle_mqtt_transport.set_extension_control_handler(handler)
def set_extension_control_rejection_handler(
self,
handler: Callable[[str, str], None] | None,
) -> None:
"""Project broker-level ``extension/ctl`` rejections into the UI status."""
self._vehicle_mqtt_transport.set_extension_control_rejection_handler(handler)
def _synchronize_vehicle_state_once(
self,
conversation_id: str,
vehicle_id: str | None = None,
*,
control_transport: str = WIRED_CAN_TRANSPORT,
) -> dict[str, Any]:
if not CONVERSATION_ID_PATTERN.fullmatch(conversation_id):
raise ValueError(
"conversation_id must start with conv- and contain only lowercase letters, numbers, or hyphens"
)
control_transport = self._validate_control_transport(control_transport)
mqtt_selected = control_transport == WIRELESS_MQTT_TRANSPORT
if mqtt_selected:
if not self.vehicle_mqtt_settings.state_configured:
raise HardwareSafetyError("the MQTT vehicle CAN relay is not configured")
elif not self.vehicle_can_settings.state_configured:
raise HardwareSafetyError("the CH1 vehicle CAN runtime is not configured")
vehicle_profile = self.vehicle_profiles.get(vehicle_id)
shared_runtime_enabled = (
self.vehicle_can_settings.backend != "zlgcan-linux"
and self.vehicle_can_settings.configured
and os.getenv(
"COGNITIVE_AGENT_SHARED_CAN_RUNTIME_ENABLED",
os.getenv(
"COGNITIVE_AGENT_SEAT_SHARED_ZCANPRO_BRIDGE_ENABLED", "true"
),
).strip().lower() in {"1", "true", "yes", "on"}
)
bridge = None
capture_sequence: int | None = None
if mqtt_selected:
snapshot = self._vehicle_mqtt_transport.snapshot()
feedback_states = list(snapshot.feedback_states)
observed_ids = set(snapshot.observed_ids)
captured_at = snapshot.captured_at
capture_sequence = snapshot.capture_sequence
elif self._vehicle_state_monitor is not None:
snapshot = self._vehicle_state_monitor.snapshot()
feedback_states = list(snapshot.feedback_states)
observed_ids = set(snapshot.observed_ids)
captured_at = snapshot.captured_at
capture_sequence = snapshot.capture_sequence
elif shared_runtime_enabled:
from cognitive_agent.application.self_repair.service import BRIDGE_MANAGER
if not hasattr(BRIDGE_MANAGER, "channel_bus_factory"):
raise HardwareSafetyError(
"CH1 feedback requires the standalone multi-channel CAN runtime"
)
bus = BRIDGE_MANAGER.channel_bus_factory(
self.vehicle_can_settings.channel
)()
else:
bridge = self.vehicle_can_settings.bridge(
[self.vehicle_can_settings.channel_config()],
monitor_only=True,
)
bus = bridge.channel_bus(self.vehicle_can_settings.channel)
if not mqtt_selected and self._vehicle_state_monitor is None:
feedback_states = []
observed_ids = set()
captured_at = None
# Keep driver-seat completeness compatible with the existing endpoint,
# while passively collecting every recognized CH1 body-state frame for
# the whole-vehicle dashboard during the same bounded receive window.
deadline = (
time.monotonic()
+ self.vehicle_can_settings.feedback_timeout_ms / 1000
)
try:
while time.monotonic() < deadline and observed_ids != OBSERVABLE_IDS:
remaining_ms = max(1, int((deadline - time.monotonic()) * 1000))
frame = bus.receive(remaining_ms, include_fd=True)
if frame is None:
break
if frame.channel != self.vehicle_can_settings.channel:
continue
if frame.arbitration_id not in OBSERVABLE_IDS or len(frame.data) < 8:
continue
decoded = decode_feedback(frame)
if not any(
key in decoded
for key in ("actuator_state", "domain_state", "vehicle_safety")
):
# 0x4EB is multiplexed. Only subtype 0x07 carries the
# verified speed signal, so unrelated subframes must not
# make the body-state snapshot look complete.
continue
frame_id = str(decoded.get("frame_id", ""))
decoded.update(
{
"valid": True,
"feedback_frame_id": frame_id,
"captured_at": _now(),
"verification": {
"authoritative": True,
"channel": 1,
"simulated": False,
"read_only": True,
"frame_id": frame_id,
"payload_size": len(frame.data),
},
}
)
feedback_states.append(decoded)
observed_ids.add(frame.arbitration_id)
captured_at = str(decoded["captured_at"])
finally:
if bridge is not None:
bridge.close()
else:
bus.close()
context = self.conversations.synchronize_physical_ch1_feedback(
conversation_id,
feedback_states,
)
context = self._context_for_vehicle(context, vehicle_profile)
missing = [
f"0x{frame_id:03X}"
for frame_id in sorted(REQUIRED_SEAT_IDS - observed_ids)
]
body_state_missing = [
f"0x{frame_id:03X}"
for frame_id in sorted(REQUIRED_BODY_IDS - observed_ids)
]
return {
"status": "succeeded",
"conversation_id": conversation_id,
"conversation_turn": context["turn_count"],
"source": (
"wireless_mqtt_ch1" if mqtt_selected else "physical_can_ch1"
),
"control_transport": control_transport,
"read_only": True,
"captured_at": captured_at or _now(),
"capture_sequence": capture_sequence,
"complete": not missing,
"missing": missing,
"body_state_complete": not body_state_missing,
"body_state_missing": body_state_missing,
"observed_frame_ids": [f"0x{frame_id:03X}" for frame_id in sorted(observed_ids)],
"actuator_state": context["devices"],
"domain_state": context["domain_devices"],
}
def _new_hardware_executor(
self,
bus_factory: Any | None = None,
*,
remote_transport: bool = False,
) -> HardwareSeatExecutor | MqttNativeSeatExecutor | ZcanBridgeSeatExecutor:
shared_bridge_enabled = os.getenv(
"COGNITIVE_AGENT_SHARED_CAN_RUNTIME_ENABLED",
os.getenv("COGNITIVE_AGENT_SEAT_SHARED_ZCANPRO_BRIDGE_ENABLED", "true"),
).strip().lower() in {"1", "true", "yes", "on"}
if (
bus_factory is None
and shared_bridge_enabled
and self.hardware_settings.backend != "zlgcan-linux"
):
# Import lazily so the ordinary dry-run service does not initialize
# any self-repair runtime state. Both modules then share the one
# process that is allowed to own the Windows ZLG adapter.
from cognitive_agent.application.self_repair.service import (
BRIDGE_MANAGER,
PENDING_DIR,
REQUIRED_BRIDGE_VERSION,
STATUS_PATH,
)
manager = BRIDGE_MANAGER
manager_snapshot = manager.snapshot()
if (
manager_snapshot.get("backend") == "standalone_zlg"
or Path(str(manager_snapshot.get("zcan_exe") or "")).is_file()
):
return ZcanBridgeSeatExecutor(
self.hardware_matrix,
manager,
STATUS_PATH,
PENDING_DIR,
required_version=REQUIRED_BRIDGE_VERSION,
auto_home=self.hardware_settings.auto_home_enabled,
)
resolved_bus_factory = bus_factory or self.hardware_settings.bus_factory()
executor = HardwareSeatExecutor(
self.hardware_matrix,
resolved_bus_factory,
thermal_timeout_ms=self.hardware_settings.thermal_timeout_ms,
ack_timeout_ms=self.hardware_settings.ack_timeout_ms,
axis_maximum_x100={
capability: int(
self.hardware_compiler.protocol["normalized_position_max"]
)
for capability in self.hardware_compiler.axes
},
motor_max_hall={
int(axis["motor_selector"]): int(axis["default_max_hall_count"])
for axis in self.hardware_compiler.axes.values()
},
stop_all_motor_selector=int(
self.hardware_compiler.protocol["stop_all_motor_selector"]
),
auto_home=(
self.hardware_settings.auto_home_enabled
if not remote_transport
else False
),
home_timeout_s=self.hardware_settings.home_timeout_s,
firmware_guard=(
None
if remote_transport
or self.hardware_settings.native_passenger_can0
else self.hardware_settings.firmware_guard(
resolved_bus_factory,
allow_programming=not remote_transport,
)
),
synchronize_position_before_first_motion=(
self.hardware_settings.position_sync_enabled
if not remote_transport
else False
),
position_sync_state=self._seat_position_memory_state,
require_thermal_feedback=not remote_transport,
feedback_required=not remote_transport,
)
if remote_transport:
return MqttNativeSeatExecutor(executor, resolved_bus_factory)
return executor
def _new_linux_wired_dual_channel_bridge(self) -> ZlgCanBridgeProcess:
"""Open CAN0 passenger classic CAN and CAN1 body CAN FD under one owner."""
if not self.hardware_settings.native_passenger_can0:
raise HardwareSafetyError(
"the shared Linux dual-channel bridge requires native passenger CAN0"
)
if not self.vehicle_can_settings.configured:
raise HardwareSafetyError(
"the shared Linux dual-channel bridge requires configured vehicle CAN1"
)
if (
self.hardware_settings.device_type
!= self.vehicle_can_settings.device_type
or self.hardware_settings.device_index
!= self.vehicle_can_settings.device_index
or self.hardware_settings.zlg_dll.resolve()
!= self.vehicle_can_settings.zlg_dll.resolve()
):
raise HardwareSafetyError(
"CAN0 and CAN1 must use the same Linux ZLG device and SDK library"
)
return self.vehicle_can_settings.bridge(
[
ZlgChannelConfig(
channel=0,
frame_format="classic_can",
arbitration_bitrate=self.hardware_settings.bitrate,
data_bitrate=self.vehicle_can_settings.data_bitrate,
can_fd_iso=True,
termination_enabled=(
self.hardware_settings.termination_enabled
),
),
self.vehicle_can_settings.channel_config(),
]
)
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,
assistant_plan: dict[str, Any] | None = None,
mode_profile_draft_context: dict[str, Any] | None = None,
_cloud_instruction_context: dict[str, Any] | None = None,
) -> dict[str, Any]:
"""Validate and execute deterministic controls before hosted parsing."""
local_stage_started = time.perf_counter()
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 = self.is_mode_profile_configuration_request(utterance)
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,
_assistant_plan=assistant_plan,
_mode_profile_draft_context=mode_profile_draft_context,
_cloud_instruction_context=_cloud_instruction_context,
)
result.update(
{
"phase": "control_plane_complete",
"preview_request_id": result["request_id"],
"unresolved_clauses": list(
result.get("unresolved_clauses") or []
),
"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)
locally_parsed_clauses = [
clause
for clause, _, status in clause_results
if status != "unparsed"
]
locally_resolved_clauses = [
clause
for clause, _, status in clause_results
if status == "complete"
]
locally_unresolved_clauses = [
clause
for clause, _, status in clause_results
if status != "complete"
]
supplied_routing = (
_cloud_instruction_context
if isinstance(_cloud_instruction_context, dict)
else {}
)
def routing_clauses(key: str) -> list[str]:
raw = supplied_routing.get(key)
if not isinstance(raw, list):
return []
return [
text
for item in raw[:64]
for text in [str(item).strip()[:1000]]
if text
]
def unique_clauses(*groups: list[str]) -> list[str]:
return list(dict.fromkeys(item for group in groups for item in group))
parsed_clauses = unique_clauses(
routing_clauses("parsed_clauses"),
locally_parsed_clauses,
)
resolved_clauses = unique_clauses(
routing_clauses("resolved_clauses"),
locally_resolved_clauses,
)
unresolved_clauses = locally_unresolved_clauses
routing_unresolved_clauses = unique_clauses(
routing_clauses("unresolved_clauses"),
locally_unresolved_clauses,
)
original_utterance = str(
supplied_routing.get("original_utterance") or utterance
).strip()[:1000]
natural_language_routing = {
"original_utterance": original_utterance,
"parsed_clauses": parsed_clauses,
"resolved_clauses": resolved_clauses,
"unresolved_clauses": routing_unresolved_clauses,
}
local_dispatch_elapsed_ms: float | None = None
def publish_local_commands(validated: dict[str, Any]) -> None:
nonlocal local_dispatch_elapsed_ms
local_dispatch_elapsed_ms = round(
(time.perf_counter() - local_stage_started) * 1000,
3,
)
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
),
"natural_language_routing": deepcopy(
natural_language_routing
),
"local_control_budget_ms": LOCAL_CONTROL_BUDGET_MS,
"local_control_elapsed_ms": local_dispatch_elapsed_ms,
"local_control_within_budget": (
local_dispatch_elapsed_ms < LOCAL_CONTROL_BUDGET_MS
),
}
)
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,
_assistant_plan=assistant_plan,
_mode_profile_draft_context=mode_profile_draft_context,
_cloud_instruction_context=natural_language_routing,
)
elapsed_ms = round(
(time.perf_counter() - local_stage_started) * 1000,
3,
)
local_control_elapsed_ms = (
local_dispatch_elapsed_ms
if local_dispatch_elapsed_ms is not None
else elapsed_ms
)
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,
"local_control_budget_ms": LOCAL_CONTROL_BUDGET_MS,
"local_control_elapsed_ms": local_control_elapsed_ms,
"local_control_within_budget": (
local_control_elapsed_ms < LOCAL_CONTROL_BUDGET_MS
),
"parsed_clauses": parsed_clauses,
"resolved_clauses": resolved_clauses,
"unresolved_clauses": unresolved_clauses,
"natural_language_routing": deepcopy(
natural_language_routing
),
"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,
"parsed_clauses": parsed_clauses,
"resolved_clauses": resolved_clauses,
"unresolved_clauses": unresolved_clauses,
"natural_language_routing": natural_language_routing,
"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)
}
manifest_routing = manifest.get("natural_language_routing")
natural_language_routing = (
deepcopy(manifest_routing)
if isinstance(manifest_routing, dict)
else {
"original_utterance": str(manifest.get("utterance") or ""),
"parsed_clauses": list(manifest.get("parsed_clauses") or []),
"resolved_clauses": list(manifest.get("resolved_clauses") or []),
"unresolved_clauses": unresolved_clauses,
}
)
routing_unresolved = natural_language_routing.get("unresolved_clauses")
natural_language_routing["unresolved_clauses"] = list(
dict.fromkeys(
[
str(item).strip()
for item in (
routing_unresolved
if isinstance(routing_unresolved, list)
else []
)
if str(item).strip()
]
+ unresolved_clauses
)
)
cloud_utterance = ",".join(unresolved_clauses)
natural_language_routing["cloud_input_utterance"] = cloud_utterance
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(
cloud_utterance,
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,
_cloud_instruction_context=natural_language_routing,
)
merged = self._merge_native_can_phases(preview, supplement)
merged.update(
{
"phase": "two_stage_complete",
"utterance": str(
preview.get("utterance")
if preview.get("orchestration_route") == "unified_can_infrared"
else 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,
"natural_language_routing": natural_language_routing,
}
)
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")),
**deepcopy(preview.get("phase_execution_reports") or {}),
}
merged["operation_results"] = [
*deepcopy(preview.get("operation_results") or []),
*deepcopy(supplement.get("operation_results") or []),
]
if preview.get("orchestration_route") == "unified_can_infrared":
merged["orchestration_route"] = "unified_can_infrared"
for key in (
"infrared_actions",
"peripheral",
"control_transports",
):
merged[key] = deepcopy(preview.get(key))
merged["provider_used"] = (
f"{supplement.get('provider_used') or 'cloud_supplement'}"
"+infrared_local_rules"
)
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_can_commands(
self,
commands: object,
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,
control_transport: str = WIRED_CAN_TRANSPORT,
) -> dict[str, Any]:
"""Compile and execute an exact UI command batch without intent parsing."""
resolved_conversation_id = conversation_id or f"conv-{uuid.uuid4().hex}"
if not CONVERSATION_ID_PATTERN.fullmatch(resolved_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)
context = self._context_for_vehicle(
self.conversations.snapshot(resolved_conversation_id),
vehicle_profile,
)
direct_can_batch = build_direct_can_intent(commands, context)
normalized_commands, _parsed = direct_can_batch
return self.process(
";".join(normalized_commands),
provider="local",
conversation_id=resolved_conversation_id,
execution_mode=execution_mode,
approval_token=approval_token,
vehicle_id=vehicle_id,
simulation_fault=simulation_fault,
hardware_client_id=hardware_client_id,
hardware_access_bypass=hardware_access_bypass,
control_transport=control_transport,
_direct_can_batch=direct_can_batch,
)
def _adapt_speech_understanding(
self,
understanding: dict[str, Any],
context: dict[str, Any],
vehicle_profile: Any,
) -> dict[str, Any]:
"""Revalidate a server-owned speech result into the normal control IR.
Speech is allowed to skip a second natural-language parse, but it never
skips the authoritative capability catalog or ModelPlanAdapter. Native
CAN actions and configured-domain/seat actions are routed through the
same structures used by the rest of one-sentence orchestration.
"""
if understanding.get("execution_eligible") is not True:
raise ValueError("speech understanding is not execution eligible")
commands = understanding.get("commands")
if not isinstance(commands, list) or not commands:
raise ValueError("speech understanding has no executable commands")
native_actions: list[dict[str, Any]] = []
model_requests: list[dict[str, Any]] = []
detected_domains: list[str] = []
def add_payload(payload: object, source_text: str) -> None:
if not isinstance(payload, dict):
raise ValueError("speech command has no validated executor payload")
domain = str(payload.get("domain", ""))
capability = str(payload.get("capability", ""))
target = str(payload.get("target", ""))
operation = str(payload.get("operation", "set"))
parameters = payload.get("parameters")
if not domain or not capability or not target or not isinstance(parameters, dict):
raise ValueError("speech executor payload is incomplete")
definitions = tuple(
definition
for definition in self.capability_registry.find(capability)
if definition.domain == domain
)
if len(definitions) != 1:
raise ValueError(
f"speech capability is not uniquely registered: {domain}.{capability}"
)
if domain not in detected_domains:
detected_domains.append(domain)
# Catalog availability describes where a capability is declared,
# not necessarily which executor owns it. Roof, climate and
# ambient capabilities are declared in domain catalogs but some
# are byte-exact NativeCanRuntime operations. Keep this routing in
# lockstep with CapabilityResolver._executor_binding().
if capability in self.native_can_runtime.supported_capabilities:
native_actions.append(
{
"domain": domain,
"capability": capability,
"target": target,
"operation": operation,
"parameters": deepcopy(parameters),
"parse_source": "speech_semantic",
"source_span": source_text,
"reason": "语音语义层已按真实能力目录解析并由服务端重新校验。",
}
)
return
model_requests.append(
{
"domain": domain,
"capability": capability,
"target_scope": target,
"operation": operation,
"parameters": [
{"name": name, "value": value}
for name, value in parameters.items()
],
"reason": "语音语义层已按真实能力目录解析并由服务端重新校验。",
}
)
for command in commands:
if not isinstance(command, dict):
raise ValueError("speech command must be an object")
if (
command.get("status") != "active"
or command.get("negated") is True
or command.get("resolution_status") != "executable"
or command.get("validation_issues")
):
raise ValueError("speech command failed execution invariants")
source_text = str(command.get("source_text") or understanding.get("raw_text") or "")
payload = command.get("executor_payload")
if command.get("mode_profile_id"):
if not isinstance(payload, dict) or not isinstance(payload.get("operations"), list):
raise ValueError("speech mode command has no validated operations")
for operation_payload in payload["operations"]:
add_payload(operation_payload, source_text)
else:
add_payload(payload, source_text)
plan = {
"detected_domains": detected_domains,
"requests": model_requests,
"ambiguities": [],
"decision_summary": [
"复用服务端语音理解结果;未对 cleanText 进行第二次自然语言解析。"
],
"confidence": min(
float(command.get("confidence", 0.0))
for command in commands
if isinstance(command, dict)
),
}
parsed = self.model_plan_adapter.adapt(
plan,
context=context,
vehicle_profile=vehicle_profile,
utterance=str(understanding.get("raw_text") or ""),
)
parsed["native_can_actions"] = native_actions
parsed.setdefault("semantic_constraints", [])
parsed.setdefault("unresolved_spans", [])
if parsed.get("ambiguities") or parsed.get("deferred_requests"):
raise ValueError("speech command no longer passes capability resolution")
mapped_count = (
len(parsed.get("actions", ()))
+ len(parsed.get("domain_actions", ()))
+ len(parsed.get("native_can_actions", ()))
)
expected_count = len(model_requests) + len(native_actions)
if mapped_count != expected_count:
raise ValueError("speech command mapping lost a validated capability mutation")
return parsed
def process(
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,
_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,
_direct_can_batch: tuple[list[str], dict[str, Any]] | None = None,
_direct_mode_profile_id: str | None = None,
_speech_understanding: dict[str, Any] | None = None,
_assistant_plan: dict[str, Any] | None = None,
_mode_profile_draft_context: dict[str, Any] | None = None,
_cloud_instruction_context: dict[str, Any] | None = None,
) -> dict[str, Any]:
control_transport = self._validate_control_transport(control_transport)
# Creating a reusable scene is a configuration workflow. Synthesize
# and validate it in dry-run even when the console currently targets
# the real bench, otherwise virtual domains such as climate or ambient
# light would be rejected before the profile can be persisted.
draft_reference_name = (
str(_mode_profile_draft_context.get("name") or "").strip()
if isinstance(_mode_profile_draft_context, dict)
else ""
)
draft_update = parse_mode_profile_update(
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,
)
if (
self._parse_mode_profile_create_name(utterance.strip()) is not None
or draft_update is not None
or mode_delete_name is not None
or mode_profile_query
):
execution_mode = "dry_run"
schedule_requested = "模式" in utterance or _direct_mode_profile_id or is_schedule_stop(utterance)
schedule_vehicle_id = vehicle_id or (self.vehicle_profiles.default().id if schedule_requested else "")
if execution_mode not in {"dry_run", "simulated_hardware", "hardware"}:
raise ValueError(f"unsupported execution mode: {execution_mode}")
if is_schedule_stop(utterance):
return self._cancel_mode_schedule(schedule_vehicle_id, conversation_id, utterance, execution_mode)
schedule_profile = (self.mode_profiles.get_profile(_direct_mode_profile_id, schedule_vehicle_id)
if _direct_mode_profile_id else None)
if schedule_requested and schedule_profile is None and not (draft_update or mode_delete_name or mode_profile_query or parse_mode_profile_create(utterance)):
candidates = self.mode_profiles.match_text(utterance, vehicle_id=schedule_vehicle_id)
if len(candidates) == 1 and candidates[0].schedule is not None:
aliases = "|".join(re.escape(name) for name in (candidates[0].name, *candidates[0].aliases))
if re.fullmatch(
r"(?:(?:请|麻烦|帮我|帮忙|给我|我想|我要|想要)\s*){0,4}"
r"(?:(?:主驾|副驾|驾驶位|副驾驶|主驾驶)(?:座椅)?\s*)?"
r"(?:进入|启动|执行|开启|启用|运行|打开|调用|应用|使用|切换到)\s*"
rf"(?:{aliases})(?:吧|一下)?[。!!\s]*", utterance.strip(),
):
schedule_profile = candidates[0]
if schedule_profile is not None and schedule_profile.schedule is not None:
return self._run_mode_schedule(
schedule_profile, utterance=utterance, conversation_id=conversation_id,
execution_mode=execution_mode, approval_token=approval_token, vehicle_id=schedule_vehicle_id,
simulation_fault=simulation_fault, hardware_client_id=hardware_client_id,
hardware_access_bypass=hardware_access_bypass, control_transport=control_transport,
)
if execution_mode != "hardware":
return self._process_once(
utterance,
provider,
conversation_id,
execution_mode,
approval_token,
vehicle_id,
simulation_fault,
reasoning_effort,
control_transport,
_local_preview,
_excluded_control_signatures,
_model_parse_source,
_allow_empty_control_result,
_on_commands_validated,
_direct_can_batch,
_direct_mode_profile_id,
_speech_understanding,
assistant_plan=_assistant_plan,
mode_profile_draft_context=_mode_profile_draft_context,
cloud_instruction_context=_cloud_instruction_context,
)
if control_transport == WIRELESS_MQTT_TRANSPORT:
# The head unit owns both physical CAN channels in wireless mode.
# Local T2G/ZLG hardware access, firmware profiles and the local
# physical-bench lease are unrelated to MQTT relay execution.
result = self._process_once(
utterance,
provider,
conversation_id,
execution_mode,
approval_token,
vehicle_id,
simulation_fault,
reasoning_effort,
control_transport,
_local_preview,
_excluded_control_signatures,
_model_parse_source,
_allow_empty_control_result,
_on_commands_validated,
_direct_can_batch,
_direct_mode_profile_id,
_speech_understanding,
assistant_plan=_assistant_plan,
mode_profile_draft_context=_mode_profile_draft_context,
cloud_instruction_context=_cloud_instruction_context,
)
return self._finalize_n7_control_transfer(
result,
execution_mode=execution_mode,
local_preview=_local_preview,
)
if hardware_access_bypass or self._driver_only_vehicle_execution_active(
control_transport
):
with self.hardware_execution.reserve():
result = self._process_once(
utterance,
provider,
conversation_id,
execution_mode,
approval_token,
vehicle_id,
simulation_fault,
reasoning_effort,
control_transport,
_local_preview,
_excluded_control_signatures,
_model_parse_source,
_allow_empty_control_result,
_on_commands_validated,
_direct_can_batch,
_direct_mode_profile_id,
_speech_understanding,
assistant_plan=_assistant_plan,
mode_profile_draft_context=_mode_profile_draft_context,
cloud_instruction_context=_cloud_instruction_context,
)
return self._finalize_n7_control_transfer(
result,
execution_mode=execution_mode,
local_preview=_local_preview,
)
authorized_client_id = self.hardware_access.ensure_authorized(hardware_client_id)
with self.hardware_execution.reserve():
with self.hardware_access.use(authorized_client_id):
result = self._process_once(
utterance,
provider,
conversation_id,
execution_mode,
approval_token,
vehicle_id,
simulation_fault,
reasoning_effort,
control_transport,
_local_preview,
_excluded_control_signatures,
_model_parse_source,
_allow_empty_control_result,
_on_commands_validated,
_direct_can_batch,
_direct_mode_profile_id,
_speech_understanding,
assistant_plan=_assistant_plan,
mode_profile_draft_context=_mode_profile_draft_context,
cloud_instruction_context=_cloud_instruction_context,
)
return self._finalize_n7_control_transfer(
result,
execution_mode=execution_mode,
local_preview=_local_preview,
)
def _process_once(
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",
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,
direct_can_batch: tuple[list[str], dict[str, Any]] | None = None,
direct_mode_profile_id: str | None = None,
speech_understanding: dict[str, Any] | None = None,
assistant_plan: dict[str, Any] | None = None,
allow_fuzzy_mode_profile_match: bool = True,
allow_scene_composition: bool = False,
scene_scope_utterance: str | None = None,
mode_creation_scopes: frozenset[str] | None = None,
mode_profile_draft_context: dict[str, Any] | None = None,
cloud_instruction_context: dict[str, Any] | None = None,
) -> dict[str, Any]:
control_transport = self._validate_control_transport(control_transport)
utterance = utterance.strip()
if not utterance:
raise ValueError("utterance must not be empty")
if len(utterance) > 1000:
raise ValueError("utterance must be at most 1000 characters")
if provider not in {"auto", "openai", "local"}:
raise ValueError("provider must be auto, openai, or local")
effective_reasoning_effort = (
self.openai.reasoning_effort
if reasoning_effort is None
else self.openai.validate_reasoning_effort(reasoning_effort)
)
if execution_mode not in {"dry_run", "simulated_hardware", "hardware"}:
raise ValueError(
"execution_mode must be dry_run, simulated_hardware, or hardware"
)
simulated_hardware_request = execution_mode == "simulated_hardware"
if simulated_hardware_request and not self.simulated_hardware_enabled:
raise HardwareSafetyError(
"simulated hardware is disabled; set "
"COGNITIVE_AGENT_SIMULATED_HARDWARE_ENABLED=true"
)
if simulation_fault not in SIMULATED_HARDWARE_FAULTS:
raise ValueError(
"simulation_fault must be one of "
+ ", ".join(SIMULATED_HARDWARE_FAULTS)
)
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)
else ""
)
mode_update = parse_mode_profile_update(
utterance,
expected_mode_name=draft_reference_name or None,
)
mode_profile_query = is_mode_profile_query(
utterance,
expected_mode_name=draft_reference_name or None,
)
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
)
hardware_request = execution_mode == "hardware"
if hardware_request and _has_mixed_driver_passenger_seat_scope(utterance):
raise HardwareSafetyError(
"hardware mode refuses a mixed CH1 driver/front-left and "
"CH0 passenger/front-right seat request"
)
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)
direct_mode_profile = (
self.mode_profiles.get_profile(
direct_mode_profile_id,
vehicle_profile.id,
)
if direct_mode_profile_id
else None
)
if mode_create_name is not None:
return self._process_mode_profile_create(
utterance,
provider,
conversation_id,
execution_mode,
approval_token,
vehicle_profile,
simulation_fault,
mode_create_name,
control_transport,
assistant_plan=assistant_plan,
)
# A request id is also the opaque capability used to reference a
# server-owned draft artifact. Keep the full 128 bits of randomness;
# the previous six-hex suffix was guessable under multi-user traffic.
request_id = (
f"req-{datetime.now(UTC).strftime('%Y%m%d%H%M%S')}-{uuid.uuid4().hex}"
)
context = self._context_for_vehicle(
self.conversations.snapshot(conversation_id),
vehicle_profile,
)
context["mode_profiles"] = self.mode_profiles.public_modes(vehicle_profile.id)
matched_modes = (
[direct_mode_profile]
if direct_mode_profile is not None
else []
if (
direct_can_batch is not None
or speech_understanding is not None
or not allow_fuzzy_mode_profile_match
)
else self.mode_profiles.match_text(
utterance,
vehicle_id=vehicle_profile.id,
)
)
traces: list[dict[str, Any]] = []
result: dict[str, Any] = {
"request_id": request_id,
"utterance": utterance,
"conversation_id": conversation_id,
"conversation_turn": context["turn_count"],
"status": "processing",
"provider_requested": provider,
"reasoning_effort_requested": effective_reasoning_effort,
"reasoning_effort_used": None,
"execution_mode": execution_mode,
"control_transport": control_transport,
"hardware_execution_allowed": (
hardware_request and not control_plane_request
),
"simulated_hardware": simulated_hardware_request,
"simulation_fault": simulation_fault if simulated_hardware_request else None,
"vehicle": vehicle_profile.public_dict(),
"matched_modes": [
profile.public_dict() for profile in matched_modes
],
"mode_profile": None,
"decision_summary": [],
"deferred_requests": [],
"model_requests": [],
"intent_ir": None,
"function_config": None,
"function_configs": {},
"scene_config": None,
"preset_config": None,
"validation": [],
"command_plan": None,
"command_plans": {},
"execution_report": None,
"actuator_state": context["devices"],
"domain_state": context["domain_devices"],
"saved_presets": [
{"name": item["name"], "target": item["target"]}
for item in context.get("presets", [])
],
"trace_events": traces,
"can_commands": (
list(direct_can_batch[0]) if direct_can_batch is not None else []
),
"speech_understanding": (
{
"utterance_id": speech_understanding.get("utterance_id"),
"raw_text": speech_understanding.get("raw_text"),
"clean_text": speech_understanding.get("clean_text"),
"semantic_status": speech_understanding.get("semantic_status"),
}
if speech_understanding is not None
else None
),
}
started = time.perf_counter()
if mode_profile_query:
result["status"] = "needs_confirmation"
result["execution_mode"] = "dry_run"
result["hardware_execution_allowed"] = False
result["orchestration_route"] = "mode_profile_query"
result["validation"] = [
{
"severity": "warning",
"code": "MODE_PROFILE_QUERY_NOT_CONTROL",
"path": "utterance",
"message": "该语句是在询问功能模式,不是模式修改或车辆执行指令。",
}
]
result["decision_summary"] = [
"识别为功能模式问答;未生成或执行任何车辆控制动作。"
]
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,
request_id,
conversation_id,
context,
mode_save_name,
vehicle_profile,
result,
traces,
)
if mode_restore_name is not None:
return self._process_mode_profile_restore(
utterance,
request_id,
conversation_id,
mode_restore_name,
vehicle_profile,
result,
traces,
)
if mode_update is not None:
mode_name, change_text = mode_update
if (
draft_reference_name
and normalize_mode_profile_name(draft_reference_name)
== normalize_mode_profile_name(mode_name)
):
return self._process_mode_profile_draft_update(
utterance,
request_id,
provider,
conversation_id,
context,
mode_name,
change_text,
vehicle_profile,
result,
traces,
mode_profile_draft_context,
approval_token=approval_token,
simulation_fault=simulation_fault,
control_transport=control_transport,
assistant_plan=assistant_plan,
)
return self._process_mode_profile_update(
utterance,
request_id,
provider,
conversation_id,
context,
mode_name,
change_text,
vehicle_profile,
result,
traces,
)
if direct_mode_profile is not None:
mode_requests, n7_transfer_actions = (
self._mode_requests_and_n7_transfers(direct_mode_profile)
)
if mode_creation_scopes is not None:
mode_requests = project_seed_operations(mode_requests, mode_creation_scopes)
detected_domains = list(
dict.fromkeys(
str(item.get("domain") or "")
for item in mode_requests
if str(item.get("domain") or "")
)
)
parsed = self.model_plan_adapter.adapt(
{
"detected_domains": detected_domains or ["seat"],
"requests": mode_requests,
"ambiguities": [],
"decision_summary": [
f"已载入模式“{direct_mode_profile.name}”保存的结构化操作。"
],
"confidence": 1.0,
},
context,
vehicle_profile,
)
saved_native_actions: list[dict[str, Any]] = []
for request in mode_requests:
parameters = {
str(item.get("name")): deepcopy(item.get("value"))
for item in request.get("parameters", [])
if isinstance(item, dict) and str(item.get("name") or "")
}
action = {
"domain": str(request.get("domain") or ""),
"capability": str(request.get("capability") or ""),
"target": str(request.get("target_scope") or "vehicle"),
"parameters": parameters,
"operation": str(request.get("operation") or "set"),
"reason": str(request.get("reason") or ""),
"execution_backend": "native_can",
"parse_source": "saved_mode_command_flow",
}
if self.native_can_runtime.supports(action):
saved_native_actions.append(action)
parsed["native_can_actions"] = [
*list(parsed.get("native_can_actions", [])),
*saved_native_actions,
]
if n7_transfer_actions:
parsed["deferred_requests"] = [
*list(parsed.get("deferred_requests", [])),
*self._n7_transfer_deferred_requests(
n7_transfer_actions
),
]
parsed["_n7_transfer_actions"] = n7_transfer_actions
parsed["_n7_scene_profile"] = {
"id": direct_mode_profile.id,
"name": direct_mode_profile.name,
}
parsed["detected_domains"] = self._merge_domains(
list(parsed.get("detected_domains", [])),
[
str(action.get("domain") or "")
for action in n7_transfer_actions
if str(action.get("domain") or "")
],
)
if self._has_local_mode_peripheral(direct_mode_profile):
parsed["_saved_mode_local_peripheral"] = True
provider_used = "saved_mode_command_flow"
provider_notice = None
elif direct_can_batch is not None:
parsed = deepcopy(direct_can_batch[1])
provider_used = "direct_can_control"
provider_notice = None
elif speech_understanding is not None:
parsed = self._adapt_speech_understanding(
speech_understanding,
context,
vehicle_profile,
)
parsed = self._mark_control_parse_source(
parsed,
"speech_semantic",
overwrite=True,
)
provider_used = "speech_semantic"
provider_notice = "使用服务端缓存并重新校验的语音结构化意图。"
else:
parsed, provider_used, provider_notice = self._interpret(
utterance,
provider,
context,
vehicle_profile,
reasoning_effort=reasoning_effort,
local_preview=local_preview,
model_parse_source=model_parse_source,
assistant_plan=assistant_plan,
allow_scene_composition=allow_scene_composition,
scene_scope_utterance=scene_scope_utterance,
cloud_instruction_context=cloud_instruction_context,
)
scene_label_only = assistant_plan is not None and allow_scene_composition
dynamic_mode_stop = (
not scene_label_only and self._is_dynamic_mode_stop_request(utterance)
)
if dynamic_mode_stop:
parsed = self._apply_dynamic_mode_stop_request(parsed, utterance)
# Reuse the same conservative clause segmentation for the final
# native pass. Without this normalization a Chinese enumeration
# comma between independent controls can leak an early target
# (for example ``副驾``) into a later HVAC clause and can also
# hide otherwise valid window/temperature actions.
# Name-only scene plans have already been checked against exact
# targets by _interpret. Their synthesized 进入<name> label is not
# another atomic instruction: reparsing 座椅舒适模式 here applies
# the unscoped-seat driver default and discards valid passenger
# actions. Explicit user instructions never enter this branch.
segmented_utterance = "" if scene_label_only else ",".join(
self._split_intent_clauses(utterance)
)
relative_resolution = (
self.native_can_relative_resolver.resolve(
segmented_utterance,
context,
)
if self.native_can_relative_resolver.matches(
segmented_utterance
)
else None
)
native_merge_utterance = segmented_utterance
if any(
request.get("capability") in {SAVE_PRESET_CAPABILITY, APPLY_PRESET_CAPABILITY}
for request in parsed.get("model_requests", [])
):
# These registered capabilities currently have one executable
# target (the passenger-seat posture store). Keep that
# adapter-resolved target from being discarded by the native
# parser's general unscoped-seat => driver default. The
# original utterance is still used by the language safety
# guards below.
native_merge_utterance = f"副驾座椅,{utterance}"
parsed = merge_native_can_intent(
parsed,
native_merge_utterance,
relative_actions=(
relative_resolution.actions if relative_resolution else None
),
relative_ambiguities=(
relative_resolution.ambiguities if relative_resolution else None
),
relative_claimed_controls=(
relative_resolution.claimed_controls if relative_resolution else None
),
)
if not dynamic_mode_stop:
# Apply repository-backed modes after the final native-CAN
# merge. Otherwise that merge can discard a valid passenger
# mode operation while enforcing its generic unscoped-seat
# CH1 default.
parsed = self._apply_mode_profiles(
parsed,
matched_modes,
context,
vehicle_profile,
utterance=utterance,
)
parsed = self._mark_control_parse_source(parsed, "local_rule")
if direct_mode_profile is not None:
parsed = self._mark_control_parse_source(
parsed,
"saved_mode_command_flow",
)
if local_preview:
# A preview may discard ordinary model-supplement questions, but
# target and safety conflicts are execution gates. Keeping these
# makes the assistant path no less strict than normal process().
parsed["ambiguities"] = [
item
for item in parsed.get("ambiguities", [])
if str(item.get("field", ""))
in {"target", "target_scope", "mode_profile", "risk", "safety", "utterance"}
or str(item.get("reason", "")).startswith(
("mode_target_", "safety_", "risk_", "mixed_")
)
]
parsed["unresolved_spans"] = [
item
for item in parsed.get("unresolved_spans", [])
if str(item.get("reason", "")).startswith(
("mode_target_", "safety_", "risk_", "mixed_")
)
]
parsed["model_requests"] = []
if excluded_control_signatures:
parsed = self._filter_excluded_controls(
parsed,
excluded_control_signatures,
)
parsed = apply_language_safety_guards(
parsed,
utterance,
scene_composition=assistant_plan is not None and allow_scene_composition,
scene_scope_utterance=scene_scope_utterance,
)
parsed = self._partition_native_actions(
parsed,
promote_supported=(
direct_mode_profile is not None
or (
hardware_request
and (
self.vehicle_mqtt_settings.enabled
if control_transport == WIRELESS_MQTT_TRANSPORT
else self.vehicle_can_settings.enabled
)
)
),
)
n7_transfer_actions = parsed.get("_n7_transfer_actions", [])
n7_scene_profile = parsed.get("_n7_scene_profile")
if isinstance(n7_transfer_actions, list) and n7_transfer_actions:
result["n7_transfer_candidates"] = deepcopy(n7_transfer_actions)
if isinstance(n7_scene_profile, dict):
result["n7_source_scene"] = deepcopy(n7_scene_profile)
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",
"preset_actions",
)
)
if (
(
allow_empty_control_result
or parsed.get("_saved_mode_local_peripheral") is True
)
and not has_control
and not parsed.get("ambiguities")
):
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
and parsed.get("domain_actions")
and (parsed.get("actions") or parsed.get("native_can_actions"))
):
parsed = self._defer_nonwhitelisted_wireless_actions(parsed)
if parsed.get("domain_actions"):
unsupported = sorted(
{
str(action.get("capability", "unknown"))
for action in parsed["domain_actions"]
}
)
raise HardwareSafetyError(
"hardware mode refuses virtual or unverified vehicle actions: "
+ ", ".join(unsupported)
)
if (
not parsed.get("actions")
and not parsed.get("native_can_actions")
and not parsed.get("_n7_transfer_actions")
):
raise HardwareSafetyError(
"hardware mode requires at least one verified seat or vehicle CAN operation"
)
if parsed.get("actions") and parsed.get("native_can_actions"):
if (
control_transport == WIRED_CAN_TRANSPORT
and self.hardware_settings.backend
not in {"zlgcan", "zlgcan-linux"}
):
raise HardwareSafetyError(
"linked seat/vehicle execution requires a shared ZLG backend"
)
if control_transport == WIRED_CAN_TRANSPORT:
try:
seat_channel = int(self.hardware_settings.channel, 0)
except ValueError as exc:
raise HardwareSafetyError(
"ZLG seat CAN channel must be an integer"
) from exc
if seat_channel == self.vehicle_can_settings.channel:
raise HardwareSafetyError(
"seat and vehicle CAN must use different adapter channels"
)
self._trace(
traces,
request_id,
"intent_parse",
"succeeded",
f"使用 {provider_used} 完成结构化意图抽取。",
started,
{
"provider": provider_used,
"confidence": parsed["confidence"],
"conversation_turn": context["turn_count"],
},
)
if provider_notice:
parsed["decision_summary"].append(provider_notice)
result["provider_used"] = provider_used
if "openai:" in provider_used:
result["reasoning_effort_used"] = effective_reasoning_effort
result["decision_summary"] = parsed["decision_summary"]
result["deferred_requests"] = parsed.get("deferred_requests", [])
result["model_requests"] = parsed.get("model_requests", [])
if (
parsed.get("semantic_constraints")
and not parsed.get("actions")
and not parsed.get("domain_actions")
and not parsed.get("native_can_actions")
and not parsed.get("ambiguities")
and not parsed.get("unresolved_spans")
):
intent_ir = build_intent_ir(
parsed,
utterance,
request_id,
provider_used,
self.semantic_capability_metadata,
)
result["intent_ir"] = intent_ir
result["validation"] = self.contracts.validate(intent_ir)
result["status"] = "no_action"
self._trace(
traces,
request_id,
"safety_check",
"succeeded",
"检测到禁止或保持约束;未生成、编译或发送任何 CAN 动作。",
time.perf_counter(),
{"constraint_count": len(intent_ir["spec"]["constraints"]), "execution_eligible": False},
domain="orchestration",
)
self._persist(result)
return result
context_started = time.perf_counter()
self._trace(
traces,
request_id,
"context_resolve",
"succeeded",
(
f"已将会话 {conversation_id} 的第 {context['turn_count']} 轮执行状态"
"、域能力注册表和长期模式配置作为模型上下文;"
"本地层仅注入已命中的长期模式动作。"
),
context_started,
{
"conversation_id": conversation_id,
"turn_count": context["turn_count"],
"last_target": context["last_target"],
"state_before": context["devices"],
"capability_domains": [
item["domain"] for item in self.capability_context["domains"]
],
"matched_modes": [profile.id for profile in matched_modes],
},
)
route_started = time.perf_counter()
detected_domains = parsed.get("detected_domains", ["seat"])
seat_only = detected_domains == ["seat"]
self._trace(
traces,
request_id,
"domain_route",
"succeeded",
(
"仅识别到 seat 领域,使用已验证的座椅单域闭环。"
if seat_only
else (
f"识别到 {','.join(detected_domains)} 领域,"
"按领域边界生成独立 FunctionConfig。"
)
),
route_started,
{
"domains": detected_domains,
"mode": "single_domain" if len(detected_domains) == 1 else "cross_domain",
},
)
if parsed.get("domain_actions") or parsed.get("native_can_actions") or any(
domain != "seat" for domain in detected_domains
):
return self._process_orchestrated(
utterance,
request_id,
provider_used,
conversation_id,
context,
parsed,
result,
traces,
hardware_request=hardware_request,
simulated_hardware_request=simulated_hardware_request,
simulation_fault=simulation_fault,
vehicle_profile=vehicle_profile,
control_transport=control_transport,
approval_token=approval_token,
on_commands_validated=on_commands_validated,
)
intent_ir = build_intent_ir(
parsed,
utterance,
request_id,
provider_used,
self.semantic_capability_metadata,
)
result["intent_ir"] = intent_ir
generation_started = time.perf_counter()
function_config = build_function_config(parsed, request_id, provider_used)
preset_config = None
if parsed.get("preset_actions"):
preset_action = parsed["preset_actions"][0]
preset_config = build_function_preset(
preset_action["target"],
preset_action["preset_name"],
context["devices"][preset_action["target"]],
request_id,
provider_used,
)
result["function_config"] = function_config
result["function_configs"] = {"seat": function_config} if function_config else {}
result["preset_config"] = preset_config
generated_document = function_config or preset_config
self._trace(
traces,
request_id,
"function_generate",
"succeeded" if generated_document else "waiting_confirmation",
(
"已生成座椅 FunctionConfig。"
if function_config
else (
"已从当前姿态生成 FunctionPreset。"
if preset_config
else "没有足够信息生成可执行配置。"
)
),
generation_started,
{
"operation_count": len(parsed["actions"]),
"preset_operation_count": (
len(preset_config["spec"]["operations"]) if preset_config else 0
),
},
)
schema_started = time.perf_counter()
validation = self.contracts.validate(intent_ir)
if function_config is not None:
validation.extend(self.contracts.validate(function_config))
if preset_config is not None:
validation.extend(self.contracts.validate(preset_config))
schema_errors = [issue for issue in validation if issue["severity"] == "error"]
self._trace(
traces,
request_id,
"schema_validate",
"failed" if schema_errors else "succeeded",
f"公共与领域 Schema 校验{'发现错误' if schema_errors else '通过'}。",
schema_started,
{"error_count": len(schema_errors)},
)
semantic_started = time.perf_counter()
semantic_issues = validate_semantics(parsed, function_config)
validation.extend(semantic_issues)
if hardware_request:
self._adapt_validation_for_hardware(validation)
semantic_errors = [issue for issue in semantic_issues if issue["severity"] == "error"]
self._trace(
traces,
request_id,
"domain_validate",
"failed" if semantic_errors else "succeeded",
"座椅语义校验阻止了不受支持的请求。" if semantic_errors else "座椅能力和目标映射校验通过。",
semantic_started,
{"error_count": len(semantic_errors), "warning_count": len(semantic_issues) - len(semantic_errors)},
)
result["validation"] = validation
all_errors = [issue for issue in validation if issue["severity"] == "error"]
if all_errors:
if any(issue["code"] == "AMBIGUOUS_INTENT" for issue in all_errors):
result["status"] = "needs_confirmation"
else:
result["status"] = "validation_failed"
self._persist(result)
return result
if parsed.get("deferred_requests") and not parsed.get("actions"):
result["status"] = "capability_unavailable"
self._persist(result)
return result
if preset_config is not None and not parsed.get("actions"):
preset_action = parsed["preset_actions"][0]
compile_started = time.perf_counter()
self._trace(
traces,
request_id,
"compile",
"succeeded",
"FunctionPreset 属于控制平面数据,不生成 CAN 命令。",
compile_started,
{
"preset_name": preset_action["preset_name"],
"command_count": 0,
},
)
safety_started = time.perf_counter()
self._trace(
traces,
request_id,
"safety_check",
"succeeded",
"预设仅保存已验证的五轴目标,不改变硬件发送边界。",
safety_started,
{"hardware_execution_allowed": False},
)
execute_started = time.perf_counter()
execution_report = {
"apiVersion": "cognitive-agent/v1alpha1",
"kind": "ExecutionReport",
"metadata": {
"id": f"report-{request_id}",
"name": "Seat posture preset save report",
"version": "1.0.0",
"request_id": request_id,
"labels": {"control_plane": "preset_store"},
},
"spec": {
"mode": "dry_run",
"status": "succeeded",
"started_at": _now(),
"finished_at": _now(),
"results": [
{
"ref": f"save-{preset_action['target']}-posture-preset",
"domain": "seat",
"status": "succeeded",
"message": (
f"已将 {preset_action['target']} 当前五轴姿态保存为"
f"“{preset_action['preset_name']}”。"
),
"telemetry": {
"operation": "save_posture_preset",
"target": preset_action["target"],
"preset_name": preset_action["preset_name"],
"captured_axis_count": len(
preset_config["spec"]["operations"]
),
"storage_scope": "conversation_durable",
"can_command_emitted": False,
},
}
],
},
}
report_issues = self.contracts.validate(execution_report)
validation.extend(report_issues)
result["execution_report"] = execution_report
if report_issues:
result["status"] = "execution_report_invalid"
self._persist(result)
return result
updated_context = self.conversations.save_preset(
conversation_id,
utterance,
preset_action["target"],
preset_action["preset_name"],
preset_config,
parsed["decision_summary"],
)
updated_context = self._context_for_vehicle(
updated_context,
vehicle_profile,
)
result["status"] = "succeeded"
result["conversation_turn"] = updated_context["turn_count"]
result["actuator_state"] = updated_context["devices"]
result["saved_presets"] = [
{"name": item["name"], "target": item["target"]}
for item in updated_context["presets"]
]
self._trace(
traces,
request_id,
"execute",
"succeeded",
"当前姿态已写入会话预设仓库;未生成 CAN 帧。",
execute_started,
{
"mode": "control_plane",
"preset_name": preset_action["preset_name"],
},
)
self._trace(
traces,
request_id,
"state_update",
"succeeded",
f"预设“{preset_action['preset_name']}”已持久化,可在后续指令中直接调用。",
time.perf_counter(),
{
"preset_count": len(updated_context["presets"]),
"storage_scope": "conversation_durable",
},
)
self._persist(result)
return result
compile_started = time.perf_counter()
try:
if hardware_request:
if control_transport != WIRELESS_MQTT_TRANSPORT:
self.hardware_settings.validate_function_config(function_config)
command_plan = self.hardware_compiler.compile(
function_config, context["devices"]
)
else:
command_plan = self.compiler.compile(function_config)
except ValueError as exc:
compile_issue = {
"severity": "error",
"code": "CAN_COMPILE_REJECTED",
"path": "spec.operations",
"message": str(exc),
}
validation.append(compile_issue)
result["validation"] = validation
result["status"] = "compile_failed"
self._trace(
traces,
request_id,
"compile",
"failed",
f"座椅 CAN 编译被安全约束阻止:{exc}",
compile_started,
{"error": str(exc), "hardware_request": hardware_request},
)
self._persist(result)
return result
plan_issues = self.contracts.validate(command_plan)
validation.extend(plan_issues)
result["command_plan"] = command_plan
result["command_plans"] = {"seat": command_plan}
self._trace(
traces,
request_id,
"compile",
"failed" if plan_issues else "succeeded",
"配置已确定性编译为座椅 CAN 语义信号计划。",
compile_started,
{
"messages": [
command["message"] for command in command_plan["spec"]["commands"]
],
"command_count": len(command_plan["spec"]["commands"]),
"signal_count": sum(
len(command["signals"])
for command in command_plan["spec"]["commands"]
),
},
)
if plan_issues:
result["status"] = "compile_failed"
self._persist(result)
return result
self._emit_validated_commands(result, on_commands_validated)
if hardware_request:
self._authorize_compiled_hardware_delivery(
parsed,
approval_token,
control_transport,
)
safety_started = time.perf_counter()
self._trace(
traces,
request_id,
"safety_check",
"succeeded",
(
"无线 MQTT CAN0 座椅语义白名单与固件原生 0x300 报文白名单已通过;"
"本地 T2G 固件 profile、USB-CAN 与物理台架连接不参与校验。"
if hardware_request
and control_transport == WIRELESS_MQTT_TRANSPORT
else (
"硬件启用、授权令牌、固件 profile 与目标座椅配置门禁已通过;"
if self.hardware_settings.approval_required
else "硬件启用、调试免令牌、固件 profile 与目标座椅配置门禁已通过;"
)
+ "板端 Position_Valid 将在发送绝对移动命令前实时检查。"
if hardware_request
else (
"模拟台架隔离门禁已通过;仅生成故障注入反馈,物理 CAN、ZLG 与 IAR 路径均未打开。"
if simulated_hardware_request
else "平台、矩阵和策略均限制为 dry-run;硬件发送路径未启用。"
)
),
safety_started,
{
"hardware_execution_allowed": hardware_request,
"simulated_hardware": simulated_hardware_request,
"simulation_fault": (
simulation_fault if simulated_hardware_request else None
),
"position_calibration_gate": (
"remote_native_can0_feedback"
if hardware_request
and control_transport == WIRELESS_MQTT_TRANSPORT
else "runtime_can_preflight"
if hardware_request
else "not_applicable"
),
"mapping_status": (
"mqtt_can0_native_firmware_whitelist"
if hardware_request
and control_transport == WIRELESS_MQTT_TRANSPORT
else "protocol_hil_verified_combined_firmware_hil_pending"
if hardware_request
else "draft"
),
"firmware_profiles": (
sorted(self.hardware_settings.profiles)
if hardware_request
and control_transport != WIRELESS_MQTT_TRANSPORT
else []
),
},
)
execute_started = time.perf_counter()
if hardware_request:
mqtt_selected = control_transport == WIRELESS_MQTT_TRANSPORT
vehicle_safety_preflight = None
wired_bridge = None
try:
if mqtt_selected:
vehicle_safety_preflight = _wireless_mqtt_publish_preflight()
hardware_executor = self._new_hardware_executor(
lambda: self._vehicle_mqtt_transport.bus(channel=0),
remote_transport=True,
)
elif (
self.hardware_settings.native_passenger_can0
and self.vehicle_can_settings.configured
):
wired_bridge = self._new_linux_wired_dual_channel_bridge()
vehicle_safety_preflight = observe_vehicle_safety(
wired_bridge.channel_bus(1),
timeout_ms=min(
5_000,
max(
1_000,
self.vehicle_can_settings.feedback_timeout_ms,
),
),
required_channel=1,
)
require_stationary_vehicle(vehicle_safety_preflight)
hardware_executor = self._new_hardware_executor(
lambda: wired_bridge.channel_bus(0)
)
else:
hardware_executor = self._new_hardware_executor()
execution_report = hardware_executor.execute(
command_plan, function_config, context["devices"]
)
except VehicleSafetyPreflightError as exc:
raise HardwareSafetyError(
"CH1 vehicle safety preflight refused passenger CAN0 execution: "
f"{exc}"
) from exc
finally:
if wired_bridge is not None:
wired_bridge.close()
if vehicle_safety_preflight is not None:
for item in execution_report["spec"].get("results", []):
item.setdefault("telemetry", {})[
"vehicle_safety_preflight"
] = vehicle_safety_preflight
elif simulated_hardware_request:
execution_report = self.simulated_hardware_executor.execute(
command_plan,
function_config,
context["devices"],
fault_scenario=simulation_fault,
)
else:
execution_report = self.executor.execute(
command_plan, function_config, context["devices"]
)
execution_outcome, execution_partially_succeeded = (
_annotate_execution_report(execution_report)
)
report_issues = self.contracts.validate(execution_report)
validation.extend(report_issues)
result["execution_report"] = execution_report
result["execution_outcome"] = execution_outcome
execution_succeeded = (
not report_issues
and execution_report["spec"]["status"] == "succeeded"
)
if report_issues:
result["status"] = "execution_report_invalid"
elif execution_partially_succeeded:
result["status"] = "partially_succeeded"
elif not execution_succeeded:
result["status"] = "execution_failed"
elif parsed.get("deferred_requests"):
result["status"] = "partially_succeeded"
else:
result["status"] = "succeeded"
execution_results = execution_report["spec"]["results"]
stop_all_attempted = any(
item.get("telemetry", {}).get("stop_all_attempted")
for item in execution_results
)
firmware_gate_failed = any(
item.get("telemetry", {}).get("error_type")
== "FirmwareProgrammingError"
for item in execution_results
)
failed_execution = next(
(item for item in execution_results if item.get("status") == "failed"),
None,
)
failed_telemetry = (
failed_execution.get("telemetry", {}) if failed_execution else {}
)
hardware_failure_summary = (
f"真实 CAN 执行失败:{failed_execution['message']}"
if failed_execution
else "真实 CAN 执行失败,未获得满足目标的物理反馈。"
)
if stop_all_attempted:
hardware_failure_summary += " 移动路径已尝试发送 STOP_ALL。"
self._trace(
traces,
request_id,
"execute",
"succeeded" if execution_succeeded else "failed",
(
"真实 CAN 命令已提交;当前反馈窗口未确认车辆效果,状态以实时 CAN 回读为准。"
if hardware_request
and execution_succeeded
and execution_outcome == "submitted_unverified"
else "真实 CAN 命令已发送,并收到 IAR 固件的实际状态回读。"
if hardware_request and execution_succeeded
else (
(
"固件身份验证或自动烧录失败;未发送座椅控制帧。"
if firmware_gate_failed
else hardware_failure_summary
)
if hardware_request
else (
"模拟台架已生成隔离的 HIL 反馈,未访问物理 CAN。"
if simulated_hardware_request and execution_succeeded
else (
f"模拟台架故障已触发:{failed_execution['message']}"
if simulated_hardware_request and failed_execution
else "虚拟 CAN 已编码命令帧并生成模拟回读。"
)
)
)
),
execute_started,
{
"mode": execution_report["spec"]["mode"],
"result_count": len(execution_report["spec"]["results"]),
"firmware_gate_failed": firmware_gate_failed,
"stop_all_attempted": stop_all_attempted,
"motor_result": failed_telemetry.get("motor_result_name"),
"failure_category": failed_telemetry.get("failure_category"),
"hall_before": failed_telemetry.get("hall_before"),
"hall_after": failed_telemetry.get("hall_after"),
},
)
if execution_succeeded:
execution_notices = [
str(item.get("telemetry", {}).get("user_notice"))
for item in execution_results
if item.get("telemetry", {}).get("user_notice")
]
for notice in execution_notices:
if notice not in parsed["decision_summary"]:
parsed["decision_summary"].append(notice)
state_actions = (
self._hardware_feedback_actions(
parsed["actions"], function_config, execution_report
)
if hardware_request
else parsed["actions"]
)
updated_context = self.conversations.apply_successful_actions(
conversation_id,
utterance,
state_actions,
parsed["decision_summary"],
)
physical_state = execution_report.get("spec", {}).get(
"physical_state"
)
if hardware_request and isinstance(physical_state, dict):
updated_context = self.conversations.synchronize_physical_passenger(
conversation_id,
physical_state,
)
updated_context = self._context_for_vehicle(
updated_context,
vehicle_profile,
)
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,
"state_update",
"succeeded",
f"会话状态已更新为第 {updated_context['turn_count']} 轮,可用于下一条指令。",
time.perf_counter(),
{"state_after": updated_context["devices"]},
)
elif hardware_request:
physical_state = execution_report.get("spec", {}).get(
"physical_state"
)
if isinstance(physical_state, dict) and (
physical_state.get("position")
or physical_state.get("ventilation")
or physical_state.get("heating")
):
updated_context = self.conversations.synchronize_physical_passenger(
conversation_id,
physical_state,
)
updated_context = self._context_for_vehicle(
updated_context,
vehicle_profile,
)
result["actuator_state"] = updated_context["devices"]
result["domain_state"] = updated_context["domain_devices"]
self._trace(
traces,
request_id,
"state_sync",
"succeeded",
"真实执行未完成,但已按 CAN 物理回读同步副驾状态。",
time.perf_counter(),
{"state_after": updated_context["devices"]},
)
telemetry = next(
(
item.get("telemetry", {})
for item in execution_report["spec"]["results"]
if item.get("telemetry", {}).get("feedback_frame")
),
execution_report["spec"]["results"][0].get("telemetry", {}),
)
self._trace(
traces,
request_id,
"telemetry",
"succeeded" if execution_succeeded else "failed",
(
"IAR 固件反馈帧中的实际状态与请求目标一致。"
if hardware_request and execution_succeeded
else (
(
f"未获得满足目标的真实硬件反馈:"
f"{failed_execution['message']}"
if failed_execution
else "未获得满足目标的真实硬件反馈。"
)
if hardware_request
else (
"模拟台架反馈与请求目标一致,物理 CAN 始终关闭。"
if simulated_hardware_request and execution_succeeded
else (
f"模拟台架故障反馈:{failed_execution['message']}"
if simulated_hardware_request and failed_execution
else "虚构反馈帧中的最终状态与请求目标一致。"
)
)
)
),
time.perf_counter(),
{
"command_frame": telemetry.get("command_frame", {}).get("data_hex"),
"feedback_frame": telemetry.get("feedback_frame", {}).get("data_hex"),
"simulated": not hardware_request,
"simulation_fault": (
simulation_fault if simulated_hardware_request else None
),
},
)
self._persist(result)
return result
@staticmethod
def _adapt_validation_for_hardware(
validation: list[dict[str, Any]],
) -> None:
"""Replace reference-vehicle dry-run warnings with HIL bench semantics."""
hardware_messages = {
"LEVEL_COMMAND_SIMULATED": (
"LEVEL_COMMAND_T2G_MAPPED",
"目标档位已映射到统一 T2G 固件的 0x29E 档位信号;"
"执行结果以 0x4FA 物理 CAN 回读为准。",
),
"SYNTHETIC_MOTION_CAN": (
"T2G_MOTION_PROTOCOL_MAPPED",
"参考车型的虚拟运动矩阵未用于本次 HIL;座椅移动通过统一 T2G 固件的 "
"0x300 命令和 0x301/0x302 物理反馈闭环执行。",
),
"DRIVER_MOTION_INTERLOCK_NOT_IMPLEMENTED": (
"BENCH_ONLY_NO_VEHICLE_INTERLOCKS",
"当前真实移动路径仅限独立 T2G 台架;尚未接入车速、挡位、乘员和防夹信号,"
"不得直接用于车载座椅系统。",
),
"COMBINED_THERMAL_SIMULATED": (
"COMBINED_THERMAL_T2G_MAPPED",
"加热与通风组合目标由统一 T2G 固件执行并以 0x4FA 物理反馈确认;"
"接入完整座椅总成前仍需补充热安全互锁验证。",
),
"VIRTUAL_EXECUTION_ONLY": (
"REFERENCE_MATRIX_OVERRIDDEN_BY_T2G_BENCH",
"参考车型矩阵元数据仍不完整;本次 HIL 仅使用仓库内已验证的 "
"T2G 统一固件协议、身份门禁和物理反馈闭环。",
),
}
for issue in validation:
replacement = hardware_messages.get(str(issue.get("code")))
if replacement is not None:
issue["code"], issue["message"] = replacement
@staticmethod
def _hardware_feedback_actions(
actions: list[dict[str, Any]],
function_config: dict[str, Any],
execution_report: dict[str, Any],
) -> list[dict[str, Any]]:
"""Project authoritative physical feedback into conversation/UI state."""
feedback_by_ref = {
item["ref"]: item.get("telemetry", {})
for item in execution_report["spec"]["results"]
if _operation_verification_status(item) == "verified_success"
}
updated: list[dict[str, Any]] = []
operations = function_config["spec"]["operations"]
for source_action, operation in zip(actions, operations, strict=True):
telemetry = feedback_by_ref.get(operation["id"])
if telemetry is None:
# Submission alone is not an authoritative vehicle-state
# update. The asynchronous CAN/MQTT state monitor will apply
# the change if and when the vehicle reports it.
continue
action = deepcopy(source_action)
if action.get("action_type") == "position":
if telemetry.get("effective_position_pct") is not None:
action["position_pct"] = int(
telemetry["effective_position_pct"]
)
if telemetry.get("position_valid") is not None:
action["position_valid"] = bool(
telemetry["position_valid"]
)
if telemetry.get("motion_mode") in {
"relative_timed_duty",
"timed_user_request",
}:
action["position_estimated"] = True
action["feedback_basis"] = (
"timed_endpoint_or_hall_confirmed_open_loop"
if telemetry.get("motion_mode") == "timed_user_request"
else "relative_hall_confirmed_open_loop"
)
updated.append(action)
continue
raw = telemetry.get("feedback_value")
if raw is not None:
raw_value = int(raw)
action["state"] = "off" if raw_value <= 1 else "on"
action["requested_level"] = max(0, raw_value - 1)
updated.append(action)
physical_state = execution_report.get("spec", {}).get("physical_state")
if isinstance(physical_state, dict):
positions = physical_state.get("position", {})
by_axis = {
action.get("axis"): action
for action in updated
if action.get("action_type") == "position"
}
for axis, feedback in positions.items():
if not isinstance(feedback, dict) or feedback.get("position_pct") is None:
continue
action = by_axis.get(axis)
if action is None:
action = {
"domain": "seat",
"target": "passenger",
"action_type": "position",
"axis": axis,
}
updated.append(action)
by_axis[axis] = action
action["position_pct"] = int(feedback["position_pct"])
if feedback.get("position_valid") is not None:
action["position_valid"] = bool(feedback["position_valid"])
action["feedback_basis"] = "physical_can_snapshot"
by_function = {
action.get("function"): action
for action in updated
if action.get("function") in {"ventilation", "heating"}
}
for feature in ("ventilation", "heating"):
feedback = physical_state.get(feature)
if not isinstance(feedback, dict):
continue
action = by_function.get(feature)
if action is None:
action = {
"domain": "seat",
"target": "passenger",
"function": feature,
}
updated.append(action)
by_function[feature] = action
action["state"] = (
"on" if feedback.get("state") == "on" else "off"
)
action["requested_level"] = int(feedback.get("level", 0))
for action in updated:
action["physical_target_alias"] = "t2g_primary_seat"
return updated
@staticmethod
def _physical_ch1_feedback_states(
reports: dict[str, dict[str, Any]],
) -> list[dict[str, Any]]:
"""Return only fully verified decoded CH1 feedback state patches."""
states: list[dict[str, Any]] = []
for report in reports.values():
for item in report.get("spec", {}).get("results", []):
feedback_state = authoritative_physical_ch1_feedback_state(item)
if feedback_state is not None:
states.append(feedback_state)
return states
@staticmethod
def _parse_mode_profile_save_name(utterance: str) -> str | None:
explicit = SAVE_MODE_PROFILE_NAME.search(utterance)
if explicit is not None:
return explicit.group("name").strip()
save_like = any(word in utterance for word in ("保存", "记住", "记为"))
mode_context = any(
word in utterance
for word in ("这个模式", "这个配置", "当前模式", "长期", "固化")
)
if not save_like or not mode_context:
return None
generic = re.search(
r"(?:保存为|保存成|记为|命名为)(?P<name>[^,。,.!!\s]{1,64}?模式)",
utterance,
)
return generic.group("name").strip() if generic is not None else None
@classmethod
def is_mode_profile_configuration_request(cls, utterance: str) -> bool:
"""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 = 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:
return parse_mode_profile_update(utterance)
@staticmethod
def _parse_mode_profile_restore_name(utterance: str) -> str | None:
explicit = RESTORE_MODE_PROFILE_PREFIX.search(utterance)
if explicit is not None:
return explicit.group("name").strip()
suffix = RESTORE_MODE_PROFILE_SUFFIX.search(utterance)
return suffix.group("name").strip() if suffix is not None else None
def _apply_mode_profiles(
self,
parsed: dict[str, Any],
matched_modes: list[ModeProfile],
context: dict[str, Any],
vehicle_profile: VehicleProfile,
*,
utterance: str,
) -> dict[str, Any]:
if not matched_modes:
return parsed
if any(mode.schedule is not None for mode in matched_modes):
rejected = deepcopy(parsed)
for key in ("actions", "domain_actions", "native_can_actions", "preset_actions",
"preset_events", "deferred_requests", "model_requests"):
rejected[key] = []
rejected["ambiguities"] = [{
"field": "mode_profile",
"question": "动态模式必须通过时间轴执行,请独立输入“进入模式名称”;不会将各阶段压平执行。",
}]
return rejected
if self._contains_seat_preset_request(parsed):
return parsed
if len(matched_modes) > 1:
rejected = deepcopy(parsed)
for key in (
"actions",
"domain_actions",
"preset_actions",
"preset_events",
"deferred_requests",
"model_requests",
):
rejected[key] = []
rejected["ambiguities"] = [
{
"field": "mode_profile",
"question": "一条请求只能执行一个座椅功能模式,请确认要执行的模式。",
}
]
rejected["decision_summary"] = [
"同一条请求命中多个功能模式,已停止生成控制指令以避免模式冲突。"
]
return rejected
target_conflict = self._explicit_mode_target_conflict(
matched_modes[0],
utterance,
)
if target_conflict is not None:
rejected = deepcopy(parsed)
for key in (
"actions",
"domain_actions",
"native_can_actions",
"preset_actions",
"preset_events",
"deferred_requests",
"model_requests",
):
rejected[key] = []
requested_scope, supported_scopes = target_conflict
rejected["ambiguities"] = [
*rejected.get("ambiguities", []),
{
"field": "mode_profile",
"question": (
f"该模式的座椅目标为 {', '.join(supported_scopes)},"
f"与用户指定的 {requested_scope} 不一致,请重新确认。"
),
"reason": "mode_target_scope_mismatch",
},
]
rejected["unresolved_spans"] = [
*rejected.get("unresolved_spans", []),
{
"text": utterance,
"reason": "mode_target_scope_mismatch",
},
]
rejected["decision_summary"] = [
*rejected.get("decision_summary", []),
"用户显式座位与功能模式登记目标不一致,已停止所有模式动作。",
]
return rejected
parsed = self._apply_implicit_mode_target_policy(
parsed,
matched_modes,
utterance,
)
safety_ambiguities = [
item
for item in parsed.get("ambiguities", [])
if item.get("field") != "capability"
]
if safety_ambiguities:
return parsed
matched_profile = matched_modes[0]
mode_requests, n7_transfer_actions = (
self._mode_requests_and_n7_transfers(matched_profile)
)
selected_seat_scope = self._explicit_mode_seat_selection(
matched_profile, utterance,
)
if selected_seat_scope is not None:
# A cabin profile can carry independent comfort operations for both
# front seats. An explicit single-seat invocation selects only that
# seat; it must not authorize its sibling or mutate the stored mode.
mode_requests = [
request for request in mode_requests
if request["domain"] != "seat"
or self._normalized_mode_seat_scope(request["target_scope"])
in selected_seat_scope
]
detected_domains = []
for request in mode_requests:
if request["domain"] not in detected_domains:
detected_domains.append(request["domain"])
mode_plan = {
"detected_domains": detected_domains or ["seat"],
"requests": mode_requests,
"ambiguities": [],
"decision_summary": [
"命中长期模式配置:"
+ "、".join(profile.name for profile in matched_modes)
+ "。"
],
"confidence": max(float(parsed.get("confidence", 0)), 0.97),
}
mode_parsed = self.model_plan_adapter.adapt(
mode_plan,
context,
vehicle_profile,
)
# Native-only comfort capabilities are represented as deferred items by
# the generic adapter. A verified stored mode must materialize them too;
# otherwise a successful named-mode run silently drops its driver seat.
mode_native_actions = []
for item in mode_parsed.get("deferred_requests", []):
if item.get("execution_backend") != "native_can":
continue
action = {
"domain": item["domain"],
"capability": item["capability"],
"target": item["target_scope"],
"parameters": deepcopy(item.get("parameters", {})),
"operation": item.get("desired_change", "set"),
"reason": item.get("reason", ""),
"execution_backend": "native_can",
}
if self.native_can_runtime.supports(action):
mode_native_actions.append(action)
parsed_has_explicit_request = bool(
parsed.get("actions")
or parsed.get("domain_actions")
or parsed.get("preset_actions")
or parsed.get("preset_events")
or parsed.get("model_requests")
)
merged = deepcopy(parsed)
merged["actions"] = self._merge_control_actions(
mode_parsed.get("actions", []),
parsed.get("actions", []),
)
merged["domain_actions"] = self._merge_control_actions(
mode_parsed.get("domain_actions", []),
parsed.get("domain_actions", []),
)
merged["native_can_actions"] = self._merge_control_actions(
mode_native_actions,
parsed.get("native_can_actions", []),
)
for key in (
"preset_actions",
"preset_events",
"deferred_requests",
"ambiguities",
"model_requests",
):
parsed_values = parsed.get(key, [])
if key == "ambiguities" and not parsed_has_explicit_request:
# A local parser does not know arbitrary named modes and emits
# a generic unsupported-capability ambiguity. A unique,
# repository-backed mode match is authoritative for that case.
parsed_values = []
merged[key] = self._merge_unique(
mode_parsed.get(key, []),
parsed_values,
)
merged["decision_summary"] = [
*mode_parsed.get("decision_summary", []),
*parsed.get("decision_summary", []),
]
merged["detected_domains"] = self._merge_domains(
mode_parsed.get("detected_domains", []),
parsed.get("detected_domains", []),
)
merged["confidence"] = max(
float(mode_parsed.get("confidence", 0)),
float(parsed.get("confidence", 0)),
)
if n7_transfer_actions:
merged["deferred_requests"] = self._merge_unique(
list(merged.get("deferred_requests", [])),
self._n7_transfer_deferred_requests(n7_transfer_actions),
)
merged["_n7_transfer_actions"] = deepcopy(n7_transfer_actions)
merged["_n7_scene_profile"] = {
"id": matched_profile.id,
"name": matched_profile.name,
}
merged["detected_domains"] = self._merge_domains(
list(merged.get("detected_domains", [])),
[
str(action.get("domain") or "")
for action in n7_transfer_actions
if str(action.get("domain") or "")
],
)
if self._has_local_mode_peripheral(matched_profile):
merged["_saved_mode_local_peripheral"] = True
return merged
@classmethod
def _explicit_mode_target_conflict(
cls,
profile: ModeProfile,
utterance: str,
) -> tuple[str, tuple[str, ...]] | None:
"""Reject a named mode when its persisted seat scope contradicts text."""
requested, normalized_scopes = cls._explicit_mode_seat_scopes(profile, utterance)
if requested is None or requested == normalized_scopes:
return None
if cls._explicit_mode_seat_selection(profile, utterance) is not None:
return None
return ",".join(sorted(requested)), tuple(sorted(normalized_scopes))
@staticmethod
def _normalized_mode_seat_scope(scope: str) -> str:
return (
"driver" if scope == "front_left" else
"passenger" if scope == "front_right" else
"rear" if scope in {"rear", "rear_left", "rear_right"} else scope
)
@classmethod
def _explicit_mode_seat_scopes(
cls,
profile: ModeProfile,
utterance: str,
) -> tuple[frozenset[str] | None, set[str]]:
aliases = (profile.id, profile.name, *profile.aliases)
mode_clause = next(
(
clause
for clause in re.split(r"[,,;;。!?!?\n]+", utterance)
if any(
re.sub(r"\s+", "", alias.casefold())
in re.sub(r"\s+", "", clause.casefold())
for alias in aliases
if alias
)
),
utterance,
)
requested = resolve_explicit_seat_scopes(
mode_clause.replace("主副驾", "主驾和副驾"),
assume_seat_context=True,
)
normalized_scopes = {
cls._normalized_mode_seat_scope(operation.target_scope)
for operation in profile.operations
if operation.domain == "seat"
}
return requested, normalized_scopes
@classmethod
def _explicit_mode_seat_selection(
cls,
profile: ModeProfile,
utterance: str,
) -> frozenset[str] | None:
"""Scope a dual-seat mode invocation, never mode creation or editing."""
requested, supported = cls._explicit_mode_seat_scopes(profile, utterance)
if (
requested is None
or len(requested) != 1
or supported != {"driver", "passenger"}
or not requested.issubset(supported)
or cls.is_mode_profile_configuration_request(utterance)
or not re.search(r"进入|开启|启动|执行|启用|运行|切换|打开|调用|应用|使用", utterance)
):
return None
return requested
@staticmethod
def _apply_implicit_mode_target_policy(
parsed: dict[str, Any],
matched_modes: list[ModeProfile],
utterance: str,
) -> dict[str, Any]:
"""Let one matched mode own only an omitted seat target.
The native CH1 parser can add an implicit-driver ambiguity before or
after mode injection. A unique repository-backed mode already carries
authoritative target scopes on every operation, so that one tagged
default is redundant. Explicit scopes and all other safety questions
remain untouched.
"""
if len(matched_modes) != 1 or (
DRIVER_SEAT_SCOPE_PATTERN.search(utterance)
or PASSENGER_SEAT_SCOPE_PATTERN.search(utterance)
):
return parsed
ambiguities = list(parsed.get("ambiguities", []))
filtered = [
item
for item in ambiguities
if item.get("reason") != "implicit_driver_default_unresolved"
]
if len(filtered) == len(ambiguities):
return parsed
resolved = deepcopy(parsed)
resolved["ambiguities"] = filtered
return resolved
@staticmethod
def _is_dynamic_mode_stop_request(utterance: str) -> bool:
compact = re.sub(r"\s+", "", utterance)
if any(negation in compact for negation in ("不要停止", "无需停止", "别停止")):
return False
return any(word in compact for word in DYNAMIC_MODE_STOP_WORDS) and any(
word in compact for word in DYNAMIC_MODE_WORDS
)
def _apply_dynamic_mode_stop_request(
self,
parsed: dict[str, Any],
utterance: str,
) -> dict[str, Any]:
"""Make explicit stop language authoritative over a matched start profile."""
stopped = deepcopy(parsed)
stopped["actions"] = [
action
for action in stopped.get("actions", [])
if action.get("action_type") not in {"dynamic_mode", "dynamic_mode_stop"}
]
stopped["actions"].append(
{"action_type": "dynamic_mode_stop", "target": "passenger"}
)
stopped["model_requests"] = [
request
for request in stopped.get("model_requests", [])
if request.get("capability")
not in {"seat.start_dynamic_mode", DYNAMIC_MODE_STOP_CAPABILITY}
]
stopped["model_requests"].append(
{
"domain": "seat",
"capability": DYNAMIC_MODE_STOP_CAPABILITY,
"target_scope": "passenger",
"operation": "deactivate",
"parameters": [],
"reason": "用户明确要求停止动态座椅循环。",
"source_span": utterance,
}
)
stopped["ambiguities"] = [
item
for item in stopped.get("ambiguities", [])
if item.get("field") != "capability"
]
stopped["detected_domains"] = self._merge_domains(
["seat"], stopped.get("detected_domains", [])
)
stopped["decision_summary"] = [
"识别到停止动态座椅模式指令;不会重新启动同名长期模式。",
*stopped.get("decision_summary", []),
]
stopped["confidence"] = max(float(stopped.get("confidence", 0)), 0.99)
return stopped
@staticmethod
def _contains_seat_preset_request(parsed: dict[str, Any]) -> bool:
capabilities = {
request.get("capability")
for request in parsed.get("model_requests", [])
if isinstance(request, dict)
}
return bool(
parsed.get("preset_actions")
or parsed.get("preset_events")
or SAVE_PRESET_CAPABILITY in capabilities
or APPLY_PRESET_CAPABILITY in capabilities
)
@classmethod
def _merge_control_actions(
cls,
mode_items: list[dict[str, Any]],
parsed_items: list[dict[str, Any]],
) -> list[dict[str, Any]]:
parsed_controls = {
cls._control_signature(item)
for item in parsed_items
}
candidates = [
*parsed_items,
*(
item
for item in mode_items
if cls._control_signature(item) not in parsed_controls
),
]
return cls._merge_unique([], candidates)
@staticmethod
def _control_signature(action: dict[str, Any]) -> tuple[Any, ...]:
if action.get("domain"):
return (
action.get("domain"),
action.get("capability"),
action.get("target"),
)
if action.get("action_type") == "position":
return (
"seat",
"position",
action.get("target"),
action.get("axis"),
)
if action.get("action_type") == "dynamic_mode_stop":
return (
"seat",
"dynamic_mode_stop",
action.get("target"),
)
if action.get("action_type") == "dynamic_mode":
return (
"seat",
"dynamic_mode",
action.get("target"),
action.get("mode_id"),
)
return (
"seat",
"function",
action.get("target"),
action.get("function"),
)
@staticmethod
def _merge_unique(
first: list[dict[str, Any]],
second: list[dict[str, Any]],
) -> list[dict[str, Any]]:
merged = []
seen: set[str] = set()
for item in [*first, *second]:
signature = json.dumps(item, ensure_ascii=False, sort_keys=True)
if signature in seen:
continue
seen.add(signature)
merged.append(item)
return merged
@staticmethod
def _merge_domains(
first: list[str],
second: list[str],
) -> list[str]:
domains = []
for domain in [*first, *second]:
if domain not in domains:
domains.append(domain)
return domains or ["seat"]
@staticmethod
def _preview_mode_operations(
source_result: dict[str, Any],
) -> list[dict[str, Any]]:
"""Recover every reusable operation from server-compiled preview data."""
plans = source_result.get("command_plans")
configs = source_result.get("function_configs")
if not isinstance(plans, dict) or not isinstance(configs, dict):
return []
operations: list[dict[str, Any]] = []
seen: set[tuple[str, str, str]] = set()
for key, plan in plans.items():
if (
not isinstance(plan, dict)
or not isinstance(plan.get("spec"), dict)
or not isinstance(plan["spec"].get("commands"), list)
or not plan["spec"]["commands"]
):
continue
config = configs.get(key)
spec = config.get("spec") if isinstance(config, dict) else None
if not isinstance(spec, dict):
continue
domain = str(spec.get("domain") or "").strip()
raw_operations = spec.get("operations")
if not domain or not isinstance(raw_operations, list):
continue
for operation in raw_operations:
if not isinstance(operation, dict):
continue
capability = str(operation.get("capability") or "").strip()
raw_target = operation.get("target")
target = (
str(
raw_target.get("instance")
or raw_target.get("zone")
or raw_target.get("scope")
or ""
).strip()
if isinstance(raw_target, dict)
else ""
)
parameters = operation.get("parameters")
signature = (domain, capability, target)
if (
not capability
or not target
or not isinstance(parameters, dict)
or signature in seen
):
continue
reusable_parameters = deepcopy(parameters)
if (
capability.startswith("seat.set_")
and reusable_parameters.get("position_pct") is not None
and reusable_parameters.get("angle_deg") is not None
):
# Seat compilation adds the calibrated angle alongside the
# requested percentage. The scene contract intentionally
# permits one absolute representation only; retain the
# portable percentage and let the new compile derive angle.
reusable_parameters.pop("angle_deg", None)
seen.add(signature)
operations.append(
{
"domain": domain,
"capability": capability,
"target_scope": target,
"operation": "set",
"parameters": reusable_parameters,
"reason": "由服务端已校验功能模式草稿保留。",
}
)
return operations
def _load_mode_profile_draft_reference(
self,
reference: dict[str, Any],
*,
conversation_id: str,
mode_name: str,
vehicle_id: str,
) -> tuple[dict[str, Any], dict[str, Any], list[dict[str, Any]]]:
"""Resolve an opaque client reference back to one trusted local artifact."""
if not isinstance(reference, dict) or set(reference) != {
"name",
"source_request_id",
"vehicle_id",
}:
raise ValueError("待保存功能模式草稿引用格式无效。")
clean_name = str(reference.get("name") or "").strip()
clean_request_id = str(reference.get("source_request_id") or "").strip()
clean_vehicle_id = str(reference.get("vehicle_id") or "").strip()
if not MODE_EDITOR_REQUEST_ID_PATTERN.fullmatch(clean_request_id):
raise ValueError("待保存功能模式草稿来源请求编号无效。")
if (
normalize_mode_profile_name(clean_name)
!= normalize_mode_profile_name(mode_name)
):
raise ValueError("修改请求与待保存功能模式草稿名称不匹配。")
if clean_vehicle_id != vehicle_id:
raise ValueError("待保存功能模式草稿不属于当前车型。")
result_path = self.artifact_root / clean_request_id / "result.json"
if not result_path.is_file():
raise ValueError("未找到待保存功能模式草稿,请重新构建。")
try:
source_result = json.loads(result_path.read_text(encoding="utf-8"))
except (OSError, json.JSONDecodeError) as exc:
raise ValueError("待保存功能模式草稿无法读取,请重新构建。") from exc
if not isinstance(source_result, dict):
raise ValueError("待保存功能模式草稿内容无效,请重新构建。")
source_vehicle = source_result.get("vehicle")
source_vehicle_id = (
str(source_vehicle.get("id") or "").strip()
if isinstance(source_vehicle, dict)
else ""
)
if (
source_result.get("request_id") != clean_request_id
or source_result.get("conversation_id") != conversation_id
or source_vehicle_id != vehicle_id
or source_result.get("status") != "succeeded"
or source_result.get("execution_mode") != "dry_run"
or source_result.get("hardware_execution_allowed") is not False
or source_result.get("orchestration_route")
!= "mode_profile_create_preview"
):
raise ValueError("待保存功能模式草稿与当前会话不匹配,请重新构建。")
draft = source_result.get("mode_profile_draft")
if not isinstance(draft, dict):
raise ValueError("来源请求不是可修改的功能模式草稿。")
if (
str(draft.get("name") or "").strip() != clean_name
or str(draft.get("source_request_id") or "").strip()
!= clean_request_id
or str(draft.get("vehicle_id") or "").strip() != vehicle_id
):
raise ValueError("待保存功能模式草稿引用与来源内容不匹配。")
validation = source_result.get("validation")
if isinstance(validation, list) and any(
isinstance(issue, dict) and issue.get("severity") == "error"
for issue in validation
):
raise ValueError("待保存功能模式草稿没有通过完整安全校验。")
schedule = self._validated_schedule_artifact(source_result)
operations = ([deepcopy(op) for phase in schedule.phases for op in phase.operations]
if schedule is not None else self._preview_mode_operations(source_result))
operation_count = draft.get("operation_count")
if (
not isinstance(operation_count, int)
or isinstance(operation_count, bool)
or operation_count <= 0
or len(operations) != operation_count
):
raise ValueError("待保存功能模式草稿动作证据不完整,请重新构建。")
return source_result, draft, operations
@staticmethod
def _absolute_mode_operations(
operations: list[dict[str, Any]],
) -> list[dict[str, Any]]:
"""Convert bounded motion commands into replayable absolute mode targets."""
normalized: list[dict[str, Any]] = []
window_targets = {
"all": ("front_left", "front_right", "rear_left", "rear_right"),
"front": ("front_left", "front_right"),
"rear": ("rear_left", "rear_right"),
}
for operation in operations:
item = deepcopy(operation)
capability = str(item.get("capability") or "")
parameters = dict(item.get("parameters") or {})
if capability == "roof.set_shade_motion":
positions = {"close": 0, "half": 50, "open": 100}
motion = str(parameters.get("motion") or "")
if motion not in positions:
raise ValueError("遮阳帘停止动作没有可保存的最终位置,请改为明确开度。")
item["capability"] = "roof.set_shade_position"
item["parameters"] = {"position_pct": positions[motion]}
normalized.append(item)
continue
if capability == "window.set_motion":
positions = {"close": 0, "open": 100}
motion = str(parameters.get("motion") or "")
if motion not in positions:
raise ValueError("车窗停止动作没有可保存的最终位置,请改为明确开度。")
target = str(item.get("target_scope") or "")
targets = window_targets.get(target, (target,))
for resolved_target in targets:
absolute = deepcopy(item)
absolute["capability"] = "window.set_position"
absolute["target_scope"] = resolved_target
absolute["parameters"] = {"position_pct": positions[motion]}
normalized.append(absolute)
continue
normalized.append(item)
return normalized
@staticmethod
def _mode_profile_draft_plan(
operations: list[dict[str, Any]],
) -> dict[str, Any]:
return {
"detected_domains": list(
dict.fromkeys(str(item["domain"]) for item in operations)
),
"requests": [
{
"domain": str(item["domain"]),
"capability": str(item["capability"]),
"target_scope": str(item["target_scope"]),
"operation": "set",
"duration_ms": None,
"parameters": [
{"name": str(name), "value": deepcopy(value)}
for name, value in dict(item.get("parameters") or {}).items()
],
"reason": "按用户要求修改待保存功能模式草稿。",
}
for item in operations
],
"ambiguities": [],
"decision_summary": ["已在服务端合并并重新校验待保存模式动作。"],
"confidence": 1.0,
}
def _mode_device_default_operations(
self,
mode_name: str,
) -> list[dict[str, Any]]:
"""Return registered static-scene defaults for a bounded device append."""
template = self.scene_templates.resolve_exact_name(mode_name)
if template is None or template.schedule is not None:
return []
return [operation.to_yaml() for operation in template.operations]
@staticmethod
def _mode_update_target_is_explicit(change_text: str, target: str) -> bool:
patterns = {
"driver": r"主驾|驾驶位|驾驶员|左前",
"front_left": r"主驾|驾驶位|驾驶员|左前",
"passenger": r"副驾|乘客位|右前",
"front_right": r"副驾|乘客位|右前",
"front_row": r"前排|前舱",
"rear": r"后排|后座|后舱|所有后排",
"rear_row": r"后排|后座|后舱|所有后排",
"rear_left": r"左后|后排左|左侧后排",
"rear_right": r"右后|后排右|右侧后排",
"vehicle": r"全车|整车|所有|全部",
"all": r"全车|整车|所有|全部",
}
pattern = patterns.get(str(target))
return bool(pattern and re.search(pattern, change_text))
def _process_mode_profile_draft_update(
self,
utterance: str,
request_id: str,
provider: str,
conversation_id: str,
context: dict[str, Any],
mode_name: str,
change_text: str,
vehicle_profile: VehicleProfile,
result: dict[str, Any],
traces: list[dict[str, Any]],
draft_reference: dict[str, Any],
*,
approval_token: str | None,
simulation_fault: str,
control_transport: str,
assistant_plan: dict[str, Any] | None,
) -> dict[str, Any]:
"""Patch an unsaved preview and produce a new atomic dry-run artifact."""
del request_id, traces
try:
_source_result, source_draft, source_operations = (
self._load_mode_profile_draft_reference(
draft_reference,
conversation_id=conversation_id,
mode_name=mode_name,
vehicle_id=vehicle_profile.id,
)
)
if source_draft.get("schedule") is not None:
from cognitive_agent.application.mode_schedule_edit import revise_schedule
return revise_schedule(
self, ModeSchedule.from_dict(source_draft["schedule"]),
change_text=change_text, utterance=utterance, name=mode_name,
conversation_id=conversation_id, vehicle_profile=vehicle_profile,
context=context, control_transport=control_transport,
source_draft=source_draft,
)
base_operations = self._absolute_mode_operations(source_operations)
if not change_text:
raise ValueError("请说明要调整的设备和明确目标值。")
registered_additions = mode_device_additions(
change_text,
base_operations,
self._mode_device_default_operations(mode_name),
)
if registered_additions is not None:
normalized_changes = self._absolute_mode_operations(
registered_additions
)
else:
parseable_change_text = re.sub(
r"改为|改成|变更为|换成|变成|变为",
"调整为",
change_text,
)
unresolved_change_clauses = [
clause
for clause, _parsed, status in self._local_clause_results(
parseable_change_text,
context,
)
if status != "complete"
]
if unresolved_change_clauses and assistant_plan is None:
raise ValueError(
"调整说明包含未完整识别的内容:"
+ ";".join(unresolved_change_clauses)
)
change_result = self._process_once(
parseable_change_text,
provider,
conversation_id,
"dry_run",
approval_token,
vehicle_profile.id,
simulation_fault,
control_transport=control_transport,
assistant_plan=assistant_plan,
allow_fuzzy_mode_profile_match=False,
)
change_operations = self._absolute_mode_operations(
self._preview_mode_operations(change_result)
)
if (
change_result.get("status") != "succeeded"
or not change_operations
or change_result.get("supplement_required") is True
or bool(change_result.get("unresolved_clauses"))
or bool(change_result.get("deferred_requests"))
):
raise ValueError("没有从调整说明中得到完整、可复用的设备目标。")
validation = change_result.get("validation")
if isinstance(validation, list) and any(
isinstance(issue, dict) and issue.get("severity") == "error"
for issue in validation
):
raise ValueError("调整动作没有通过车辆能力或安全校验。")
normalized_changes = []
for change in change_operations:
same_capability = [
item
for item in base_operations
if item["domain"] == change["domain"]
and item["capability"] == change["capability"]
]
normalized = deepcopy(change)
if len(same_capability) == 1 and not self._mode_update_target_is_explicit(
change_text,
str(change["target_scope"]),
):
normalized["target_scope"] = same_capability[0]["target_scope"]
elif len(same_capability) > 1 and not self._mode_update_target_is_explicit(
change_text,
str(change["target_scope"]),
):
raise ValueError(
f"“{mode_name}”中该设备能力包含多个目标,请明确主驾、副驾或具体区域。"
)
normalized_changes.append(normalized)
change_by_key = {
(
str(item["domain"]),
str(item["capability"]),
str(item["target_scope"]),
): 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 = (
str(operation["domain"]),
str(operation["capability"]),
str(operation["target_scope"]),
)
merged.append(deepcopy(change_by_key.pop(key, operation)))
merged.extend(deepcopy(list(change_by_key.values())))
revised_result = self._process_mode_profile_create(
utterance,
"local",
conversation_id,
"dry_run",
approval_token,
vehicle_profile,
simulation_fault,
mode_name,
control_transport,
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"
result["hardware_execution_allowed"] = False
result["orchestration_route"] = "mode_profile_create_preview"
result["validation"] = [
{
"severity": "error",
"code": "MODE_PROFILE_DRAFT_UPDATE_REJECTED",
"path": "mode_profile_draft_context",
"message": str(exc),
}
]
result["mode_creation_failure"] = {
"code": "MODE_PROFILE_DRAFT_UPDATE_REJECTED",
"reason": str(exc),
"next_step": "请保留当前模式卡片,并明确要修改的设备、区域和目标值后重试",
"unresolved_clauses": [change_text] if change_text else [],
"issues": [],
}
result["decision_summary"] = [
f"未修改待保存的“{mode_name}”草稿;原草稿保持不变。"
]
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,
request_id: str,
provider: str,
conversation_id: str,
context: dict[str, Any],
mode_name: str,
change_text: str,
vehicle_profile: VehicleProfile,
result: dict[str, Any],
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}”。"
]
self._trace(
traces,
request_id,
"intent_parse",
"succeeded",
"识别到长期模式配置修改请求;本次不调用 CAN 编译路径。",
started,
{"mode_name": mode_name, "change_text": change_text},
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 profile.schedule is not None:
from cognitive_agent.application.mode_schedule_edit import update_saved_schedule
return update_saved_schedule(
self, profile, change_text=change_text, utterance=utterance,
conversation_id=conversation_id, vehicle_profile=vehicle_profile,
context=context, result=result,
)
if not change_text:
result["status"] = "needs_confirmation"
result["validation"] = [
{
"severity": "error",
"code": "MODE_PROFILE_UPDATE_MISSING_CHANGE",
"path": "utterance",
"message": "请说明要把长期模式中的哪些参数改成什么目标值。",
}
]
self._persist(result)
return result
registered_additions = mode_device_additions(
change_text,
[operation.to_yaml() for operation in profile.operations],
self._mode_device_default_operations(mode_name),
)
if registered_additions is not None:
parsed = {
"decision_summary": [
"已从只读场景模板载入缺失设备的登记默认动作。"
],
"deferred_requests": [],
"model_requests": [],
}
result["provider_used"] = (
"mode_profile_registry+registered_scene_template"
)
result["decision_summary"].extend(parsed["decision_summary"])
result["deferred_requests"] = []
result["model_requests"] = []
mode_operations = self._absolute_mode_operations(
registered_additions
)
source_actions = [
{
"domain": operation["domain"],
"capability": operation["capability"],
"target": operation["target_scope"],
"operation": operation.get("operation", "set"),
"parameters": deepcopy(operation.get("parameters", {})),
"reason": operation.get(
"reason",
"从登记场景模板增补的设备默认动作。",
),
}
for operation in mode_operations
]
else:
parsed, provider_used, provider_notice = self._interpret(
change_text,
provider,
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:
result["decision_summary"].append(provider_notice)
result["deferred_requests"] = parsed.get("deferred_requests", [])
result["model_requests"] = parsed.get("model_requests", [])
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 = [
{
"severity": "error",
"code": "NO_MODE_PROFILE_UPDATE_OPERATIONS",
"path": "spec.operations",
"message": "没有从修改说明中得到可写入长期模式库的可执行配置。",
}
]
validation.extend(
{
"severity": "error",
"code": "AMBIGUOUS_INTENT",
"path": ambiguity.get("field", "utterance"),
"message": ambiguity.get("question", "修改说明不完整。"),
}
for ambiguity in parsed.get("ambiguities", [])
)
result["validation"] = validation
result["status"] = (
"capability_unavailable"
if parsed.get("deferred_requests") and not parsed.get("ambiguities")
else "needs_confirmation"
)
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")
)
result["mode_profile"] = {
**profile_public,
"source": source_path,
"initial_backup_source": backup_path,
}
result["matched_modes"] = [profile_public]
result["model_requests"] = self.mode_profiles.requests_for_profiles(
[updated_profile]
)
result["decision_summary"].append(
f"已更新长期模式“{updated_profile.name}”,原始配置备份保留为初始化来源。"
)
self._trace(
traces,
request_id,
"function_generate",
"succeeded",
"已将修改后的可执行配置写回长期模式 YAML。",
time.perf_counter(),
{
"mode_profile_id": updated_profile.id,
"operation_count": len(updated_profile.operations),
"updated_operation_count": len(mode_operations),
"source": source_path,
"initial_backup_source": backup_path,
},
domain="mode",
)
execution_report = self._mode_profile_control_report(
request_id,
"update_mode_profile",
updated_profile,
f"已更新长期模式“{updated_profile.name}”。",
{
"updated_operation_count": len(mode_operations),
"source": source_path,
"initial_backup_source": backup_path,
},
)
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,
result["mode_profile"],
source_actions,
result["decision_summary"],
"mode_profile_update",
)
result["status"] = (
"partially_succeeded"
if parsed.get("deferred_requests")
else "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": updated_profile.id,
"can_command_emitted": False,
},
domain="mode",
)
self._trace(
traces,
request_id,
"state_update",
"succeeded",
f"长期模式“{updated_profile.name}”已更新,可被后续自然语言请求命中。",
time.perf_counter(),
{
"turn_count": updated_context["turn_count"],
"mode_profile_id": updated_profile.id,
},
domain="mode",
)
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,
request_id: str,
conversation_id: str,
mode_name: str,
vehicle_profile: VehicleProfile,
result: dict[str, Any],
traces: list[dict[str, Any]],
) -> dict[str, Any]:
started = time.perf_counter()
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
try:
restored_profile = self.mode_profiles.restore_initial_mode(profile)
except ValueError as exc:
result["status"] = "needs_confirmation"
result["validation"] = [
{
"severity": "error",
"code": "MODE_PROFILE_INITIAL_BACKUP_MISSING",
"path": "mode_profile.initial_backup",
"message": str(exc),
}
]
self._persist(result)
return result
profile_public = restored_profile.public_dict()
source_path = self._display_path(Path(restored_profile.source))
backup_path = self._display_path(
Path(restored_profile.source).with_suffix(".yaml.initial.bak")
)
result["mode_profile"] = {
**profile_public,
"source": source_path,
"initial_backup_source": backup_path,
}
result["matched_modes"] = [profile_public]
result["model_requests"] = self.mode_profiles.requests_for_profiles(
[restored_profile]
)
result["decision_summary"].append(
f"已将长期模式“{restored_profile.name}”恢复为初始配置。"
)
self._trace(
traces,
request_id,
"function_generate",
"succeeded",
"已从初始备份恢复长期模式 YAML。",
time.perf_counter(),
{
"mode_profile_id": restored_profile.id,
"operation_count": len(restored_profile.operations),
"source": source_path,
"initial_backup_source": backup_path,
},
domain="mode",
)
execution_report = self._mode_profile_control_report(
request_id,
"restore_mode_profile",
restored_profile,
f"已恢复长期模式“{restored_profile.name}”。",
{
"source": source_path,
"initial_backup_source": backup_path,
},
)
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,
result["mode_profile"],
[],
result["decision_summary"],
"mode_profile_restore",
)
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": restored_profile.id,
"can_command_emitted": False,
},
domain="mode",
)
self._trace(
traces,
request_id,
"state_update",
"succeeded",
f"长期模式“{restored_profile.name}”已恢复,可被后续自然语言请求命中。",
time.perf_counter(),
{
"turn_count": updated_context["turn_count"],
"mode_profile_id": restored_profile.id,
},
domain="mode",
)
self._persist(result)
return result
def _resolve_single_mode_profile(
self,
mode_name: str,
vehicle_profile: VehicleProfile,
result: dict[str, Any],
traces: list[dict[str, Any]],
request_id: str,
) -> ModeProfile | None:
exact = self.mode_profiles.resolve_exact_name(
mode_name,
vehicle_id=vehicle_profile.id,
)
matches = [exact] if exact is not None else []
result["matched_modes"] = [profile.public_dict() for profile in matches]
if len(matches) == 1:
return matches[0]
result["status"] = "needs_confirmation"
result["validation"] = [
{
"severity": "error",
"code": (
"MODE_PROFILE_NOT_FOUND"
if not matches
else "MODE_PROFILE_AMBIGUOUS"
),
"path": "utterance.mode_profile",
"message": (
f"未在车型 {vehicle_profile.id} 的长期模式库中找到“{mode_name}”。"
if not matches
else f"“{mode_name}”命中了多个长期模式,请指定更完整名称。"
),
}
]
self._trace(
traces,
request_id,
"context_resolve",
"failed",
result["validation"][0]["message"],
time.perf_counter(),
{
"mode_name": mode_name,
"vehicle_id": vehicle_profile.id,
"match_count": len(matches),
},
domain="mode",
)
return None
def _mode_profile_control_report(
self,
request_id: str,
operation: str,
profile: ModeProfile,
message: str,
telemetry: dict[str, Any],
) -> dict[str, Any]:
return {
"apiVersion": "cognitive-agent/v1alpha1",
"kind": "ExecutionReport",
"metadata": {
"id": f"report-{request_id}",
"name": "Mode profile control report",
"version": "1.0.0",
"request_id": request_id,
"labels": {"control_plane": "mode_profile_store"},
},
"spec": {
"mode": "dry_run",
"status": "succeeded",
"started_at": _now(),
"finished_at": _now(),
"results": [
{
"ref": f"{operation}-{profile.id}",
"domain": "mode",
"status": "succeeded",
"message": message,
"telemetry": {
"operation": operation,
"mode_profile_id": profile.id,
"operation_count": len(profile.operations),
"storage_scope": "repository_config",
"can_command_emitted": False,
**telemetry,
},
}
],
},
}
def _display_path(self, path: Path) -> str:
try:
return str(path.relative_to(self.root))
except ValueError:
return str(path)
@staticmethod
def _mode_feedback_text(value: object, *, limit: int = 180) -> str:
text = re.sub(r"[\x00-\x1f\x7f]+", " ", str(value or ""))
return re.sub(r"\s+", " ", text).strip(" ,,。;;::")[:limit]
@classmethod
def _mode_failure_issue(
cls,
clause: str,
parsed: dict[str, Any],
) -> dict[str, str]:
display_clause = cls._mode_feedback_text(
re.sub(r"^(?:副驾座椅|主驾座椅)[,,]", "", clause),
limit=120,
)
ambiguities = [
item
for item in parsed.get("ambiguities", [])
if isinstance(item, dict) and str(item.get("field") or "") != "capability"
]
ambiguity = ambiguities[0] if ambiguities else None
field = str(ambiguity.get("field") or "") if ambiguity else ""
question = cls._mode_feedback_text(
ambiguity.get("question") if ambiguity else "",
limit=220,
)
# Parser questions may contain both the diagnosis and a concrete
# correction. Split those parts so every public failure has the same
# reason/next-step contract.
reason_from_question = question
next_from_question = ""
for separator in (";请", "。请"):
if separator in question:
reason_from_question, suffix = question.split(separator, 1)
next_from_question = f"请{suffix}"
break
if reason_from_question and not re.search(
r"(?:CAN|CH[01]|JSON|schema|0x[0-9a-f]+|内部路径)",
reason_from_question,
re.IGNORECASE,
):
reason = f"设置“{display_clause}”未通过:{reason_from_question}"
else:
reason = (
f"设置“{display_clause}”未被识别为当前支持的座舱控制动作"
)
if next_from_question:
next_step = next_from_question
elif "temperature" in field or "温度" in display_clause:
next_step = (
"请只保留一个18至32℃的整数温度,例如“空调调整到24度”,然后重试"
)
elif field == "target" or "target_scope" in field:
next_step = (
"请改用当前支持的副驾座椅目标,并明确部件与位置,例如"
"“副驾腿托调整到80%”"
)
elif "position" in field or re.search(
r"座椅|靠背|椅背|坐垫|坐盆|腿托|脚托|滑轨",
display_clause,
):
next_step = (
"请按“副驾 + 座椅部件 + 方向或0到100%位置”重述,例如"
"“副驾腿托向上调整到最大位置”或“副驾腿托调整到80%”"
)
else:
next_step = (
"请按“设备 + 动作 + 明确目标”重述,例如“关闭所有车窗”、"
"“副驾靠背调整到80%”或“空调调整到24度”"
)
return {
"clause": display_clause,
"reason": reason,
"next_step": cls._mode_feedback_text(next_step, limit=260),
}
@classmethod
def _mode_creation_failure_from_clauses(
cls,
clause_results: list[tuple[str, dict[str, Any], str]],
unresolved_clauses: list[str],
) -> dict[str, Any]:
unresolved = set(unresolved_clauses)
issues = [
cls._mode_failure_issue(clause, parsed)
for clause, parsed, status in clause_results
if status != "complete" and clause in unresolved
][:3]
if not issues:
issues = [
cls._mode_failure_issue(clause, {})
for clause in unresolved_clauses[:3]
]
reason = ";".join(dict.fromkeys(item["reason"] for item in issues))
next_step = ";".join(
dict.fromkeys(item["next_step"] for item in issues)
)
return {
"code": "MODE_PROFILE_DRAFT_INCOMPLETE",
"reason": reason or "部分座舱设置没有转换为可保存的控制动作",
"next_step": next_step
or "请将未识别设置改写为明确的设备、动作和目标值后重试",
"unresolved_clauses": [item["clause"] for item in issues],
"issues": issues,
}
@staticmethod
def _scene_plan_signatures(
assistant_plan: dict[str, Any],
) -> Counter[tuple[str, str, str]]:
signatures: Counter[tuple[str, str, str]] = Counter()
requests = assistant_plan.get("requests", [])
if not isinstance(requests, list):
return signatures
for request in requests:
if not isinstance(request, dict):
continue
domain = str(request.get("domain") or "")
capability = str(request.get("capability") or "")
target = str(request.get("target_scope") or "")
if domain == "seat" and target == "front_right":
target = "passenger"
if domain and capability and target:
signatures[(domain, capability, target)] += 1
return signatures
@staticmethod
def _scene_action_signatures(
actions: list[dict[str, Any]],
) -> Counter[tuple[str, str, str]]:
signatures: Counter[tuple[str, str, str]] = Counter()
for action in actions:
domain = str(action.get("domain") or "")
capability = str(action.get("capability") or "")
raw_target = action.get("target")
target = (
str(raw_target.get("instance") or raw_target.get("zone") or "")
if isinstance(raw_target, dict)
else str(action.get("target_scope") or raw_target or "")
)
if domain == "seat" and target == "front_right":
target = "passenger"
if domain and capability and target:
signatures[(domain, capability, target)] += 1
return signatures
@staticmethod
def _scene_parameters_preserved(
assistant_plan: dict[str, Any],
actions: list[dict[str, Any]],
) -> bool:
actual_parameters: dict[tuple[str, str, str], dict[str, Any]] = {}
for action in actions:
domain = str(action.get("domain") or "")
capability = str(action.get("capability") or "")
raw_target = action.get("target")
target = (
str(raw_target.get("instance") or raw_target.get("zone") or "")
if isinstance(raw_target, dict)
else str(action.get("target_scope") or raw_target or "")
)
if domain == "seat" and target == "front_right":
target = "passenger"
parameters = action.get("parameters")
if domain and capability and target and isinstance(parameters, dict):
actual_parameters[(domain, capability, target)] = parameters
requests = assistant_plan.get("requests", [])
if not isinstance(requests, list):
return False
for request in requests:
if not isinstance(request, dict):
return False
domain = str(request.get("domain") or "")
capability = str(request.get("capability") or "")
target = str(request.get("target_scope") or "")
if domain == "seat" and target == "front_right":
target = "passenger"
parameters = request.get("parameters")
if not isinstance(parameters, list):
return False
expected = {
str(parameter.get("name") or ""): parameter.get("value")
for parameter in parameters
if isinstance(parameter, dict)
}
actual = actual_parameters.get((domain, capability, target))
if actual is None or any(actual.get(name) != value for name, value in expected.items()):
return False
return True
@staticmethod
def _scrub_failed_scene_preview(result: dict[str, Any]) -> None:
failed_intent = result.get("intent_ir")
failed_spec = (
failed_intent.get("spec") if isinstance(failed_intent, dict) else None
)
if isinstance(failed_spec, dict):
failed_spec["actions"] = []
failed_spec["requests"] = []
result["deferred_requests"] = []
result["model_requests"] = []
result["function_config"] = None
result["function_configs"] = {}
result["command_plan"] = None
result["command_plans"] = {}
result["scene_config"] = None
result["execution_report"] = None
def _mode_creation_seat_failure(
self, utterance: str, conversation_id: str, vehicle: VehicleProfile, reason: str,
) -> dict[str, Any]:
result = {
"request_id": f"req-{datetime.now(UTC).strftime('%Y%m%d%H%M%S')}-{uuid.uuid4().hex}",
"utterance": utterance, "conversation_id": conversation_id,
"vehicle": vehicle.public_dict(), "status": "validation_failed",
"execution_mode": "dry_run", "hardware_execution_allowed": False,
"orchestration_route": "mode_profile_create_preview", "trace_events": [],
"validation": [{"severity": "error", "code": "MODE_SEAT_SCOPE_UNAVAILABLE",
"path": "mode_profile_draft.seat_scope", "message": reason}],
"mode_creation_failure": {"code": "MODE_SEAT_SCOPE_UNAVAILABLE", "reason": reason,
"next_step": "请选择该模式实际支持的座位,或改用当前座位支持的舒适功能"},
"decision_summary": [reason, "没有保存模式或下发控制。"],
}
self._scrub_failed_scene_preview(result)
self._persist(result)
return result
def _process_mode_profile_create(
self,
utterance: str,
provider: str,
conversation_id: str,
execution_mode: str,
approval_token: str | None,
vehicle_profile: VehicleProfile,
simulation_fault: str,
mode_name: str,
control_transport: str,
assistant_plan: dict[str, Any] | None = None,
source_draft: dict[str, Any] | None = None,
) -> dict[str, Any]:
"""Build and verify a named-mode draft for explicit user confirmation.
A request such as ``构建一个K歌模式`` used to be interpreted as an
ordinary temporary scene. Generate the scene through the normal
dry-run path, but leave persistence to the existing mode-card API so
the assistant can show the exact actions before the user saves them.
"""
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 ""
selected_seats = creation_seat_scopes(utterance) if source_draft is None else None
if selected_seats is not None and "unsupported" in selected_seats and re.search(
r"座椅|座位|靠背|椅背|腿托|脚托|坐垫|坐盆|按摩|零重力", utterance
):
return self._mode_creation_seat_failure(
utterance, conversation_id, vehicle_profile,
"当前模式只支持明确的主驾或副驾座椅,不会将未支持的座位替换为其他座位。",
)
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")
or assistant_plan.get("schedule") != candidate_template.plan().get("schedule")
):
raise ValueError("内置常用模式模板已更新,请重新提交。")
scene_template = candidate_template
source_existing_mode_id = (
str(source_draft.get("existing_mode_id") or "").strip()
if isinstance(source_draft, dict)
else ""
)
if source_draft is not None and source_existing_mode_id:
try:
existing_profile = self.mode_profiles.get_profile(
source_existing_mode_id,
vehicle_profile.id,
)
except KeyError:
existing_profile = None
elif source_draft is not None:
existing_profile = None
else:
existing_profile = self.mode_profiles.resolve_exact_name(
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
else None
)
if source_profile is None and source_draft is None and not requested_instruction and scene_template is None:
source_name = mode_creation_source_name(mode_name)
if source_name != mode_name:
source_profile = self.mode_profiles.resolve_exact_name(source_name, vehicle_id=vehicle_profile.id)
draft_mode_name = (
str(source_draft.get("name") or mode_name).strip()
if isinstance(source_draft, dict)
else existing_profile.name
if existing_profile is not None
else mode_name
)
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"
if assistant_plan is not None and (not requested_instruction or needs_mode_schedule(utterance))
else "explicit_scene_requirements"
)
# A name-only request may already describe a repository-backed scene.
# Reuse that vehicle-scoped profile as trusted structured input instead
# of appending a model prompt to the user's utterance. The latter was
# being parsed as a second control clause on the assistant's local fast
# path, turning a valid nine-action mode into an incomplete draft.
source_utterance = requested_instruction or f"进入{draft_mode_name}"
if requested_instruction and selected_seats is not None and len(selected_seats) == 1:
# Qualify only explicit seat-device clauses, never an HVAC/window
# zone or unrelated control. General one-shot controls keep their
# existing target policy; this is scoped to new mode construction.
label = "主驾" if selected_seats == {"driver"} else "副驾"
instruction_clauses = self._split_intent_clauses(requested_instruction)
qualified_clauses = [
f"{label}{clause}" if (
re.search(r"座椅|座位|靠背|椅背|腿托|脚托|坐垫|坐盆|按摩", clause)
and resolve_explicit_seat_scopes(clause, assume_seat_context=True) is None
) else clause
for clause in instruction_clauses
]
# Preserve the user's punctuation and wording unless a seat really
# needs qualification; explicit instructions need no rewriting.
if qualified_clauses != instruction_clauses:
source_utterance = ",".join(qualified_clauses)
raw_schedule = (assistant_plan.get("schedule") if isinstance(assistant_plan, dict) else None)
if source_profile is not None and source_profile.schedule is not None:
raw_schedule = source_profile.schedule.to_dict()
if raw_schedule is not None:
if isinstance(assistant_plan, dict) and assistant_plan.get("ambiguities"):
raise ValueError("动态需求仍有未解决的问题,不能生成部分成功的时间轴,请补充或调整需求。")
if source_profile is None and source_draft is None and scene_template is None and not needs_mode_schedule(utterance):
raise ValueError("用户未请求动态或分阶段模式,不能擅自生成定时任务。")
schedule = ModeSchedule.from_dict(raw_schedule)
if selected_seats is not None and (source_profile is not None or scene_template is not None):
try:
schedule = project_seed_schedule(schedule, selected_seats)
except ModeCreationSeatPolicyError as exc:
return self._mode_creation_seat_failure(utterance, conversation_id, vehicle_profile, str(exc))
return self._preview_mode_schedule(
schedule, name=draft_mode_name, utterance=utterance,
conversation_id=conversation_id, vehicle_profile=vehicle_profile,
control_transport=control_transport, generation_source=generation_source,
source_profile=existing_profile, source_template=scene_template, source_draft=source_draft,
scene_scope_utterance="" if source_draft is not None else utterance,
)
if needs_mode_schedule(utterance) and scene_template is None and not (source_profile is not None and source_profile.timeline):
result = {
"request_id": f"req-{datetime.now(UTC).strftime('%Y%m%d%H%M%S')}-{uuid.uuid4().hex}",
"utterance": utterance, "conversation_id": conversation_id,
"vehicle": vehicle_profile.public_dict(), "validation": [], "trace_events": [],
"status": "validation_failed", "execution_mode": "dry_run", "hardware_execution_allowed": False,
"orchestration_route": "mode_profile_create_preview",
}
result["mode_creation_failure"] = {
"code": "MODE_SCHEDULE_REQUIRED",
"reason": "该需求包含时间变化,但没有生成完整的分阶段时间轴",
"next_step": "请重试,或补充每个阶段的设备目标和持续时间;未保存或执行静态替代动作",
}
self._scrub_failed_scene_preview(result)
self._persist(result)
return result
try:
if scene_template is not None and selected_seats is not None:
assistant_plan = project_seed_plan(assistant_plan, selected_seats)
source_result = self._process_once(
source_utterance,
provider,
conversation_id,
"dry_run",
approval_token,
vehicle_profile.id,
simulation_fault,
control_transport=control_transport,
direct_mode_profile_id=(
source_profile.id if source_profile is not None else None
),
mode_creation_scopes=selected_seats,
assistant_plan=assistant_plan,
# ``source_utterance`` is synthesized for a name-only, unknown
# scene. It must not fall back to substring matching (for example
# ``我的小憩模式`` -> ``小憩模式``), otherwise the exact resolver's
# ambiguity and collision checks can be bypassed.
allow_fuzzy_mode_profile_match=bool(requested_instruction),
allow_scene_composition=(
assistant_plan is not None
and source_profile is None
and not requested_instruction
),
# A revision combines a server-loaded draft with changes that
# already passed the ordinary text/target authorization path.
# Its old display name cannot veto an explicitly requested new
# seat. This override is internal, never accepted from payloads.
scene_scope_utterance="" if source_draft is not None else utterance,
)
except ModeCreationSeatPolicyError as exc:
return self._mode_creation_seat_failure(utterance, conversation_id, vehicle_profile, str(exc))
except NativeCanCompileError:
# Repository modes and model-composed scenes share the same native
# compiler. A stale mode parameter must become a bounded failed
# preview, never an uncaught HTTP 500 or a partially usable card.
failure_context = self._context_for_vehicle(
self.conversations.snapshot(conversation_id),
vehicle_profile,
)
source_result = {
"request_id": (
f"req-{datetime.now(UTC).strftime('%Y%m%d%H%M%S')}-"
f"{uuid.uuid4().hex}"
),
"utterance": source_utterance,
"conversation_id": conversation_id,
"conversation_turn": failure_context["turn_count"],
"status": "validation_failed",
"provider_requested": provider,
"provider_used": "mode_profile_native_can_guard",
"execution_mode": "dry_run",
"control_transport": control_transport,
"hardware_execution_allowed": False,
"vehicle": vehicle_profile.public_dict(),
"matched_modes": (
[source_profile.public_dict()]
if source_profile is not None
else []
),
"mode_profile": None,
"decision_summary": [
f"“{draft_mode_name}”包含当前车型协议无法编译的动作,未生成草稿。"
],
"deferred_requests": [],
"model_requests": [],
"unresolved_clauses": [],
"intent_ir": None,
"function_config": None,
"function_configs": {},
"scene_config": None,
"preset_config": None,
"validation": [
{
"severity": "error",
"code": "MODE_PROFILE_NATIVE_COMPILE_FAILED",
"path": "mode_profile_draft",
"message": "模式动作无法编译为当前车型支持的控制指令。",
}
],
"command_plan": None,
"command_plans": {},
"execution_report": None,
"actuator_state": failure_context["devices"],
"domain_state": failure_context["domain_devices"],
"saved_presets": [],
"trace_events": [],
"can_commands": [],
"orchestration_route": "mode_profile_create_preview",
"mode_creation_failure": {
"code": "MODE_PROFILE_NATIVE_COMPILE_FAILED",
"reason": "模式动作未通过当前车型 CAN 协议编译校验",
"next_step": "请检查模式中的设备参数是否符合当前车型能力范围",
"unresolved_clauses": [],
"issues": [],
},
}
self._persist(source_result)
return source_result
draft_context = self._context_for_vehicle(
self.conversations.snapshot(conversation_id),
vehicle_profile,
)
draft_context["mode_profiles"] = self.mode_profiles.public_modes(
vehicle_profile.id
)
local_clause_results = self._local_clause_results(
source_utterance,
draft_context,
)
unresolved_draft_clauses = [
clause
for clause, _parsed, status in local_clause_results
if status != "complete"
and not (
(requested_instruction or source_profile is not None)
and self.mode_profiles.match_text(
clause,
vehicle_id=vehicle_profile.id,
)
)
]
if provider == "local" and assistant_plan is None and unresolved_draft_clauses:
source_result["status"] = "needs_confirmation"
source_result["unresolved_clauses"] = unresolved_draft_clauses
source_result["mode_creation_failure"] = (
self._mode_creation_failure_from_clauses(
local_clause_results,
unresolved_draft_clauses,
)
)
source_result["supplement_required"] = False
source_result["validation"] = [
*list(source_result.get("validation", [])),
{
"severity": "error",
"code": "MODE_PROFILE_DRAFT_INCOMPLETE",
"path": "mode_profile_draft.instruction",
"message": (
"以下模式设置尚未完整解析:"
+ ";".join(unresolved_draft_clauses)
),
},
]
source_result["decision_summary"] = [
f"未能完整构建“{mode_name}”,因此没有生成可保存草稿。",
*list(source_result.get("decision_summary", [])),
"请补充为当前支持的明确设备动作后重试。",
]
self._persist(source_result)
return source_result
source_intent = source_result.get("intent_ir")
source_spec = (
source_intent.get("spec") if isinstance(source_intent, dict) else None
)
source_actions = (
source_spec.get("actions", []) if isinstance(source_spec, dict) else []
)
typed_source_actions = [
action for action in source_actions if isinstance(action, dict)
] if isinstance(source_actions, list) else []
if (
source_result.get("status") == "succeeded"
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)
!= self._scene_action_signatures(typed_source_actions)
or not self._scene_parameters_preserved(
assistant_plan,
typed_source_actions,
)
)
):
source_result["status"] = "validation_failed"
source_result["validation"] = [
*list(source_result.get("validation", [])),
{
"severity": "error",
"code": "MODE_PROFILE_SCENE_PLAN_INCOMPLETE",
"path": "intent_ir.spec.actions",
"message": (
"小迪生成的场景候选动作未全部进入当前车型的可复用执行链,"
"已拒绝生成部分功能模式。"
),
},
]
if source_result.get("status") != "succeeded":
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.
self._scrub_failed_scene_preview(source_result)
source_result["mode_creation_failure"] = {
"code": "MODE_PROFILE_ACTION_VALIDATION_FAILED",
"reason": "场景动作未通过车辆能力或安全校验",
"next_step": (
"请检查每项设备目标是否在当前支持范围内;仍失败时可分条输入后重试"
),
"unresolved_clauses": [],
"issues": [],
}
source_result["decision_summary"] = [
f"未能完成“{mode_name}”的场景动作验证,因此没有创建长期模式。",
*list(source_result.get("decision_summary", [])),
]
self._persist(source_result)
return source_result
intent_ir = source_result.get("intent_ir")
intent_spec = intent_ir.get("spec") if isinstance(intent_ir, dict) else None
raw_intent_actions = (
intent_spec.get("actions", []) if isinstance(intent_spec, dict) else []
)
reusable_actions = [
item for item in raw_intent_actions if isinstance(item, dict)
] if isinstance(raw_intent_actions, list) else []
if not reusable_actions:
source_result["status"] = "needs_confirmation"
source_result["mode_creation_failure"] = {
"code": "MODE_PROFILE_DRAFT_EMPTY",
"reason": "没有识别到可保存的座舱控制动作",
"next_step": (
"请按“设备 + 动作 + 明确目标”补充,例如“关闭所有车窗,空调调整到24度”"
),
"unresolved_clauses": [],
"issues": [],
}
source_result["validation"] = [
{
"severity": "error",
"code": "MODE_PROFILE_DRAFT_EMPTY",
"path": "intent_ir.spec.actions",
"message": "没有生成可保存的功能模式动作,请补充更明确的座舱设置。",
}
]
source_result["decision_summary"] = [
f"未能为“{mode_name}”生成可保存的场景动作。",
*list(source_result.get("decision_summary", [])),
]
self._persist(source_result)
return source_result
domains = list(
dict.fromkeys(
str(item.get("domain") or "")
for item in reusable_actions
if str(item.get("domain") or "")
)
)
source_request_id = str(source_result.get("request_id") or "")
source_existing_revision = (
str(source_draft.get("existing_mode_revision") or "").strip()
if isinstance(source_draft, dict)
else ""
)
existing_binding = (
{
"existing_mode_id": source_existing_mode_id,
"existing_mode_revision": source_existing_revision,
}
if source_draft is not None and source_existing_mode_id
else {
"existing_mode_id": existing_profile.id,
"existing_mode_revision": (
self.mode_profiles.profile_revision(existing_profile)
),
}
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)
else None
)
source_revision = (
raw_source_revision
if isinstance(raw_source_revision, int)
and not isinstance(raw_source_revision, bool)
and raw_source_revision >= 1
else 1
if source_draft is not None
else 0
)
source_result["mode_profile_draft"] = {
"name": draft_mode_name,
"instruction": source_utterance,
"source_request_id": source_request_id,
"vehicle_id": vehicle_profile.id,
"domains": domains,
"operation_count": len(reusable_actions),
**existing_binding,
**source_template_binding,
"generation_source": generation_source,
"draft_revision": source_revision + 1,
**(
{
"revises_source_request_id": str(
source_draft.get("source_request_id") or ""
).strip()
}
if isinstance(source_draft, dict)
else {}
),
}
source_result["mode_creation"] = {
"requested_utterance": utterance,
"source_utterance": source_utterance,
"source_request_id": source_request_id,
"source_status": source_result.get("status"),
"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,
}
source_result["orchestration_route"] = "mode_profile_create_preview"
source_result["decision_summary"] = [
(
f"已从当前车型的工程模式目录载入并验证“{source_profile.name}”的"
f" {len(reusable_actions)} 项设备动作。"
if source_profile is not None
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"
else f"已构建并验证“{mode_name}”场景。"
),
*list(source_result.get("decision_summary", [])),
(
"可先进行一次模拟试运行,确认动作后更新现有功能模式。"
if existing_profile is not None
else "请确认动作后保存为功能模式,或先进行一次模拟试运行。"
),
]
# Overwrite the persisted source result with the draft metadata. The
# mode-card endpoint later re-reads this server-owned artifact and
# refuses any client-side instruction or request-id substitution.
self._persist(source_result)
return source_result
def _process_mode_profile_save(
self,
utterance: str,
request_id: str,
conversation_id: str,
context: dict[str, Any],
mode_name: str,
vehicle_profile: VehicleProfile,
result: dict[str, Any],
traces: list[dict[str, Any]],
) -> dict[str, Any]:
started = time.perf_counter()
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",
)
source_actions, mode_operations = self._last_mode_source(context)
if not mode_operations:
result["status"] = "needs_confirmation"
result["validation"] = [
{
"severity": "error",
"code": "NO_RECENT_MODE_SOURCE",
"path": "conversation.recent_turns",
"message": "当前会话没有可固化为长期模式的最近成功动作。",
}
]
self._persist(result)
return result
profile = self.mode_profiles.save_mode(
mode_name,
mode_operations,
vehicle_id=vehicle_profile.id,
)
profile_public = profile.public_dict()
profile_path = Path(profile.source)
try:
source_path = str(profile_path.relative_to(self.root))
except ValueError:
source_path = str(profile_path)
result["mode_profile"] = {
**profile_public,
"source": source_path,
}
result["matched_modes"] = [profile_public]
result["decision_summary"].append(
f"已将最近一次成功动作固化为长期模式“{profile.name}”。"
)
result["model_requests"] = self.mode_profiles.requests_for_profiles([profile])
generation_started = time.perf_counter()
self._trace(
traces,
request_id,
"function_generate",
"succeeded",
"已写入长期模式 YAML;该配置将在后续请求中重新生成 FunctionConfig。",
generation_started,
{
"mode_profile_id": profile.id,
"operation_count": len(profile.operations),
"source": source_path,
},
domain="mode",
)
execution_report = {
"apiVersion": "cognitive-agent/v1alpha1",
"kind": "ExecutionReport",
"metadata": {
"id": f"report-{request_id}",
"name": "Mode profile save report",
"version": "1.0.0",
"request_id": request_id,
"labels": {"control_plane": "mode_profile_store"},
},
"spec": {
"mode": "dry_run",
"status": "succeeded",
"started_at": _now(),
"finished_at": _now(),
"results": [
{
"ref": f"save-{profile.id}",
"domain": "mode",
"status": "succeeded",
"message": f"已保存长期模式“{profile.name}”。",
"telemetry": {
"operation": "save_mode_profile",
"mode_profile_id": profile.id,
"operation_count": len(profile.operations),
"storage_scope": "repository_config",
"source": source_path,
"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.save_mode_profile(
conversation_id,
utterance,
result["mode_profile"],
source_actions,
result["decision_summary"],
)
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": profile.id,
"can_command_emitted": False,
},
domain="mode",
)
self._trace(
traces,
request_id,
"state_update",
"succeeded",
f"长期模式“{profile.name}”已登记,可被后续自然语言请求命中。",
time.perf_counter(),
{
"turn_count": updated_context["turn_count"],
"mode_profile_id": profile.id,
},
domain="mode",
)
self._persist(result)
return result
def _last_mode_source(
self,
context: dict[str, Any],
) -> tuple[list[dict[str, Any]], list[dict[str, Any]]]:
for turn in reversed(context.get("recent_turns", [])):
actions = turn.get("actions", [])
if not isinstance(actions, list):
continue
operations = self._mode_operations_from_actions(actions)
if operations:
return deepcopy(actions), operations
return [], []
def _mode_operations_from_actions(
self,
actions: list[dict[str, Any]],
) -> list[dict[str, Any]]:
operations = []
for action in actions:
operation = self._mode_operation_from_action(action)
if operation is not None:
operations.append(operation)
return operations
def _mode_operation_from_action(
self,
action: dict[str, Any],
) -> dict[str, Any] | None:
domain = action.get("domain")
if domain in self.domain_runtimes or self.native_can_runtime.supports(action):
return {
"domain": domain,
"capability": action["capability"],
"target_scope": action["target"],
"operation": action.get("operation", "set"),
"parameters": dict(action.get("parameters", {})),
"reason": action.get("reason", "用户固化的跨域模式动作。"),
}
if action.get("action_type") == "position":
capability = SEAT_AXIS_TO_CAPABILITY.get(str(action.get("axis")))
if capability is None:
return None
parameters = {"position_pct": action["position_pct"]}
if action.get("angle_deg") is not None:
parameters["angle_deg"] = action["angle_deg"]
return {
"domain": "seat",
"capability": capability,
"target_scope": action["target"],
"operation": "set",
"parameters": parameters,
"reason": "用户固化的座椅位置模式动作。",
}
if action.get("action_type") == "dynamic_mode_stop":
return {
"domain": "seat",
"capability": DYNAMIC_MODE_STOP_CAPABILITY,
"target_scope": action["target"],
"operation": "deactivate",
"parameters": {},
"reason": "用户固化的动态座椅停止动作。",
}
if action.get("action_type") == "dynamic_mode":
return {
"domain": "seat",
"capability": "seat.start_dynamic_mode",
"target_scope": action["target"],
"operation": "set",
"parameters": {"mode_id": int(action["mode_id"])},
"reason": "用户固化的固件原生动态座椅模式。",
}
function = action.get("function")
capability = SEAT_FUNCTION_TO_CAPABILITY.get(str(function))
if capability is None:
return None
parameters = {"state": action.get("state")}
if action.get("requested_level") is not None:
parameters["level"] = action["requested_level"]
return {
"domain": "seat",
"capability": capability,
"target_scope": action["target"],
"operation": "set",
"parameters": parameters,
"reason": "用户固化的座椅舒适功能模式动作。",
}
def _partition_native_actions(
self, parsed: dict[str, Any], *, promote_supported: bool = False
) -> dict[str, Any]:
"""Promote verified CAN_DEV capabilities and leave unsupported domains virtual."""
result = deepcopy(parsed)
candidate_native_actions = list(result.get("native_can_actions", []))
native_actions = [
action
for action in candidate_native_actions
if self.native_can_runtime.supports(action)
]
native_signatures = {
(
str(action.get("domain")),
str(action.get("capability")),
str(action.get("target", action.get("target_scope", "vehicle"))),
json.dumps(action.get("parameters", {}), sort_keys=True, ensure_ascii=False),
)
for action in candidate_native_actions
}
virtual_actions: list[dict[str, Any]] = []
demoted_signatures: set[tuple[str, str, str, str]] = set()
for action in candidate_native_actions:
if str(action.get("capability")) not in UNVERIFIED_NATIVE_CAPABILITIES:
continue
demoted = dict(action)
demoted["execution_backend"] = "configured_domain"
virtual_actions.append(demoted)
demoted_signatures.add(
(
str(action.get("domain")),
str(action.get("capability")),
str(action.get("target", action.get("target_scope", "vehicle"))),
json.dumps(action.get("parameters", {}), sort_keys=True, ensure_ascii=False),
)
)
for action in result.get("domain_actions", []):
signature = (
str(action.get("domain")),
str(action.get("capability")),
str(action.get("target", action.get("target_scope", "vehicle"))),
json.dumps(action.get("parameters", {}), sort_keys=True, ensure_ascii=False),
)
if signature in native_signatures:
continue
if promote_supported and self.native_can_runtime.supports(action):
promoted = dict(action)
promoted["execution_backend"] = "native_can"
native_actions.append(promoted)
else:
virtual_actions.append(action)
deduplicated: list[dict[str, Any]] = []
signatures: set[tuple[str, str, str, str]] = set()
for action in native_actions:
signature = (
str(action.get("domain")),
str(action.get("capability")),
str(action.get("target", action.get("target_scope", "vehicle"))),
json.dumps(action.get("parameters", {}), sort_keys=True, ensure_ascii=False),
)
if signature not in signatures:
signatures.add(signature)
deduplicated.append(action)
result["domain_actions"] = virtual_actions
result["native_can_actions"] = deduplicated
detected_domains = list(result.get("detected_domains", []))
if result.get("actions") and "seat" not in detected_domains:
detected_domains.insert(0, "seat")
for action in [*virtual_actions, *deduplicated]:
if action["domain"] not in detected_domains:
detected_domains.append(action["domain"])
result["detected_domains"] = detected_domains
deferred_requests = [
item
for item in result.get("deferred_requests", [])
if not (
item.get("execution_backend") == "native_can"
and (
self.native_can_runtime.supports(item)
or (
str(item.get("domain")),
str(item.get("capability")),
str(item.get("target", item.get("target_scope", "vehicle"))),
json.dumps(item.get("parameters", {}), sort_keys=True, ensure_ascii=False),
)
in demoted_signatures
)
)
]
for item in deferred_requests:
if item.get("capability") == "door.set_vehicle_lock":
item["execution_backend"] = "unsupported"
item["rejection_code"] = "CH1_DOOR_LOCK_CONTROL_UNSUPPORTED"
item["reason"] = (
"Current CH1 whole-vehicle door-lock control is unsupported; "
"lock feedback is observational only and no verified physical "
"command will be built."
)
result["deferred_requests"] = deferred_requests
return result
@staticmethod
def _defer_nonwhitelisted_wireless_actions(
parsed: dict[str, Any],
) -> dict[str, Any]:
"""Keep approved MQTT actions executable when a model adds virtual ones."""
result = deepcopy(parsed)
deferred_requests = list(result.get("deferred_requests", []))
existing = {
(
str(item.get("domain", "")),
str(item.get("capability", "")),
str(item.get("target", item.get("target_scope", "vehicle"))),
)
for item in deferred_requests
}
for action in result.get("domain_actions", []):
target = str(
action.get("target", action.get("target_scope", "vehicle"))
)
signature = (
str(action.get("domain", "unknown")),
str(action.get("capability", "unknown")),
target,
)
if signature in existing:
continue
parameters = dict(action.get("parameters", {}))
requested_value: Any = parameters
if len(parameters) == 1:
requested_value = next(iter(parameters.values()))
deferred_requests.append(
{
"domain": signature[0],
"capability": signature[1],
"target_scope": target,
"desired_change": str(action.get("operation", "set")),
"requested_value": requested_value,
"parameters": parameters,
"reason": (
"该模型动作不在无线 MQTT CAN 控制白名单中,已单独跳过;"
"同一句中的白名单动作继续发布。"
),
"execution_backend": "unsupported",
"rejection_code": "WIRELESS_MQTT_CONTROL_NOT_WHITELISTED",
}
)
existing.add(signature)
result["domain_actions"] = []
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 _authorize_compiled_hardware_delivery(
self,
parsed: dict[str, Any],
approval_token: str | None,
control_transport: str,
) -> None:
"""Authorize physical transports after commands are safe to display.
Parsing, schema validation and deterministic CAN compilation do not
transmit frames. Keeping transport readiness checks after the
``commands_validated`` callback lets every UI target show the same
parsed commands, while these gates still run before any executor can
open a CAN or MQTT transmission path.
"""
if parsed.get("actions"):
(
self.vehicle_mqtt_settings
if control_transport == WIRELESS_MQTT_TRANSPORT
else self.hardware_settings
).authorize(approval_token)
if parsed.get("native_can_actions"):
(
self.vehicle_mqtt_settings
if control_transport == WIRELESS_MQTT_TRANSPORT
else self.vehicle_can_settings
).authorize(approval_token)
def _process_orchestrated(
self,
utterance: str,
request_id: str,
provider_used: str,
conversation_id: str,
context: dict[str, Any],
parsed: dict[str, Any],
result: dict[str, Any],
traces: list[dict[str, Any]],
*,
hardware_request: bool = False,
simulated_hardware_request: bool = False,
simulation_fault: str = "normal",
vehicle_profile: VehicleProfile | None = None,
control_transport: str = WIRED_CAN_TRANSPORT,
approval_token: str | None = None,
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()
generation_started = time.perf_counter()
intent_ir = build_intent_ir(
parsed,
utterance,
request_id,
provider_used,
self.semantic_capability_metadata,
)
intent_actions = [
*parsed.get("domain_actions", []),
*parsed.get("native_can_actions", []),
]
for action in intent_actions:
if action in parsed.get("native_can_actions", []):
capability = self.native_can_runtime.capabilities[action["capability"]]
else:
capability = self.domain_runtimes[action["domain"]].capabilities[
action["capability"]
]
intent_ir["spec"]["requests"].append(
{
"domain": action["domain"],
"goal": (
f"{action['operation']} {action['capability']} "
f"for {action['target']}"
),
"target": {
"device": capability["target_devices"][0],
"zone": action["target"],
},
"parameters": {
"capability": action["capability"],
**action["parameters"],
},
"source_span": utterance,
}
)
result["intent_ir"] = intent_ir
if intent_ir["spec"]["requires_confirmation"]:
validation = self.contracts.validate(intent_ir)
validation.extend(
{
"severity": "error",
"code": "AMBIGUOUS_INTENT",
"path": item.get("field", "utterance"),
"message": item.get("question", "控制目标需要确认。"),
}
for item in intent_ir["spec"]["ambiguities"]
)
validation.extend(
{
"severity": "error",
"code": "UNRESOLVED_UTTERANCE_SPAN",
"path": "utterance",
"message": (
f"未解析原文“{item['text']}”:{item['reason']}。"
),
}
for item in intent_ir["spec"]["unresolved_spans"]
)
result["validation"] = validation
result["status"] = "needs_confirmation"
self._trace(
traces,
request_id,
"safety_check",
"failed",
"语义意图存在歧义或未覆盖片段;未进入 CAN 编译与执行。",
time.perf_counter(),
{
"ambiguity_count": len(intent_ir["spec"]["ambiguities"]),
"unresolved_span_count": len(intent_ir["spec"]["unresolved_spans"]),
"execution_eligible": False,
},
domain="orchestration",
)
self._persist(result)
return result
configs: dict[str, dict[str, Any]] = {}
if parsed.get("actions"):
seat_config = build_function_config(parsed, request_id, provider_used)
if seat_config is not None:
configs["seat"] = seat_config
domain_actions: dict[str, list[dict[str, Any]]] = {}
for action in parsed.get("domain_actions", []):
domain_actions.setdefault(action["domain"], []).append(action)
for domain in ("climate", "roof", "ambient_light"):
if domain in domain_actions:
configs[domain] = self.domain_runtimes[domain].build_function_config(
domain_actions[domain],
request_id,
provider_used,
)
native_config_keys: set[str] = set()
native_actions_by_domain: dict[str, list[dict[str, Any]]] = {}
for action in parsed.get("native_can_actions", []):
native_actions_by_domain.setdefault(action["domain"], []).append(action)
for domain, actions in native_actions_by_domain.items():
key = f"native_{domain}"
native_config_keys.add(key)
configs[key] = self.native_can_runtime.build_function_config(
domain,
actions,
request_id,
provider_used,
)
result["function_configs"] = configs
result["function_config"] = (
configs.get("seat") or next(iter(configs.values()), None)
)
self._trace(
traces,
request_id,
"function_generate",
"succeeded" if configs else "waiting_confirmation",
(
f"已按领域边界生成 {len(configs)} 份 FunctionConfig。"
if configs
else "没有足够的绝对目标生成可执行配置。"
),
generation_started,
{
"domains": list(configs),
"operation_count": sum(
len(config["spec"]["operations"]) for config in configs.values()
),
},
domain="orchestration",
)
validation_started = time.perf_counter()
validation = self.contracts.validate(intent_ir)
for config in configs.values():
validation.extend(self.contracts.validate(config))
if parsed.get("actions"):
validation.extend(validate_semantics(parsed, configs.get("seat")))
else:
for ambiguity in parsed.get("ambiguities", []):
validation.append(
{
"severity": "error",
"code": "AMBIGUOUS_INTENT",
"path": ambiguity["field"],
"message": ambiguity["question"],
}
)
if not native_config_keys:
validation.append(
{
"severity": "warning",
"code": "VIRTUAL_EXECUTION_ONLY",
"path": "platform.reference_vehicle",
"message": "当前三域矩阵仅允许虚拟执行,禁止实车下发。",
}
)
if not configs and not parsed.get("ambiguities") and not parsed.get(
"deferred_requests"
):
validation.append(
{
"severity": "error",
"code": "NO_EXECUTABLE_OPERATION",
"path": "spec.operations",
"message": "模型没有生成任何可执行领域操作。",
}
)
if hardware_request:
self._adapt_validation_for_hardware(validation)
result["validation"] = validation
errors = [issue for issue in validation if issue["severity"] == "error"]
self._trace(
traces,
request_id,
"schema_validate",
"failed" if errors else "succeeded",
f"公共和领域 Schema 校验{'发现错误' if errors else '通过'}。",
validation_started,
{"error_count": len(errors), "document_count": len(configs) + 1},
domain="orchestration",
)
if errors:
result["status"] = (
"needs_confirmation"
if any(issue["code"] == "AMBIGUOUS_INTENT" for issue in errors)
else "validation_failed"
)
self._persist(result)
return result
if parsed.get("deferred_requests") and not configs:
result["status"] = "capability_unavailable"
self._persist(result)
return result
compile_started = time.perf_counter()
plans: dict[str, dict[str, Any]] = {}
wireless_hardware_request = (
hardware_request
and control_transport == WIRELESS_MQTT_TRANSPORT
)
for domain, config in configs.items():
if domain == "seat":
if hardware_request:
if not wireless_hardware_request:
self.hardware_settings.validate_function_config(config)
plans[domain] = self.hardware_compiler.compile(
config,
context["devices"],
)
else:
plans[domain] = self.compiler.compile(config)
elif domain in native_config_keys:
plans[domain] = self.native_can_runtime.compile(config)
else:
runtime = self.domain_runtimes[domain]
plans[domain] = runtime.compile(config)
validation.extend(self.contracts.validate(plans[domain]))
result["command_plans"] = plans
result["command_plan"] = plans.get("seat") or next(
iter(plans.values()), None
)
scene_config = (
self._build_scene_config(request_id, configs, plans)
if len(configs) > 1
else None
)
result["scene_config"] = scene_config
if scene_config is not None:
validation.extend(self.contracts.validate(scene_config))
compile_errors = [
issue for issue in validation if issue["severity"] == "error"
]
self._trace(
traces,
request_id,
"compile",
"failed" if compile_errors else "succeeded",
(
f"{len(configs)} 份配置已编译为相互隔离的 CAN 命令计划"
+ (",并生成 SceneConfig。" if scene_config else "。")
),
compile_started,
{
"domains": list(plans),
"command_count": sum(
len(plan["spec"]["commands"]) for plan in plans.values()
),
"scene_generated": scene_config is not None,
},
domain="orchestration",
)
if compile_errors:
result["status"] = "compile_failed"
self._persist(result)
return result
self._emit_validated_commands(result, on_commands_validated)
if hardware_request:
self._authorize_compiled_hardware_delivery(
parsed,
approval_token,
control_transport,
)
self._trace(
traces,
request_id,
"safety_check",
"succeeded",
(
"所有动作已通过无线 MQTT 能力、参数及 CAN 帧白名单门禁。"
if (
hardware_request
and control_transport == WIRELESS_MQTT_TRANSPORT
)
else "所有动作已通过 CH1 车身 CAN 白名单和物理反馈门禁。"
if hardware_request and native_config_keys and "seat" not in configs
else "副驾 T2G 台架动作已通过独立硬件安全门。"
if hardware_request
else (
"模拟台架仅对座椅结果注入故障;全部领域的物理发送路径保持关闭。"
if simulated_hardware_request
else "所有领域均为 dry-run;矩阵缺少实车完整性信息,硬件发送保持禁用。"
)
),
time.perf_counter(),
{
"hardware_execution_allowed": hardware_request,
"simulated_hardware": simulated_hardware_request,
"simulation_fault": (
simulation_fault if simulated_hardware_request else None
),
"hardware_domains": (
[configs[key]["spec"]["domain"] for key in native_config_keys]
+ (["seat"] if "seat" in configs else [])
if hardware_request
else []
),
"virtual_domains": [
domain
for domain in configs
if not hardware_request
],
"domains": list(configs),
},
domain="orchestration",
)
execute_started = time.perf_counter()
execution_started_at = _now()
reports = self._execute_orchestrated_reports(
configs,
plans,
native_config_keys,
context,
hardware_request=hardware_request,
simulated_hardware_request=simulated_hardware_request,
simulation_fault=simulation_fault,
control_transport=control_transport,
)
for domain_report in reports.values():
_annotate_execution_report(domain_report)
report_results = [
item
for domain_report in reports.values()
for item in domain_report["spec"]["results"]
]
operation_results = [
item
for item in report_results
if item.get("ref") != "seat-controller-firmware"
]
operation_domains = {
str(item.get("domain")) for item in operation_results
}
successful_domains = {
domain
for domain in operation_domains
if all(
_operation_verification_status(item) != "failed_or_blocked"
for item in operation_results
if str(item.get("domain")) == domain
)
}
failed_domains = {
str(item.get("domain"))
for item in operation_results
if _operation_verification_status(item) == "failed_or_blocked"
}
execution_succeeded = not failed_domains
execution_partially_succeeded = bool(successful_domains and failed_domains)
execution_outcome = (
"failed_or_blocked"
if failed_domains
else "submitted_unverified"
if any(
_operation_verification_status(item) == "submitted_unverified"
for item in operation_results
)
else "verified_success"
)
verified_domains = {
domain
for domain in operation_domains
if all(
_operation_verification_status(item) == "verified_success"
for item in operation_results
if str(item.get("domain")) == domain
)
}
physical_state = (
reports.get("seat", {}).get("spec", {}).get("physical_state")
if hardware_request
else None
)
simulated_seat = simulated_hardware_request and "seat" in reports
report_spec: dict[str, Any] = {
"mode": "hil" if hardware_request or simulated_seat else "dry_run",
"status": (
"partially_succeeded"
if execution_partially_succeeded
else "succeeded"
if execution_succeeded
else "failed"
),
"verification_status": execution_outcome,
"started_at": execution_started_at,
"finished_at": _now(),
"results": report_results,
}
if isinstance(physical_state, dict):
report_spec["physical_state"] = physical_state
execution_report = {
"apiVersion": "cognitive-agent/v1alpha1",
"kind": "ExecutionReport",
"metadata": {
"id": f"report-{request_id}",
"name": (
"Cabin orchestration mixed HIL execution report"
if hardware_request
else (
"Cabin orchestration simulated hardware execution report"
if simulated_seat
else "Cabin orchestration virtual execution report"
)
),
"version": "1.0.0",
"request_id": request_id,
"labels": {
"simulation": "mixed" if hardware_request else "true",
"seat_feedback": (
"wireless_mqtt_ch0"
if wireless_hardware_request and "seat" in configs
else "wireless_mqtt_ch1"
if wireless_hardware_request and native_config_keys
else "physical_can_ch1"
if hardware_request and native_config_keys and "seat" not in configs
else "physical_can"
if hardware_request
else (
"simulated_hardware"
if simulated_seat
else "simulated"
)
),
"fault_scenario": (
simulation_fault if simulated_seat else "none"
),
"orchestration": "cross_domain"
if len(configs) > 1
else "single_domain",
},
},
"spec": report_spec,
}
report_issues = self.contracts.validate(execution_report)
validation.extend(report_issues)
result["execution_report"] = execution_report
result["execution_outcome"] = execution_outcome
result["validation"] = validation
if report_issues:
result["status"] = "execution_report_invalid"
elif execution_partially_succeeded:
result["status"] = "partially_succeeded"
elif not execution_succeeded:
result["status"] = "execution_failed"
elif parsed.get("deferred_requests"):
result["status"] = "partially_succeeded"
else:
result["status"] = "succeeded"
failed_execution = next(
(item for item in report_results if item.get("status") == "failed"),
None,
)
self._trace(
traces,
request_id,
"execute",
"succeeded" if execution_succeeded and not report_issues else "failed",
(
"无线 MQTT CAN0/CAN1 白名单控制帧已提交;"
"车辆实际状态以状态话题回读为准。"
if wireless_hardware_request and execution_succeeded
else "有线 CAN0/CAN1 白名单控制帧已提交;"
"当前反馈窗口未确认车辆效果,状态以实时 CAN 回读为准。"
if hardware_request
and execution_succeeded
and execution_outcome == "submitted_unverified"
else "CH1 车身 CAN 动作已按目标字段物理回读闭环执行。"
if hardware_request and native_config_keys and execution_succeeded
else "副驾座椅已按独立 T2G 物理 CAN 回读闭环执行。"
if hardware_request and execution_succeeded
else (
(
"跨域指令部分执行成功;成功域="
f"{','.join(sorted(successful_domains))},失败域="
f"{','.join(sorted(failed_domains))}:"
f"{failed_execution['message']}"
)
if execution_partially_succeeded and failed_execution
else f"真实 CAN 执行失败:{failed_execution['message']}"
if hardware_request and failed_execution
else (
f"模拟台架已执行 {len(report_results)} 个领域操作,物理 CAN 未打开。"
if simulated_seat and execution_succeeded
else (
f"模拟台架故障已触发:{failed_execution['message']}"
if simulated_seat and failed_execution
else (
"本次没有座椅动作,故障注入未应用;"
f"已虚拟执行 {len(report_results)} 个领域操作。"
if simulated_hardware_request
else f"已虚拟执行 {len(report_results)} 个领域操作并汇总反馈。"
)
)
)
)
),
execute_started,
{
"mode": execution_report["spec"]["mode"],
"simulation": execution_report["metadata"]["labels"]["simulation"],
"domains": list(configs),
"result_count": len(report_results),
"failed_domain": (
failed_execution.get("domain") if failed_execution else None
),
"successful_domains": sorted(successful_domains),
"failed_domains": sorted(failed_domains),
"partially_succeeded": execution_partially_succeeded,
"execution_outcome": execution_outcome,
},
domain="orchestration",
)
if (execution_succeeded or execution_partially_succeeded) and not report_issues:
seat_actions = (
parsed.get("actions", [])
if execution_succeeded or "seat" in successful_domains
else []
)
if hardware_request and seat_actions and "seat" in reports:
seat_actions = self._hardware_feedback_actions(
seat_actions,
configs["seat"],
reports["seat"],
)
all_actions = [
*seat_actions,
*[
action
for action in parsed.get("domain_actions", [])
if (
not hardware_request
and (
execution_succeeded
or str(action.get("domain")) in successful_domains
)
)
or (
hardware_request
and str(action.get("domain")) in verified_domains
)
],
*[
{
**action,
**(
{"feedback_source": "physical_can_ch1"}
if hardware_request
else {}
),
}
for action in parsed.get("native_can_actions", [])
if not hardware_request
and (
execution_succeeded
or str(action.get("domain")) in successful_domains
)
],
]
updated_context = self.conversations.apply_successful_actions(
conversation_id,
utterance,
all_actions,
parsed["decision_summary"],
)
if hardware_request and isinstance(physical_state, dict):
updated_context = self.conversations.synchronize_physical_passenger(
conversation_id,
physical_state,
)
if hardware_request:
physical_ch1_feedback = self._physical_ch1_feedback_states(reports)
if physical_ch1_feedback:
updated_context = self.conversations.synchronize_physical_ch1_feedback(
conversation_id,
physical_ch1_feedback,
)
updated_context = self._context_for_vehicle(
updated_context,
vehicle_profile,
)
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,
"state_update",
"succeeded",
(
f"跨域会话状态已更新为第 {updated_context['turn_count']} 轮。"
if execution_succeeded
else "仅成功设备已写入会话状态;失败设备保持执行前状态。"
),
time.perf_counter(),
{
"seat_state": updated_context["devices"],
"domain_state": updated_context["domain_devices"],
"successful_domains": sorted(successful_domains),
"failed_domains": sorted(failed_domains),
},
domain="orchestration",
)
elif hardware_request and isinstance(physical_state, dict) and (
physical_state.get("position")
or physical_state.get("ventilation")
or physical_state.get("heating")
):
updated_context = self.conversations.synchronize_physical_passenger(
conversation_id,
physical_state,
)
updated_context = self._context_for_vehicle(
updated_context,
vehicle_profile,
)
result["actuator_state"] = updated_context["devices"]
result["domain_state"] = updated_context["domain_devices"]
self._trace(
traces,
request_id,
"state_sync",
"succeeded",
"跨域真实执行未完成,但已按 CAN 回读同步副驾物理状态。",
time.perf_counter(),
{"seat_state": updated_context["devices"].get("passenger", {})},
domain="orchestration",
)
self._persist(result)
return result
def _execute_orchestrated_reports(
self,
configs: dict[str, dict[str, Any]],
plans: dict[str, dict[str, Any]],
native_config_keys: set[str],
context: dict[str, Any],
*,
hardware_request: bool,
simulated_hardware_request: bool,
simulation_fault: str,
control_transport: str = WIRED_CAN_TRANSPORT,
) -> dict[str, dict[str, Any]]:
reports: dict[str, dict[str, Any]] = {}
bridge = None
seat_bus_factory = None
native_bus_factory = None
vehicle_safety_preflight: dict[str, Any] | None = None
mqtt_selected = control_transport == WIRELESS_MQTT_TRANSPORT
wired_native_passenger = bool(
not mqtt_selected
and "seat" in configs
and self.hardware_settings.native_passenger_can0
and self.vehicle_can_settings.configured
)
if hardware_request and (
native_config_keys
or (mqtt_selected and "seat" in configs)
or wired_native_passenger
):
shared_runtime_enabled = (
not mqtt_selected
and self.vehicle_can_settings.backend != "zlgcan-linux"
and os.getenv(
"COGNITIVE_AGENT_SHARED_CAN_RUNTIME_ENABLED",
os.getenv(
"COGNITIVE_AGENT_SEAT_SHARED_ZCANPRO_BRIDGE_ENABLED", "true"
),
).strip().lower() in {"1", "true", "yes", "on"}
)
if mqtt_selected:
native_bus_factory = lambda: self._vehicle_mqtt_transport.bus(channel=1)
if "seat" in configs:
seat_bus_factory = lambda: self._vehicle_mqtt_transport.bus(channel=0)
elif shared_runtime_enabled:
from cognitive_agent.application.self_repair.service import (
BRIDGE_MANAGER,
)
if not hasattr(BRIDGE_MANAGER, "channel_bus_factory"):
raise HardwareSafetyError(
"vehicle CAN requires the standalone multi-channel runtime; "
"the ZCANPRO compatibility backend owns CH0 only"
)
native_bus_factory = BRIDGE_MANAGER.channel_bus_factory(
self.vehicle_can_settings.channel
)
# Seat commands continue through the high-level request bridge
# on CH0; passing a raw bus factory here would bypass its one-
# owner queue and allow self-repair to race orchestration.
seat_bus_factory = None
elif wired_native_passenger:
bridge = self._new_linux_wired_dual_channel_bridge()
native_bus_factory = lambda: bridge.channel_bus(1)
seat_bus_factory = lambda: bridge.channel_bus(0)
else:
channel_configs = [self.vehicle_can_settings.channel_config()]
if "seat" in configs:
seat_channel = int(self.hardware_settings.channel, 0)
channel_configs.insert(
0,
ZlgChannelConfig(
channel=seat_channel,
frame_format="classic_can",
arbitration_bitrate=self.hardware_settings.bitrate,
data_bitrate=self.vehicle_can_settings.data_bitrate,
can_fd_iso=True,
),
)
bridge = self.vehicle_can_settings.bridge(channel_configs)
native_bus_factory = lambda: bridge.channel_bus(
self.vehicle_can_settings.channel
)
if "seat" in configs:
seat_channel = int(self.hardware_settings.channel, 0)
seat_bus_factory = lambda: bridge.channel_bus(seat_channel)
if mqtt_selected:
# The head unit is the physical CAN owner. Once an action has
# passed the capability/parameter compiler, MqttCanTransport
# applies the final channel, ID and frame-format allow-list at
# publish time. Missing 0x342/0x4EB state frames must not
# suppress an otherwise approved MQTT command.
vehicle_safety_preflight = _wireless_mqtt_publish_preflight()
else:
# The wired bridge is open but no control frame has been
# submitted yet. Require two independent, fresh CH1 status
# frames before entering any actuator executor; CH0 traffic is
# ignored by the observer.
try:
vehicle_safety_preflight = observe_vehicle_safety(
native_bus_factory(),
timeout_ms=min(
5_000,
max(1_000, self.vehicle_can_settings.feedback_timeout_ms),
),
required_channel=1,
)
require_stationary_vehicle(vehicle_safety_preflight)
except VehicleSafetyPreflightError as exc:
if bridge is not None:
bridge.close()
raise HardwareSafetyError(
"CH1 vehicle safety preflight refused physical execution: "
f"{exc}"
) from exc
except Exception:
if bridge is not None:
bridge.close()
raise
try:
def execute_native_configs() -> dict[str, dict[str, Any]]:
native_reports: dict[str, dict[str, Any]] = {}
for key in sorted(native_config_keys):
if not hardware_request:
native_reports[key] = self.native_can_runtime.execute_virtual(
plans[key], configs[key]
)
continue
executor = NativeCanExecutor(
native_bus_factory,
feedback_timeout_ms=(
self.vehicle_mqtt_settings.feedback_timeout_ms
if control_transport == WIRELESS_MQTT_TRANSPORT
else self.vehicle_can_settings.feedback_timeout_ms
),
feedback_required=not mqtt_selected,
)
native_reports[key] = executor.execute(
self.native_can_runtime,
plans[key],
configs[key],
)
native_reports[key]["spec"]["vehicle_safety_preflight"] = (
vehicle_safety_preflight
)
for item in native_reports[key]["spec"].get("results", []):
item.setdefault("telemetry", {})[
"vehicle_safety_preflight"
] = vehicle_safety_preflight
return native_reports
def execute_seat_config() -> dict[str, Any]:
config = configs["seat"]
if hardware_request:
seat_executor = (
self._new_hardware_executor(
seat_bus_factory,
remote_transport=mqtt_selected,
)
if seat_bus_factory is not None
else self._new_hardware_executor()
)
return seat_executor.execute(
plans["seat"], config, context["devices"]
)
if simulated_hardware_request:
return self.simulated_hardware_executor.execute(
plans["seat"],
config,
context["devices"],
fault_scenario=simulation_fault,
)
return self.executor.execute(
plans["seat"], config, context["devices"]
)
if hardware_request and native_config_keys and "seat" in configs:
# Both channel views share one adapter owner; executor threads let
# the two channels start within normal USB scheduling jitter.
with ThreadPoolExecutor(max_workers=2) as pool:
native_future = pool.submit(execute_native_configs)
seat_future = pool.submit(execute_seat_config)
reports.update(native_future.result())
reports["seat"] = seat_future.result()
else:
reports.update(execute_native_configs())
if "seat" in configs:
reports["seat"] = execute_seat_config()
for domain, config in configs.items():
if domain in native_config_keys or domain == "seat":
continue
reports[domain] = self.domain_runtimes[domain].execute(
plans[domain], config
)
finally:
if bridge is not None:
bridge.close()
return reports
@staticmethod
def _build_scene_config(
request_id: str,
configs: dict[str, dict[str, Any]],
plans: dict[str, dict[str, Any]],
) -> dict[str, Any]:
function_refs = []
steps = []
for domain in configs:
reference_id = f"{domain}_function"
function_refs.append(
{
"id": reference_id,
"domain": domain,
"config_ref": f"functions/{domain}.yaml",
"sha256": plans[domain]["spec"]["source"]["config_sha256"],
}
)
steps.append(
{
"id": f"execute_{domain}",
"function_ref": reference_id,
"start_offset_ms": 0,
"failure_policy": "abort" if domain == "seat" else "continue",
}
)
return {
"apiVersion": "cognitive-agent/v1alpha1",
"kind": "SceneConfig",
"metadata": {
"id": f"scene-{request_id}",
"name": "Generated cabin scene",
"version": "1.0.0",
"request_id": request_id,
"generated_at": _now(),
"labels": {"execution_scope": "virtual_only"},
},
"spec": {
"function_refs": function_refs,
"steps": steps,
"execution": {
"mode": "parallel",
"default_failure_policy": "continue",
"max_parallelism": min(4, len(configs)),
"scene_timeout_ms": 30000,
},
},
}
@staticmethod
def _split_intent_clauses(utterance: str) -> list[str]:
def looks_like_control(value: str) -> bool:
return bool(
CONTROL_CLAUSE_SUBJECT_PATTERN.search(value)
and CONTROL_CLAUSE_PREDICATE_PATTERN.search(value)
)
def looks_like_independent_request(value: str) -> bool:
return looks_like_control(value) or bool(
CLOUD_REQUEST_CLAUSE_PATTERN.search(value)
)
def split_independent_controls(
value: str,
separator_pattern: str,
) -> list[str]:
tokens = re.split(f"({separator_pattern})", value)
current = tokens[0].strip()
separated: list[str] = []
for index in range(1, len(tokens), 2):
separator = tokens[index]
next_item = tokens[index + 1].strip()
if (
looks_like_independent_request(current)
and looks_like_independent_request(next_item)
and (
looks_like_control(current)
or looks_like_control(next_item)
)
):
if current:
separated.append(current)
current = next_item
else:
current = f"{current}{separator}{next_item}".strip()
if current:
separated.append(current)
return separated
coarse_fragments = [
item.strip()
for item in re.split(r"[,,。;;::!!??\r\n]+", utterance)
if item.strip()
]
fragments: list[str] = []
for coarse_fragment in coarse_fragments:
list_fragments = split_independent_controls(coarse_fragment, r"、")
for list_fragment in list_fragments:
fragments.extend(
split_independent_controls(
list_fragment,
r"然后|随后|接着|最后|同时|并且|以及|再|并",
)
)
clauses: list[str] = []
for fragment in fragments:
fragment = re.sub(
r"^(?:然后|随后|接着|最后|同时|并且|以及|并)",
"",
fragment,
).strip()
fragment = re.sub(
r"^再(?=(?:打开|开启|启动|关闭|关掉|关上|停止|暂停|取消))",
"",
fragment,
).strip()
if not fragment:
continue
# A massage mode/strength phrase may omit both "主驾" and "按摩"
# after the first clause. Keep the whole request atomic so the ON
# fallback does not emit relax before an explicit later mode.
massage_continuation = bool(
clauses
and "按摩" in clauses[-1]
and DRIVER_SEAT_MASSAGE_CONTINUATION_PATTERN.search(fragment)
)
# An exclusion is a qualifier of the control immediately before it,
# not an independent command. Keeping both pieces together lets
# the deterministic window parser remove the excluded targets
# before the command list is finalized.
exclusion_qualifier = bool(
clauses and re.search(r"除外|除去|不包括|不含", fragment)
)
if massage_continuation or exclusion_qualifier:
clauses[-1] = f"{clauses[-1]},{fragment}"
else:
clauses.append(fragment)
return clauses or [utterance.strip()]
@staticmethod
def _local_clause_status(parsed: dict[str, Any], clause: str) -> str:
"""Classify one deterministic parse as complete, partial, or unparsed."""
controls = any(
parsed.get(key)
for key in (
"actions",
"domain_actions",
"native_can_actions",
"preset_actions",
"preset_events",
"semantic_constraints",
)
)
real_ambiguities = [
item
for item in parsed.get("ambiguities", [])
if str(item.get("field", "")) != "capability"
]
unresolved = bool(parsed.get("unresolved_spans"))
# Scene/fuzzy-goal language may contain valid atomic controls and still
# ask for additional orchestration. Keep the trusted local controls but
# give only that clause to the model for the uncovered meaning.
fuzzy_goal = bool(
re.search(
r"观影|影院|看电影|休息|睡眠|露营|播放|音乐|场景|"
r"舒适(?:点|一些)?|凉快(?:点|一些)?|暖和(?:点|一些)?",
clause,
re.IGNORECASE,
)
)
if controls and not real_ambiguities and not unresolved and not fuzzy_goal:
return "complete"
if controls:
return "partial"
return "unparsed"
@classmethod
def _merge_parsed_results(
cls, results: list[dict[str, Any]]
) -> dict[str, Any]:
"""Merge clause-level parses while retaining their source order."""
merged: dict[str, Any] = {
"actions": [],
"domain_actions": [],
"native_can_actions": [],
"preset_actions": [],
"preset_events": [],
"deferred_requests": [],
"model_requests": [],
"ambiguities": [],
"decision_summary": [],
"semantic_constraints": [],
"unresolved_spans": [],
"detected_domains": [],
"confidence": 0.0,
}
list_fields = tuple(
key
for key in merged
if key not in {"detected_domains", "confidence"}
)
for parsed in results:
for key in list_fields:
merged[key] = cls._merge_unique(
merged[key], list(parsed.get(key, []))
)
merged["detected_domains"] = cls._merge_domains(
merged["detected_domains"],
list(parsed.get("detected_domains", [])),
)
merged["confidence"] = max(
float(merged["confidence"]), float(parsed.get("confidence", 0))
)
return merged
@classmethod
def _merge_local_and_model_intent(
cls,
local: dict[str, Any],
model: dict[str, Any],
) -> dict[str, Any]:
"""Merge a fallback model plan without overriding local controls."""
merged = deepcopy(model)
for key in ("actions", "domain_actions", "native_can_actions"):
local_items = list(local.get(key, []))
local_signatures = {
cls._control_signature(item) for item in local_items
}
model_items = [
item
for item in model.get(key, [])
if cls._control_signature(item) not in local_signatures
]
merged[key] = cls._merge_unique(local_items, model_items)
for key in (
"preset_actions",
"preset_events",
"deferred_requests",
"model_requests",
"ambiguities",
"semantic_constraints",
"unresolved_spans",
):
merged[key] = cls._merge_unique(
list(local.get(key, [])), list(model.get(key, []))
)
merged["decision_summary"] = cls._merge_unique(
list(local.get("decision_summary", [])),
list(model.get("decision_summary", [])),
)
merged["detected_domains"] = cls._merge_domains(
list(local.get("detected_domains", [])),
list(model.get("detected_domains", [])),
)
merged["confidence"] = max(
float(local.get("confidence", 0)), float(model.get("confidence", 0))
)
return merged
@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,
context: dict[str, Any],
) -> 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]:
local = parse_local_intent(clause, context)
relative = (
self.native_can_relative_resolver.resolve(clause, context)
if self.native_can_relative_resolver.matches(clause)
else None
)
local = merge_native_can_intent(
local,
clause,
relative_actions=relative.actions if relative else None,
relative_ambiguities=relative.ambiguities if relative else None,
relative_claimed_controls=(
relative.claimed_controls if relative else None
),
)
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", []):
return True
for key in (
"domain_actions",
"native_can_actions",
"deferred_requests",
"model_requests",
):
if any(item.get("domain") == "seat" for item in parsed.get(key, [])):
return True
return any(
str(item.get("field", "")).startswith("seat.")
for item in parsed.get("ambiguities", [])
)
inherited_seat_scope: str | None = None
for raw_clause in self._split_intent_clauses(utterance):
clause = raw_clause
local = parse_clause(clause)
clause_has_seat = has_seat_domain(local)
explicit_driver = bool(DRIVER_SEAT_SCOPE_PATTERN.search(raw_clause))
explicit_passenger = bool(PASSENGER_SEAT_SCOPE_PATTERN.search(raw_clause))
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:
scope_label = (
"主驾座椅"
if inherited_seat_scope == "driver"
else "副驾座椅"
)
clause = f"{scope_label},{raw_clause}"
local = parse_clause(clause)
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",
assistant_plan: dict[str, Any] | None = None,
allow_scene_composition: bool = False,
scene_scope_utterance: str | None = None,
cloud_instruction_context: dict[str, Any] | None = None,
) -> tuple[dict[str, Any], str, str | None]:
vehicle_profile = vehicle_profile or self.vehicle_profiles.default()
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"]
)
preview_local = self._trusted_local_preview(clause_results)
all_local = self._merge_parsed_results(
[parsed for _, parsed, _ in clause_results]
)
fallback_clauses = [
clause for clause, _, status in clause_results if status != "complete"
]
trusted_controls = any(
trusted_local.get(key)
for key in ("actions", "domain_actions", "native_can_actions")
)
safe_fallback = trusted_local if trusted_controls else all_local
# A Codex-authored plan is an untrusted normalization hint for only
# the clauses the deterministic parser could not finish. The guard
# binds every proposed capability back to that server-owned text and
# prevents it from replacing a locally resolved capability/target.
# ModelPlanAdapter then applies the same target, parameter, range and
# runtime-availability checks as the established hosted-model path.
if assistant_plan is not None and fallback_clauses:
fallback_utterance = ",".join(fallback_clauses)
guarded = guard_assistant_plan(
assistant_plan,
fallback_text=fallback_utterance,
capability_context=self.capability_context,
executable_capabilities={
str(capability.get("name") or "")
for domain in self.capability_context.get("domains", [])
for capability in domain.get("capabilities", [])
if domain.get("availability") == "executable"
or str(capability.get("name") or "")
in self.native_can_runtime.supported_capabilities
},
fallback_intents=[
parsed
for _, parsed, status in clause_results
if status != "complete"
],
trusted_local=trusted_local,
allow_scene_composition=allow_scene_composition,
scene_scope_utterance=scene_scope_utterance,
)
model = self.model_plan_adapter.adapt(
guarded.plan,
context,
vehicle_profile,
utterance=fallback_utterance,
)
if allow_scene_composition:
promoted_native: list[dict[str, Any]] = []
remaining_domain_actions: list[dict[str, Any]] = []
for action in model.get("domain_actions", []):
if self.native_can_runtime.supports(action):
promoted = dict(action)
promoted["execution_backend"] = "native_can"
promoted_native.append(promoted)
else:
remaining_domain_actions.append(action)
remaining_deferred: list[dict[str, Any]] = []
for deferred in model.get("deferred_requests", []):
candidate = {
"domain": str(deferred.get("domain") or ""),
"capability": str(deferred.get("capability") or ""),
"target": str(
deferred.get("target_scope")
or deferred.get("target")
or ""
),
"operation": str(deferred.get("desired_change") or "set"),
"parameters": dict(deferred.get("parameters") or {}),
"reason": "小迪生成的场景候选动作已通过工程能力契约校验。",
"execution_backend": "native_can",
}
if self.native_can_runtime.supports(candidate):
promoted_native.append(candidate)
else:
remaining_deferred.append(deferred)
model.setdefault("ambiguities", []).append(
{
"field": "assistant_plan",
"question": (
f"能力 {candidate['capability']} 无法进入当前车型的"
"可复用执行链。"
),
}
)
model["native_can_actions"] = [
*model.get("native_can_actions", []),
*promoted_native,
]
model["domain_actions"] = remaining_domain_actions
model["deferred_requests"] = remaining_deferred
if allow_scene_composition and model.get("ambiguities"):
# Adapter validation is the second half of the scene gate: it
# owns targets, required parameters, types and ranges. If any
# candidate fails there, discard every sibling action so the
# reusable draft can never become a silently partial scene.
for key in (
"actions",
"domain_actions",
"native_can_actions",
"deferred_requests",
"preset_actions",
"preset_events",
"model_requests",
):
model[key] = []
model = self._mark_control_parse_source(
model,
model_parse_source,
overwrite=True,
)
return (
self._merge_local_and_model_intent(trusted_local, model),
"local_rules+codex_app_server",
"本地规则结果优先;小迪生成的结构化计划仅补足未解析子句,并已重新通过能力注册表校验。",
)
if provider == "local" or not fallback_clauses:
return (
(
preview_local
if provider == "local" and local_preview
else all_local
if provider == "local"
else trusted_local
),
"local_rules",
None,
)
if not self.openai.available:
if provider == "openai":
raise ValueError("存在本地规则无法完整解析的子句;GPT 模式需要设置 OPENAI_API_KEY")
return (
safe_fallback,
"local_rules",
"未配置 OPENAI_API_KEY;已保留本地规则结果,未解析子句没有下发控制。",
)
fallback_utterance = ",".join(fallback_clauses)
try:
interpret_options = {}
if reasoning_effort is not None:
interpret_options["reasoning_effort"] = reasoning_effort
hosted_context = deepcopy(context)
if isinstance(cloud_instruction_context, dict):
routing_context = deepcopy(cloud_instruction_context)
else:
routing_context = {
"original_utterance": utterance,
"parsed_clauses": [
clause
for clause, _, status in clause_results
if status != "unparsed"
],
"resolved_clauses": [
clause
for clause, _, status in clause_results
if status == "complete"
],
"unresolved_clauses": fallback_clauses,
}
routing_context["cloud_input_utterance"] = fallback_utterance
hosted_context["natural_language_routing"] = routing_context
plan = self.openai.interpret(
fallback_utterance,
hosted_context,
self.capability_context,
**interpret_options,
)
model = self.model_plan_adapter.adapt(
plan,
context,
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}",
"本地规则先完成确定性解析;仅未覆盖或有歧义的子句交由 GPT 补充。",
)
except OpenAIIntentError:
return (
safe_fallback,
"local_rules_fallback",
"GPT 调用失败,已降级到本地规则解析(逐子句);已保留确定性结果,CAN 编译和执行路径不变。",
)
@staticmethod
def _context_for_vehicle(
context: dict[str, Any],
vehicle_profile: VehicleProfile,
) -> dict[str, Any]:
projected = deepcopy(context)
projected["vehicle"] = vehicle_profile.public_dict()
for target_state in projected.get("devices", {}).values():
positions = target_state.get("position", {})
for axis, state in positions.items():
if not isinstance(state, dict):
continue
motor = vehicle_profile.seat_motors.get(str(axis))
if motor is None or motor.display_angle is None:
continue
position_pct = state.get("position_pct")
if (
not isinstance(position_pct, (int, float))
or isinstance(position_pct, bool)
):
continue
current_angle = state.get("angle_deg")
legacy_angle = position_pct_to_angle(str(axis), float(position_pct))
should_project = current_angle is None or (
legacy_angle is not None
and isinstance(current_angle, (int, float))
and not isinstance(current_angle, bool)
and abs(float(current_angle) - float(legacy_angle)) < 0.0001
)
if should_project:
state["angle_deg"] = _clean_vehicle_number(
motor.display_angle_for_pct(int(position_pct))
)
return projected
@staticmethod
def _trace(
traces: list[dict[str, Any]],
request_id: str,
stage: str,
status: str,
summary: str,
started: float,
details: dict[str, Any],
domain: str = "seat",
) -> None:
traces.append(
{
"apiVersion": "cognitive-agent/v1alpha1",
"kind": "TraceEvent",
"event_id": f"evt-{len(traces) + 1:02d}-{request_id}",
"request_id": request_id,
"timestamp": _now(),
"stage": stage,
"status": status,
"domain": domain,
"summary": summary,
"details": details,
"duration_ms": max(0, int((time.perf_counter() - started) * 1000)),
}
)
def persist_composed_result(self, result: dict[str, Any]) -> None:
"""Persist a validated result composed with a non-CAN control domain."""
request_id = str(result.get("request_id") or "")
if not MODE_EDITOR_REQUEST_ID_PATTERN.fullmatch(request_id):
raise ValueError("invalid composed request id")
result.setdefault("trace_events", [])
self._persist(result)
def _persist(self, result: dict[str, Any]) -> None:
request_dir = self.artifact_root / result["request_id"]
(request_dir / "functions").mkdir(parents=True, exist_ok=True)
(request_dir / "presets").mkdir(parents=True, exist_ok=True)
(request_dir / "command-plans").mkdir(parents=True, exist_ok=True)
try:
artifact_path = request_dir.relative_to(self.root)
except ValueError:
artifact_path = request_dir
result["artifact_path"] = str(artifact_path)
self._write_yaml(request_dir / "intent.yaml", result.get("intent_ir"))
function_configs = result.get("function_configs") or {}
if function_configs:
for domain, document in function_configs.items():
self._write_yaml(
request_dir / "functions" / f"{domain}.yaml", document
)
else:
self._write_yaml(
request_dir / "functions" / "seat.yaml",
result.get("function_config"),
)
self._write_yaml(request_dir / "scene.yaml", result.get("scene_config"))
self._write_yaml(
request_dir / "presets" / "seat.yaml", result.get("preset_config")
)
command_plans = result.get("command_plans") or {}
if command_plans:
for domain, document in command_plans.items():
self._write_yaml(
request_dir / "command-plans" / f"{domain}.yaml", document
)
else:
self._write_yaml(
request_dir / "command-plans" / "seat.yaml",
result.get("command_plan"),
)
self._write_yaml(
request_dir / "execution-report.yaml", result.get("execution_report")
)
trace_text = "".join(
json.dumps(event, ensure_ascii=False) + "\n" for event in result["trace_events"]
)
self._atomic_write(request_dir / "trace.jsonl", trace_text)
self._atomic_write(
request_dir / "result.json",
json.dumps(result, ensure_ascii=False, indent=2) + "\n",
)
def _write_yaml(self, path: Path, document: dict[str, Any] | None) -> None:
if document is None:
return
text = yaml.safe_dump(document, allow_unicode=True, sort_keys=False)
self._atomic_write(path, text)
@staticmethod
def _atomic_write(path: Path, content: str) -> None:
temporary = path.with_suffix(path.suffix + ".tmp")
temporary.write_text(content, encoding="utf-8")
temporary.replace(path)