mqtt切换为远程服务器部署,优化简单指令解析时长
This commit is contained in:
@@ -828,6 +828,8 @@ class CodexAssistantService:
|
||||
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,
|
||||
@@ -894,6 +896,14 @@ class CodexAssistantService:
|
||||
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",
|
||||
@@ -942,6 +952,7 @@ class CodexAssistantService:
|
||||
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"
|
||||
@@ -978,7 +989,7 @@ class CodexAssistantService:
|
||||
else "codex-app-server-scene-composer"
|
||||
if scene_composition_preview
|
||||
else "n7web-control-router"
|
||||
if fast_comfort_control or exact_quick_control
|
||||
if fast_comfort_control or exact_quick_control or local_first_control
|
||||
else "codex-app-server"
|
||||
)
|
||||
|
||||
@@ -1024,6 +1035,8 @@ class CodexAssistantService:
|
||||
if fast_comfort_control
|
||||
else "正在执行快捷车控……"
|
||||
if exact_quick_control
|
||||
else "正在本地解析并下发明确控制指令……"
|
||||
if local_first_control
|
||||
else "小迪正在理解并处理……"
|
||||
),
|
||||
}
|
||||
@@ -1092,6 +1105,25 @@ class CodexAssistantService:
|
||||
),
|
||||
}
|
||||
|
||||
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(
|
||||
(
|
||||
@@ -1535,7 +1567,16 @@ class CodexAssistantService:
|
||||
active = start_structured_turn(
|
||||
thread_id,
|
||||
message,
|
||||
application_context=scene_context,
|
||||
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),
|
||||
)
|
||||
@@ -1730,6 +1771,285 @@ class CodexAssistantService:
|
||||
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,
|
||||
@@ -1753,6 +2073,10 @@ class CodexAssistantService:
|
||||
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",
|
||||
@@ -1776,6 +2100,8 @@ class CodexAssistantService:
|
||||
if saved_mode_execution
|
||||
else "正在通过车辆安全链执行快捷控制……"
|
||||
if exact_quick_control
|
||||
else "正在通过本地规则解析并下发控制……"
|
||||
if explicit_local_control
|
||||
else "正在通过车辆安全链执行舒适性调节……"
|
||||
),
|
||||
}
|
||||
@@ -1790,6 +2116,20 @@ class CodexAssistantService:
|
||||
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(
|
||||
{
|
||||
@@ -1802,6 +2142,8 @@ class CodexAssistantService:
|
||||
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),
|
||||
@@ -1909,6 +2251,14 @@ class CodexAssistantService:
|
||||
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 "舒适性调节模拟编排已完成"
|
||||
@@ -2251,7 +2601,11 @@ class CodexAssistantService:
|
||||
result: Mapping[str, object],
|
||||
successful: bool,
|
||||
) -> str:
|
||||
if reason in {"exact_quick_control", "explicit_saved_mode_execute"}:
|
||||
if reason in {
|
||||
"exact_quick_control",
|
||||
"explicit_registered_control",
|
||||
"explicit_saved_mode_execute",
|
||||
}:
|
||||
instruction = re.sub(
|
||||
r"\s+",
|
||||
" ",
|
||||
@@ -2260,7 +2614,7 @@ class CodexAssistantService:
|
||||
or (
|
||||
"功能模式"
|
||||
if reason == "explicit_saved_mode_execute"
|
||||
else "快捷控制指令"
|
||||
else "控制指令"
|
||||
)
|
||||
),
|
||||
).strip()[:160]
|
||||
@@ -2332,12 +2686,24 @@ class CodexAssistantService:
|
||||
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=capability_context,
|
||||
application_context=application_context,
|
||||
)
|
||||
terminal: dict[str, object] | None = None
|
||||
while terminal is None:
|
||||
@@ -2642,6 +3008,17 @@ class CodexAssistantService:
|
||||
)
|
||||
|
||||
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",
|
||||
@@ -2960,8 +3337,9 @@ class CodexAssistantService:
|
||||
for raw_key, raw_value in list(value.items())[:24]:
|
||||
key = str(raw_key)[:80]
|
||||
if re.search(
|
||||
r"token|secret|password|authorization|approval|raw.?can|"
|
||||
r"can.?frame|data.?hex|payload.?hex",
|
||||
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,
|
||||
):
|
||||
@@ -2984,7 +3362,290 @@ class CodexAssistantService:
|
||||
)
|
||||
for item in list(value)[:24]
|
||||
]
|
||||
return str(value).replace(secret, "[内部令牌已隐藏]")[:240]
|
||||
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(
|
||||
|
||||
Reference in New Issue
Block a user