feat: update CAN integration and T2G seat firmware
This commit is contained in:
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -170,6 +170,63 @@ class ConversationStateStore:
|
||||
state = self._state(conversation_id)
|
||||
return deepcopy(state)
|
||||
|
||||
def synchronize_physical_passenger(
|
||||
self,
|
||||
conversation_id: str,
|
||||
physical_state: dict[str, Any],
|
||||
) -> dict[str, Any]:
|
||||
"""Merge authoritative bench feedback into passenger state only."""
|
||||
|
||||
with self._lock:
|
||||
state = self._state(conversation_id)
|
||||
passenger = state["devices"]["passenger"]
|
||||
for axis, feedback in physical_state.get("position", {}).items():
|
||||
if axis not in MOTION_INITIAL_POSITIONS or not isinstance(
|
||||
feedback, dict
|
||||
):
|
||||
continue
|
||||
raw_position = feedback.get("position_pct")
|
||||
if (
|
||||
not isinstance(raw_position, (int, float))
|
||||
or isinstance(raw_position, bool)
|
||||
):
|
||||
continue
|
||||
position = max(0, min(100, round(float(raw_position))))
|
||||
position_state = _motion_state(str(axis), position)
|
||||
position_state["motion_state"] = str(
|
||||
feedback.get("motion_state", "stopped")
|
||||
)
|
||||
position_state["fault"] = bool(feedback.get("fault", False))
|
||||
if feedback.get("position_valid") is not None:
|
||||
position_state["position_valid"] = bool(
|
||||
feedback["position_valid"]
|
||||
)
|
||||
position_state["feedback_source"] = "physical_can"
|
||||
passenger["position"][str(axis)] = position_state
|
||||
|
||||
for feature in THERMAL_FUNCTIONS:
|
||||
feedback = physical_state.get(feature)
|
||||
if not isinstance(feedback, dict):
|
||||
continue
|
||||
requested_state = str(feedback.get("state", "off"))
|
||||
state_value = "on" if requested_state == "on" else "off"
|
||||
level = (
|
||||
max(1, min(3, int(feedback.get("level", 1))))
|
||||
if state_value == "on"
|
||||
else 0
|
||||
)
|
||||
passenger[feature] = {
|
||||
"state": state_value,
|
||||
"level": level,
|
||||
"feedback_source": "physical_can",
|
||||
}
|
||||
|
||||
state["physical_state_updated_at"] = str(
|
||||
physical_state.get("captured_at") or _now()
|
||||
)
|
||||
state["updated_at"] = _now()
|
||||
return deepcopy(state)
|
||||
|
||||
def apply_successful_actions(
|
||||
self,
|
||||
conversation_id: str,
|
||||
|
||||
@@ -329,11 +329,11 @@ class ModelPlanAdapter:
|
||||
ambiguities: list[dict[str, str]],
|
||||
) -> dict[str, str] | None:
|
||||
target = request.get("target_scope")
|
||||
if target not in {"driver", "passenger"}:
|
||||
if target != "passenger":
|
||||
ambiguities.append(
|
||||
{
|
||||
"field": f"requests[{index}].target_scope",
|
||||
"question": "座椅姿态预设需要明确主驾或副驾。",
|
||||
"question": "当前座椅能力仅控制副驾,请将目标指定为 passenger。",
|
||||
}
|
||||
)
|
||||
return None
|
||||
@@ -379,11 +379,11 @@ class ModelPlanAdapter:
|
||||
vehicle_profile: VehicleProfile | None = None,
|
||||
) -> dict[str, Any] | None:
|
||||
target = request.get("target_scope")
|
||||
if target not in {"driver", "passenger"}:
|
||||
if target != "passenger":
|
||||
ambiguities.append(
|
||||
{
|
||||
"field": f"requests[{index}].target_scope",
|
||||
"question": "座椅执行适配器需要 driver 或 passenger 目标。",
|
||||
"question": "当前座椅能力仅控制副驾,请将目标指定为 passenger。",
|
||||
}
|
||||
)
|
||||
return None
|
||||
|
||||
@@ -16,12 +16,12 @@ ANGLE_CALIBRATIONS = {
|
||||
"seat.set_backrest_recline": AngleCalibration(
|
||||
axis="backrest_recline",
|
||||
minimum_deg=95,
|
||||
maximum_deg=140,
|
||||
maximum_deg=163,
|
||||
),
|
||||
"seat.set_legrest_height": AngleCalibration(
|
||||
axis="legrest_height",
|
||||
minimum_deg=95,
|
||||
maximum_deg=160,
|
||||
maximum_deg=167,
|
||||
),
|
||||
}
|
||||
AXIS_ANGLE_CALIBRATIONS = {
|
||||
|
||||
@@ -218,6 +218,62 @@ class SeatDemoService:
|
||||
"domain_runtimes": domain_runtime_info,
|
||||
}
|
||||
|
||||
def synchronize_hardware_state(
|
||||
self,
|
||||
conversation_id: str,
|
||||
vehicle_id: str | None = None,
|
||||
) -> dict[str, Any]:
|
||||
"""Read the bench and project it into the current passenger conversation."""
|
||||
|
||||
if not CONVERSATION_ID_PATTERN.fullmatch(conversation_id):
|
||||
raise ValueError(
|
||||
"conversation_id must start with conv- and contain only lowercase letters, numbers, or hyphens"
|
||||
)
|
||||
if not self.hardware_settings.configured:
|
||||
raise HardwareSafetyError("the T2G seat hardware runtime is not configured")
|
||||
vehicle_profile = self.vehicle_profiles.get(vehicle_id)
|
||||
physical_state = self._new_hardware_executor().read_physical_state()
|
||||
context = self.conversations.synchronize_physical_passenger(
|
||||
conversation_id,
|
||||
physical_state,
|
||||
)
|
||||
context = self._context_for_vehicle(context, vehicle_profile)
|
||||
return {
|
||||
"status": "succeeded",
|
||||
"conversation_id": conversation_id,
|
||||
"conversation_turn": context["turn_count"],
|
||||
"source": "physical_can",
|
||||
"captured_at": physical_state.get("captured_at"),
|
||||
"complete": bool(physical_state.get("complete")),
|
||||
"missing": list(physical_state.get("missing", [])),
|
||||
"physical_state": physical_state,
|
||||
"actuator_state": context["devices"],
|
||||
}
|
||||
|
||||
def _new_hardware_executor(self) -> HardwareSeatExecutor:
|
||||
return HardwareSeatExecutor(
|
||||
self.hardware_matrix,
|
||||
self.hardware_settings.bus_factory(),
|
||||
thermal_timeout_ms=self.hardware_settings.thermal_timeout_ms,
|
||||
ack_timeout_ms=self.hardware_settings.ack_timeout_ms,
|
||||
axis_maximum_x100={
|
||||
capability: int(
|
||||
self.hardware_compiler.protocol["normalized_position_max"]
|
||||
)
|
||||
for capability in self.hardware_compiler.axes
|
||||
},
|
||||
motor_max_hall={
|
||||
int(axis["motor_selector"]): int(axis["default_max_hall_count"])
|
||||
for axis in self.hardware_compiler.axes.values()
|
||||
},
|
||||
stop_all_motor_selector=int(
|
||||
self.hardware_compiler.protocol["stop_all_motor_selector"]
|
||||
),
|
||||
auto_home=self.hardware_settings.auto_home_enabled,
|
||||
home_timeout_s=self.hardware_settings.home_timeout_s,
|
||||
firmware_guard=self.hardware_settings.firmware_guard(),
|
||||
)
|
||||
|
||||
def process(
|
||||
self,
|
||||
utterance: str,
|
||||
@@ -412,9 +468,10 @@ class SeatDemoService:
|
||||
if parsed.get("domain_actions") or any(
|
||||
domain != "seat" for domain in detected_domains
|
||||
):
|
||||
if hardware_request:
|
||||
if hardware_request and not parsed.get("actions"):
|
||||
raise HardwareSafetyError(
|
||||
"hardware mode only accepts seat-only requests; cross-domain execution stays dry-run"
|
||||
"hardware mode requires at least one passenger seat operation; "
|
||||
"non-seat-only requests stay dry-run"
|
||||
)
|
||||
return self._process_orchestrated(
|
||||
utterance,
|
||||
@@ -425,6 +482,8 @@ class SeatDemoService:
|
||||
parsed,
|
||||
result,
|
||||
traces,
|
||||
hardware_request=hardware_request,
|
||||
vehicle_profile=vehicle_profile,
|
||||
)
|
||||
|
||||
intent_ir = build_intent_ir(parsed, utterance, request_id, provider_used)
|
||||
@@ -697,8 +756,12 @@ class SeatDemoService:
|
||||
"safety_check",
|
||||
"succeeded",
|
||||
(
|
||||
"硬件启用、授权令牌、固件 profile 与目标座椅配置门禁已通过;"
|
||||
"板端 Position_Valid 将在发送绝对移动命令前实时检查。"
|
||||
(
|
||||
"硬件启用、授权令牌、固件 profile 与目标座椅配置门禁已通过;"
|
||||
if self.hardware_settings.approval_required
|
||||
else "硬件启用、调试免令牌、固件 profile 与目标座椅配置门禁已通过;"
|
||||
)
|
||||
+ "板端 Position_Valid 将在发送绝对移动命令前实时检查。"
|
||||
if hardware_request
|
||||
else "平台、矩阵和策略均限制为 dry-run;硬件发送路径未启用。"
|
||||
),
|
||||
@@ -721,17 +784,7 @@ class SeatDemoService:
|
||||
|
||||
execute_started = time.perf_counter()
|
||||
if hardware_request:
|
||||
hardware_executor = HardwareSeatExecutor(
|
||||
self.hardware_matrix,
|
||||
self.hardware_settings.bus_factory(),
|
||||
thermal_timeout_ms=self.hardware_settings.thermal_timeout_ms,
|
||||
ack_timeout_ms=self.hardware_settings.ack_timeout_ms,
|
||||
axis_maximum_x100={
|
||||
capability: int(axis["maximum_value_x100"])
|
||||
for capability, axis in self.hardware_compiler.axes.items()
|
||||
},
|
||||
firmware_guard=self.hardware_settings.firmware_guard(),
|
||||
)
|
||||
hardware_executor = self._new_hardware_executor()
|
||||
execution_report = hardware_executor.execute(
|
||||
command_plan, function_config, context["devices"]
|
||||
)
|
||||
@@ -821,6 +874,14 @@ class SeatDemoService:
|
||||
state_actions,
|
||||
parsed["decision_summary"],
|
||||
)
|
||||
physical_state = execution_report.get("spec", {}).get(
|
||||
"physical_state"
|
||||
)
|
||||
if hardware_request and isinstance(physical_state, dict):
|
||||
updated_context = self.conversations.synchronize_physical_passenger(
|
||||
conversation_id,
|
||||
physical_state,
|
||||
)
|
||||
updated_context = self._context_for_vehicle(
|
||||
updated_context,
|
||||
vehicle_profile,
|
||||
@@ -837,6 +898,34 @@ class SeatDemoService:
|
||||
time.perf_counter(),
|
||||
{"state_after": updated_context["devices"]},
|
||||
)
|
||||
elif hardware_request:
|
||||
physical_state = execution_report.get("spec", {}).get(
|
||||
"physical_state"
|
||||
)
|
||||
if isinstance(physical_state, dict) and (
|
||||
physical_state.get("position")
|
||||
or physical_state.get("ventilation")
|
||||
or physical_state.get("heating")
|
||||
):
|
||||
updated_context = self.conversations.synchronize_physical_passenger(
|
||||
conversation_id,
|
||||
physical_state,
|
||||
)
|
||||
updated_context = self._context_for_vehicle(
|
||||
updated_context,
|
||||
vehicle_profile,
|
||||
)
|
||||
result["actuator_state"] = updated_context["devices"]
|
||||
result["domain_state"] = updated_context["domain_devices"]
|
||||
self._trace(
|
||||
traces,
|
||||
request_id,
|
||||
"state_sync",
|
||||
"succeeded",
|
||||
"真实执行未完成,但已按 CAN 物理回读同步副驾状态。",
|
||||
time.perf_counter(),
|
||||
{"state_after": updated_context["devices"]},
|
||||
)
|
||||
telemetry = next(
|
||||
(
|
||||
item.get("telemetry", {})
|
||||
@@ -944,34 +1033,60 @@ class SeatDemoService:
|
||||
raw = telemetry.get("feedback_value")
|
||||
if raw is not None:
|
||||
raw_value = int(raw)
|
||||
action["state"] = "off" if raw_value == 1 else "on"
|
||||
action["state"] = "off" if raw_value <= 1 else "on"
|
||||
action["requested_level"] = max(0, raw_value - 1)
|
||||
synchronized = list(updated)
|
||||
existing = {
|
||||
(
|
||||
item.get("target"),
|
||||
item.get("action_type"),
|
||||
item.get("function"),
|
||||
item.get("axis"),
|
||||
)
|
||||
for item in synchronized
|
||||
}
|
||||
|
||||
physical_state = execution_report.get("spec", {}).get("physical_state")
|
||||
if isinstance(physical_state, dict):
|
||||
positions = physical_state.get("position", {})
|
||||
by_axis = {
|
||||
action.get("axis"): action
|
||||
for action in updated
|
||||
if action.get("action_type") == "position"
|
||||
}
|
||||
for axis, feedback in positions.items():
|
||||
if not isinstance(feedback, dict) or feedback.get("position_pct") is None:
|
||||
continue
|
||||
action = by_axis.get(axis)
|
||||
if action is None:
|
||||
action = {
|
||||
"domain": "seat",
|
||||
"target": "passenger",
|
||||
"action_type": "position",
|
||||
"axis": axis,
|
||||
}
|
||||
updated.append(action)
|
||||
by_axis[axis] = action
|
||||
action["position_pct"] = int(feedback["position_pct"])
|
||||
if feedback.get("position_valid") is not None:
|
||||
action["position_valid"] = bool(feedback["position_valid"])
|
||||
action["feedback_basis"] = "physical_can_snapshot"
|
||||
|
||||
by_function = {
|
||||
action.get("function"): action
|
||||
for action in updated
|
||||
if action.get("function") in {"ventilation", "heating"}
|
||||
}
|
||||
for feature in ("ventilation", "heating"):
|
||||
feedback = physical_state.get(feature)
|
||||
if not isinstance(feedback, dict):
|
||||
continue
|
||||
action = by_function.get(feature)
|
||||
if action is None:
|
||||
action = {
|
||||
"domain": "seat",
|
||||
"target": "passenger",
|
||||
"function": feature,
|
||||
}
|
||||
updated.append(action)
|
||||
by_function[feature] = action
|
||||
action["state"] = (
|
||||
"on" if feedback.get("state") == "on" else "off"
|
||||
)
|
||||
action["requested_level"] = int(feedback.get("level", 0))
|
||||
for action in updated:
|
||||
counterpart = "passenger" if action.get("target") == "driver" else "driver"
|
||||
signature = (
|
||||
counterpart,
|
||||
action.get("action_type"),
|
||||
action.get("function"),
|
||||
action.get("axis"),
|
||||
)
|
||||
if signature in existing:
|
||||
continue
|
||||
mirrored = deepcopy(action)
|
||||
mirrored["target"] = counterpart
|
||||
mirrored["physical_target_alias"] = "t2g_primary_seat"
|
||||
synchronized.append(mirrored)
|
||||
existing.add(signature)
|
||||
return synchronized
|
||||
action["physical_target_alias"] = "t2g_primary_seat"
|
||||
return updated
|
||||
|
||||
@staticmethod
|
||||
def _parse_mode_profile_save_name(utterance: str) -> str | None:
|
||||
@@ -1048,6 +1163,13 @@ class SeatDemoService:
|
||||
context,
|
||||
vehicle_profile,
|
||||
)
|
||||
parsed_has_explicit_request = bool(
|
||||
parsed.get("actions")
|
||||
or parsed.get("domain_actions")
|
||||
or parsed.get("preset_actions")
|
||||
or parsed.get("preset_events")
|
||||
or parsed.get("model_requests")
|
||||
)
|
||||
merged = deepcopy(parsed)
|
||||
merged["actions"] = self._merge_control_actions(
|
||||
mode_parsed.get("actions", []),
|
||||
@@ -1064,9 +1186,15 @@ class SeatDemoService:
|
||||
"ambiguities",
|
||||
"model_requests",
|
||||
):
|
||||
parsed_values = parsed.get(key, [])
|
||||
if key == "ambiguities" and not parsed_has_explicit_request:
|
||||
# A local parser does not know arbitrary named modes and emits
|
||||
# a generic unsupported-capability ambiguity. A unique,
|
||||
# repository-backed mode match is authoritative for that case.
|
||||
parsed_values = []
|
||||
merged[key] = self._merge_unique(
|
||||
mode_parsed.get(key, []),
|
||||
parsed.get(key, []),
|
||||
parsed_values,
|
||||
)
|
||||
merged["decision_summary"] = [
|
||||
*mode_parsed.get("decision_summary", []),
|
||||
@@ -1839,8 +1967,12 @@ class SeatDemoService:
|
||||
parsed: dict[str, Any],
|
||||
result: dict[str, Any],
|
||||
traces: list[dict[str, Any]],
|
||||
*,
|
||||
hardware_request: bool = False,
|
||||
vehicle_profile: VehicleProfile | None = None,
|
||||
) -> dict[str, Any]:
|
||||
"""Run one or more isolated domain adapters and aggregate their reports."""
|
||||
"""Run isolated adapters, with only the passenger seat eligible for HIL."""
|
||||
vehicle_profile = vehicle_profile or self.vehicle_profiles.default()
|
||||
generation_started = time.perf_counter()
|
||||
intent_ir = build_intent_ir(parsed, utterance, request_id, provider_used)
|
||||
for action in parsed.get("domain_actions", []):
|
||||
@@ -1942,6 +2074,8 @@ class SeatDemoService:
|
||||
"message": "模型没有生成任何可执行领域操作。",
|
||||
}
|
||||
)
|
||||
if hardware_request:
|
||||
self._adapt_validation_for_hardware(validation)
|
||||
result["validation"] = validation
|
||||
errors = [issue for issue in validation if issue["severity"] == "error"]
|
||||
self._trace(
|
||||
@@ -1971,7 +2105,14 @@ class SeatDemoService:
|
||||
plans: dict[str, dict[str, Any]] = {}
|
||||
for domain, config in configs.items():
|
||||
if domain == "seat":
|
||||
plans[domain] = self.compiler.compile(config)
|
||||
if hardware_request:
|
||||
self.hardware_settings.validate_function_config(config)
|
||||
plans[domain] = self.hardware_compiler.compile(
|
||||
config,
|
||||
context["devices"],
|
||||
)
|
||||
else:
|
||||
plans[domain] = self.compiler.compile(config)
|
||||
else:
|
||||
runtime = self.domain_runtimes[domain]
|
||||
plans[domain] = runtime.compile(config)
|
||||
@@ -2021,22 +2162,38 @@ class SeatDemoService:
|
||||
request_id,
|
||||
"safety_check",
|
||||
"succeeded",
|
||||
"所有领域均为 dry-run;矩阵缺少实车完整性信息,硬件发送保持禁用。",
|
||||
(
|
||||
"副驾座椅已通过 T2G HIL 安全门,其他座舱领域保持虚拟 CAN。"
|
||||
if hardware_request
|
||||
else "所有领域均为 dry-run;矩阵缺少实车完整性信息,硬件发送保持禁用。"
|
||||
),
|
||||
time.perf_counter(),
|
||||
{
|
||||
"hardware_execution_allowed": False,
|
||||
"hardware_execution_allowed": hardware_request,
|
||||
"hardware_domains": ["seat"] if hardware_request else [],
|
||||
"virtual_domains": [
|
||||
domain
|
||||
for domain in configs
|
||||
if not hardware_request or domain != "seat"
|
||||
],
|
||||
"domains": list(configs),
|
||||
},
|
||||
domain="orchestration",
|
||||
)
|
||||
|
||||
execute_started = time.perf_counter()
|
||||
execution_started_at = _now()
|
||||
reports: dict[str, dict[str, Any]] = {}
|
||||
for domain, config in configs.items():
|
||||
if domain == "seat":
|
||||
reports[domain] = self.executor.execute(
|
||||
plans[domain], config, context["devices"]
|
||||
)
|
||||
if hardware_request:
|
||||
reports[domain] = self._new_hardware_executor().execute(
|
||||
plans[domain], config, context["devices"]
|
||||
)
|
||||
else:
|
||||
reports[domain] = self.executor.execute(
|
||||
plans[domain], config, context["devices"]
|
||||
)
|
||||
else:
|
||||
reports[domain] = self.domain_runtimes[domain].execute(
|
||||
plans[domain], config
|
||||
@@ -2046,28 +2203,47 @@ class SeatDemoService:
|
||||
for domain_report in reports.values()
|
||||
for item in domain_report["spec"]["results"]
|
||||
]
|
||||
execution_succeeded = all(
|
||||
domain_report["spec"]["status"] == "succeeded"
|
||||
for domain_report in reports.values()
|
||||
) and all(item.get("status") == "succeeded" for item in report_results)
|
||||
physical_state = (
|
||||
reports.get("seat", {}).get("spec", {}).get("physical_state")
|
||||
if hardware_request
|
||||
else None
|
||||
)
|
||||
report_spec: dict[str, Any] = {
|
||||
"mode": "hil" if hardware_request else "dry_run",
|
||||
"status": "succeeded" if execution_succeeded else "failed",
|
||||
"started_at": execution_started_at,
|
||||
"finished_at": _now(),
|
||||
"results": report_results,
|
||||
}
|
||||
if isinstance(physical_state, dict):
|
||||
report_spec["physical_state"] = physical_state
|
||||
execution_report = {
|
||||
"apiVersion": "cognitive-agent/v1alpha1",
|
||||
"kind": "ExecutionReport",
|
||||
"metadata": {
|
||||
"id": f"report-{request_id}",
|
||||
"name": "Cabin orchestration virtual execution report",
|
||||
"name": (
|
||||
"Cabin orchestration mixed HIL execution report"
|
||||
if hardware_request
|
||||
else "Cabin orchestration virtual execution report"
|
||||
),
|
||||
"version": "1.0.0",
|
||||
"request_id": request_id,
|
||||
"labels": {
|
||||
"simulation": "true",
|
||||
"simulation": "mixed" if hardware_request else "true",
|
||||
"seat_feedback": (
|
||||
"physical_can" if hardware_request else "simulated"
|
||||
),
|
||||
"orchestration": "cross_domain"
|
||||
if len(configs) > 1
|
||||
else "single_domain",
|
||||
},
|
||||
},
|
||||
"spec": {
|
||||
"mode": "dry_run",
|
||||
"status": "succeeded",
|
||||
"started_at": _now(),
|
||||
"finished_at": _now(),
|
||||
"results": report_results,
|
||||
},
|
||||
"spec": report_spec,
|
||||
}
|
||||
report_issues = self.contracts.validate(execution_report)
|
||||
validation.extend(report_issues)
|
||||
@@ -2075,36 +2251,67 @@ class SeatDemoService:
|
||||
result["validation"] = validation
|
||||
if report_issues:
|
||||
result["status"] = "execution_report_invalid"
|
||||
elif not execution_succeeded:
|
||||
result["status"] = "execution_failed"
|
||||
elif parsed.get("deferred_requests"):
|
||||
result["status"] = "partially_succeeded"
|
||||
else:
|
||||
result["status"] = "succeeded"
|
||||
failed_execution = next(
|
||||
(item for item in report_results if item.get("status") == "failed"),
|
||||
None,
|
||||
)
|
||||
self._trace(
|
||||
traces,
|
||||
request_id,
|
||||
"execute",
|
||||
"failed" if report_issues else "succeeded",
|
||||
f"已虚拟执行 {len(report_results)} 个领域操作并汇总反馈。",
|
||||
"succeeded" if execution_succeeded and not report_issues else "failed",
|
||||
(
|
||||
"副驾座椅已按物理 CAN 回读闭环执行,其他领域已完成虚拟 CAN 执行。"
|
||||
if hardware_request and execution_succeeded
|
||||
else (
|
||||
f"副驾座椅真实执行失败:{failed_execution['message']}"
|
||||
if hardware_request and failed_execution
|
||||
else f"已虚拟执行 {len(report_results)} 个领域操作并汇总反馈。"
|
||||
)
|
||||
),
|
||||
execute_started,
|
||||
{
|
||||
"mode": "dry_run",
|
||||
"mode": execution_report["spec"]["mode"],
|
||||
"simulation": execution_report["metadata"]["labels"]["simulation"],
|
||||
"domains": list(configs),
|
||||
"result_count": len(report_results),
|
||||
"failed_domain": (
|
||||
failed_execution.get("domain") if failed_execution else None
|
||||
),
|
||||
},
|
||||
domain="orchestration",
|
||||
)
|
||||
|
||||
if not report_issues:
|
||||
all_actions = [
|
||||
*parsed.get("actions", []),
|
||||
*parsed.get("domain_actions", []),
|
||||
]
|
||||
if execution_succeeded and not report_issues:
|
||||
seat_actions = parsed.get("actions", [])
|
||||
if hardware_request:
|
||||
seat_actions = self._hardware_feedback_actions(
|
||||
seat_actions,
|
||||
configs["seat"],
|
||||
reports["seat"],
|
||||
)
|
||||
all_actions = [*seat_actions, *parsed.get("domain_actions", [])]
|
||||
updated_context = self.conversations.apply_successful_actions(
|
||||
conversation_id,
|
||||
utterance,
|
||||
all_actions,
|
||||
parsed["decision_summary"],
|
||||
)
|
||||
if hardware_request and isinstance(physical_state, dict):
|
||||
updated_context = self.conversations.synchronize_physical_passenger(
|
||||
conversation_id,
|
||||
physical_state,
|
||||
)
|
||||
updated_context = self._context_for_vehicle(
|
||||
updated_context,
|
||||
vehicle_profile,
|
||||
)
|
||||
result["conversation_turn"] = updated_context["turn_count"]
|
||||
result["actuator_state"] = updated_context["devices"]
|
||||
result["domain_state"] = updated_context["domain_devices"]
|
||||
@@ -2121,6 +2328,31 @@ class SeatDemoService:
|
||||
},
|
||||
domain="orchestration",
|
||||
)
|
||||
elif hardware_request and isinstance(physical_state, dict) and (
|
||||
physical_state.get("position")
|
||||
or physical_state.get("ventilation")
|
||||
or physical_state.get("heating")
|
||||
):
|
||||
updated_context = self.conversations.synchronize_physical_passenger(
|
||||
conversation_id,
|
||||
physical_state,
|
||||
)
|
||||
updated_context = self._context_for_vehicle(
|
||||
updated_context,
|
||||
vehicle_profile,
|
||||
)
|
||||
result["actuator_state"] = updated_context["devices"]
|
||||
result["domain_state"] = updated_context["domain_devices"]
|
||||
self._trace(
|
||||
traces,
|
||||
request_id,
|
||||
"state_sync",
|
||||
"succeeded",
|
||||
"跨域真实执行未完成,但已按 CAN 回读同步副驾物理状态。",
|
||||
time.perf_counter(),
|
||||
{"seat_state": updated_context["devices"].get("passenger", {})},
|
||||
domain="orchestration",
|
||||
)
|
||||
self._persist(result)
|
||||
return result
|
||||
|
||||
@@ -2186,12 +2418,19 @@ class SeatDemoService:
|
||||
if provider == "openai":
|
||||
if not self.openai.available:
|
||||
raise ValueError("GPT 模式需要设置 OPENAI_API_KEY")
|
||||
plan = self.openai.interpret(utterance, context, self.capability_context)
|
||||
return (
|
||||
self.model_plan_adapter.adapt(plan, context, vehicle_profile),
|
||||
f"openai:{self.openai.model}",
|
||||
None,
|
||||
)
|
||||
try:
|
||||
plan = self.openai.interpret(utterance, context, self.capability_context)
|
||||
return (
|
||||
self.model_plan_adapter.adapt(plan, context, vehicle_profile),
|
||||
f"openai:{self.openai.model}",
|
||||
None,
|
||||
)
|
||||
except OpenAIIntentError:
|
||||
return (
|
||||
parse_local_intent(utterance, context),
|
||||
"local_rules_fallback",
|
||||
"GPT 调用失败,已降级到本地规则解析;CAN 编译和执行路径不变。",
|
||||
)
|
||||
if self.openai.available:
|
||||
try:
|
||||
plan = self.openai.interpret(utterance, context, self.capability_context)
|
||||
|
||||
Reference in New Issue
Block a user