Merge branch 'feature/N7Web' of https://git.kendeji.fun/vee/Cognitive_Agent into feature/N7Web

# Conflicts:
#	apps/web-console/app.js
#	apps/web-console/index.html
#	apps/web-console/service-worker.js
#	src/cognitive_agent/server.py
#	tests/e2e/test_seat_web_api.py
This commit is contained in:
minghanchen
2026-08-27 14:43:44 +08:00
19 changed files with 3808 additions and 115 deletions
+211 -59
View File
@@ -38,6 +38,8 @@ from cognitive_agent.application.self_extension import ( # noqa: E402
ExtensionIntegrationError,
GatewayError,
SelfExtensionService,
active_peripheral_plan_catalog,
resolve_active_peripheral_command,
)
from cognitive_agent.application.self_repair import SelfRepairService # noqa: E402
from cognitive_agent.infrastructure.can.hardware import ( # noqa: E402
@@ -49,6 +51,7 @@ from cognitive_agent.infrastructure.can.access import ( # noqa: E402
HardwareAccessRequiredError,
)
from cognitive_agent.infrastructure.llm.openai_responses import OpenAIIntentError # noqa: E402
from cognitive_agent.infrastructure.nfc import NfcReaderService # noqa: E402
STATIC_ROOT = REPOSITORY_ROOT / "apps" / "web-console"
@@ -141,6 +144,15 @@ class VehicleDisplaySync:
payload["extension_health"] = self._safe_copy(health)
return payload
def extension_catalog(self) -> list[dict[str, object]]:
with self._lock:
extension = self._snapshot.get("extension", {})
catalog = extension.get("catalog", []) if isinstance(extension, dict) else []
payload = self._safe_copy(catalog)
if not isinstance(payload, list):
return []
return [item for item in payload if isinstance(item, dict)]
def _lan_ipv4_address() -> str | None:
"""Return the IPv4 address selected by the host's default route."""
@@ -159,6 +171,7 @@ class SeatDemoHandler(BaseHTTPRequestHandler):
service = SeatDemoService()
gesture = GestureCapabilityBridge(REPOSITORY_ROOT)
extension = SelfExtensionService()
nfc = NfcReaderService()
calibration = CalibrationService(
REPOSITORY_ROOT / "artifacts" / "tmp" / "self-calibration",
repository_root=REPOSITORY_ROOT,
@@ -232,7 +245,7 @@ class SeatDemoHandler(BaseHTTPRequestHandler):
self._json(self.service.runtime_info())
return
if path == "/api/vehicle-display-sync":
self._json(self.vehicle_display_sync.snapshot(self.extension.health()))
self._json(self.vehicle_display_sync.snapshot(self._extension_health()))
return
if path in {"/api/hardware/seat-state", "/api/hardware/vehicle-state"}:
query = parse_qs(parsed_url.query)
@@ -297,7 +310,10 @@ class SeatDemoHandler(BaseHTTPRequestHandler):
self._serve_gesture_model()
return
if path == "/api/extension/health":
self._json(self.extension.health())
self._json(self._extension_health())
return
if path == "/api/extension/nfc/status":
self._json({"success": True, **self.nfc.snapshot()})
return
if path == "/api/extension/gscu/info":
self._handle_extension_gateway("info", {})
@@ -439,17 +455,18 @@ class SeatDemoHandler(BaseHTTPRequestHandler):
if payload.get("reasoning_effort") is not None
else None
)
provider = str(payload.get("provider", "auto"))
execution_mode = str(payload.get("execution_mode", "dry_run"))
conversation_id = (
str(payload["conversation_id"])
if payload.get("conversation_id")
else None
)
common_arguments = {
"utterance": utterance,
"provider": str(payload.get("provider", "auto")),
"conversation_id": (
str(payload["conversation_id"])
if payload.get("conversation_id")
else None
),
"execution_mode": str(
payload.get("execution_mode", "dry_run")
),
"provider": provider,
"conversation_id": conversation_id,
"execution_mode": execution_mode,
# Hardware authorization is a server deployment policy. The
# browser never receives or transmits this secret.
"approval_token": os.getenv(
@@ -508,25 +525,35 @@ class SeatDemoHandler(BaseHTTPRequestHandler):
payload.get("control_transport", "wired_can")
),
)
elif path == "/api/requests/preview":
result = self.service.preview_native_can(**common_arguments)
elif supplement_match:
result = self.service.supplement_native_can(
supplement_match.group("request_id"),
provider=(
str(payload["provider"])
if payload.get("provider")
else None
),
approval_token=os.getenv(
"COGNITIVE_AGENT_HARDWARE_APPROVAL_TOKEN"
),
hardware_client_id=hardware_client_id,
hardware_access_bypass=self._is_unproxied_loopback_request(),
reasoning_effort=reasoning_effort,
)
else:
result = self.service.process(**common_arguments)
result = None
if path in {"/api/requests", "/api/requests/preview"}:
result = self._execute_active_peripheral_request(
utterance,
provider=provider,
conversation_id=conversation_id,
execution_mode=execution_mode,
)
if result is None:
if path == "/api/requests/preview":
result = self.service.preview_native_can(**common_arguments)
elif supplement_match:
result = self.service.supplement_native_can(
supplement_match.group("request_id"),
provider=(
str(payload["provider"])
if payload.get("provider")
else None
),
approval_token=os.getenv(
"COGNITIVE_AGENT_HARDWARE_APPROVAL_TOKEN"
),
hardware_client_id=hardware_client_id,
hardware_access_bypass=self._is_unproxied_loopback_request(),
reasoning_effort=reasoning_effort,
)
else:
result = self.service.process(**common_arguments)
utterance = str(result.get("utterance") or utterance)
self.vehicle_display_sync.update_task(
status=str(result.get("status", "succeeded")),
@@ -578,6 +605,18 @@ class SeatDemoHandler(BaseHTTPRequestHandler):
},
HTTPStatus.SERVICE_UNAVAILABLE,
)
except GatewayError as exc:
fail_display_sync("gscu_unavailable", exc)
self._json(
{"error": "gscu_unavailable", "message": str(exc)},
HTTPStatus.SERVICE_UNAVAILABLE,
)
except ExtensionIntegrationError as exc:
fail_display_sync("extension_request_ambiguous", exc)
self._json(
{"error": "extension_request_ambiguous", "message": str(exc)},
HTTPStatus.UNPROCESSABLE_ENTITY,
)
except (ValueError, json.JSONDecodeError) as exc:
fail_display_sync("invalid_request", exc)
self._json(
@@ -612,6 +651,94 @@ class SeatDemoHandler(BaseHTTPRequestHandler):
HTTPStatus.INTERNAL_SERVER_ERROR,
)
def _execute_active_peripheral_request(
self,
utterance: str,
*,
provider: str,
conversation_id: str | None,
execution_mode: str,
) -> dict[str, object] | None:
"""Route a command to the NFC-bound peripheral when one is present."""
extension_catalog = self.vehicle_display_sync.extension_catalog()
nfc_snapshot = self.nfc.snapshot()
plan_catalog = active_peripheral_plan_catalog(
utterance,
extension_catalog,
nfc_snapshot,
)
peripheral = None
if not plan_catalog:
peripheral = resolve_active_peripheral_command(
utterance,
extension_catalog,
nfc_snapshot,
)
if not plan_catalog and peripheral is None:
return None
if conversation_id and not re.fullmatch(
r"conv-[a-z0-9-]+", conversation_id
):
raise ValueError(
"conversation_id must start with conv- and contain only "
"lowercase letters, numbers, or hyphens"
)
conversation_turn = 1
if conversation_id:
context = self.service.conversations.snapshot(conversation_id)
conversation_turn = int(context.get("turn_count", 0)) + 1
if plan_catalog:
plan_result = self.extension.plan(
{
"utterance": utterance,
"peripherals": plan_catalog,
"vehicle_devices": [],
}
)
if plan_result.get("needs_confirmation"):
raise ExtensionIntegrationError(
str(plan_result.get("question") or "请明确要控制的外设。")
)
result = self.extension.execute_peripheral_plan_request(
utterance,
plan_result,
plan_catalog,
conversation_id=conversation_id,
conversation_turn=conversation_turn,
execution_mode=execution_mode,
provider_requested=provider,
)
self.vehicle_display_sync.update_extension(
action={
"type": "sequence_emit",
"source": "main_orchestration",
"step_count": len(plan_result.get("steps", [])),
},
result=result,
)
return result
assert peripheral is not None
result = self.extension.execute_peripheral_request(
utterance,
peripheral,
conversation_id=conversation_id,
conversation_turn=conversation_turn,
execution_mode=execution_mode,
provider_requested=provider,
)
self.vehicle_display_sync.update_extension(
action={
"type": "emit",
"source": "main_orchestration",
"slot": peripheral["slot"],
"capability_id": peripheral.get("id"),
},
result=result,
)
return result
def _stream_control_request(
self,
path: str,
@@ -681,40 +808,55 @@ class SeatDemoHandler(BaseHTTPRequestHandler):
utterance = str(common_arguments.get("utterance") or "")
try:
if path == "/api/requests/preview":
result = self.service.preview_native_can(
**common_arguments,
on_commands_validated=on_commands_validated,
)
elif supplement_match:
result = self.service.supplement_native_can(
supplement_match.group("request_id"),
provider=(
str(payload["provider"])
if payload.get("provider")
result = None
if path in {"/api/requests", "/api/requests/preview"}:
result = self._execute_active_peripheral_request(
utterance,
provider=str(common_arguments.get("provider") or "auto"),
conversation_id=(
str(common_arguments["conversation_id"])
if common_arguments.get("conversation_id")
else None
),
approval_token=os.getenv(
"COGNITIVE_AGENT_HARDWARE_APPROVAL_TOKEN"
execution_mode=str(
common_arguments.get("execution_mode") or "dry_run"
),
hardware_client_id=(
str(payload["hardware_client_id"])
if payload.get("hardware_client_id")
else None
),
hardware_access_bypass=self._is_unproxied_loopback_request(),
reasoning_effort=(
str(payload["reasoning_effort"])
if payload.get("reasoning_effort") is not None
else None
),
on_commands_validated=on_commands_validated,
)
else:
result = self.service.process(
**common_arguments,
_on_commands_validated=on_commands_validated,
)
if result is None:
if path == "/api/requests/preview":
result = self.service.preview_native_can(
**common_arguments,
on_commands_validated=on_commands_validated,
)
elif supplement_match:
result = self.service.supplement_native_can(
supplement_match.group("request_id"),
provider=(
str(payload["provider"])
if payload.get("provider")
else None
),
approval_token=os.getenv(
"COGNITIVE_AGENT_HARDWARE_APPROVAL_TOKEN"
),
hardware_client_id=(
str(payload["hardware_client_id"])
if payload.get("hardware_client_id")
else None
),
hardware_access_bypass=self._is_unproxied_loopback_request(),
reasoning_effort=(
str(payload["reasoning_effort"])
if payload.get("reasoning_effort") is not None
else None
),
on_commands_validated=on_commands_validated,
)
else:
result = self.service.process(
**common_arguments,
_on_commands_validated=on_commands_validated,
)
utterance = str(result.get("utterance") or utterance)
self.vehicle_display_sync.update_task(
status=str(result.get("status", "succeeded")),
@@ -739,6 +881,10 @@ class SeatDemoHandler(BaseHTTPRequestHandler):
emit_terminal_error("hardware_safety_gate", str(exc))
except CanGatewayError as exc:
emit_terminal_error("can_transport_failed", str(exc))
except GatewayError as exc:
emit_terminal_error("gscu_unavailable", str(exc))
except ExtensionIntegrationError as exc:
emit_terminal_error("extension_request_ambiguous", str(exc))
except (ValueError, json.JSONDecodeError) as exc:
emit_terminal_error("invalid_request", str(exc))
except OpenAIIntentError as exc:
@@ -1171,6 +1317,9 @@ class SeatDemoHandler(BaseHTTPRequestHandler):
HTTPStatus.SERVICE_UNAVAILABLE,
)
def _extension_health(self) -> dict[str, object]:
return {**self.extension.health(), "nfc": self.nfc.snapshot()}
def _handle_hardware_access_post(self, path: str) -> None:
local_path = path.startswith("/api/local/")
if local_path and not self._require_local_decision_point():
@@ -1499,6 +1648,7 @@ class SeatDemoHandler(BaseHTTPRequestHandler):
"style-src 'self'; "
"img-src 'self' data: blob:; "
"media-src 'self' blob:; "
"frame-src 'self' http://*:3001; "
"connect-src 'self' blob: http://172.20.10.6:8889; "
"object-src 'none'; base-uri 'none'; frame-ancestors 'none'",
)
@@ -1517,6 +1667,7 @@ def main() -> None:
server = ThreadingHTTPServer((args.host, args.port), SeatDemoHandler)
SeatDemoHandler.service.start()
SeatDemoHandler.extension.start()
SeatDemoHandler.nfc.start()
SeatDemoHandler.calibration.start()
SeatDemoHandler.repair.start()
if args.host in {"0.0.0.0", "::"}:
@@ -1555,6 +1706,7 @@ def main() -> None:
server.server_close()
SeatDemoHandler.service.close()
SeatDemoHandler.extension.close()
SeatDemoHandler.nfc.close()
SeatDemoHandler.calibration.close()
SeatDemoHandler.repair.close()