Files
Cognitive_Agent/src/cognitive_agent/application/codex_assistant.py
T

3860 lines
153 KiB
Python
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.
"""Codex App Server orchestration for the web-console assistant."""
from __future__ import annotations
import hmac
import json
import logging
import os
import queue
import re
import secrets
import threading
import time
import uuid
from collections import OrderedDict
from collections.abc import Callable, Mapping, Sequence
from dataclasses import dataclass, field
from pathlib import Path
from typing import Protocol
from cognitive_agent.application.mode_profile_intent import (
is_mode_profile_query,
normalize_mode_profile_name,
parse_mode_profile_create,
parse_mode_profile_delete,
parse_mode_profile_update,
)
from cognitive_agent.application.scene_composer import (
SCENE_COMPOSER_INSTRUCTIONS,
SCENE_PLANNER_VERSION,
SceneCompositionError,
build_scene_output_schema,
normalize_scene_output,
)
from cognitive_agent.application.scene_templates import normalize_scene_template_name
LOGGER = logging.getLogger(__name__)
INTERNAL_SCENE_BUILDER_MARKER = "根据该场景名称使用当前可控原子能力"
class CodexAssistantError(RuntimeError):
"""Safe failure raised by the embedded Codex assistant."""
class CodexVehicleToolError(CodexAssistantError):
"""Safe failure returned to the vehicle MCP bridge."""
@dataclass(frozen=True)
class ActiveCodexTurn:
thread_id: str
turn_id: str
events: queue.Queue[dict[str, object]]
class CodexClient(Protocol):
@property
def available(self) -> bool: ...
def start(self, callback_url: str, callback_token: str) -> None: ...
def close(self) -> None: ...
@property
def status(self) -> str: ...
def start_thread(
self,
*,
developer_instructions: str,
ephemeral: bool = True,
replace_developer_instructions: bool = False,
timeout: float | None = None,
) -> str: ...
def start_turn(
self,
thread_id: str,
text: str,
request_token: str,
*,
application_context: Mapping[str, object] | None = None,
) -> ActiveCodexTurn: ...
def start_structured_turn(
self,
thread_id: str,
text: str,
*,
application_context: Mapping[str, object],
output_schema: Mapping[str, object],
timeout: float | None = None,
) -> ActiveCodexTurn: ...
def finish_turn(
self,
turn: ActiveCodexTurn,
timeout: float | None = None,
) -> None: ...
def interrupt(
self,
turn: ActiveCodexTurn,
timeout: float | None = None,
) -> None: ...
def unsubscribe_thread(
self,
thread_id: str,
timeout: float | None = None,
) -> None: ...
def abandon_turn(self, turn: ActiveCodexTurn) -> None: ...
EmitEvent = Callable[[dict[str, object]], bool | None]
# The third argument is an optional, untrusted structured plan. Legacy/local
# calls omit it; the application validates it against the original request
# before it can reach the normal orchestration pipeline.
ControlExecutor = Callable[..., dict[str, object]]
CapabilityQuery = Callable[[Mapping[str, object], str], dict[str, object]]
@dataclass
class _Conversation:
thread_id: str
touched_at: float = field(default_factory=time.monotonic)
lock: threading.Lock = field(default_factory=threading.Lock)
in_use: int = 0
@dataclass
class _SceneFlight:
ready: threading.Event = field(default_factory=threading.Event)
plan: dict[str, object] | None = None
error: BaseException | None = None
@dataclass
class _VehicleTurnContext:
original_message: str
execute_control: ControlExecutor
authorization: _VehicleAuthorization
capability_snapshot: Mapping[str, object]
query_capabilities: CapabilityQuery
events: queue.Queue[dict[str, object]] = field(default_factory=queue.Queue)
lock: threading.Lock = field(default_factory=threading.Lock)
called: bool = False
capability_queries: int = 0
action_succeeded: bool | None = None
action_state: str | None = None
action_result: Mapping[str, object] | None = None
@dataclass(frozen=True)
class _VehicleAuthorization:
instruction: str | None
reason: str
class _ScenePlannerFallback(RuntimeError):
"""Internal signal that the general Codex path should handle this request."""
class _ScenePlannerTimeout(RuntimeError):
"""Internal signal that the bounded scene-planning deadline expired."""
class _ScenePlannerOverloaded(RuntimeError):
"""Internal signal that all bounded scene-planning slots are busy."""
class _ScenePlannerCancelled(RuntimeError):
"""Internal signal that the browser stopped accepting streamed events."""
_CONTROL_VERB_PATTERN = re.compile(
r"打开|开启|关闭|关掉|关上|启动|停止|设置|调节|调整|调高|调低|升高|降低|"
r"增加|减少|切换|进入|退出|复位|移动|伸出|收回|开到|关到|调到|降到|升到|"
r"拉开|拉上|锁车|上锁|解锁|执行|运行|启用|停用|保存|记住|恢复|召回|调用|"
r"前移|后退|前进|调直|放倒|抬高|放低|半开|标定|抬到|降下|升起|"
r"帮我|替我|给我|请把|请将"
)
_CONTROL_VALUE_PATTERN = re.compile(r"-?\d+(?:\.\d+)?(?:%|%|度|档|秒|毫秒|ms)", re.IGNORECASE)
_SUPPORT_QUERY_PATTERN = re.compile(
r"支持|能否|能不能|可不可以|有没有|是否|哪些(?:设备|能力|功能)|能做什么"
)
_DENIAL_PATTERN = re.compile(
r"不要|无需|不用|禁止|不许|不得|取消操作|"
r"(?:不|别)(?:执行|下发|发送|操作|控制|调整)|"
r"(?:只|仅)(?:需|要)?(?:做)?(?:解析|预览|展示|显示)|"
r"(?:只|仅)(?:需|要)?(?:给出|返回)(?:解析(?:结果)?|控制(?:指令|命令))|"
r"只回答|仅回答"
)
_KNOWLEDGE_PATTERN = re.compile(
r"为什么|什么是|解释|介绍|分析|科普|教程|如何|怎么做|"
r"如果|假如|假设|举例|示例|翻译|改写|总结|复述|引用|造句|"
r"故事|剧本|小说|作文|角色|主角|这句话|以下内容|"
r"忽略.{0,12}(规则|指令|提示)|系统提示|调用工具"
)
_NON_VEHICLE_WORK_PATTERN = re.compile(
r"文件|代码|源码|网页|浏览器|文档|数据库|目录|仓库|项目|终端|命令行|日志"
)
_MODE_EDITOR_REQUEST_ID_PATTERN = re.compile(r"^req-[a-z0-9-]{6,100}$")
_HOT_DISCOMFORT_PATTERN = re.compile(r"热|闷热|好热|太晒")
_COLD_DISCOMFORT_PATTERN = re.compile(r"冷|好冷")
_IMPLICIT_HOT_REQUEST_PATTERN = re.compile(
r"^(?:(?:我|这里|车里|车内|车上|座舱|舱内)(?:感觉|觉得)?(?:有点|太|很|好)?|"
r"(?:有点|太|很|好))(?:闷热|热|晒)(?:了|啊|呀)?"
r"(?:(?:请)?(?:帮我)?(?:处理|调节|调整|解决)(?:一下)?|怎么办)?$"
)
_IMPLICIT_COLD_REQUEST_PATTERN = re.compile(
r"^(?:(?:我|这里|车里|车内|车上|座舱|舱内)(?:感觉|觉得)?(?:有点|太|很|好)?|"
r"(?:有点|太|很|好))冷(?:了|啊|呀)?"
r"(?:(?:请)?(?:帮我)?(?:处理|调节|调整|解决)(?:一下)?|怎么办)?$"
)
_COOLING_INSTRUCTION = "打开空调,将全车温度设置为23度,风量设置为2档"
_WARMING_INSTRUCTION = "打开空调,将全车温度设置为26度,风量设置为2档"
_FAST_COMFORT_REASONS = frozenset({"implicit_hot_comfort", "implicit_cold_comfort"})
def _normalize_instruction(value: object) -> str:
return re.sub(r"[\s,。!?!?、;;::]", "", str(value).lower())
def _safe_authoritative_suggestions(
value: Sequence[Mapping[str, object]],
) -> list[dict[str, object]]:
"""Bound server-owned quick controls before placing them on the stream."""
suggestions: list[dict[str, object]] = []
seen_ids: set[str] = set()
for raw in value[:6]:
suggestion_id = str(raw.get("id") or "").strip()
label = " ".join(str(raw.get("label") or "").strip().split())
command = " ".join(str(raw.get("command") or "").strip().split())
domain = str(raw.get("domain") or "").strip()
if (
not re.fullmatch(r"[a-z0-9][a-z0-9-]{0,63}", suggestion_id)
or suggestion_id in seen_ids
or not label
or len(label) > 80
or not command
or len(command) > 160
or not re.fullmatch(r"[a-z][a-z0-9_]{0,39}", domain)
):
continue
raw_capability_ids = raw.get("capability_ids")
capability_ids = (
[
capability_id
for item in raw_capability_ids[:8]
for capability_id in [str(item or "").strip()]
if re.fullmatch(r"[a-z][a-z0-9_]*\.[a-z][a-z0-9_]*", capability_id)
]
if isinstance(raw_capability_ids, Sequence)
and not isinstance(raw_capability_ids, (str, bytes, bytearray))
else []
)
if not capability_ids:
continue
seen_ids.add(suggestion_id)
suggestions.append(
{
"id": suggestion_id,
"label": label,
"command": command,
"domain": domain,
"capability_ids": list(dict.fromkeys(capability_ids)),
}
)
return suggestions
def _mode_profile_create_parts(message: str) -> tuple[str, str] | None:
"""Return the requested scene name and optional explicit settings."""
return parse_mode_profile_create(message)
def is_mode_profile_create_request(message: str) -> bool:
"""Return whether the original user text is a bounded mode-create request."""
return _mode_profile_create_parts(message) is not None
def _mode_profile_update_parts(
message: str,
*,
expected_mode_name: object | None = None,
) -> tuple[str, str] | None:
"""Return a bounded named-mode update without interpreting its action."""
return parse_mode_profile_update(
message,
expected_mode_name=expected_mode_name,
)
def _validated_mode_profile_draft_context(
value: Mapping[str, object] | None,
*,
message: str,
) -> dict[str, str] | None:
"""Validate the browser's opaque draft reference, never client actions."""
if value is None:
return None
update = _mode_profile_update_parts(
message,
expected_mode_name=(value.get("name") if isinstance(value, Mapping) else None),
)
if update is None:
return None
if not isinstance(value, Mapping):
raise ValueError("mode_profile_draft_context must be an object")
referenced_name = " ".join(str(value.get("name") or "").strip().split())
if normalize_mode_profile_name(update[0]) != normalize_mode_profile_name(
referenced_name
):
return None
required = {"name", "source_request_id", "vehicle_id"}
if set(value) != required:
raise ValueError("mode_profile_draft_context fields are invalid")
clean = {key: " ".join(str(value.get(key) or "").strip().split()) for key in required}
if (
not clean["name"]
or len(clean["name"]) > 64
or not clean["vehicle_id"]
or len(clean["vehicle_id"]) > 80
or not _MODE_EDITOR_REQUEST_ID_PATTERN.fullmatch(clean["source_request_id"])
):
raise ValueError("mode_profile_draft_context values are invalid")
# The UI keeps the latest card selected while ordinary questions are
# asked. A reference is only a routing hint when this exact sentence edits
# that exact draft; all non-matching references returned above are inert.
return clean
def _catalog_exact_mode_match_count(
catalog: Mapping[str, object],
requested_name: str,
) -> int | None:
"""Count exact visible mode matches; ``None`` means catalog unavailable."""
needle = "".join(str(requested_name).casefold().split())
source_status = catalog.get("source_status")
if "modes" not in catalog or (
isinstance(source_status, Mapping)
and source_status.get("modes") != "ready"
):
return None
matched_ids: set[str] = set()
raw_modes = catalog.get("modes", [])
if not isinstance(raw_modes, Sequence) or isinstance(
raw_modes,
(str, bytes, bytearray),
):
return None
for raw_mode in raw_modes:
if not isinstance(raw_mode, Mapping):
continue
candidates = [raw_mode.get("id"), raw_mode.get("name")]
aliases = raw_mode.get("aliases", [])
if isinstance(aliases, Sequence) and not isinstance(
aliases,
(str, bytes, bytearray),
):
candidates.extend(aliases)
if needle in {
"".join(str(candidate or "").casefold().split())
for candidate in candidates
if str(candidate or "").strip()
}:
matched_ids.add(str(raw_mode.get("id") or ""))
matched_ids.discard("")
return len(matched_ids)
def _catalog_exact_mode_execution_id(
catalog: Mapping[str, object],
message: str,
) -> str | None:
"""Resolve an explicit saved-mode run without asking the model to route it."""
source_status = catalog.get("source_status")
if not isinstance(source_status, Mapping) or source_status.get("modes") != "ready":
return None
normalized = re.sub(r"[\s,。!?!?、;;::]", "", str(message).casefold())
if not normalized:
return None
prefixes = ("", "请", "请帮我", "帮我", "麻烦", "麻烦帮我", "现在", "立即", "马上")
verbs = ("进入", "启动", "执行", "开启", "打开", "切换到")
suffixes = ("", "吧", "一下", "现在", "立即")
matched_ids: set[str] = set()
raw_modes = catalog.get("modes", [])
if not isinstance(raw_modes, Sequence) or isinstance(
raw_modes,
(str, bytes, bytearray),
):
return None
for raw_mode in raw_modes:
if not isinstance(raw_mode, Mapping) or raw_mode.get("assistant_callable") is not True:
continue
mode_id = str(raw_mode.get("id") or "").strip()
if not mode_id:
continue
aliases = raw_mode.get("aliases", [])
candidates = [
raw_mode.get("name"),
*(
aliases
if isinstance(aliases, Sequence)
and not isinstance(aliases, (str, bytes, bytearray))
else []
),
]
for candidate in candidates:
mode_name = re.sub(
r"[\s,。!?!?、;;::]",
"",
str(candidate or "").casefold(),
)
if not mode_name:
continue
accepted = {
f"{prefix}{verb}{mode_name}{suffix}"
for prefix in prefixes
for verb in verbs
for suffix in suffixes
}
if normalized in accepted:
matched_ids.add(mode_id)
break
return next(iter(matched_ids)) if len(matched_ids) == 1 else None
def _catalog_exact_scene_template_plan(
catalog: Mapping[str, object],
requested_name: str,
) -> dict[str, object] | None:
"""Return one exact, callable template plan from the server-owned catalog."""
source_status = catalog.get("source_status")
if (
not isinstance(source_status, Mapping)
or source_status.get("scene_templates") != "ready"
):
return None
raw_templates = catalog.get("scene_templates")
if not isinstance(raw_templates, Sequence) or isinstance(
raw_templates, (str, bytes, bytearray)
):
return None
needle = normalize_scene_template_name(requested_name)
matches: dict[str, Mapping[str, object]] = {}
for raw_template in raw_templates:
if (
not isinstance(raw_template, Mapping)
or raw_template.get("assistant_callable") is not True
):
continue
candidates = [raw_template.get("id"), raw_template.get("name")]
aliases = raw_template.get("aliases", [])
if isinstance(aliases, Sequence) and not isinstance(
aliases, (str, bytes, bytearray)
):
candidates.extend(aliases)
if needle and needle in {
normalize_scene_template_name(candidate)
for candidate in candidates
if str(candidate or "").strip()
}:
template_id = str(raw_template.get("id") or "").strip()
if template_id:
matches[template_id] = raw_template
if len(matches) != 1:
return None
template = next(iter(matches.values()))
plan = template.get("plan")
metadata = plan.get("metadata") if isinstance(plan, Mapping) else None
if (
not isinstance(plan, Mapping)
or not isinstance(metadata, Mapping)
or metadata.get("source") != "scene_template"
or metadata.get("template_id") != template.get("id")
or metadata.get("template_revision") != template.get("template_revision")
):
return None
try:
return json.loads(
json.dumps(dict(plan), ensure_ascii=False, allow_nan=False)
)
except (TypeError, ValueError):
return None
def _vehicle_result_succeeded(result: Mapping[str, object]) -> bool:
"""Only a terminal, fully successful service result completes a tool call."""
return str(result.get("status") or "").lower() == "succeeded"
def _mode_profile_result_succeeded(result: Mapping[str, object]) -> bool:
"""Require a complete, non-physical mode preview before claiming success."""
if (
not _vehicle_result_succeeded(result)
or str(result.get("execution_mode") or "").lower() != "dry_run"
or result.get("hardware_execution_allowed") is not False
or result.get("orchestration_route") != "mode_profile_create_preview"
):
return False
draft = result.get("mode_profile_draft")
if not isinstance(draft, Mapping):
return False
operation_count = draft.get("operation_count")
if (
not isinstance(operation_count, int)
or isinstance(operation_count, bool)
or operation_count <= 0
):
return False
intent_ir = result.get("intent_ir")
spec = intent_ir.get("spec") if isinstance(intent_ir, Mapping) else None
actions = spec.get("actions") if isinstance(spec, Mapping) else None
if not isinstance(actions, Sequence) or isinstance(
actions,
(str, bytes, bytearray),
):
return False
if len(actions) != operation_count or not all(
isinstance(action, Mapping) for action in actions
):
return False
validation = result.get("validation", [])
return not (
isinstance(validation, Sequence)
and not isinstance(validation, (str, bytes, bytearray))
and any(
isinstance(issue, Mapping) and issue.get("severity") == "error"
for issue in validation
)
)
def _mode_profile_management_result_succeeded(
result: Mapping[str, object],
route: str,
) -> bool:
"""Require complete control-plane evidence for a saved-mode mutation."""
if (
not _vehicle_result_succeeded(result)
or str(result.get("execution_mode") or "").lower() != "dry_run"
or result.get("hardware_execution_allowed") is not False
or result.get("orchestration_route") != route
):
return False
evidence_key = (
"deleted_mode_profile"
if route == "mode_profile_delete"
else "mode_profile"
)
evidence = result.get(evidence_key)
return isinstance(evidence, Mapping) and bool(evidence.get("id"))
def _vehicle_verification_status(result: Mapping[str, object]) -> str:
"""Normalize the service's execution evidence without inventing feedback."""
execution_mode = str(result.get("execution_mode") or "").strip().lower()
if execution_mode in {"dry_run", "simulated_hardware"}:
return "simulated"
for key in ("execution_outcome", "verification_status"):
value = str(result.get(key) or "")
if value:
return value
execution_report = result.get("execution_report")
if isinstance(execution_report, Mapping):
spec = execution_report.get("spec")
if isinstance(spec, Mapping):
value = str(spec.get("verification_status") or "")
if value:
return value
if result.get("physical_feedback_confirmed") is True:
return "verified_success"
return "unknown"
def _vehicle_action_state(result: Mapping[str, object]) -> tuple[str, bool, str]:
"""Describe accepted, simulated and physically verified outcomes distinctly."""
if not _vehicle_result_succeeded(result):
return "failed", False, "车辆控制未完成"
execution_mode = str(result.get("execution_mode") or "").lower()
if execution_mode == "dry_run":
return "simulated", True, "车辆控制模拟编排已完成"
if execution_mode == "simulated_hardware":
return "simulated", True, "车辆控制已在模拟硬件完成"
if _vehicle_verification_status(result) == "verified_success":
return "verified", True, "车辆控制已完成并通过状态确认"
return "submitted", True, "控制指令已提交,等待车辆状态确认"
def _authorize_vehicle_request(
message: str,
control_terms: Sequence[str] = (),
mode_profile_draft_context: Mapping[str, object] | None = None,
) -> _VehicleAuthorization:
"""Create a server-owned capability envelope from the original user text."""
expected_draft_name = (
mode_profile_draft_context.get("name")
if isinstance(mode_profile_draft_context, Mapping)
else None
)
mode_profile_draft_context = _validated_mode_profile_draft_context(
mode_profile_draft_context,
message=message,
)
normalized = "".join(str(message).lower().split())
mode_profile_create = _mode_profile_create_parts(message)
mode_profile_update = _mode_profile_update_parts(message)
mode_profile_delete = parse_mode_profile_delete(message)
if (
mode_profile_create is None
and mode_profile_update is None
and mode_profile_delete is None
and _DENIAL_PATTERN.search(normalized)
):
return _VehicleAuthorization(None, "denied_by_user")
if (
mode_profile_create is not None
and not _KNOWLEDGE_PATTERN.search(normalized)
and not _NON_VEHICLE_WORK_PATTERN.search(normalized)
):
# Performative questions such as ``能不能帮我创建……`` are still
# concrete configuration requests. The canonical parser has already
# rejected bare capability questions and quoted examples, so handle
# this before the broader question detector below.
return _VehicleAuthorization(message, "explicit_mode_profile_create")
if (
"模式" in normalized
and re.search(r"构建|创建|新建|新增|生成|添加", normalized)
and _KNOWLEDGE_PATTERN.search(normalized)
):
return _VehicleAuthorization(None, "knowledge_or_non_vehicle_request")
if is_mode_profile_query(
normalized,
expected_mode_name=expected_draft_name,
):
# A question about a named mode may contain device names, control
# verbs and numeric targets. It remains a question and must never
# fall through to the registered-control executor.
return _VehicleAuthorization(None, "mode_profile_query")
if mode_profile_update is not None:
return _VehicleAuthorization(
message,
(
"explicit_mode_profile_draft_update"
if mode_profile_draft_context is not None
else "explicit_mode_profile_update"
),
)
if mode_profile_delete is not None:
return _VehicleAuthorization(message, "explicit_mode_profile_delete")
implicit_request = re.sub(r"[,。!?!?、]", "", normalized)
if not _KNOWLEDGE_PATTERN.search(normalized):
if _IMPLICIT_HOT_REQUEST_PATTERN.fullmatch(implicit_request):
return _VehicleAuthorization(_COOLING_INSTRUCTION, "implicit_hot_comfort")
if _IMPLICIT_COLD_REQUEST_PATTERN.fullmatch(implicit_request):
return _VehicleAuthorization(_WARMING_INSTRUCTION, "implicit_cold_comfort")
if _KNOWLEDGE_PATTERN.search(normalized) or _NON_VEHICLE_WORK_PATTERN.search(
normalized
):
return _VehicleAuthorization(None, "knowledge_or_non_vehicle_request")
if _SUPPORT_QUERY_PATTERN.search(normalized):
return _VehicleAuthorization(None, "knowledge_or_non_vehicle_request")
normalized_terms = {
"".join(str(term).lower().split()) for term in control_terms if str(term).strip()
}
matched = any(term and term in normalized for term in normalized_terms)
if matched and (
_CONTROL_VERB_PATTERN.search(normalized) or _CONTROL_VALUE_PATTERN.search(normalized)
):
# The callback executes this exact server-owned original instruction.
# The model can choose whether to request execution, but cannot broaden it.
return _VehicleAuthorization(message, "explicit_registered_control")
return _VehicleAuthorization(None, "not_a_registered_control")
class _StreamingSecretRedactor:
"""Redact a token even when it is split across streamed model deltas."""
def __init__(self, secret: str) -> None:
self._secret = secret
self._buffer = ""
def feed(self, text: str) -> str:
self._buffer += text
self._buffer = self._buffer.replace(self._secret, "[内部令牌已隐藏]")
keep = 0
maximum = min(len(self._secret) - 1, len(self._buffer))
for length in range(maximum, 0, -1):
if self._buffer.endswith(self._secret[:length]):
keep = length
break
if keep:
ready, self._buffer = self._buffer[:-keep], self._buffer[-keep:]
return ready
ready, self._buffer = self._buffer, ""
return ready
def finish(self) -> str:
ready = self._buffer.replace(self._secret, "[内部令牌已隐藏]")
self._buffer = ""
return ready
class CodexAssistantService:
"""Map browser conversations onto Codex threads and trusted vehicle tools."""
_conversation_pattern = re.compile(r"^conv-[a-z0-9-]{8,100}$")
def __init__(
self,
client: CodexClient,
*,
prompt_path: Path | None = None,
max_sessions: int = 128,
fast_implicit_controls: bool = False,
) -> None:
self.client = client
self._prompt_path = prompt_path or (
Path(__file__).resolve().parents[1] / "agent" / "prompts" / "codex_cockpit_assistant.md"
)
self._instructions = self._prompt_path.read_text(encoding="utf-8")
self._max_sessions = max(8, max_sessions)
self._fast_implicit_controls = bool(fast_implicit_controls)
self._sessions: OrderedDict[tuple[str, str], _Conversation] = OrderedDict()
self._sessions_lock = threading.Lock()
self._tool_contexts: dict[str, _VehicleTurnContext] = {}
self._tool_contexts_lock = threading.Lock()
self._scene_cache: OrderedDict[
tuple[str, str, str, str], tuple[float, dict[str, object]]
] = OrderedDict()
self._scene_flights: dict[tuple[str, str, str, str], _SceneFlight] = {}
self._scene_cache_lock = threading.Lock()
self._scene_cache_ttl = max(
1.0,
min(float(os.getenv("CODEX_ASSISTANT_SCENE_CACHE_TTL_SECONDS", "3600")), 86400.0),
)
self._scene_cache_limit = max(
8,
min(int(os.getenv("CODEX_ASSISTANT_SCENE_CACHE_MAX_ENTRIES", "256")), 4096),
)
self._scene_turn_timeout = max(
5.0,
min(float(os.getenv("CODEX_ASSISTANT_SCENE_TIMEOUT_SECONDS", "45")), 300.0),
)
self._scene_max_concurrent_plans = max(
1,
min(
int(os.getenv("CODEX_ASSISTANT_SCENE_MAX_CONCURRENT_PLANS", "8")),
64,
),
)
self._scene_planner_slots = threading.BoundedSemaphore(
self._scene_max_concurrent_plans
)
self._scene_progress_interval = 2.0
self._internal_token = secrets.token_urlsafe(32)
self._turn_timeout = max(
10.0,
min(float(os.getenv("CODEX_ASSISTANT_TURN_TIMEOUT_SECONDS", "300")), 1800.0),
)
@property
def internal_token(self) -> str:
return self._internal_token
def start(self, callback_url: str) -> None:
self.client.start(callback_url, self._internal_token)
def close(self) -> None:
with self._sessions_lock:
conversations = list(self._sessions.values())
self._sessions.clear()
for conversation in conversations:
self._unsubscribe_thread(conversation.thread_id)
self.client.close()
def status(self) -> dict[str, object]:
client_status = self.client.status
payload: dict[str, object] = {
"status": str(client_status),
"available": self.client.available,
}
with self._sessions_lock:
payload["active_conversations"] = len(self._sessions)
with self._scene_cache_lock:
payload["scene_composer"] = {
"planner_version": SCENE_PLANNER_VERSION,
"cached_plans": len(self._scene_cache),
"in_flight": len(self._scene_flights),
"max_concurrent_plans": self._scene_max_concurrent_plans,
}
payload["engine"] = "codex-app-server"
return payload
def stream(
self,
message: str,
*,
conversation_id: str,
principal: str,
history: Sequence[dict[str, str]],
emit: EmitEvent,
execute_control: ControlExecutor,
preview_control: ControlExecutor | None = None,
supplement_control: ControlExecutor | None = None,
capability_snapshot: Mapping[str, object] | None = None,
capability_context: Mapping[str, object] | None = None,
scene_capability_context: Mapping[str, object] | None = None,
control_terms: Sequence[str] = (),
query_capabilities: CapabilityQuery | None = None,
authoritative_answer: str | None = None,
authoritative_topic: str | None = None,
authoritative_suggestions: Sequence[Mapping[str, object]] = (),
quick_control_commands: Sequence[str] = (),
mode_profile_draft_context: Mapping[str, object] | None = None,
) -> dict[str, object]:
del history # Codex owns conversation history after the thread is created.
message = " ".join(str(message).strip().split())
if not message:
raise ValueError("message must not be empty")
if len(message) > 4000:
raise ValueError("message must not exceed 4000 characters")
if not self._conversation_pattern.fullmatch(conversation_id):
raise ValueError("invalid conversation_id")
catalog = json.loads(
json.dumps(dict(capability_snapshot or {}), ensure_ascii=False, default=str)
)
model_context = json.loads(
json.dumps(dict(capability_context or {}), ensure_ascii=False, default=str)
)
scene_context = json.loads(
json.dumps(dict(scene_capability_context or {}), ensure_ascii=False, default=str)
)
catalog_query = query_capabilities or (lambda snapshot, _query: dict(snapshot))
authoritative_answer = " ".join(str(authoritative_answer or "").strip().split())
if len(authoritative_answer) > 16_000:
raise ValueError("authoritative_answer must not exceed 16000 characters")
authoritative_topic = str(authoritative_topic or "").strip()
if not re.fullmatch(r"[a-z][a-z0-9_]{0,63}", authoritative_topic):
authoritative_topic = ""
authoritative_suggestions = _safe_authoritative_suggestions(
authoritative_suggestions
)
normalized_quick_controls = {
normalized
for raw in quick_control_commands[:6]
for normalized in [_normalize_instruction(str(raw)[:160])]
if normalized
}
draft_context = _validated_mode_profile_draft_context(
mode_profile_draft_context,
message=message,
)
authorization = _authorize_vehicle_request(
message,
control_terms,
draft_context,
)
saved_mode_execution_id = _catalog_exact_mode_execution_id(catalog, message)
if saved_mode_execution_id is not None:
authorization = _VehicleAuthorization(
message,
"explicit_saved_mode_execute",
)
fast_comfort_control = (
self._fast_implicit_controls and authorization.reason in _FAST_COMFORT_REASONS
)
exact_quick_control = (
authorization.reason == "explicit_registered_control"
and _normalize_instruction(message) in normalized_quick_controls
)
explicit_local_control = (
authorization.reason == "explicit_registered_control"
)
local_first_control = bool(
explicit_local_control
and preview_control is not None
and supplement_control is not None
)
mode_profile_preview = authorization.reason in {
"explicit_mode_profile_create",
"explicit_mode_profile_draft_update",
}
mode_parts = _mode_profile_create_parts(message) if mode_profile_preview else None
exact_mode_matches = (
_catalog_exact_mode_match_count(catalog, mode_parts[0])
if mode_parts
else None
)
scene_template_plan = (
_catalog_exact_scene_template_plan(catalog, mode_parts[0])
if mode_parts and not mode_parts[1] and exact_mode_matches == 0
else None
)
direct_mode_profile_preview = bool(
mode_parts
and (
mode_parts[1]
# Ambiguous or unavailable mode catalogs fail closed in the
# local builder. Only a known-zero match is safe to hand to
# semantic scene composition.
or exact_mode_matches != 0
or scene_template_plan is not None
)
)
direct_mode_profile_update = (
authorization.reason == "explicit_mode_profile_draft_update"
)
saved_mode_management_route = {
"explicit_mode_profile_update": "mode_profile_update",
"explicit_mode_profile_delete": "mode_profile_delete",
}.get(authorization.reason)
scene_composition_preview = bool(
mode_parts
and not mode_parts[1]
and exact_mode_matches == 0
and scene_template_plan is None
and scene_context.get("composition_revision")
and callable(getattr(self.client, "start_structured_turn", None))
)
# Explicit device settings and repository-backed scenes remain on the
# zero-model-latency path. A genuinely new name-only scene needs Codex
# to compose a candidate plan from the live capability catalog; the
# vehicle service still limits it to dry-run and requires confirmation.
direct_vehicle_control = (
fast_comfort_control
or exact_quick_control
or local_first_control
or saved_mode_execution_id is not None
or direct_mode_profile_preview
or authorization.reason == "explicit_mode_profile_draft_update"
or saved_mode_management_route is not None
)
route = (
"unsupported_control"
if authoritative_answer and authoritative_topic == "unsupported_vehicle_control"
else "capability_catalog"
if authoritative_answer
else "scene_template"
if scene_template_plan is not None
else saved_mode_management_route
if saved_mode_management_route is not None
else "mode_profile_execute"
if saved_mode_execution_id is not None
else "vehicle_control"
if direct_vehicle_control
else "scene_composer"
if scene_composition_preview
else "agent"
)
engine = (
"n7web-capability-registry"
if authoritative_answer
else "n7web-scene-template"
if scene_template_plan is not None
else "n7web-mode-builder"
if direct_mode_profile_preview or direct_mode_profile_update
else "n7web-mode-profile-manager"
if saved_mode_management_route is not None
else "n7web-mode-profile-runner"
if saved_mode_execution_id is not None
else "codex-app-server-scene-composer"
if scene_composition_preview
else "n7web-control-router"
if fast_comfort_control or exact_quick_control or local_first_control
else "codex-app-server"
)
message_id = f"msg-{uuid.uuid4().hex}"
if (
emit(
{
"type": "message.accepted",
"message_id": message_id,
"conversation_id": conversation_id,
"route": route,
"engine": engine,
}
)
is False
):
return {"cancelled": True}
if (
emit(
{
"type": "work.started",
"message_id": message_id,
"display_text": (
"正在核对车端控制能力……"
if route == "unsupported_control"
else "正在读取工程能力目录……"
if authoritative_answer
else "正在载入并校验常用模式模板……"
if scene_template_plan is not None
else "正在构建并校验功能模式……"
if direct_mode_profile_preview or direct_mode_profile_update
else "正在更新已保存的功能模式……"
if saved_mode_management_route == "mode_profile_update"
else "正在删除自定义功能模式……"
if saved_mode_management_route == "mode_profile_delete"
else "正在载入并执行已保存的功能模式……"
if saved_mode_execution_id is not None
else "正在根据实时能力目录快速组合模式……"
if scene_composition_preview
else "小迪正在理解场景并组合工程能力……"
if mode_profile_preview
else "正在快速处理座舱舒适需求……"
if fast_comfort_control
else "正在执行快捷车控……"
if exact_quick_control
else "正在本地解析并下发明确控制指令……"
if local_first_control
else "小迪正在理解并处理……"
),
}
)
is False
):
return {"cancelled": True}
if authoritative_answer:
if (
emit(
{
"type": "answer.delta",
"message_id": message_id,
"delta": authoritative_answer,
}
)
is False
):
return {"cancelled": True}
if authoritative_suggestions and (
emit(
{
"type": "control.suggestions",
"message_id": message_id,
"suggestions": authoritative_suggestions,
}
)
is False
):
return {"cancelled": True}
emit(
{
"type": "work.completed",
"message_id": message_id,
"success": True,
"display_text": (
"已核对当前车端能力"
if route == "unsupported_control"
else "已按工程能力目录核对"
),
}
)
emit(
{
"type": "answer.completed",
"message_id": message_id,
"route": route,
"answer": authoritative_answer,
**({"topic": authoritative_topic} if authoritative_topic else {}),
**(
{"suggestions": authoritative_suggestions}
if authoritative_suggestions
else {}
),
}
)
return {
"route": route,
"answer": authoritative_answer,
**({"topic": authoritative_topic} if authoritative_topic else {}),
**(
{"suggestions": authoritative_suggestions}
if authoritative_suggestions
else {}
),
}
if (
local_first_control
and preview_control is not None
and supplement_control is not None
):
return self._stream_local_first_vehicle_control(
authorization,
message_id=message_id,
emit=emit,
preview_control=preview_control,
supplement_control=supplement_control,
conversation_id=conversation_id,
principal=principal,
capability_snapshot=catalog,
capability_context=model_context,
control_terms=control_terms,
query_capabilities=catalog_query,
)
if direct_vehicle_control:
return self._stream_direct_vehicle_control(
(
_VehicleAuthorization(
authorization.instruction,
"exact_quick_control",
)
if exact_quick_control
else authorization
),
message_id=message_id,
emit=emit,
execute_control=execute_control,
assistant_plan=scene_template_plan,
response_route=(
"scene_template"
if scene_template_plan is not None
else saved_mode_management_route
if saved_mode_management_route is not None
else "mode_profile_execute"
if saved_mode_execution_id is not None
else "vehicle_control"
),
)
if not self.client.available:
raise CodexAssistantError("Codex App Server is not available")
if scene_composition_preview and mode_parts is not None:
try:
return self._stream_scene_composition(
message,
mode_name=mode_parts[0],
message_id=message_id,
emit=emit,
execute_control=execute_control,
scene_context=scene_context,
)
except _ScenePlannerCancelled:
return {"cancelled": True}
except _ScenePlannerTimeout:
return self._stream_scene_planner_failure(
message_id=message_id,
emit=emit,
display_text="功能模式规划超时",
answer=(
f"功能模式暂未生成:场景规划超时,已超过"
f"{self._scene_turn_timeout:g}秒。未执行或保存任何车辆操作,"
"请重试。"
),
)
except _ScenePlannerOverloaded:
return self._stream_scene_planner_failure(
message_id=message_id,
emit=emit,
display_text="功能模式规划任务较多",
answer=(
"功能模式暂未生成:当前场景规划任务较多。"
"未执行或保存任何车辆操作,请稍后重试。"
),
)
except _ScenePlannerFallback as exc:
LOGGER.warning("Scene Composer fell back to the general agent: %s", exc)
if (
emit(
{
"type": "work.progress",
"message_id": message_id,
"display_text": "快速规划暂不可用,正在切换完整理解链路……",
}
)
is False
):
return {"cancelled": True}
conversation, conversation_key = self._conversation(principal, conversation_id)
try:
with conversation.lock:
return self._stream_locked(
conversation,
message,
message_id=message_id,
emit=emit,
execute_control=execute_control,
capability_snapshot=catalog,
capability_context=model_context,
control_terms=control_terms,
query_capabilities=catalog_query,
)
finally:
self._release_conversation(conversation_key, conversation)
@staticmethod
def _stream_scene_planner_failure(
*,
message_id: str,
emit: EmitEvent,
display_text: str,
answer: str,
) -> dict[str, object]:
if (
emit(
{
"type": "work.completed",
"message_id": message_id,
"success": False,
"display_text": display_text,
}
)
is False
):
return {"cancelled": True}
if (
emit(
{
"type": "answer.delta",
"message_id": message_id,
"delta": answer,
}
)
is False
):
return {"cancelled": True}
emit(
{
"type": "answer.completed",
"message_id": message_id,
"route": "scene_composer",
"answer": answer,
}
)
return {
"route": "scene_composer",
"success": False,
"answer": answer,
}
@staticmethod
def _copy_scene_plan(plan: Mapping[str, object]) -> dict[str, object]:
return json.loads(json.dumps(dict(plan), ensure_ascii=False, allow_nan=False))
def _scene_cache_key(
self,
*,
mode_name: str,
scene_context: Mapping[str, object],
) -> tuple[str, str, str, str]:
vehicle = scene_context.get("vehicle")
vehicle_id = (
str(vehicle.get("id") or "default")
if isinstance(vehicle, Mapping)
else "default"
)
revision = str(scene_context.get("composition_revision") or "")
if not revision:
raise _ScenePlannerFallback("scene capability revision is unavailable")
return (
vehicle_id,
revision,
normalize_mode_profile_name(mode_name),
SCENE_PLANNER_VERSION,
)
def _begin_scene_plan(
self,
key: tuple[str, str, str, str],
) -> tuple[dict[str, object] | None, _SceneFlight | None, bool]:
now = time.monotonic()
with self._scene_cache_lock:
expired = [
cached_key
for cached_key, (expires_at, _plan) in self._scene_cache.items()
if expires_at <= now
]
for cached_key in expired:
self._scene_cache.pop(cached_key, None)
cached = self._scene_cache.get(key)
if cached is not None:
self._scene_cache.move_to_end(key)
return self._copy_scene_plan(cached[1]), None, False
flight = self._scene_flights.get(key)
if flight is not None:
return None, flight, False
flight = _SceneFlight()
self._scene_flights[key] = flight
return None, flight, True
def _publish_scene_plan(
self,
key: tuple[str, str, str, str],
flight: _SceneFlight,
*,
plan: Mapping[str, object] | None = None,
error: BaseException | None = None,
) -> None:
with self._scene_cache_lock:
if plan is not None:
flight.plan = self._copy_scene_plan(plan)
flight.error = error
if error is not None:
self._scene_flights.pop(key, None)
flight.ready.set()
def _release_scene_flight(
self,
key: tuple[str, str, str, str],
flight: _SceneFlight,
) -> None:
with self._scene_cache_lock:
if self._scene_flights.get(key) is flight:
self._scene_flights.pop(key, None)
def _cache_validated_scene_plan(
self,
key: tuple[str, str, str, str],
plan: Mapping[str, object],
) -> None:
with self._scene_cache_lock:
self._scene_cache[key] = (
time.monotonic() + self._scene_cache_ttl,
self._copy_scene_plan(plan),
)
self._scene_cache.move_to_end(key)
while len(self._scene_cache) > self._scene_cache_limit:
self._scene_cache.popitem(last=False)
def _stream_scene_composition(
self,
message: str,
*,
mode_name: str,
message_id: str,
emit: EmitEvent,
execute_control: ControlExecutor,
scene_context: Mapping[str, object],
planning_deadline: float | None = None,
) -> dict[str, object]:
started_at = time.monotonic()
deadline = planning_deadline or (started_at + self._scene_turn_timeout)
key = self._scene_cache_key(
mode_name=mode_name,
scene_context=scene_context,
)
plan, flight, leader = self._begin_scene_plan(key)
cache_hit = plan is not None
if plan is not None:
if (
emit(
{
"type": "work.progress",
"message_id": message_id,
"display_text": "已复用场景组合,正在按实时能力重新校验……",
}
)
is False
):
raise _ScenePlannerCancelled()
elif not leader:
assert flight is not None
if (
emit(
{
"type": "work.progress",
"message_id": message_id,
"display_text": "相同场景正在组合,完成后将立即复用……",
}
)
is False
):
raise _ScenePlannerCancelled()
next_progress_at = time.monotonic() + self._scene_progress_interval
while not flight.ready.is_set():
remaining = deadline - time.monotonic()
if remaining <= 0:
raise _ScenePlannerTimeout(
"timed out waiting for an identical scene plan"
)
flight.ready.wait(min(0.25, remaining))
if not flight.ready.is_set() and time.monotonic() >= next_progress_at:
if (
emit(
{
"type": "work.progress",
"message_id": message_id,
"display_text": "相同场景仍在组合,请稍候……",
}
)
is False
):
raise _ScenePlannerCancelled()
next_progress_at = time.monotonic() + self._scene_progress_interval
if flight.error is not None:
if isinstance(flight.error, _ScenePlannerCancelled):
# The planner belongs to request scope, not to the browser
# connection that happened to become singleflight leader.
# Re-elect one surviving follower instead of forcing every
# waiter onto the slower general-agent path.
return self._stream_scene_composition(
message,
mode_name=mode_name,
message_id=message_id,
emit=emit,
execute_control=execute_control,
scene_context=scene_context,
planning_deadline=deadline,
)
if isinstance(flight.error, _ScenePlannerTimeout):
raise _ScenePlannerTimeout(str(flight.error)) from flight.error
if isinstance(flight.error, _ScenePlannerOverloaded):
raise _ScenePlannerOverloaded(str(flight.error)) from flight.error
raise _ScenePlannerFallback(str(flight.error)) from flight.error
if flight.plan is None:
raise _ScenePlannerFallback("identical scene planning returned no plan")
plan = self._copy_scene_plan(flight.plan)
cache_hit = True
else:
assert flight is not None
if not self._scene_planner_slots.acquire(blocking=False):
error = _ScenePlannerOverloaded("all Scene Composer slots are busy")
self._publish_scene_plan(key, flight, error=error)
raise error
planner_error: Exception | None = None
try:
plan = self._request_structured_scene_plan(
message,
message_id=message_id,
emit=emit,
scene_context=scene_context,
deadline=deadline,
)
except Exception as exc:
planner_error = exc
finally:
self._scene_planner_slots.release()
if planner_error is not None:
self._publish_scene_plan(key, flight, error=planner_error)
if isinstance(
planner_error,
(
_ScenePlannerCancelled,
_ScenePlannerTimeout,
_ScenePlannerOverloaded,
_ScenePlannerFallback,
),
):
raise planner_error
raise _ScenePlannerFallback(str(planner_error)) from planner_error
self._publish_scene_plan(key, flight, plan=plan)
assert plan is not None
try:
if (
emit(
{
"type": "work.progress",
"message_id": message_id,
"display_text": "模式候选动作已生成,正在进行参数与安全校验……",
}
)
is False
):
raise _ScenePlannerCancelled()
validation_started_at = time.monotonic()
response = self._stream_direct_vehicle_control(
_VehicleAuthorization(message, "explicit_mode_profile_create"),
message_id=message_id,
emit=emit,
execute_control=execute_control,
assistant_plan=plan,
response_route="scene_composer",
)
if response.get("success") is True:
self._cache_validated_scene_plan(key, plan)
LOGGER.info(
"Scene Composer finished revision=%s cache_hit=%s success=%s "
"validation_ms=%d elapsed_ms=%d",
key[1],
cache_hit,
response.get("success") is True,
round((time.monotonic() - validation_started_at) * 1000),
round((time.monotonic() - started_at) * 1000),
)
return response
finally:
if leader and flight is not None:
self._release_scene_flight(key, flight)
def _request_structured_scene_plan(
self,
message: str,
*,
message_id: str,
emit: EmitEvent,
scene_context: Mapping[str, object],
deadline: float,
) -> dict[str, object]:
start_structured_turn = getattr(self.client, "start_structured_turn", None)
if not callable(start_structured_turn):
raise _ScenePlannerFallback("structured turns are not supported")
planning_started_at = time.monotonic()
output_schema = build_scene_output_schema(scene_context)
context_bytes = len(
json.dumps(
dict(scene_context),
ensure_ascii=False,
separators=(",", ":"),
).encode("utf-8")
)
schema_bytes = len(
json.dumps(
output_schema,
ensure_ascii=False,
separators=(",", ":"),
).encode("utf-8")
)
thread_id: str | None = None
active: ActiveCodexTurn | None = None
final_text = ""
streamed_text = ""
progress_emitted = False
next_heartbeat_at = time.monotonic() + self._scene_progress_interval
thread_ready_at: float | None = None
turn_ready_at: float | None = None
first_event_at: float | None = None
terminal_received = False
interrupt_requested = False
try:
try:
thread_id = self.client.start_thread(
developer_instructions=SCENE_COMPOSER_INSTRUCTIONS,
ephemeral=True,
replace_developer_instructions=True,
timeout=self._remaining_scene_time(deadline),
)
except Exception as exc:
self._raise_if_scene_timeout(exc, deadline, "thread/start")
raise
thread_ready_at = time.monotonic()
try:
active = start_structured_turn(
thread_id,
message,
application_context={
**dict(scene_context),
"natural_language_routing": {
"original_utterance": message,
"parsed_clauses": [],
"resolved_clauses": [],
"unresolved_clauses": [message],
"cloud_input_utterance": message,
},
},
output_schema=output_schema,
timeout=self._remaining_scene_time(deadline),
)
except Exception as exc:
self._raise_if_scene_timeout(exc, deadline, "turn/start")
raise
turn_ready_at = time.monotonic()
terminal: dict[str, object] | None = None
while terminal is None:
remaining = deadline - time.monotonic()
if remaining <= 0:
self._interrupt_scene_turn(active)
interrupt_requested = True
raise _ScenePlannerTimeout("Scene Composer turn timed out")
try:
event = active.events.get(timeout=min(0.2, remaining))
except queue.Empty:
if time.monotonic() >= next_heartbeat_at:
if (
emit(
{
"type": "work.progress",
"message_id": message_id,
"display_text": "小迪正在生成结构化模式方案……",
}
)
is False
):
self._interrupt_scene_turn(active)
interrupt_requested = True
raise _ScenePlannerCancelled()
next_heartbeat_at = (
time.monotonic() + self._scene_progress_interval
)
continue
if first_event_at is None:
first_event_at = time.monotonic()
method = str(event.get("method") or "")
params = event.get("params")
params = params if isinstance(params, dict) else {}
if not progress_emitted and method in {
"turn/started",
"item/started",
"item/agentMessage/delta",
}:
progress_emitted = True
if (
emit(
{
"type": "work.progress",
"message_id": message_id,
"display_text": "小迪已开始生成结构化模式方案……",
}
)
is False
):
self._interrupt_scene_turn(active)
interrupt_requested = True
raise _ScenePlannerCancelled()
if method == "transport/error":
raise _ScenePlannerFallback(
str(params.get("message") or "Codex disconnected")
)
if method == "item/agentMessage/delta":
streamed_text += str(params.get("delta") or "")
if len(streamed_text.encode("utf-8")) > 64 * 1024:
self._interrupt_scene_turn(active)
interrupt_requested = True
raise SceneCompositionError("scene composer response is too large")
elif method == "item/completed":
item = params.get("item")
if isinstance(item, Mapping) and item.get("type") == "agentMessage":
final_text = str(item.get("text") or final_text)
elif method == "error":
detail = params.get("error")
if isinstance(detail, Mapping):
detail = detail.get("message") or detail.get("codexErrorInfo")
raise _ScenePlannerFallback(
str(detail or params.get("message") or "Scene Composer turn failed")
)
elif method == "turn/completed":
terminal = params
terminal_received = True
if terminal is None and time.monotonic() >= next_heartbeat_at:
if (
emit(
{
"type": "work.progress",
"message_id": message_id,
"display_text": "小迪正在生成结构化模式方案……",
}
)
is False
):
self._interrupt_scene_turn(active)
interrupt_requested = True
raise _ScenePlannerCancelled()
next_heartbeat_at = time.monotonic() + self._scene_progress_interval
turn = terminal.get("turn") if terminal else None
turn = turn if isinstance(turn, Mapping) else {}
status = str(turn.get("status") or "failed")
if status != "completed":
detail = turn.get("error")
if isinstance(detail, Mapping):
detail = detail.get("message") or detail.get("codexErrorInfo")
raise _ScenePlannerFallback(str(detail or f"Scene Composer turn {status}"))
output_text = final_text or streamed_text
if not output_text:
raise SceneCompositionError("scene composer produced an empty response")
try:
raw_output = json.loads(output_text)
except json.JSONDecodeError as exc:
raise SceneCompositionError("scene composer output was not valid JSON") from exc
if not isinstance(raw_output, Mapping):
raise SceneCompositionError("scene composer output must be an object")
normalized = normalize_scene_output(raw_output, scene_context)
finished_at = time.monotonic()
LOGGER.info(
"Scene Composer model completed context_bytes=%d schema_bytes=%d "
"thread_start_ms=%d turn_start_ms=%d first_event_ms=%d model_ms=%d",
context_bytes,
schema_bytes,
round(((thread_ready_at or finished_at) - planning_started_at) * 1000),
round(
(
(turn_ready_at or finished_at)
- (thread_ready_at or planning_started_at)
)
* 1000
),
(
round((first_event_at - planning_started_at) * 1000)
if first_event_at is not None
else -1
),
round((finished_at - planning_started_at) * 1000),
)
return normalized
finally:
if active is not None:
if not terminal_received and not interrupt_requested:
self._interrupt_scene_turn(active)
if not self._finish_scene_turn(active):
abandon = getattr(self.client, "abandon_turn", None)
if callable(abandon):
abandon(active)
if thread_id is not None:
self._unsubscribe_scene_thread(thread_id)
@staticmethod
def _remaining_scene_time(deadline: float) -> float:
remaining = deadline - time.monotonic()
if remaining <= 0:
raise _ScenePlannerTimeout("Scene Composer deadline expired")
return remaining
@staticmethod
def _raise_if_scene_timeout(
exc: BaseException,
deadline: float,
stage: str,
) -> None:
if (
exc.__class__.__name__ == "CodexAppServerTimeout"
or time.monotonic() >= deadline
):
raise _ScenePlannerTimeout(
f"Scene Composer {stage} exceeded its deadline"
) from exc
def _interrupt_scene_turn(self, active: ActiveCodexTurn) -> None:
try:
self.client.interrupt(active, timeout=0.25)
except (RuntimeError, TypeError) as exc:
LOGGER.debug("Scene Composer turn interrupt failed", exc_info=exc)
def _finish_scene_turn(self, active: ActiveCodexTurn) -> bool:
try:
self.client.finish_turn(active, timeout=0.25)
return True
except (RuntimeError, TypeError) as exc:
LOGGER.debug("Scene Composer turn cleanup failed", exc_info=exc)
return False
def _unsubscribe_scene_thread(self, thread_id: str) -> None:
unsubscribe = getattr(self.client, "unsubscribe_thread", None)
if not callable(unsubscribe):
return
try:
unsubscribe(thread_id, timeout=0.25)
except (RuntimeError, TypeError) as exc:
LOGGER.debug("Scene Composer thread cleanup failed", exc_info=exc)
def _stream_local_first_vehicle_control(
self,
authorization: _VehicleAuthorization,
*,
message_id: str,
emit: EmitEvent,
preview_control: ControlExecutor,
supplement_control: ControlExecutor,
conversation_id: str,
principal: str,
capability_snapshot: Mapping[str, object],
capability_context: Mapping[str, object],
control_terms: Sequence[str],
query_capabilities: CapabilityQuery,
) -> dict[str, object]:
"""Publish deterministic controls, then delegate only unresolved text."""
instruction = authorization.instruction
if instruction is None:
raise CodexAssistantError("local-first vehicle control was not authorized")
if (
emit(
{
"type": "action.started",
"message_id": message_id,
"display_text": "正在通过本地规则解析明确控制指令……",
}
)
is False
):
return {"cancelled": True}
stream_open = True
def on_progress(payload: dict[str, object]) -> None:
nonlocal stream_open
if not stream_open:
return
validated_payload = self._validated_control_payload(payload)
if validated_payload is not None:
stream_open = (
emit(
{
"type": "validated_control_commands",
"message_id": message_id,
"payload": validated_payload,
}
)
is not False
)
if not stream_open:
return
stream_open = (
emit(
{
"type": "action.progress",
"message_id": message_id,
"display_text": "本地控制指令已解析并进入下发链路……",
"payload": self._safe_vehicle_result(payload),
}
)
is not False
)
try:
preview = preview_control(instruction, on_progress)
if not isinstance(preview, dict):
raise TypeError("vehicle preview returned an invalid result")
except Exception: # noqa: BLE001 - stable browser-facing failure
answer = "车辆控制未完成,本地安全执行链没有返回有效结果。"
for event in (
{
"type": "action.completed",
"message_id": message_id,
"success": False,
"state": "failed",
"display_text": "本地控制未完成",
},
{
"type": "answer.delta",
"message_id": message_id,
"delta": answer,
},
{
"type": "work.completed",
"message_id": message_id,
"success": False,
"display_text": "车辆控制未完成",
},
{
"type": "answer.completed",
"message_id": message_id,
"route": "vehicle_control",
"answer": answer,
},
):
if emit(event) is False:
return {"cancelled": True, "answer": answer}
return {
"route": "vehicle_control",
"answer": answer,
"success": False,
}
if not stream_open:
return {"cancelled": True}
routing_value = preview.get("natural_language_routing")
routing = dict(routing_value) if isinstance(routing_value, Mapping) else {}
raw_unresolved = routing.get("unresolved_clauses")
if not isinstance(raw_unresolved, Sequence) or isinstance(
raw_unresolved,
(str, bytes, bytearray),
):
raw_unresolved = preview.get("unresolved_clauses")
unresolved_clauses = list(
dict.fromkeys(
text
for item in (
list(raw_unresolved)[:64]
if isinstance(raw_unresolved, Sequence)
and not isinstance(raw_unresolved, (str, bytes, bytearray))
else []
)
for text in [str(item).strip()[:1000]]
if text
)
)
safe_preview = self._safe_vehicle_result(preview)
if not unresolved_clauses:
action_state, successful, completion_text = _vehicle_action_state(preview)
answer = self._fast_comfort_answer(
"explicit_registered_control",
preview,
successful,
)
if not stream_open:
return {"cancelled": True, "answer": answer}
for event in (
{
"type": "action.completed",
"message_id": message_id,
"success": successful,
"state": action_state,
"display_text": completion_text,
"payload": safe_preview,
},
{
"type": "answer.delta",
"message_id": message_id,
"delta": answer,
},
{
"type": "work.completed",
"message_id": message_id,
"success": successful,
"state": action_state,
"display_text": completion_text,
},
{
"type": "answer.completed",
"message_id": message_id,
"route": "vehicle_control",
"answer": answer,
},
):
if emit(event) is False:
return {"cancelled": True, "answer": answer}
return {
"route": "vehicle_control",
"answer": answer,
"success": successful,
"result": safe_preview,
}
local_has_commands = bool(
preview.get("function_configs") and preview.get("command_plans")
)
local_status = str(preview.get("status") or "")
local_succeeded = local_has_commands and local_status in {
"succeeded",
"partially_succeeded",
"no_action",
}
if stream_open and emit(
{
"type": "action.completed",
"message_id": message_id,
"success": local_succeeded,
"state": "submitted" if local_succeeded else "failed",
"display_text": (
"本地明确控制已先行处理"
if local_succeeded
else "本地阶段未生成控制指令"
),
"payload": safe_preview,
}
) is False:
return {"cancelled": True}
if emit(
{
"type": "work.progress",
"message_id": message_id,
"display_text": "本地明确子句已处理,正在补充未解析子句……",
}
) is False:
return {"cancelled": True}
cloud_message = ",".join(unresolved_clauses)
routing.update(
{
"original_utterance": str(
routing.get("original_utterance") or instruction
)[:1000],
"parsed_clauses": list(routing.get("parsed_clauses") or []),
"resolved_clauses": list(routing.get("resolved_clauses") or []),
"unresolved_clauses": unresolved_clauses,
"cloud_input_utterance": cloud_message,
}
)
if not self.client.available:
answer = (
"已先行处理本地可解析的控制;其余子句暂无法连接云端模型,"
"未生成额外控制。"
)
emit(
{
"type": "answer.delta",
"message_id": message_id,
"delta": answer,
}
)
emit(
{
"type": "work.completed",
"message_id": message_id,
"success": local_succeeded,
"display_text": "本地控制已处理,云端补充不可用",
}
)
emit(
{
"type": "answer.completed",
"message_id": message_id,
"route": "vehicle_control",
"answer": answer,
}
)
return {
"route": "vehicle_control",
"answer": answer,
"success": local_succeeded,
"result": safe_preview,
}
conversation, conversation_key = self._conversation(
principal,
conversation_id,
)
try:
with conversation.lock:
return self._stream_locked(
conversation,
cloud_message,
message_id=message_id,
emit=emit,
execute_control=supplement_control,
capability_snapshot=capability_snapshot,
capability_context={
**dict(capability_context),
"natural_language_routing": routing,
},
control_terms=control_terms,
query_capabilities=query_capabilities,
)
finally:
self._release_conversation(conversation_key, conversation)
def _stream_direct_vehicle_control(
self,
authorization: _VehicleAuthorization,
*,
message_id: str,
emit: EmitEvent,
execute_control: ControlExecutor,
assistant_plan: Mapping[str, object] | None = None,
response_route: str = "vehicle_control",
) -> dict[str, object]:
"""Execute deterministic high-confidence routes without model latency."""
instruction = authorization.instruction
if instruction is None:
raise CodexAssistantError("direct vehicle control was not authorized")
mode_profile_preview = authorization.reason in {
"explicit_mode_profile_create",
"explicit_mode_profile_draft_update",
}
mode_profile_draft_update = (
authorization.reason == "explicit_mode_profile_draft_update"
)
exact_quick_control = authorization.reason == "exact_quick_control"
explicit_local_control = authorization.reason in {
"exact_quick_control",
"explicit_registered_control",
}
saved_mode_execution = authorization.reason == "explicit_saved_mode_execute"
mode_profile_management_route = {
"explicit_mode_profile_update": "mode_profile_update",
"explicit_mode_profile_delete": "mode_profile_delete",
}.get(authorization.reason)
if (
emit(
{
"type": "action.started",
"message_id": message_id,
"display_text": (
"正在生成模式动作预览……"
if mode_profile_preview and not mode_profile_draft_update
else "正在调整待保存模式动作……"
if mode_profile_draft_update
else "正在更新已保存的功能模式……"
if mode_profile_management_route == "mode_profile_update"
else "正在删除自定义功能模式……"
if mode_profile_management_route == "mode_profile_delete"
else "正在通过安全执行链进入功能模式……"
if saved_mode_execution
else "正在通过车辆安全链执行快捷控制……"
if exact_quick_control
else "正在通过本地规则解析并下发控制……"
if explicit_local_control
else "正在通过车辆安全链执行舒适性调节……"
),
}
)
is False
):
return {"cancelled": True}
stream_open = True
def on_progress(payload: dict[str, object]) -> None:
nonlocal stream_open
if not stream_open:
return
validated_payload = self._validated_control_payload(payload)
if validated_payload is not None:
stream_open = (
emit(
{
"type": "validated_control_commands",
"message_id": message_id,
"payload": validated_payload,
}
)
is not False
)
if not stream_open:
return
stream_open = (
emit(
{
"type": "action.progress",
"message_id": message_id,
"display_text": (
"模式动作已解析,正在进行安全校验……"
if mode_profile_preview
else "已保存模式动作已载入,正在校验并执行……"
if saved_mode_execution
else "功能模式存储请求已解析,正在校验……"
if mode_profile_management_route is not None
else "本地控制指令已通过校验并进入下发链路……"
if explicit_local_control
else "控制指令已通过校验,正在执行……"
),
"payload": self._safe_vehicle_result(payload),
}
)
is not False
)
try:
result = (
execute_control(instruction, on_progress, self._copy_scene_plan(assistant_plan))
if assistant_plan is not None
else execute_control(instruction, on_progress)
)
if not isinstance(result, dict):
raise TypeError("vehicle control returned an invalid result")
safe_result = self._safe_vehicle_result(result)
action_state, successful, completion_text = _vehicle_action_state(result)
if mode_profile_preview:
successful = _mode_profile_result_succeeded(result)
action_state = "simulated" if successful else "failed"
completion_text = (
"功能模式预览已生成" if successful else "功能模式预览生成失败"
)
elif mode_profile_management_route is not None:
successful = _mode_profile_management_result_succeeded(
result,
mode_profile_management_route,
)
action_state = "simulated" if successful else "failed"
completion_text = (
"功能模式已更新"
if successful
and mode_profile_management_route == "mode_profile_update"
else "自定义功能模式已删除"
if successful
else "功能模式更新未完成"
if mode_profile_management_route == "mode_profile_update"
else "功能模式删除未完成"
)
except Exception: # noqa: BLE001 - return a stable public failure
result = {}
safe_result = {"status": "failed", "execution_mode": "unknown", "verified": False}
action_state = "failed"
completion_text = "车辆控制未完成"
successful = False
answer = (
self._mode_profile_preview_answer(result, successful)
if mode_profile_preview
else self._mode_profile_management_answer(
result,
successful,
mode_profile_management_route,
)
if mode_profile_management_route is not None
else self._fast_comfort_answer(authorization.reason, result, successful)
)
if not stream_open:
return {
"cancelled": True,
"route": response_route,
"answer": answer,
"success": successful,
}
events = (
{
"type": "action.completed",
"message_id": message_id,
"success": successful,
"state": action_state,
"display_text": completion_text,
"payload": safe_result,
},
{
"type": "answer.delta",
"message_id": message_id,
"delta": answer,
},
{
"type": "work.completed",
"message_id": message_id,
"success": successful,
"state": action_state,
"display_text": (
"功能模式预览已生成"
if mode_profile_preview and successful
else "功能模式预览生成失败"
if mode_profile_preview
else completion_text
if mode_profile_management_route is not None
else "功能模式已提交,等待车辆状态确认"
if saved_mode_execution and action_state == "submitted"
else "功能模式模拟编排已完成"
if saved_mode_execution and action_state == "simulated"
else "功能模式执行已完成"
if saved_mode_execution and successful
else "功能模式执行未完成"
if saved_mode_execution
else "快捷控制已提交,等待车辆状态确认"
if exact_quick_control and action_state == "submitted"
else "快捷控制模拟编排已完成"
if exact_quick_control and action_state == "simulated"
else "快捷控制已完成"
if exact_quick_control and successful
else "快捷控制未完成"
if exact_quick_control
else "明确控制已提交,等待车辆状态确认"
if explicit_local_control and action_state == "submitted"
else "明确控制模拟编排已完成"
if explicit_local_control and action_state == "simulated"
else "明确控制已完成"
if explicit_local_control and successful
else "明确控制未完成"
if explicit_local_control
else "舒适性调节已提交,等待车辆状态确认"
if action_state == "submitted"
else "舒适性调节模拟编排已完成"
if action_state == "simulated"
else "舒适性调节已完成"
if successful
else "舒适性调节未完成"
),
},
{
"type": "answer.completed",
"message_id": message_id,
"route": response_route,
"answer": answer,
},
)
for event in events:
if emit(event) is False:
return {
"cancelled": True,
"route": response_route,
"answer": answer,
"success": successful,
}
return {
"route": response_route,
"answer": answer,
"success": successful,
"result": safe_result,
}
@staticmethod
def _mode_profile_failure_feedback(
result: Mapping[str, object],
) -> tuple[str, str]:
def clean(value: object, *, limit: int = 420) -> str:
text = re.sub(r"[\x00-\x1f\x7f]+", " ", str(value or ""))
return re.sub(r"\s+", " ", text).strip(" ,,。;;::")[:limit]
failure = result.get("mode_creation_failure")
if isinstance(failure, Mapping):
reason = clean(failure.get("reason"))
next_step = clean(failure.get("next_step"))
if reason and next_step:
if (
INTERNAL_SCENE_BUILDER_MARKER in reason
or INTERNAL_SCENE_BUILDER_MARKER in next_step
):
return (
"未能根据场景名称生成通过工程能力校验的座舱动作",
"请稍后重试;若该场景尚未登记,可补充希望联动的设备状态",
)
return reason, next_step
unresolved = result.get("unresolved_clauses")
if isinstance(unresolved, Sequence) and not isinstance(
unresolved,
(str, bytes, bytearray),
):
clauses = list(
dict.fromkeys(
clean(item, limit=120)
for item in unresolved[:3]
if clean(item, limit=120)
and INTERNAL_SCENE_BUILDER_MARKER
not in clean(item, limit=120)
)
)
if clauses:
rendered = "、".join(f"“{item}”" for item in clauses)
return (
f"以下设置未能转换为可保存的座舱动作:{rendered}",
(
"请按“设备 + 动作 + 明确目标”重述,例如“副驾腿托调整到80%”"
"或“空调调整到24度”,然后重试"
),
)
validation = result.get("validation")
if isinstance(validation, Sequence) and not isinstance(
validation,
(str, bytes, bytearray),
):
safe_codes = {
"AMBIGUOUS_INTENT",
"MODE_PROFILE_DRAFT_INCOMPLETE",
"MODE_PROFILE_DRAFT_EMPTY",
"NO_EXECUTABLE_OPERATION",
"UNRESOLVED_UTTERANCE_SPAN",
}
for item in validation[:12]:
if not isinstance(item, Mapping):
continue
if str(item.get("severity") or "") != "error":
continue
if str(item.get("code") or "") not in safe_codes:
continue
message = clean(item.get("message"), limit=240)
if not message or re.search(
r"(?:CAN|CH[01]|JSON|schema|0x[0-9a-f]+|\\|/[A-Za-z])",
message,
re.IGNORECASE,
):
continue
return (
message,
"请根据上述原因改为当前支持且目标唯一的座舱设置,然后重新提交",
)
status = clean(result.get("status"), limit=60)
if status in {"needs_confirmation", "validation_failed"}:
return (
"场景设置存在未识别、冲突或超出支持范围的内容",
"请逐项明确设备、动作和单一目标值后重试;仍失败时可将设置分条提交",
)
return (
"模式构建服务未返回可验证的成功结果",
"请稍后重试;若持续失败,请将各项设置按“设备 + 动作 + 目标值”分条输入",
)
@staticmethod
def _mode_profile_preview_answer(
result: Mapping[str, object], successful: bool
) -> str:
if not successful:
reason, next_step = CodexAssistantService._mode_profile_failure_feedback(
result
)
return (
f"功能模式未生成。失败原因:{reason}。"
f"下一步:{next_step}。"
)
draft = result.get("mode_profile_draft")
name = (
str(draft.get("name") or "新功能模式")
if isinstance(draft, Mapping)
else "新功能模式"
)
if (
isinstance(draft, Mapping)
and draft.get("generation_source") == "draft_revision"
):
raw_operation_count = draft.get("operation_count")
operation_count = (
raw_operation_count
if isinstance(raw_operation_count, int)
and not isinstance(raw_operation_count, bool)
and raw_operation_count > 0
else 0
)
persistence_hint = (
"确认后可更新现有模式。"
if draft.get("existing_mode_id")
else "确认后可保存为功能模式。"
)
requested_operation_count = draft.get("requested_operation_count")
changed_operation_count = draft.get("changed_operation_count")
no_effective_change = (
isinstance(requested_operation_count, int)
and not isinstance(requested_operation_count, bool)
and requested_operation_count > 0
and isinstance(changed_operation_count, int)
and not isinstance(changed_operation_count, bool)
and changed_operation_count == 0
)
if no_effective_change:
return (
f"待保存的“{name}”模式中,该项原本已是目标状态,"
f"已保持不变并重新校验全部{operation_count}项组合动作。"
f"原草稿未被保存或执行,可以继续调整;{persistence_hint}"
)
return (
f"已调整待保存的“{name}”模式,并重新校验全部"
f"{operation_count}项组合动作。原草稿未被保存或执行,"
f"可以继续调整;{persistence_hint}"
)
if isinstance(draft, Mapping) and draft.get("existing_mode_id"):
raw_operation_count = draft.get("operation_count")
operation_count = (
raw_operation_count
if isinstance(raw_operation_count, int)
and not isinstance(raw_operation_count, bool)
and raw_operation_count >= 0
else 0
)
if draft.get("generation_source") == "registered_mode_profile":
return (
f"工程中已有“{name}”,已按当前车型载入预览并验证"
f"{operation_count}项设备动作。可以先试运行,确认后更新现有模式。"
)
return (
f"已按你提供的设置为现有“{name}”生成预览并验证"
f"{operation_count}项设备动作。可以先试运行,确认后更新现有模式。"
)
if (
isinstance(draft, Mapping)
and draft.get("generation_source") == "scene_template_copy"
):
raw_operation_count = draft.get("operation_count")
operation_count = (
raw_operation_count
if isinstance(raw_operation_count, int)
and not isinstance(raw_operation_count, bool)
and raw_operation_count > 0
else 0
)
return (
f"已从常用模式模板快速生成“{name}”,并按当前工程能力验证了"
f"{operation_count}项设备动作。当前只是独立草稿,可以继续调整;"
"确认预览后可保存为新的功能模式,也可以先试运行。"
)
if (
isinstance(draft, Mapping)
and draft.get("generation_source") == "codex_scene_composition"
):
raw_operation_count = draft.get("operation_count")
operation_count = (
raw_operation_count
if isinstance(raw_operation_count, int)
and not isinstance(raw_operation_count, bool)
and raw_operation_count > 0
else 0
)
return (
f"已理解“{name}”的场景意图,并从当前工程能力中组合、验证了"
f"{operation_count}项设备动作。确认预览后可保存为功能模式,也可以先试运行。"
)
return f"已为你生成“{name}”预览。确认动作后可保存为功能模式,也可以先试运行。"
@staticmethod
def _mode_profile_management_answer(
result: Mapping[str, object],
successful: bool,
route: str,
) -> str:
"""Render saved-mode mutations from service evidence, not model claims."""
deleting = route == "mode_profile_delete"
evidence = result.get(
"deleted_mode_profile" if deleting else "mode_profile"
)
if successful and isinstance(evidence, Mapping):
name = str(evidence.get("name") or "该模式")
if deleting:
return (
f"已删除自定义模式“{name}”。本次只修改模式库,"
"没有向车辆下发控制动作。"
)
operation_count = evidence.get("operation_count")
count_text = (
f",当前共包含{operation_count}项设备动作"
if isinstance(operation_count, int)
and not isinstance(operation_count, bool)
else ""
)
change_text = CodexAssistantService._mode_profile_change_summary(
result.get("mode_profile_changes")
)
return (
f"已更新并保存“{name}”{count_text}。"
f"{change_text}"
"本次只修改模式配置,"
"没有向车辆下发控制动作。"
)
issue_code = ""
issue_message = ""
validation = result.get("validation")
if isinstance(validation, Sequence) and not isinstance(
validation,
(str, bytes, bytearray),
):
issue = next(
(item for item in validation if isinstance(item, Mapping)),
None,
)
if isinstance(issue, Mapping):
issue_code = str(issue.get("code") or "")
issue_message = re.sub(
r"\s+",
" ",
str(issue.get("message") or ""),
).strip()[:300]
if deleting and issue_code == "MODE_PROFILE_DELETE_PROTECTED":
return issue_message or "工程预设模式受保护,不能删除。"
if issue_code == "MODE_PROFILE_NOT_FOUND":
return issue_message or "未找到指定的功能模式,未修改模式库。"
action = "删除" if deleting else "更新"
if issue_message:
return f"功能模式{action}未完成:{issue_message}"
return f"功能模式{action}未完成,模式库没有发生变化。"
@staticmethod
def _mode_profile_change_summary(value: object) -> str:
if not isinstance(value, Sequence) or isinstance(
value,
(str, bytes, bytearray),
):
return ""
target_labels = {
"driver": "主驾",
"passenger": "副驾",
"rear": "后排",
"vehicle": "全车",
"all": "全部区域",
}
summaries: list[str] = []
for item in list(value)[:4]:
if not isinstance(item, Mapping):
continue
capability = str(item.get("capability") or "")
target = target_labels.get(str(item.get("target_scope") or ""), "")
parameters = item.get("parameters")
if not isinstance(parameters, Mapping):
continue
if capability == "climate.set_temperature" and isinstance(
parameters.get("temperature_c"),
(int, float),
):
summaries.append(
f"{target}空调温度调整为{parameters['temperature_c']}℃"
)
elif capability == "climate.set_fan_level" and isinstance(
parameters.get("level"),
int,
):
summaries.append(f"{target}空调风量调整为{parameters['level']}档")
elif capability == "roof.set_shade_position" and isinstance(
parameters.get("position_pct"),
(int, float),
):
summaries.append(
f"遮阳帘位置调整为{parameters['position_pct']}%"
)
return "已应用:" + ";".join(summaries) + "。" if summaries else ""
@staticmethod
def _fast_comfort_answer(
reason: str,
result: Mapping[str, object],
successful: bool,
) -> str:
if reason in {
"exact_quick_control",
"explicit_registered_control",
"explicit_saved_mode_execute",
}:
instruction = re.sub(
r"\s+",
" ",
str(
result.get("utterance")
or (
"功能模式"
if reason == "explicit_saved_mode_execute"
else "控制指令"
)
),
).strip()[:160]
if not successful:
return f"未能完成“{instruction}”,请检查车辆状态后重试。"
execution_mode = str(result.get("execution_mode") or "dry_run")
if execution_mode == "dry_run":
return f"已完成模拟编排:{instruction}。"
if execution_mode == "simulated_hardware":
return f"已在模拟硬件环境完成:{instruction}。"
physically_confirmed = (
result.get("physical_feedback_confirmed") is True
or _vehicle_verification_status(result) == "verified_success"
)
if physically_confirmed:
return f"已完成:{instruction}。"
return f"已提交:{instruction},正在等待车辆状态确认。"
cooling = reason == "implicit_hot_comfort"
task = "降温调节" if cooling else "升温调节"
temperature = 23 if cooling else 26
if not successful:
return f"{task}未完成,请检查车辆状态后重试。"
detail = f"空调已开启,全车温度设置为{temperature}℃,风量设置为2档。"
execution_mode = str(result.get("execution_mode") or "dry_run")
if execution_mode == "dry_run":
return f"已完成模拟编排:{detail}"
if execution_mode == "simulated_hardware":
return f"已在模拟硬件环境完成{task}:{detail}"
physically_confirmed = (
result.get("physical_feedback_confirmed") is True
or _vehicle_verification_status(result) == "verified_success"
)
if physically_confirmed:
return f"已完成{task}:{detail}"
return f"已提交{task},等待车辆状态确认。"
def _stream_locked(
self,
conversation: _Conversation,
message: str,
*,
message_id: str,
emit: EmitEvent,
execute_control: ControlExecutor,
capability_snapshot: Mapping[str, object],
capability_context: Mapping[str, object],
control_terms: Sequence[str],
query_capabilities: CapabilityQuery,
) -> dict[str, object]:
request_token = secrets.token_urlsafe(24)
tool_context = _VehicleTurnContext(
message,
execute_control,
_authorize_vehicle_request(message, control_terms),
capability_snapshot,
query_capabilities,
)
vehicle_control_expected = tool_context.authorization.instruction is not None
mode_profile_expected = (
tool_context.authorization.reason == "explicit_mode_profile_create"
)
with self._tool_contexts_lock:
self._tool_contexts[request_token] = tool_context
active: ActiveCodexTurn | None = None
item_phases: dict[str, str] = {}
final_answer = ""
streamed_answer = ""
answer_redactor = _StreamingSecretRedactor(request_token)
cancelled = False
deadline = time.monotonic() + self._turn_timeout
application_context = dict(capability_context)
if not isinstance(
application_context.get("natural_language_routing"),
Mapping,
):
application_context["natural_language_routing"] = {
"original_utterance": message,
"parsed_clauses": [],
"resolved_clauses": [],
"unresolved_clauses": [message],
"cloud_input_utterance": message,
}
try:
active = self.client.start_turn(
conversation.thread_id,
message,
request_token,
application_context=application_context,
)
terminal: dict[str, object] | None = None
while terminal is None:
if time.monotonic() >= deadline:
self.client.interrupt(active)
raise CodexAssistantError("Codex turn timed out")
while True:
try:
tool_event = tool_context.events.get_nowait()
except queue.Empty:
break
if emit(tool_event) is False:
cancelled = True
self.client.interrupt(active)
break
if cancelled:
break
try:
event = active.events.get(timeout=0.2)
except queue.Empty:
continue
method = str(event.get("method") or "")
params = event.get("params")
params = params if isinstance(params, dict) else {}
if method == "transport/error":
raise CodexAssistantError(str(params.get("message") or "Codex disconnected"))
if method == "item/started":
item = params.get("item")
if isinstance(item, dict):
item_id = str(item.get("id") or "")
item_phases[item_id] = str(item.get("phase") or "")
ui_event = self._item_started_event(item, message_id)
if ui_event and emit(ui_event) is False:
cancelled = True
elif method == "item/agentMessage/delta":
delta = str(params.get("delta") or "")
item_id = str(params.get("itemId") or "")
phase = item_phases.get(item_id, "")
if delta and phase == "commentary":
if (
emit(
{
"type": "work.progress",
"message_id": message_id,
"display_text": "小迪正在处理……",
}
)
is False
):
cancelled = True
elif delta:
safe_delta = answer_redactor.feed(delta)
if safe_delta:
streamed_answer += safe_delta
if (
not vehicle_control_expected
and emit(
{
"type": "answer.delta",
"message_id": message_id,
"delta": safe_delta,
}
)
is False
):
cancelled = True
elif method == "item/mcpToolCall/progress":
if (
emit(
{
"type": "action.progress",
"message_id": message_id,
# App Server progress text is model/tool
# controlled and may split secrets across
# multiple notifications. Keep this UI event
# fixed instead of attempting per-event
# redaction.
"display_text": "正在执行受控座舱操作……",
}
)
is False
):
cancelled = True
elif method == "item/completed":
item = params.get("item")
if isinstance(item, dict):
if item.get("type") == "agentMessage":
phase = str(
item.get("phase") or item_phases.get(str(item.get("id") or ""), "")
)
if phase != "commentary":
final_answer = str(item.get("text") or final_answer).replace(
request_token, "[内部令牌已隐藏]"
)
ui_event = self._item_completed_event(item, message_id)
if ui_event and emit(ui_event) is False:
cancelled = True
elif method == "error":
if bool(params.get("willRetry")):
if (
emit(
{
"type": "work.progress",
"message_id": message_id,
"display_text": "小迪正在重新连接……",
}
)
is False
):
cancelled = True
else:
detail = params.get("error")
if isinstance(detail, dict):
detail = detail.get("message") or detail.get("codexErrorInfo")
if not detail:
detail = params.get("message")
raise CodexAssistantError(str(detail or "Codex turn failed"))
elif method == "turn/completed":
terminal = params
if cancelled:
self.client.interrupt(active)
break
trailing_answer = answer_redactor.finish()
if trailing_answer:
streamed_answer += trailing_answer
if (
not cancelled
and not vehicle_control_expected
and emit(
{
"type": "answer.delta",
"message_id": message_id,
"delta": trailing_answer,
}
)
is False
):
cancelled = True
self.client.interrupt(active)
if cancelled:
return {"cancelled": True, "answer": final_answer or streamed_answer}
turn = terminal.get("turn") if terminal else None
turn = turn if isinstance(turn, dict) else {}
status = str(turn.get("status") or "failed")
if status != "completed":
detail = turn.get("error")
if isinstance(detail, dict):
detail = detail.get("message") or detail.get("codexErrorInfo")
raise CodexAssistantError(str(detail or f"Codex turn {status}"))
if not final_answer:
final_answer = streamed_answer.strip()
if vehicle_control_expected:
if not tool_context.called:
tool_context.action_succeeded = False
final_answer = (
"车辆控制未完成:AI 助手没有调用受控执行链,未向车辆下发任何操作。"
)
emit(
{
"type": "action.completed",
"message_id": message_id,
"success": False,
"display_text": "车辆控制未执行",
}
)
elif tool_context.action_succeeded is not True:
final_answer = (
self._mode_profile_preview_answer(
tool_context.action_result or {},
False,
)
if mode_profile_expected
else "车辆控制未完成,受控执行链未返回成功结果。"
)
elif mode_profile_expected:
final_answer = self._mode_profile_preview_answer(
tool_context.action_result or {},
True,
)
elif tool_context.action_state == "submitted":
final_answer = "控制指令已提交,正在等待车辆状态确认。"
elif not final_answer and tool_context.action_state == "simulated":
final_answer = "车辆控制模拟编排已完成。"
elif not final_answer and tool_context.action_state == "verified":
final_answer = "车辆控制已完成并通过状态确认。"
if (
emit(
{
"type": "answer.delta",
"message_id": message_id,
"delta": final_answer,
}
)
is False
):
return {"cancelled": True, "answer": final_answer}
if not final_answer:
raise CodexAssistantError("Codex produced an empty response")
work_succeeded = (
tool_context.called and tool_context.action_succeeded is True
if vehicle_control_expected
else tool_context.action_succeeded is not False
)
emit(
{
"type": "work.completed",
"message_id": message_id,
"success": work_succeeded,
"display_text": (
"功能模式预览已生成"
if mode_profile_expected and work_succeeded
else "功能模式预览生成失败"
if mode_profile_expected
else "车辆控制模拟编排已完成"
if vehicle_control_expected
and work_succeeded
and tool_context.action_state == "simulated"
else "控制指令已提交,等待车辆状态确认"
if vehicle_control_expected
and work_succeeded
and tool_context.action_state == "submitted"
else "车辆控制已完成并通过状态确认"
if vehicle_control_expected and work_succeeded
else "小迪已完成"
if work_succeeded
else "车辆控制未完成"
),
}
)
emit(
{
"type": "answer.completed",
"message_id": message_id,
"route": "agent",
"answer": final_answer,
}
)
conversation.touched_at = time.monotonic()
return {"route": "agent", "answer": final_answer}
finally:
if active is not None:
try:
self.client.finish_turn(active)
except RuntimeError as exc:
# The stream error has already been translated for the
# browser. Cleanup must not replace it with a second error.
LOGGER.debug("Codex turn cleanup failed", exc_info=exc)
with self._tool_contexts_lock:
self._tool_contexts.pop(request_token, None)
def execute_vehicle_control(
self,
request_token: str,
instruction: str,
*,
assistant_plan: Mapping[str, object] | None = None,
) -> dict[str, object]:
request_token = str(request_token).strip()
instruction = " ".join(str(instruction).strip().split())
if not request_token or not instruction:
raise ValueError("request_token and instruction are required")
if len(instruction) > 1000:
raise ValueError("instruction must not exceed 1000 characters")
with self._tool_contexts_lock:
context = self._tool_contexts.get(request_token)
if context is None:
raise CodexVehicleToolError("vehicle control context is missing or expired")
with context.lock:
if context.called:
raise CodexVehicleToolError("vehicle control was already requested in this turn")
context.called = True
authorization = context.authorization
if authorization.instruction is None or not hmac.compare_digest(
_normalize_instruction(instruction).encode("utf-8"),
_normalize_instruction(authorization.instruction).encode("utf-8"),
):
context.action_succeeded = False
context.events.put(
{
"type": "action.completed",
"success": False,
"display_text": "已阻止未获授权的车辆控制",
}
)
raise CodexVehicleToolError(
"vehicle control was not authorized by the original user request"
)
context.events.put(
{
"type": "action.started",
"display_text": "正在通过车辆安全链执行控制……",
}
)
def on_progress(payload: dict[str, object]) -> None:
validated_payload = self._validated_control_payload(
payload,
secret=request_token,
)
if validated_payload is not None:
context.events.put(
{
"type": "validated_control_commands",
"payload": validated_payload,
}
)
context.events.put(
{
"type": "action.progress",
"display_text": "控制指令已通过校验,正在执行……",
"payload": self._safe_vehicle_result(
payload,
secret=request_token,
),
}
)
try:
# Execute the server-owned instruction, never a model-expanded
# variant. This binds the capability token to the user's intent.
if assistant_plan is None:
result = context.execute_control(
authorization.instruction,
on_progress,
)
else:
result = context.execute_control(
authorization.instruction,
on_progress,
assistant_plan,
)
except Exception as exc:
context.action_succeeded = False
context.events.put(
{
"type": "action.completed",
"success": False,
"display_text": "车辆控制未完成",
}
)
raise CodexVehicleToolError(self._safe_control_error(exc)) from exc
safe_result = self._safe_vehicle_result(
result,
secret=request_token,
)
action_state, successful, completion_text = _vehicle_action_state(result)
if context.authorization.reason == "explicit_mode_profile_create":
successful = _mode_profile_result_succeeded(result)
action_state = "simulated" if successful else "failed"
completion_text = (
"功能模式预览已生成"
if successful
else "功能模式预览生成失败"
)
context.action_result = result
context.action_succeeded = successful
context.action_state = action_state
context.events.put(
{
"type": "action.completed",
"success": successful,
"state": action_state,
"display_text": completion_text,
"payload": safe_result,
}
)
return safe_result
def execute_cockpit_request(
self,
request_token: str,
assistant_plan: Mapping[str, object] | None = None,
) -> dict[str, object]:
"""Execute the bound request, optionally using a guarded semantic proposal."""
request_token = str(request_token).strip()
with self._tool_contexts_lock:
context = self._tool_contexts.get(request_token)
if context is None:
raise CodexVehicleToolError("vehicle control context is missing or expired")
instruction = context.authorization.instruction
if instruction is None:
raise CodexVehicleToolError(
"vehicle control was not authorized by the original user request"
)
normalized_plan: dict[str, object] | None = None
if assistant_plan is not None:
if not isinstance(assistant_plan, Mapping):
raise ValueError("assistant_plan must be an object")
try:
encoded_plan = json.dumps(
dict(assistant_plan),
ensure_ascii=False,
allow_nan=False,
separators=(",", ":"),
)
except (TypeError, ValueError) as exc:
raise ValueError("assistant_plan must contain JSON values") from exc
if len(encoded_plan.encode("utf-8")) > 24 * 1024:
raise ValueError("assistant_plan is too large")
normalized_plan = json.loads(encoded_plan.replace(request_token, "[内部令牌已隐藏]"))
# Free-form model prose is neither needed for execution nor safe
# to persist. Keep only structural intent and replace narrative
# fields before the plan reaches artifacts or progress payloads.
if "decision_summary" in normalized_plan:
normalized_plan["decision_summary"] = ["小迪生成的结构化计划已提交应用安全链复核。"]
if "ambiguities" in normalized_plan:
normalized_plan["ambiguities"] = (
[
{
"field": "assistant_plan",
"question": "小迪生成的候选计划包含未解决歧义。",
}
]
if isinstance(normalized_plan["ambiguities"], list)
and normalized_plan["ambiguities"]
else []
)
requests = normalized_plan.get("requests")
if isinstance(requests, list):
for request in requests:
if isinstance(request, dict) and "reason" in request:
request["reason"] = "原始用户请求的结构化候选动作。"
return self.execute_vehicle_control(
request_token,
instruction,
assistant_plan=normalized_plan,
)
def get_n7web_capabilities(
self,
request_token: str,
query: str = "",
) -> dict[str, object]:
"""Return the turn-bound authoritative catalog through the read-only MCP tool."""
request_token = str(request_token).strip()
query = " ".join(str(query).strip().split())
if not request_token:
raise ValueError("request_token is required")
if len(query) > 160:
raise ValueError("query must not exceed 160 characters")
with self._tool_contexts_lock:
context = self._tool_contexts.get(request_token)
if context is None:
raise CodexVehicleToolError("capability context is missing or expired")
with context.lock:
if context.capability_queries >= 4:
raise CodexVehicleToolError("capability query limit exceeded for this turn")
context.capability_queries += 1
try:
result = context.query_capabilities(context.capability_snapshot, query)
serialized = json.dumps(result, ensure_ascii=False, default=str)
except Exception as exc:
raise CodexVehicleToolError(
"capability catalog is temporarily unavailable"
) from exc
if len(serialized.encode("utf-8")) > 512 * 1024:
raise CodexVehicleToolError("capability catalog response is too large")
normalized = json.loads(serialized)
if not isinstance(normalized, dict):
raise CodexVehicleToolError("capability catalog returned an invalid result")
return {
"ok": True,
"source": "n7web_runtime_registry",
"catalog": normalized,
}
def authenticate_internal_request(self, candidate: str) -> bool:
return hmac.compare_digest(
str(candidate).encode("utf-8"),
self._internal_token.encode("utf-8"),
)
def _conversation(
self,
principal: str,
conversation_id: str,
) -> tuple[_Conversation, tuple[str, str]]:
key = (str(principal or "anonymous")[:128], conversation_id)
with self._sessions_lock:
existing = self._sessions.pop(key, None)
if existing is not None:
existing.in_use += 1
self._sessions[key] = existing
evicted = self._prune_sessions_locked(protected=key)
if existing is not None:
for conversation in evicted:
self._unsubscribe_thread(conversation.thread_id)
return existing, key
# App Server startup can involve model-provider I/O. Keep that latency
# outside the global session lock so unrelated conversations continue.
created = _Conversation(
self.client.start_thread(
developer_instructions=self._instructions,
ephemeral=True,
)
)
redundant: _Conversation | None = None
with self._sessions_lock:
existing = self._sessions.pop(key, None)
if existing is None:
existing = created
else:
# Another request won the same-conversation creation race. Both
# callers share its per-conversation lock; release our extra
# App Server subscription after leaving the global lock.
redundant = created
existing.in_use += 1
self._sessions[key] = existing
evicted = self._prune_sessions_locked(protected=key)
if redundant is not None:
self._unsubscribe_thread(redundant.thread_id)
for conversation in evicted:
self._unsubscribe_thread(conversation.thread_id)
return existing, key
def _release_conversation(
self,
key: tuple[str, str],
conversation: _Conversation,
) -> None:
with self._sessions_lock:
current = self._sessions.get(key)
if current is conversation:
conversation.in_use = max(0, conversation.in_use - 1)
conversation.touched_at = time.monotonic()
evicted = self._prune_sessions_locked()
for stale in evicted:
self._unsubscribe_thread(stale.thread_id)
def _prune_sessions_locked(
self,
*,
protected: tuple[str, str] | None = None,
) -> list[_Conversation]:
evicted: list[_Conversation] = []
while len(self._sessions) > self._max_sessions:
stale_key = next(
(
candidate_key
for candidate_key, candidate in self._sessions.items()
if candidate_key != protected and candidate.in_use == 0
),
None,
)
if stale_key is None:
break
evicted.append(self._sessions.pop(stale_key))
return evicted
def _unsubscribe_thread(self, thread_id: str) -> None:
unsubscribe = getattr(self.client, "unsubscribe_thread", None)
if not callable(unsubscribe):
return
try:
unsubscribe(thread_id)
except RuntimeError as exc:
LOGGER.debug("Codex thread unsubscribe failed", exc_info=exc)
@staticmethod
def _item_started_event(
item: dict[str, object],
message_id: str,
) -> dict[str, object] | None:
item_type = str(item.get("type") or "")
if item_type in {"agentMessage", "reasoning", "mcpToolCall"}:
return None
labels = {
"commandExecution": "正在执行受限工作步骤……",
"webSearch": "正在检索信息……",
"fileChange": "正在处理文件变更……",
}
return {
"type": "work.started",
"message_id": message_id,
"work_type": item_type,
"display_text": labels.get(item_type, "小迪正在处理……"),
}
@staticmethod
def _item_completed_event(
item: dict[str, object],
message_id: str,
) -> dict[str, object] | None:
item_type = str(item.get("type") or "")
if item_type in {"agentMessage", "reasoning", "mcpToolCall"}:
return None
if item_type not in {"commandExecution", "webSearch", "fileChange"}:
return None
status = str(item.get("status") or "completed")
success = status not in {"failed", "declined", "cancelled"}
return {
"type": "work.completed",
"message_id": message_id,
"work_type": item_type,
"success": success,
"display_text": "工作步骤已完成" if success else "工作步骤未完成",
}
@staticmethod
def _safe_result_value(
value: object,
*,
secret: str,
depth: int = 0,
) -> object:
"""Project bounded JSON data and remove tool-boundary secrets."""
if value is None or isinstance(value, (bool, int, float)):
return value
if isinstance(value, str):
text = value.replace(secret, "[内部令牌已隐藏]") if secret else value
return text[:1000]
if depth >= 2:
return None
if isinstance(value, Mapping):
safe: dict[str, object] = {}
for raw_key, raw_value in list(value.items())[:24]:
key = str(raw_key)[:80]
if re.search(
r"token|secret|password|authorization|approval|api.?key|"
r"credential|cookie|bearer|private.?key|access.?key|"
r"raw.?can|can.?frame|data.?hex|payload.?hex",
key,
re.IGNORECASE,
):
continue
safe[key] = CodexAssistantService._safe_result_value(
raw_value,
secret=secret,
depth=depth + 1,
)
return safe
if isinstance(value, Sequence) and not isinstance(
value,
(str, bytes, bytearray),
):
return [
CodexAssistantService._safe_result_value(
item,
secret=secret,
depth=depth + 1,
)
for item in list(value)[:24]
]
text = str(value)
if secret:
text = text.replace(secret, "[内部令牌已隐藏]")
return text[:240]
@staticmethod
def _validated_control_payload(
result: Mapping[str, object],
*,
secret: str = "",
) -> dict[str, object] | None:
"""Build a browser-only allowlist projection of compiled controls.
MCP tool results intentionally exclude command plans and raw CAN
material. The web console still needs a deterministic view after
validation, so copy only semantic fields, canonical debug commands,
reconstructed IR payloads, display-safe CAN bytes, and stage timings.
"""
raw_configurations = result.get("function_configs")
configuration_entries: list[tuple[str, Mapping[str, object]]] = []
if isinstance(raw_configurations, Mapping):
configuration_entries.extend(
(str(key), value)
for key, value in raw_configurations.items()
if isinstance(value, Mapping)
)
if not configuration_entries and isinstance(
result.get("function_config"), Mapping
):
configuration_entries.append(("default", result["function_config"]))
raw_plans = result.get("command_plans")
plan_entries: list[tuple[str, Mapping[str, object]]] = []
if isinstance(raw_plans, Mapping):
plan_entries.extend(
(str(key), value)
for key, value in raw_plans.items()
if isinstance(value, Mapping)
)
if not plan_entries and isinstance(result.get("command_plan"), Mapping):
plan_entries.append(("default", result["command_plan"]))
if not configuration_entries or not plan_entries:
return None
def safe_text(value: object, limit: int) -> str:
"""Return bounded display text without copying the request secret."""
text = str(value or "")
if secret and secret in text:
return ""
return text[:limit]
def command_refs(command: Mapping[str, object]) -> tuple[str, ...]:
raw_refs = command.get("operation_ref")
if isinstance(raw_refs, Sequence) and not isinstance(
raw_refs, (str, bytes, bytearray)
):
values = raw_refs
else:
values = str(raw_refs or "").split(",")
return tuple(str(value).strip() for value in values if str(value).strip())
def matching_commands(
entries: Sequence[tuple[str, Mapping[str, object]]],
operation_id: str,
) -> list[tuple[Mapping[str, object], Mapping[str, object]]]:
matches: list[tuple[Mapping[str, object], Mapping[str, object]]] = []
for _plan_key, plan in entries:
spec = plan.get("spec")
if not isinstance(spec, Mapping):
continue
commands = spec.get("commands")
if not isinstance(commands, Sequence) or isinstance(
commands, (str, bytes, bytearray)
):
continue
matches.extend(
(command, plan)
for command in commands
if isinstance(command, Mapping)
and operation_id in command_refs(command)
)
return matches
presentations: list[dict[str, object]] = []
for config_key, configuration in configuration_entries[:16]:
spec = configuration.get("spec")
if not isinstance(spec, Mapping):
continue
operations = spec.get("operations")
if not isinstance(operations, Sequence) or isinstance(
operations, (str, bytes, bytearray)
):
continue
raw_domain = str(spec.get("domain") or "")
domain = safe_text(raw_domain, 80)
preferred_plans = [
entry for entry in plan_entries if entry[0] == config_key
]
for operation in operations[:24]:
if not isinstance(operation, Mapping):
continue
raw_operation_id = str(operation.get("id") or "").strip()[:160]
operation_id = safe_text(raw_operation_id, 160).strip()
capability = safe_text(operation.get("capability"), 160).strip()
if not raw_operation_id or not operation_id or not capability:
continue
compiled = matching_commands(preferred_plans, raw_operation_id)
if (
not preferred_plans
and len(configuration_entries) == 1
and len(plan_entries) == 1
):
# Keep the legacy function_config/command_plan pair working,
# but never search globally across multi-config results. A
# repeated operation id must fail closed instead of borrowing
# a compiled command from another domain.
compiled = matching_commands(plan_entries, raw_operation_id)
if not compiled:
continue
channel = "IR" if raw_domain == "self_extension" else "CAN1"
actual_commands: list[str] = []
frames: list[dict[str, str]] = []
for command, plan in compiled[:16]:
signals = command.get("signals")
signals = signals if isinstance(signals, Mapping) else {}
plan_spec = plan.get("spec")
plan_domain = (
str(plan_spec.get("domain") or "")
if isinstance(plan_spec, Mapping)
else ""
)
message = str(command.get("message") or "")
bus = str(command.get("bus") or "").lower()
if message == "infrared.emit" or plan_domain == "self_extension":
command_channel = "IR"
elif "seat" in bus or "can0" in bus:
command_channel = "CAN0"
else:
command_channel = "CAN1"
channel = command_channel
debug_command = " ".join(
safe_text(signals.get("debug_command"), 256).split()
)
if re.fullmatch(r"[A-Za-z0-9_.:+% -]{1,256}", debug_command):
if debug_command not in actual_commands:
actual_commands.append(debug_command)
slot_value = signals.get("slot")
slot = (
int(slot_value)
if isinstance(slot_value, int)
and not isinstance(slot_value, bool)
and 1 <= slot_value <= 248
else None
)
if message == "infrared.emit" and slot is not None:
actual = f"infrared emit {slot}"
if actual not in actual_commands:
actual_commands.append(actual)
frame = {
"label": "GSCU1B TCP",
"value": json.dumps(
{
"action": "emit",
"data": {"no": slot},
"type": "infrared",
},
ensure_ascii=False,
separators=(",", ":"),
),
}
if frame not in frames:
frames.append(frame)
continue
frame_id = str(signals.get("frame_id") or "").strip()
data_bytes = " ".join(
str(signals.get("data_hex") or "").strip().split()
).upper()
if re.fullmatch(
r"0[xX][0-9A-Fa-f]{1,8}", frame_id
) and re.fullmatch(
r"[0-9A-F]{2}(?: [0-9A-F]{2}){0,63}", data_bytes
):
frame = {
"label": f"{command_channel} 帧",
"value": f"0x{frame_id[2:].upper()} · {data_bytes}",
}
if frame not in frames:
frames.append(frame)
safe_target = CodexAssistantService._safe_result_value(
operation.get("target") or {}, secret=secret
)
safe_parameters = CodexAssistantService._safe_result_value(
operation.get("parameters") or {}, secret=secret
)
presentations.append(
{
"operation": {
"id": operation_id,
"capability": capability,
"target": safe_target if isinstance(safe_target, dict) else {},
"parameters": (
safe_parameters
if isinstance(safe_parameters, dict)
else {}
),
**(
{
"parse_source": safe_text(
operation.get("parse_source"), 80
)
}
if safe_text(operation.get("parse_source"), 80)
else {}
),
},
"domain": domain,
"channel": channel,
"actual_command": ";".join(actual_commands),
"frames": frames,
}
)
if not presentations:
return None
allowed_trace_stages = {
"intent_parse",
"context_resolve",
"domain_route",
"function_generate",
"schema_validate",
"domain_validate",
"compile",
"nfc_peripheral_route",
"local_peripheral_plan",
"nfc_peripheral_sequence",
}
safe_trace: list[dict[str, object]] = []
raw_trace = result.get("trace_events")
if isinstance(raw_trace, Sequence) and not isinstance(
raw_trace, (str, bytes, bytearray)
):
for item in raw_trace[:32]:
if not isinstance(item, Mapping):
continue
stage = str(item.get("stage") or "")
duration = item.get("duration_ms")
if (
stage in allowed_trace_stages
and isinstance(duration, (int, float))
and not isinstance(duration, bool)
and duration >= 0
):
safe_trace.append({"stage": stage, "duration_ms": duration})
payload: dict[str, object] = {
"validated_control_commands": presentations,
"trace_events": safe_trace,
}
for key in (
"request_id",
"preview_request_id",
"supplement_of",
"utterance",
"provider_used",
"execution_mode",
"control_transport",
"local_control_budget_ms",
"local_control_elapsed_ms",
"local_control_within_budget",
"natural_language_routing",
):
if result.get(key) is not None:
payload[key] = CodexAssistantService._safe_result_value(
result[key], secret=secret
)
return payload
@staticmethod
def _safe_vehicle_result(
result: dict[str, object],
*,
secret: str = "",
) -> dict[str, object]:
safe: dict[str, object] = {
"status": str(result.get("status") or "unknown"),
"execution_mode": str(result.get("execution_mode") or "dry_run"),
}
if isinstance(result.get("hardware_execution_allowed"), bool):
safe["hardware_execution_allowed"] = result[
"hardware_execution_allowed"
]
for key in ("request_id", "utterance", "decision_summary"):
if result.get(key) is not None:
safe[key] = CodexAssistantService._safe_result_value(
result[key],
secret=secret,
)
actions: list[dict[str, object]] = []
intent_ir = result.get("intent_ir")
if isinstance(intent_ir, dict):
spec = intent_ir.get("spec")
if isinstance(spec, dict) and isinstance(spec.get("actions"), list):
for item in spec["actions"][:12]:
if not isinstance(item, dict):
continue
actions.append(
{
key: CodexAssistantService._safe_result_value(
item[key],
secret=secret,
)
for key in ("domain", "capability", "target", "parameters")
if key in item
}
)
safe["actions"] = actions
infrared_actions: list[dict[str, object]] = []
raw_infrared = result.get("infrared_actions")
if isinstance(raw_infrared, list):
for item in raw_infrared[:12]:
if not isinstance(item, dict):
continue
infrared_actions.append(
{
key: CodexAssistantService._safe_result_value(
item[key],
secret=secret,
)
for key in (
"capability_id",
"device_id",
"device_name",
"action",
"slot",
"transport",
"parse_source",
)
if key in item
}
)
if infrared_actions:
safe["infrared_actions"] = infrared_actions
mode_profile = result.get("mode_profile")
if isinstance(mode_profile, dict):
safe["mode_profile"] = {
key: CodexAssistantService._safe_result_value(
mode_profile[key],
secret=secret,
)
for key in (
"id",
"name",
"description",
"domains",
"operation_count",
"origin",
)
if key in mode_profile
}
mode_profile_changes = result.get("mode_profile_changes")
if isinstance(mode_profile_changes, list):
safe["mode_profile_changes"] = [
{
key: CodexAssistantService._safe_result_value(
item[key],
secret=secret,
)
for key in ("domain", "capability", "target_scope", "parameters")
if key in item
}
for item in mode_profile_changes[:12]
if isinstance(item, Mapping)
]
deleted_mode_profile = result.get("deleted_mode_profile")
if isinstance(deleted_mode_profile, dict):
safe["deleted_mode_profile"] = {
key: CodexAssistantService._safe_result_value(
deleted_mode_profile[key],
secret=secret,
)
for key in (
"id",
"name",
"description",
"domains",
"operation_count",
"origin",
)
if key in deleted_mode_profile
}
mode_profile_draft = result.get("mode_profile_draft")
if isinstance(mode_profile_draft, dict):
safe["mode_profile_draft"] = {
key: CodexAssistantService._safe_result_value(
mode_profile_draft[key],
secret=secret,
)
for key in (
"name",
"instruction",
"source_request_id",
"vehicle_id",
"domains",
"operation_count",
"existing_mode_id",
"existing_mode_revision",
"source_template_id",
"source_template_revision",
"generation_source",
"draft_revision",
"revises_source_request_id",
"requested_operation_count",
"changed_operation_count",
)
if key in mode_profile_draft
}
failure = result.get("mode_creation_failure")
if isinstance(failure, Mapping):
reason, next_step = CodexAssistantService._mode_profile_failure_feedback(
result
)
raw_unresolved = failure.get("unresolved_clauses")
unresolved_items = (
list(raw_unresolved)[:3]
if isinstance(raw_unresolved, Sequence)
and not isinstance(raw_unresolved, (str, bytes, bytearray))
else []
)
safe["failure_feedback"] = {
"reason": reason,
"next_step": next_step,
"unresolved_clauses": [
CodexAssistantService._safe_result_value(
item,
secret=secret,
)
for item in unresolved_items
if isinstance(item, str)
and INTERNAL_SCENE_BUILDER_MARKER not in item
],
}
peripheral = result.get("peripheral")
if isinstance(peripheral, dict):
safe["peripheral"] = {
key: CodexAssistantService._safe_result_value(
peripheral[key],
secret=secret,
)
for key in ("id", "device_id", "device_name", "action", "slot")
if key in peripheral
}
if result.get("orchestration_route") is not None:
safe["orchestration_route"] = str(result["orchestration_route"])
validation = result.get("validation")
if isinstance(validation, Sequence) and not isinstance(
validation,
(str, bytes, bytearray),
):
safe["validation"] = [
{
key: CodexAssistantService._safe_result_value(
item[key],
secret=secret,
)
for key in ("severity", "code", "message")
if key in item
}
for item in list(validation)[:8]
if isinstance(item, Mapping)
]
deferred = result.get("deferred_requests")
if isinstance(deferred, list):
safe["deferred_count"] = len(deferred)
verification_status = _vehicle_verification_status(result)
safe["verification_status"] = verification_status
safe["verified"] = verification_status == "verified_success"
return safe
@staticmethod
def _safe_control_error(error: Exception) -> str:
names = {
"HardwareAccessRequiredError": "vehicle hardware access is not available",
"HardwareBusyError": "the vehicle control channel is busy",
"HardwareSafetyError": "the request did not pass the vehicle safety gate",
"CanGatewayError": "the vehicle control channel is unavailable",
}
return names.get(type(error).__name__, "vehicle control failed safely")