添加模式卡片下发

This commit is contained in:
Miayui
2026-09-09 14:37:25 +08:00
parent 76057be044
commit 51ca12b624
10 changed files with 1169 additions and 0 deletions
@@ -51,6 +51,12 @@ from cognitive_agent.application.mode_profile import ( # noqa: E402
ModeProfile,
ModeProfileRegistry,
)
from cognitive_agent.application.mode_scene_catalog import ( # noqa: E402
DEFAULT_N7_SCENE_SERVER_ID,
DEFAULT_N7_SCENE_SERVER_NAME,
ModeSceneCatalogPublisher,
build_mode_scene_catalog,
)
from cognitive_agent.application.mode_profile_intent import ( # noqa: E402
is_mode_profile_query,
normalize_mode_profile_name,
@@ -414,6 +420,17 @@ class SeatDemoService:
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.scene_templates = SceneTemplateRegistry(
repository_root / "config" / "scene_templates"
)
@@ -432,6 +449,88 @@ class SeatDemoService:
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)
@property
def driver_only_ch1_execution_active(self) -> bool:
"""Return whether deployment policy authorizes only the CH1 vehicle path."""
@@ -588,6 +687,7 @@ class SeatDemoService:
"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(),
"preset_storage": "conversation_durable",
"inferred_level_range": [1, 3],
"motion_position_range_pct": [0, 100],