feat: harden seat intent and expand HIL coverage
This commit is contained in:
@@ -3,6 +3,7 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import time
|
||||
@@ -22,9 +23,11 @@ if str(SEAT_SOURCE) not in sys.path:
|
||||
|
||||
from seat_domain import ( # noqa: E402
|
||||
HardwareSeatExecutor,
|
||||
SIMULATED_HARDWARE_FAULTS,
|
||||
SeatCanMatrix,
|
||||
SeatCompiler,
|
||||
T2GSeatCompiler,
|
||||
SimulatedHardwareSeatExecutor,
|
||||
VirtualSeatExecutor,
|
||||
build_function_config,
|
||||
build_function_preset,
|
||||
@@ -120,6 +123,10 @@ class SeatDemoService:
|
||||
)
|
||||
self.compiler = SeatCompiler(self.domain_root / "can" / "signal-mapping.yaml")
|
||||
self.executor = VirtualSeatExecutor(self.matrix)
|
||||
self.simulated_hardware_executor = SimulatedHardwareSeatExecutor(self.matrix)
|
||||
self.simulated_hardware_enabled = os.getenv(
|
||||
"COGNITIVE_AGENT_SIMULATED_HARDWARE_ENABLED", "false"
|
||||
).strip().lower() in {"1", "true", "yes", "on"}
|
||||
self.hardware_matrix = SeatCanMatrix(
|
||||
[
|
||||
self.domain_root / "can" / "seat-thermal-hardware-matrix.yaml",
|
||||
@@ -131,6 +138,10 @@ class SeatDemoService:
|
||||
repository_root / "platforms" / "t2g_seat_bench" / "calibration" / "seat.yaml",
|
||||
)
|
||||
self.hardware_settings = SeatHardwareSettings.from_env(repository_root)
|
||||
# Shared by the short-lived hardware executor instances. A process must
|
||||
# establish a fresh physical four-axis reference before trusting stored
|
||||
# Hall counts, and a firmware replacement invalidates it again.
|
||||
self._seat_position_memory_state = {"synchronized": False}
|
||||
self.openai = OpenAIIntentInterpreter()
|
||||
self.domain_runtimes = {
|
||||
domain: ConfiguredDomainRuntime(repository_root, domain)
|
||||
@@ -171,11 +182,21 @@ class SeatDemoService:
|
||||
"domain": "cabin",
|
||||
"platform": "reference_vehicle",
|
||||
"execution_mode": "dry_run",
|
||||
"execution_modes": ["dry_run", "hardware"],
|
||||
"execution_modes": ["dry_run", "simulated_hardware", "hardware"],
|
||||
"hardware_execution_allowed": self.hardware_settings.configured,
|
||||
"hardware": self.hardware_settings.public_info(),
|
||||
"seat_position_memory_synchronized": bool(
|
||||
self._seat_position_memory_state.get("synchronized", False)
|
||||
),
|
||||
"simulated_hardware": {
|
||||
"enabled": self.simulated_hardware_enabled,
|
||||
"default_fault": "normal",
|
||||
"fault_scenarios": list(SIMULATED_HARDWARE_FAULTS),
|
||||
"physical_can_opened": False,
|
||||
},
|
||||
"openai_available": self.openai.available,
|
||||
"openai_model": self.openai.model,
|
||||
"openai_responses_endpoint": self.openai.responses_endpoint,
|
||||
"conversation_memory": True,
|
||||
"default_vehicle_id": default_vehicle.id,
|
||||
"vehicle": default_vehicle.public_dict(),
|
||||
@@ -272,6 +293,10 @@ class SeatDemoService:
|
||||
auto_home=self.hardware_settings.auto_home_enabled,
|
||||
home_timeout_s=self.hardware_settings.home_timeout_s,
|
||||
firmware_guard=self.hardware_settings.firmware_guard(),
|
||||
synchronize_position_before_first_motion=(
|
||||
self.hardware_settings.position_sync_enabled
|
||||
),
|
||||
position_sync_state=self._seat_position_memory_state,
|
||||
)
|
||||
|
||||
def process(
|
||||
@@ -282,6 +307,7 @@ class SeatDemoService:
|
||||
execution_mode: str = "dry_run",
|
||||
approval_token: str | None = None,
|
||||
vehicle_id: str | None = None,
|
||||
simulation_fault: str = "normal",
|
||||
) -> dict[str, Any]:
|
||||
utterance = utterance.strip()
|
||||
if not utterance:
|
||||
@@ -290,8 +316,21 @@ class SeatDemoService:
|
||||
raise ValueError("utterance must be at most 1000 characters")
|
||||
if provider not in {"auto", "openai", "local"}:
|
||||
raise ValueError("provider must be auto, openai, or local")
|
||||
if execution_mode not in {"dry_run", "hardware"}:
|
||||
raise ValueError("execution_mode must be dry_run or hardware")
|
||||
if execution_mode not in {"dry_run", "simulated_hardware", "hardware"}:
|
||||
raise ValueError(
|
||||
"execution_mode must be dry_run, simulated_hardware, or hardware"
|
||||
)
|
||||
simulated_hardware_request = execution_mode == "simulated_hardware"
|
||||
if simulated_hardware_request and not self.simulated_hardware_enabled:
|
||||
raise HardwareSafetyError(
|
||||
"simulated hardware is disabled; set "
|
||||
"COGNITIVE_AGENT_SIMULATED_HARDWARE_ENABLED=true"
|
||||
)
|
||||
if simulation_fault not in SIMULATED_HARDWARE_FAULTS:
|
||||
raise ValueError(
|
||||
"simulation_fault must be one of "
|
||||
+ ", ".join(SIMULATED_HARDWARE_FAULTS)
|
||||
)
|
||||
mode_save_name = self._parse_mode_profile_save_name(utterance)
|
||||
mode_restore_name = self._parse_mode_profile_restore_name(utterance)
|
||||
mode_update = self._parse_mode_profile_update(utterance)
|
||||
@@ -329,6 +368,8 @@ class SeatDemoService:
|
||||
"hardware_execution_allowed": (
|
||||
hardware_request and not control_plane_request
|
||||
),
|
||||
"simulated_hardware": simulated_hardware_request,
|
||||
"simulation_fault": simulation_fault if simulated_hardware_request else None,
|
||||
"vehicle": vehicle_profile.public_dict(),
|
||||
"matched_modes": [
|
||||
profile.public_dict() for profile in matched_modes
|
||||
@@ -483,6 +524,8 @@ class SeatDemoService:
|
||||
result,
|
||||
traces,
|
||||
hardware_request=hardware_request,
|
||||
simulated_hardware_request=simulated_hardware_request,
|
||||
simulation_fault=simulation_fault,
|
||||
vehicle_profile=vehicle_profile,
|
||||
)
|
||||
|
||||
@@ -763,11 +806,19 @@ class SeatDemoService:
|
||||
)
|
||||
+ "板端 Position_Valid 将在发送绝对移动命令前实时检查。"
|
||||
if hardware_request
|
||||
else "平台、矩阵和策略均限制为 dry-run;硬件发送路径未启用。"
|
||||
else (
|
||||
"模拟台架隔离门禁已通过;仅生成故障注入反馈,物理 CAN、ZLG 与 IAR 路径均未打开。"
|
||||
if simulated_hardware_request
|
||||
else "平台、矩阵和策略均限制为 dry-run;硬件发送路径未启用。"
|
||||
)
|
||||
),
|
||||
safety_started,
|
||||
{
|
||||
"hardware_execution_allowed": hardware_request,
|
||||
"simulated_hardware": simulated_hardware_request,
|
||||
"simulation_fault": (
|
||||
simulation_fault if simulated_hardware_request else None
|
||||
),
|
||||
"position_calibration_gate": (
|
||||
"runtime_can_preflight" if hardware_request else "not_applicable"
|
||||
),
|
||||
@@ -788,6 +839,13 @@ class SeatDemoService:
|
||||
execution_report = hardware_executor.execute(
|
||||
command_plan, function_config, context["devices"]
|
||||
)
|
||||
elif simulated_hardware_request:
|
||||
execution_report = self.simulated_hardware_executor.execute(
|
||||
command_plan,
|
||||
function_config,
|
||||
context["devices"],
|
||||
fault_scenario=simulation_fault,
|
||||
)
|
||||
else:
|
||||
execution_report = self.executor.execute(
|
||||
command_plan, function_config, context["devices"]
|
||||
@@ -845,7 +903,15 @@ class SeatDemoService:
|
||||
else hardware_failure_summary
|
||||
)
|
||||
if hardware_request
|
||||
else "虚拟 CAN 已编码命令帧并生成模拟回读。"
|
||||
else (
|
||||
"模拟台架已生成隔离的 HIL 反馈,未访问物理 CAN。"
|
||||
if simulated_hardware_request and execution_succeeded
|
||||
else (
|
||||
f"模拟台架故障已触发:{failed_execution['message']}"
|
||||
if simulated_hardware_request and failed_execution
|
||||
else "虚拟 CAN 已编码命令帧并生成模拟回读。"
|
||||
)
|
||||
)
|
||||
)
|
||||
),
|
||||
execute_started,
|
||||
@@ -861,6 +927,14 @@ class SeatDemoService:
|
||||
},
|
||||
)
|
||||
if execution_succeeded:
|
||||
execution_notices = [
|
||||
str(item.get("telemetry", {}).get("user_notice"))
|
||||
for item in execution_results
|
||||
if item.get("telemetry", {}).get("user_notice")
|
||||
]
|
||||
for notice in execution_notices:
|
||||
if notice not in parsed["decision_summary"]:
|
||||
parsed["decision_summary"].append(notice)
|
||||
state_actions = (
|
||||
self._hardware_feedback_actions(
|
||||
parsed["actions"], function_config, execution_report
|
||||
@@ -950,7 +1024,15 @@ class SeatDemoService:
|
||||
else "未获得满足目标的真实硬件反馈。"
|
||||
)
|
||||
if hardware_request
|
||||
else "虚构反馈帧中的最终状态与请求目标一致。"
|
||||
else (
|
||||
"模拟台架反馈与请求目标一致,物理 CAN 始终关闭。"
|
||||
if simulated_hardware_request and execution_succeeded
|
||||
else (
|
||||
f"模拟台架故障反馈:{failed_execution['message']}"
|
||||
if simulated_hardware_request and failed_execution
|
||||
else "虚构反馈帧中的最终状态与请求目标一致。"
|
||||
)
|
||||
)
|
||||
)
|
||||
),
|
||||
time.perf_counter(),
|
||||
@@ -958,6 +1040,9 @@ class SeatDemoService:
|
||||
"command_frame": telemetry.get("command_frame", {}).get("data_hex"),
|
||||
"feedback_frame": telemetry.get("feedback_frame", {}).get("data_hex"),
|
||||
"simulated": not hardware_request,
|
||||
"simulation_fault": (
|
||||
simulation_fault if simulated_hardware_request else None
|
||||
),
|
||||
},
|
||||
)
|
||||
self._persist(result)
|
||||
@@ -1026,9 +1111,16 @@ class SeatDemoService:
|
||||
action["position_valid"] = bool(
|
||||
telemetry["position_valid"]
|
||||
)
|
||||
if telemetry.get("motion_mode") == "relative_timed_duty":
|
||||
if telemetry.get("motion_mode") in {
|
||||
"relative_timed_duty",
|
||||
"timed_user_request",
|
||||
}:
|
||||
action["position_estimated"] = True
|
||||
action["feedback_basis"] = "relative_hall_confirmed_open_loop"
|
||||
action["feedback_basis"] = (
|
||||
"timed_endpoint_or_hall_confirmed_open_loop"
|
||||
if telemetry.get("motion_mode") == "timed_user_request"
|
||||
else "relative_hall_confirmed_open_loop"
|
||||
)
|
||||
continue
|
||||
raw = telemetry.get("feedback_value")
|
||||
if raw is not None:
|
||||
@@ -1141,6 +1233,35 @@ class SeatDemoService:
|
||||
return parsed
|
||||
if self._contains_seat_preset_request(parsed):
|
||||
return parsed
|
||||
if len(matched_modes) > 1:
|
||||
rejected = deepcopy(parsed)
|
||||
for key in (
|
||||
"actions",
|
||||
"domain_actions",
|
||||
"preset_actions",
|
||||
"preset_events",
|
||||
"deferred_requests",
|
||||
"model_requests",
|
||||
):
|
||||
rejected[key] = []
|
||||
rejected["ambiguities"] = [
|
||||
{
|
||||
"field": "mode_profile",
|
||||
"question": "一条请求只能执行一个座椅功能模式,请确认要执行的模式。",
|
||||
}
|
||||
]
|
||||
rejected["decision_summary"] = [
|
||||
"同一条请求命中多个功能模式,已停止生成控制指令以避免模式冲突。"
|
||||
]
|
||||
return rejected
|
||||
|
||||
safety_ambiguities = [
|
||||
item
|
||||
for item in parsed.get("ambiguities", [])
|
||||
if item.get("field") != "capability"
|
||||
]
|
||||
if safety_ambiguities:
|
||||
return parsed
|
||||
|
||||
mode_requests = self.mode_profiles.requests_for_profiles(matched_modes)
|
||||
detected_domains = []
|
||||
@@ -1969,6 +2090,8 @@ class SeatDemoService:
|
||||
traces: list[dict[str, Any]],
|
||||
*,
|
||||
hardware_request: bool = False,
|
||||
simulated_hardware_request: bool = False,
|
||||
simulation_fault: str = "normal",
|
||||
vehicle_profile: VehicleProfile | None = None,
|
||||
) -> dict[str, Any]:
|
||||
"""Run isolated adapters, with only the passenger seat eligible for HIL."""
|
||||
@@ -2165,11 +2288,19 @@ class SeatDemoService:
|
||||
(
|
||||
"副驾座椅已通过 T2G HIL 安全门,其他座舱领域保持虚拟 CAN。"
|
||||
if hardware_request
|
||||
else "所有领域均为 dry-run;矩阵缺少实车完整性信息,硬件发送保持禁用。"
|
||||
else (
|
||||
"模拟台架仅对座椅结果注入故障;全部领域的物理发送路径保持关闭。"
|
||||
if simulated_hardware_request
|
||||
else "所有领域均为 dry-run;矩阵缺少实车完整性信息,硬件发送保持禁用。"
|
||||
)
|
||||
),
|
||||
time.perf_counter(),
|
||||
{
|
||||
"hardware_execution_allowed": hardware_request,
|
||||
"simulated_hardware": simulated_hardware_request,
|
||||
"simulation_fault": (
|
||||
simulation_fault if simulated_hardware_request else None
|
||||
),
|
||||
"hardware_domains": ["seat"] if hardware_request else [],
|
||||
"virtual_domains": [
|
||||
domain
|
||||
@@ -2190,6 +2321,13 @@ class SeatDemoService:
|
||||
reports[domain] = self._new_hardware_executor().execute(
|
||||
plans[domain], config, context["devices"]
|
||||
)
|
||||
elif simulated_hardware_request:
|
||||
reports[domain] = self.simulated_hardware_executor.execute(
|
||||
plans[domain],
|
||||
config,
|
||||
context["devices"],
|
||||
fault_scenario=simulation_fault,
|
||||
)
|
||||
else:
|
||||
reports[domain] = self.executor.execute(
|
||||
plans[domain], config, context["devices"]
|
||||
@@ -2212,8 +2350,9 @@ class SeatDemoService:
|
||||
if hardware_request
|
||||
else None
|
||||
)
|
||||
simulated_seat = simulated_hardware_request and "seat" in reports
|
||||
report_spec: dict[str, Any] = {
|
||||
"mode": "hil" if hardware_request else "dry_run",
|
||||
"mode": "hil" if hardware_request or simulated_seat else "dry_run",
|
||||
"status": "succeeded" if execution_succeeded else "failed",
|
||||
"started_at": execution_started_at,
|
||||
"finished_at": _now(),
|
||||
@@ -2229,14 +2368,27 @@ class SeatDemoService:
|
||||
"name": (
|
||||
"Cabin orchestration mixed HIL execution report"
|
||||
if hardware_request
|
||||
else "Cabin orchestration virtual execution report"
|
||||
else (
|
||||
"Cabin orchestration simulated hardware execution report"
|
||||
if simulated_seat
|
||||
else "Cabin orchestration virtual execution report"
|
||||
)
|
||||
),
|
||||
"version": "1.0.0",
|
||||
"request_id": request_id,
|
||||
"labels": {
|
||||
"simulation": "mixed" if hardware_request else "true",
|
||||
"seat_feedback": (
|
||||
"physical_can" if hardware_request else "simulated"
|
||||
"physical_can"
|
||||
if hardware_request
|
||||
else (
|
||||
"simulated_hardware"
|
||||
if simulated_seat
|
||||
else "simulated"
|
||||
)
|
||||
),
|
||||
"fault_scenario": (
|
||||
simulation_fault if simulated_seat else "none"
|
||||
),
|
||||
"orchestration": "cross_domain"
|
||||
if len(configs) > 1
|
||||
@@ -2272,7 +2424,20 @@ class SeatDemoService:
|
||||
else (
|
||||
f"副驾座椅真实执行失败:{failed_execution['message']}"
|
||||
if hardware_request and failed_execution
|
||||
else f"已虚拟执行 {len(report_results)} 个领域操作并汇总反馈。"
|
||||
else (
|
||||
f"模拟台架已执行 {len(report_results)} 个领域操作,物理 CAN 未打开。"
|
||||
if simulated_seat and execution_succeeded
|
||||
else (
|
||||
f"模拟台架故障已触发:{failed_execution['message']}"
|
||||
if simulated_seat and failed_execution
|
||||
else (
|
||||
"本次没有座椅动作,故障注入未应用;"
|
||||
f"已虚拟执行 {len(report_results)} 个领域操作。"
|
||||
if simulated_hardware_request
|
||||
else f"已虚拟执行 {len(report_results)} 个领域操作并汇总反馈。"
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
),
|
||||
execute_started,
|
||||
|
||||
@@ -6,7 +6,7 @@ import os
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def load_local_env(path: Path) -> None:
|
||||
def load_local_env(path: Path, *, override: bool = False) -> None:
|
||||
if not path.is_file():
|
||||
return
|
||||
for raw_line in path.read_text(encoding="utf-8").splitlines():
|
||||
@@ -18,6 +18,9 @@ def load_local_env(path: Path) -> None:
|
||||
value = value.strip()
|
||||
if value[:1] == value[-1:] and value.startswith(("'", '"')):
|
||||
value = value[1:-1]
|
||||
if key:
|
||||
os.environ.setdefault(key, value)
|
||||
if key and (override or not os.environ.get(key, "").strip()):
|
||||
# A number of launchers export optional variables as empty strings.
|
||||
# Treat those exactly like an unset value so a deliberate local
|
||||
# development configuration is not silently shadowed.
|
||||
os.environ[key] = value
|
||||
|
||||
|
||||
@@ -88,6 +88,7 @@ class SeatHardwareSettings:
|
||||
cspybat: Path
|
||||
auto_flash_enabled: bool
|
||||
auto_home_enabled: bool
|
||||
position_sync_enabled: bool
|
||||
home_timeout_s: int
|
||||
firmware_probe_timeout_ms: int
|
||||
firmware_boot_wait_ms: int
|
||||
@@ -171,6 +172,10 @@ class SeatHardwareSettings:
|
||||
),
|
||||
auto_flash_enabled=auto_flash_enabled,
|
||||
auto_home_enabled=_env_bool("COGNITIVE_AGENT_SEAT_AUTO_HOME_ENABLED"),
|
||||
position_sync_enabled=_env_bool(
|
||||
"COGNITIVE_AGENT_SEAT_POSITION_SYNC_ENABLED",
|
||||
default=hardware_enabled,
|
||||
),
|
||||
home_timeout_s=_env_int(
|
||||
"COGNITIVE_AGENT_SEAT_HOME_TIMEOUT_S", 120
|
||||
),
|
||||
@@ -375,12 +380,19 @@ class SeatHardwareSettings:
|
||||
"physical_target": "t2g_primary_seat",
|
||||
"target_aliasing": "disabled_front_passenger_only",
|
||||
"auto_home_enabled": self.auto_home_enabled,
|
||||
"position_sync_enabled": self.position_sync_enabled,
|
||||
"position_sync_policy": (
|
||||
"four_axis_known_range_home_before_first_process_motion_and_after_firmware_change"
|
||||
if self.position_sync_enabled
|
||||
else "disabled"
|
||||
),
|
||||
"home_timeout_s": self.home_timeout_s,
|
||||
"limitations": [
|
||||
"hardware execution requires the unified Seat_Controller_demo image on one development board",
|
||||
"all seat capabilities are restricted to passenger/front_right",
|
||||
"M0/M1/M2/M3 map to horizontal, seat-pan, backrest and legrest on one physical seat",
|
||||
"known-range homing can move an invalid axis to its near endpoint and remains separately disabled by default",
|
||||
"on-demand recovery of an unexpectedly invalid target axis remains separately disabled by default",
|
||||
"when position synchronization is enabled, the first process motion and every repository firmware replacement rebuild all four physical Hall references",
|
||||
"current diagnostics use conservative bench thresholds and do not replace external power limiting",
|
||||
],
|
||||
}
|
||||
|
||||
@@ -12,9 +12,9 @@ from typing import Any, Callable
|
||||
from cognitive_agent.infrastructure.can.gateway import CanBus
|
||||
|
||||
|
||||
FIRMWARE_PROTOCOL_SIGNATURE = "seat-controller-smv8-current-diagnostics-v3"
|
||||
FIRMWARE_PROTOCOL_SIGNATURE = "seat-controller-smv8-current-diagnostics-v6"
|
||||
FIRMWARE_PROTOCOL_FRAME_IDS = frozenset({0x301, 0x302, 0x303, 0x304, 0x305, 0x4FA})
|
||||
EXPECTED_FIRMWARE_REVISION = 3
|
||||
EXPECTED_FIRMWARE_REVISION = 6
|
||||
EXPECTED_MOTOR_MAX_HALL = (480, 374, 917, 647)
|
||||
|
||||
|
||||
|
||||
@@ -10,6 +10,13 @@ import urllib.request
|
||||
from typing import Any
|
||||
|
||||
|
||||
def _env_text(name: str, default: str) -> str:
|
||||
"""Return a non-blank environment value or its documented default."""
|
||||
|
||||
value = os.getenv(name, "").strip()
|
||||
return value or default
|
||||
|
||||
|
||||
def build_intent_schema(capability_context: dict[str, Any]) -> dict[str, Any]:
|
||||
domains = [item["domain"] for item in capability_context.get("domains", [])] or ["seat"]
|
||||
capabilities = [
|
||||
@@ -122,6 +129,17 @@ An explicit scope in the current utterance overrides conversation history.
|
||||
When multiple capabilities address the goal equally well, prefer one whose domain is executable.
|
||||
Never substitute a materially less suitable capability merely because it is executable.
|
||||
|
||||
When the registry exposes exactly one executable seat target and the user refers only to "the seat",
|
||||
"the front cabin", or the occupant without a conflicting target, use that sole executable target; do
|
||||
not manufacture a driver-seat target. Understand causal and fuzzy ergonomic requests as goals, not as
|
||||
unsupported phrases. In particular, when the occupant says the front cabin is cramped and asks for a
|
||||
comfortable seat position without numeric targets, emit the four registered seat motion capabilities
|
||||
with this conservative deterministic baseline: longitudinal 80%, cushion tilt 30%, backrest recline
|
||||
35%, and legrest height 10%. This baseline moves the seat rearward for usable occupant space while
|
||||
remaining upright and reversible. State this inferred ergonomic baseline in decision_summary. Apply
|
||||
the same causal reasoning to paraphrases such as insufficient legroom, feeling squeezed, wanting more
|
||||
space, or asking for a comfortable posture; do not require the user to name a motor or percentage.
|
||||
|
||||
When execution_policy.planning_environment is dry_run_candidate_synthesis, a named preset is not
|
||||
required. For a commonly understood mode, activity, or comfort goal, decompose the goal into the
|
||||
registered atomic capabilities and infer conservative absolute targets from their normalized ranges
|
||||
@@ -173,12 +191,14 @@ class OpenAIIntentError(RuntimeError):
|
||||
|
||||
class OpenAIIntentInterpreter:
|
||||
def __init__(self) -> None:
|
||||
self.api_key = os.getenv("OPENAI_API_KEY", "")
|
||||
self.model = os.getenv("OPENAI_MODEL", "gpt-5.6-sol")
|
||||
self.base_url = os.getenv("OPENAI_BASE_URL", "https://api.openai.com/v1").rstrip("/")
|
||||
self.api_key = os.getenv("OPENAI_API_KEY", "").strip()
|
||||
self.model = _env_text("OPENAI_MODEL", "gpt-5.6-sol")
|
||||
self.base_url = _env_text(
|
||||
"OPENAI_BASE_URL", "https://api.openai.com/v1"
|
||||
).rstrip("/")
|
||||
self.timeout_seconds = float(os.getenv("OPENAI_TIMEOUT_SECONDS", "30"))
|
||||
self.max_retries = max(0, int(os.getenv("OPENAI_MAX_RETRIES", "2")))
|
||||
self.reasoning_effort = os.getenv("OPENAI_REASONING_EFFORT", "medium")
|
||||
self.reasoning_effort = _env_text("OPENAI_REASONING_EFFORT", "medium")
|
||||
self.streaming = os.getenv("OPENAI_STREAM", "false").strip().lower() in {
|
||||
"1",
|
||||
"true",
|
||||
@@ -190,6 +210,10 @@ class OpenAIIntentInterpreter:
|
||||
def available(self) -> bool:
|
||||
return bool(self.api_key)
|
||||
|
||||
@property
|
||||
def responses_endpoint(self) -> str:
|
||||
return f"{self.base_url}/responses"
|
||||
|
||||
def interpret(
|
||||
self,
|
||||
utterance: str,
|
||||
@@ -269,7 +293,7 @@ class OpenAIIntentInterpreter:
|
||||
|
||||
def _request_output_text(self, payload: dict[str, Any]) -> str:
|
||||
request = urllib.request.Request(
|
||||
f"{self.base_url}/responses",
|
||||
self.responses_endpoint,
|
||||
data=json.dumps(payload).encode("utf-8"),
|
||||
headers={
|
||||
"Authorization": f"Bearer {self.api_key}",
|
||||
|
||||
@@ -20,8 +20,10 @@ from cognitive_agent.config_loader import load_local_env
|
||||
|
||||
|
||||
REPOSITORY_ROOT = Path(__file__).resolve().parents[2]
|
||||
load_local_env(REPOSITORY_ROOT / ".env")
|
||||
load_local_env(REPOSITORY_ROOT / ".env.auth")
|
||||
# Repository-local launch configuration is deliberate and must not be shadowed
|
||||
# by stale variables inherited from an IDE or an earlier model experiment.
|
||||
load_local_env(REPOSITORY_ROOT / ".env", override=True)
|
||||
load_local_env(REPOSITORY_ROOT / ".env.auth", override=True)
|
||||
|
||||
from cognitive_agent.auth import AuthConfig, AuthRuntime, verify_password # noqa: E402
|
||||
from cognitive_agent.application.seat_service import SeatDemoService # noqa: E402
|
||||
@@ -31,12 +33,14 @@ from cognitive_agent.infrastructure.llm.openai_responses import OpenAIIntentErro
|
||||
|
||||
|
||||
STATIC_ROOT = REPOSITORY_ROOT / "apps" / "web-console"
|
||||
mimetypes.add_type("application/manifest+json", ".webmanifest")
|
||||
REQUEST_ID_PATTERN = re.compile(r"^req-[a-z0-9-]+$")
|
||||
PUBLIC_AUTH_PATHS = {
|
||||
"/login",
|
||||
"/login.html",
|
||||
"/login.css",
|
||||
"/login.js",
|
||||
"/network.js",
|
||||
"/api/auth/status",
|
||||
"/api/auth/login",
|
||||
}
|
||||
@@ -182,6 +186,7 @@ class SeatDemoHandler(BaseHTTPRequestHandler):
|
||||
if payload.get("vehicle_id")
|
||||
else None
|
||||
),
|
||||
simulation_fault=str(payload.get("simulation_fault", "normal")),
|
||||
)
|
||||
self._json(result, HTTPStatus.CREATED)
|
||||
except HardwareSafetyError as exc:
|
||||
@@ -370,7 +375,14 @@ class SeatDemoHandler(BaseHTTPRequestHandler):
|
||||
self.send_response(HTTPStatus.OK)
|
||||
self.send_header("Content-Type", f"{content_type or 'application/octet-stream'}; charset=utf-8")
|
||||
self.send_header("Content-Length", str(len(content)))
|
||||
self.send_header("Cache-Control", "no-store")
|
||||
cache_control = "no-cache"
|
||||
if relative_path.startswith(("vendor/", "assets/", "icons/")):
|
||||
cache_control = "public, max-age=31536000, immutable"
|
||||
elif candidate.name in {"index.html", "login.html", "service-worker.js"}:
|
||||
cache_control = "no-cache"
|
||||
self.send_header("Cache-Control", cache_control)
|
||||
if candidate.name == "service-worker.js":
|
||||
self.send_header("Service-Worker-Allowed", "/")
|
||||
self._send_security_headers()
|
||||
self.end_headers()
|
||||
self.wfile.write(content)
|
||||
@@ -427,10 +439,10 @@ class SeatDemoHandler(BaseHTTPRequestHandler):
|
||||
self.send_header(
|
||||
"Content-Security-Policy",
|
||||
"default-src 'self'; "
|
||||
"script-src 'self' 'wasm-unsafe-eval' https://unpkg.com https://cdn.jsdelivr.net; "
|
||||
"script-src 'self' 'wasm-unsafe-eval'; "
|
||||
"style-src 'self'; "
|
||||
"img-src 'self' data:; "
|
||||
"connect-src 'self' https://cdn.jsdelivr.net; "
|
||||
"connect-src 'self'; "
|
||||
"object-src 'none'; base-uri 'none'; frame-ancestors 'none'",
|
||||
)
|
||||
if self._is_https():
|
||||
@@ -462,7 +474,9 @@ def main() -> None:
|
||||
print(f"Cabin orchestration demo: http://{args.host}:{args.port}", flush=True)
|
||||
hardware = SeatDemoHandler.service.hardware_settings
|
||||
print(
|
||||
"Execution modes: dry_run + hardware "
|
||||
"Execution modes: dry_run + simulated_hardware "
|
||||
f"({'enabled' if SeatDemoHandler.service.simulated_hardware_enabled else 'locked'}) "
|
||||
"+ hardware "
|
||||
f"({'configured' if hardware.configured else 'locked'})",
|
||||
flush=True,
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user