1153 lines
40 KiB
Python
1153 lines
40 KiB
Python
from __future__ import annotations
|
|
|
|
import asyncio
|
|
import os
|
|
import shutil
|
|
from collections.abc import AsyncIterator, Iterable
|
|
from contextlib import asynccontextmanager, suppress
|
|
from pathlib import Path
|
|
from typing import Any
|
|
|
|
from fastapi import FastAPI, HTTPException, Request, WebSocket, WebSocketDisconnect
|
|
from fastapi.exceptions import RequestValidationError
|
|
from fastapi.responses import JSONResponse
|
|
from fastapi.staticfiles import StaticFiles
|
|
from pydantic import ValidationError
|
|
|
|
from k1link import __version__
|
|
from k1link.artifact_gateway import configured_artifact_gateway
|
|
from k1link.compute import (
|
|
IntegratedPerceptionOverlayStore,
|
|
RecordedCalibratedFusionStore,
|
|
RecordedPerceptionEpochStore,
|
|
RecordedPerceptionOverlayMux,
|
|
RecordedPerceptionOverlayStore,
|
|
)
|
|
from k1link.sessions import (
|
|
MaterializedRecording,
|
|
RecordedMediaInspector,
|
|
RecordedMediaManifest,
|
|
RecordingPreparationQueueFull,
|
|
ReplayCommand,
|
|
SessionRecordingMaterializer,
|
|
SessionRecordingPreparationManager,
|
|
SessionStore,
|
|
)
|
|
from k1link.web.advanced_laboratory_api import build_advanced_laboratory_router
|
|
from k1link.web.artifact_health_api import build_artifact_health_router
|
|
from k1link.web.compute_contour_api import build_compute_contour_router
|
|
from k1link.web.device_plugin_composition import load_installed_device_plugins
|
|
from k1link.web.e30_engineering_api import build_e30_engineering_router
|
|
from k1link.web.e30_human_review_api import build_e30_human_review_router
|
|
from k1link.web.e30_review_api import build_e30_review_router
|
|
from k1link.web.e40_case_review_api import build_e40_case_review_router
|
|
from k1link.web.e46_blind_review_api import build_e46_blind_review_router
|
|
from k1link.web.e46a_ai_engineering_preannotation_api import (
|
|
build_e46a_ai_engineering_preannotation_router,
|
|
)
|
|
from k1link.web.e46b_temporal_motion_api import build_e46b_temporal_motion_router
|
|
from k1link.web.e46c_full_replay_world_tracks_api import (
|
|
build_e46c_full_replay_world_tracks_router,
|
|
)
|
|
from k1link.web.e46d_temporal_failure_audit_api import (
|
|
build_e46d_temporal_failure_audit_router,
|
|
)
|
|
from k1link.web.e46e_ready_stack_api import build_e46e_ready_stack_router
|
|
from k1link.web.e46f_dashcam_bakeoff_api import build_e46f_dashcam_bakeoff_router
|
|
from k1link.web.e46g_rectified_detector_bakeoff_api import (
|
|
build_e46g_rectified_detector_bakeoff_router,
|
|
)
|
|
from k1link.web.e46h_full_rectified_front_replay_api import (
|
|
build_e46h_full_rectified_front_replay_router,
|
|
)
|
|
from k1link.web.e46i_grounding_dino_full_replay_api import (
|
|
build_e46i_grounding_dino_full_replay_router,
|
|
)
|
|
from k1link.web.e46j_raw_fisheye_realtime_api import (
|
|
build_e46j_raw_fisheye_realtime_router,
|
|
)
|
|
from k1link.web.environment_api import build_environment_router
|
|
from k1link.web.l3_pointpillars_visual_api import (
|
|
build_l3_pointpillars_visual_router,
|
|
)
|
|
from k1link.web.l31_pointpillars_ravnoves_api import (
|
|
build_l31_pointpillars_ravnoves_router,
|
|
)
|
|
from k1link.web.l32_pointpillars_camera_review_api import (
|
|
build_l32_pointpillars_camera_review_router,
|
|
)
|
|
from k1link.web.l33_camera_first_detector_review_api import (
|
|
build_l33_camera_first_detector_review_router,
|
|
)
|
|
from k1link.web.l34_annotation_api import (
|
|
build_l34_annotation_router,
|
|
build_l34d_blind_annotation_router,
|
|
)
|
|
from k1link.web.l34_right_yolox_truth_island_api import (
|
|
build_l34_right_yolox_truth_island_freeze_router,
|
|
)
|
|
from k1link.web.l34a_assisted_yolox_error_audit_api import (
|
|
build_l34a_assisted_yolox_error_audit_router,
|
|
)
|
|
from k1link.web.l34b_nested_box_consolidation_api import (
|
|
build_l34b_nested_box_consolidation_router,
|
|
)
|
|
from k1link.web.l34c_tile_seam_stitch_api import (
|
|
build_l34c_tile_seam_stitch_router,
|
|
)
|
|
from k1link.web.l34d_cumulative_postprocessing_api import (
|
|
build_l34d_cumulative_postprocessing_router,
|
|
)
|
|
from k1link.web.l34e_self_review_diagnostic_api import (
|
|
build_l34e_self_review_diagnostic_router,
|
|
)
|
|
from k1link.web.l34f_adjudication_api import build_l34f_adjudication_router
|
|
from k1link.web.laboratory_api import build_laboratory_router
|
|
from k1link.web.lidar_api import build_lidar_router
|
|
from k1link.web.lidar_local_surface_service import K1LocalSurfaceReadService
|
|
from k1link.web.map_api import (
|
|
MapGatewayConfiguration,
|
|
MapGatewayProxy,
|
|
build_map_router,
|
|
)
|
|
from k1link.web.map_view_api import build_map_view_router
|
|
from k1link.web.plugin_catalog import DevicePluginCatalog, PluginCatalogError
|
|
from k1link.web.plugin_runtime import (
|
|
STATE_READ_ACTION_ID,
|
|
DevicePluginActionRequest,
|
|
DevicePluginDispatcher,
|
|
PluginActionNotFoundError,
|
|
PluginExecutionError,
|
|
PluginNotFoundError,
|
|
PluginRuntimeUnavailableError,
|
|
)
|
|
from k1link.web.polygon_api import build_polygon_router, configured_polygon_runs_root
|
|
from k1link.web.runtime_diagnostics import configure_scanner_diagnostics
|
|
from k1link.web.runtime_readiness import (
|
|
BackgroundReconcilerReadiness,
|
|
build_runtime_readiness,
|
|
)
|
|
from k1link.web.session_api import build_session_router
|
|
from k1link.web.system_telemetry_api import build_system_telemetry_router
|
|
from k1link.web.viewer_diagnostics_api import build_viewer_diagnostics_router
|
|
|
|
REPOSITORY_ROOT = Path(__file__).resolve().parents[3]
|
|
INVALID_REQUEST_DETAIL = "Некорректные параметры запроса."
|
|
|
|
|
|
def _resolve_media_tool(name: str) -> Path | None:
|
|
"""Resolve media tools under interactive shells and minimal launchd PATHs."""
|
|
|
|
discovered = shutil.which(name)
|
|
candidates = (
|
|
Path(discovered) if discovered is not None else None,
|
|
Path("/opt/homebrew/bin") / name,
|
|
Path("/usr/local/bin") / name,
|
|
Path("/usr/bin") / name,
|
|
)
|
|
for candidate in candidates:
|
|
if candidate is not None and candidate.is_file() and os.access(candidate, os.X_OK):
|
|
return candidate
|
|
return None
|
|
|
|
|
|
plugin_environment = load_installed_device_plugins(REPOSITORY_ROOT)
|
|
plugin_catalog: DevicePluginCatalog = plugin_environment.catalog
|
|
plugin_dispatcher: DevicePluginDispatcher = plugin_environment.dispatcher
|
|
session_store = SessionStore(REPOSITORY_ROOT)
|
|
session_artifact_gateway = configured_artifact_gateway(session_store.data_dir)
|
|
lidar_local_surface_read_service = K1LocalSurfaceReadService(
|
|
session_store.data_dir / "lidar-read-cache"
|
|
)
|
|
session_recording_materializer = SessionRecordingMaterializer(
|
|
session_store.data_dir,
|
|
exporters=plugin_environment.recording_exporters,
|
|
artifact_gateway=session_artifact_gateway,
|
|
)
|
|
session_recorded_media_inspector = RecordedMediaInspector(
|
|
session_store.data_dir / "recorded-media-preparations"
|
|
)
|
|
_ffmpeg = _resolve_media_tool("ffmpeg")
|
|
_ffprobe = _resolve_media_tool("ffprobe")
|
|
session_legacy_perception_overlay_store = (
|
|
RecordedPerceptionOverlayStore(
|
|
jobs_root=REPOSITORY_ROOT / ".runtime" / "compute-jobs",
|
|
results_root=REPOSITORY_ROOT / ".runtime" / "compute-results",
|
|
cache_root=session_store.data_dir / "perception-overlays",
|
|
ffmpeg_path=_ffmpeg,
|
|
ffprobe_path=_ffprobe,
|
|
)
|
|
if _ffmpeg is not None and _ffprobe is not None
|
|
else None
|
|
)
|
|
session_calibrated_fusion_store = RecordedCalibratedFusionStore(
|
|
jobs_root=REPOSITORY_ROOT / ".runtime" / "compute-jobs",
|
|
perception_results_root=REPOSITORY_ROOT / ".runtime" / "compute-results",
|
|
fusion_results_root=REPOSITORY_ROOT / ".runtime" / "compute-fusions",
|
|
cache_root=session_store.data_dir / "calibrated-fusion-overlays",
|
|
)
|
|
session_previous_perception_overlay_store = RecordedPerceptionOverlayMux(
|
|
session_calibrated_fusion_store, session_legacy_perception_overlay_store
|
|
)
|
|
session_integrated_perception_store = (
|
|
IntegratedPerceptionOverlayStore(
|
|
jobs_root=REPOSITORY_ROOT / ".runtime" / "compute-jobs",
|
|
results_root=(
|
|
REPOSITORY_ROOT / ".runtime" / "compute-experiments" / "e10" / "worker-results"
|
|
),
|
|
lidar_packs_root=(
|
|
REPOSITORY_ROOT / ".runtime" / "compute-experiments" / "e10" / "lidar-packs"
|
|
),
|
|
cache_root=session_store.data_dir / "integrated-perception-overlays",
|
|
ffmpeg_path=_ffmpeg,
|
|
artifact_gateway=session_artifact_gateway,
|
|
)
|
|
if _ffmpeg is not None
|
|
else None
|
|
)
|
|
session_perception_overlay_store = RecordedPerceptionOverlayMux(
|
|
session_integrated_perception_store or session_previous_perception_overlay_store,
|
|
(
|
|
session_previous_perception_overlay_store
|
|
if session_integrated_perception_store is not None
|
|
else None
|
|
),
|
|
)
|
|
session_perception_epoch_store = (
|
|
RecordedPerceptionEpochStore(
|
|
jobs_root=REPOSITORY_ROOT / ".runtime" / "compute-jobs",
|
|
results_root=REPOSITORY_ROOT / ".runtime" / "compute-results",
|
|
ffprobe_path=_ffprobe,
|
|
)
|
|
if _ffprobe is not None
|
|
else None
|
|
)
|
|
map_gateway_proxy = MapGatewayProxy(MapGatewayConfiguration.from_environment())
|
|
recording_reconciler_readiness = BackgroundReconcilerReadiness()
|
|
|
|
|
|
def _prepare_recorded_media_for_launch(
|
|
command: ReplayCommand,
|
|
_: MaterializedRecording,
|
|
) -> tuple[RecordedMediaManifest, ...]:
|
|
"""Prepare all camera descriptors inside the background job."""
|
|
|
|
return tuple(
|
|
session_recorded_media_inspector.inspect(artifact, command)
|
|
for artifact in session_store.list_recorded_media(command.session_id)
|
|
)
|
|
|
|
|
|
def _restore_recorded_media_for_launch(
|
|
command: ReplayCommand,
|
|
_: MaterializedRecording,
|
|
) -> tuple[RecordedMediaManifest, ...] | None:
|
|
"""Restore only previously published media descriptors."""
|
|
|
|
restored: list[RecordedMediaManifest] = []
|
|
for artifact in session_store.list_recorded_media(command.session_id):
|
|
manifest = session_recorded_media_inspector.restore_prepared(artifact, command)
|
|
if manifest is None:
|
|
return None
|
|
restored.append(manifest)
|
|
return tuple(restored)
|
|
|
|
|
|
session_recording_preparation_manager = SessionRecordingPreparationManager(
|
|
session_recording_materializer,
|
|
ready_preparer=_prepare_recorded_media_for_launch,
|
|
ready_restorer=_restore_recorded_media_for_launch,
|
|
)
|
|
|
|
|
|
def refresh_observation_catalog() -> tuple[str, ...]:
|
|
"""Discover completed or recoverable local evidence without copying payloads."""
|
|
|
|
imported = [
|
|
session_id
|
|
for archive in plugin_environment.observation_archives
|
|
for session_id in session_store.reconcile_archive(archive)
|
|
]
|
|
return tuple(dict.fromkeys(imported))
|
|
|
|
|
|
def finalized_replayable_recording_ids() -> tuple[str, ...]:
|
|
"""List finalized replayable catalog identities without scheduling work."""
|
|
|
|
finalized: list[str] = []
|
|
cursor: str | None = None
|
|
while True:
|
|
page = session_store.list_recent(limit=100, cursor=cursor)
|
|
finalized.extend(
|
|
summary.session_id
|
|
for summary in page.items
|
|
if summary.replayable and summary.status in {"ready", "interrupted", "failed"}
|
|
)
|
|
cursor = page.next_cursor
|
|
if cursor is None:
|
|
return tuple(finalized)
|
|
|
|
|
|
def enqueue_replayable_recordings(session_ids: Iterable[str]) -> tuple[str, ...]:
|
|
"""Schedule only explicitly selected newly finalized sessions."""
|
|
|
|
enqueued: list[str] = []
|
|
for session_id in dict.fromkeys(session_ids):
|
|
try:
|
|
command = session_store.prepare_replay(session_id)
|
|
session_recording_preparation_manager.enqueue(
|
|
command,
|
|
retry_interrupted=True,
|
|
)
|
|
except RecordingPreparationQueueFull:
|
|
return tuple(enqueued)
|
|
except Exception:
|
|
# One stale/corrupt row must not starve a later newly completed
|
|
# session. Historical cold caches remain operator-triggered.
|
|
continue
|
|
enqueued.append(session_id)
|
|
return tuple(enqueued)
|
|
|
|
|
|
def newly_finalized_recording_ids(
|
|
known_finalized: set[str] | None,
|
|
current_finalized: Iterable[str],
|
|
) -> tuple[str, ...]:
|
|
"""Return only post-startup completions; the first scan is a baseline."""
|
|
|
|
if known_finalized is None:
|
|
return ()
|
|
return tuple(sorted(set(current_finalized) - known_finalized))
|
|
|
|
|
|
async def _recording_preparation_reconciler() -> None:
|
|
"""Prepare sessions finalized during this process, never historical rows."""
|
|
|
|
known_finalized: set[str] | None = None
|
|
while True:
|
|
try:
|
|
await asyncio.to_thread(refresh_observation_catalog)
|
|
finalized = set(await asyncio.to_thread(finalized_replayable_recording_ids))
|
|
newly_finalized = newly_finalized_recording_ids(
|
|
known_finalized,
|
|
finalized,
|
|
)
|
|
if newly_finalized:
|
|
await asyncio.to_thread(
|
|
enqueue_replayable_recordings,
|
|
newly_finalized,
|
|
)
|
|
known_finalized = finalized
|
|
recording_reconciler_readiness.record_success()
|
|
except Exception as exc:
|
|
# A transient filesystem/catalog failure must not permanently
|
|
# disable preparation of sessions completed later in the run.
|
|
recording_reconciler_readiness.record_failure(exc)
|
|
await asyncio.sleep(2.0)
|
|
|
|
|
|
@asynccontextmanager
|
|
async def app_lifespan(_: FastAPI) -> AsyncIterator[None]:
|
|
reconciler: asyncio.Task[None] | None = None
|
|
try:
|
|
configure_scanner_diagnostics(REPOSITORY_ROOT / ".runtime" / "mission-core" / "logs")
|
|
session_recording_preparation_manager.start()
|
|
# Recovery is intentionally a one-shot startup phase. The archive
|
|
# helper owns a cross-process lease, while ordinary catalog requests
|
|
# only perform discovery and therefore never touch a live writer.
|
|
for archive in plugin_environment.observation_archives:
|
|
await asyncio.to_thread(archive.recover, archive.root)
|
|
# Full evidence discovery, hashing and RRD queue reconciliation can be
|
|
# expensive on field captures. Start it immediately in the background
|
|
# instead of holding the ASGI startup gate.
|
|
reconciler = asyncio.create_task(_recording_preparation_reconciler())
|
|
yield
|
|
finally:
|
|
await map_gateway_proxy.close()
|
|
if reconciler is not None:
|
|
reconciler.cancel()
|
|
with suppress(asyncio.CancelledError):
|
|
await reconciler
|
|
await asyncio.to_thread(session_recording_preparation_manager.close)
|
|
await asyncio.to_thread(lidar_local_surface_read_service.close)
|
|
plugin_environment.close()
|
|
|
|
|
|
app = FastAPI(
|
|
title="NODEDC MISSION CORE API",
|
|
version=__version__,
|
|
docs_url="/api/docs",
|
|
redoc_url=None,
|
|
openapi_url="/api/openapi.json",
|
|
lifespan=app_lifespan,
|
|
)
|
|
|
|
|
|
@app.exception_handler(RequestValidationError)
|
|
async def request_validation_error_handler(
|
|
_: Request,
|
|
__: RequestValidationError,
|
|
) -> JSONResponse:
|
|
"""Return validation failures without reflecting request values or credentials."""
|
|
|
|
return JSONResponse(status_code=422, content={"detail": INVALID_REQUEST_DETAIL})
|
|
|
|
|
|
@app.get("/api/health")
|
|
def health() -> dict[str, Any]:
|
|
return build_runtime_readiness(
|
|
version=__version__,
|
|
plugin_runtime_health=plugin_environment.runtime_health,
|
|
recording_materializer=session_recording_materializer,
|
|
artifact_gateway=session_artifact_gateway,
|
|
map_gateway_configured=map_gateway_proxy.configured,
|
|
ffmpeg_available=_ffmpeg is not None,
|
|
ffprobe_available=_ffprobe is not None,
|
|
reconciler=recording_reconciler_readiness,
|
|
)
|
|
|
|
|
|
@app.get("/api/v1/device-plugins")
|
|
def get_device_plugins() -> dict[str, Any]:
|
|
try:
|
|
return {"items": plugin_catalog.plugin_documents()}
|
|
except PluginCatalogError as exc:
|
|
raise HTTPException(status_code=500, detail=str(exc)) from exc
|
|
|
|
|
|
@app.get("/api/v1/device-models")
|
|
def get_device_models() -> dict[str, Any]:
|
|
try:
|
|
return {"items": plugin_catalog.model_documents()}
|
|
except PluginCatalogError as exc:
|
|
raise HTTPException(status_code=500, detail=str(exc)) from exc
|
|
|
|
|
|
@app.get("/api/v1/device-plugin-runtimes")
|
|
def get_device_plugin_runtimes() -> dict[str, Any]:
|
|
return {"items": list(plugin_environment.runtime_health)}
|
|
|
|
|
|
@app.post("/api/v1/device-plugins/{plugin_id}/actions/{action_id}")
|
|
async def invoke_device_plugin_action(
|
|
plugin_id: str,
|
|
action_id: str,
|
|
request: DevicePluginActionRequest,
|
|
) -> dict[str, Any]:
|
|
try:
|
|
state = await plugin_dispatcher.invoke(plugin_id, action_id, request.input)
|
|
return {"state": state}
|
|
except (PluginNotFoundError, PluginActionNotFoundError) as exc:
|
|
raise HTTPException(status_code=404, detail=str(exc)) from exc
|
|
except ValidationError as exc:
|
|
raise HTTPException(status_code=422, detail=INVALID_REQUEST_DETAIL) from exc
|
|
except ValueError as exc:
|
|
raise HTTPException(status_code=400, detail=str(exc)) from exc
|
|
except PluginExecutionError as exc:
|
|
raise HTTPException(status_code=502, detail=str(exc)) from exc
|
|
except PluginRuntimeUnavailableError as exc:
|
|
raise HTTPException(status_code=503, detail=str(exc)) from exc
|
|
|
|
|
|
@app.websocket("/api/v1/device-plugins/{plugin_id}/events")
|
|
async def device_plugin_events(websocket: WebSocket, plugin_id: str) -> None:
|
|
await websocket.accept()
|
|
sequence = 0
|
|
try:
|
|
while True:
|
|
state = await plugin_dispatcher.invoke(plugin_id, STATE_READ_ACTION_ID, {})
|
|
sequence += 1
|
|
await websocket.send_json({"pluginId": plugin_id, "sequence": sequence, "state": state})
|
|
await asyncio.sleep(0.5)
|
|
except WebSocketDisconnect:
|
|
return
|
|
except (PluginNotFoundError, PluginActionNotFoundError):
|
|
await websocket.close(code=1008, reason="Device plugin is not available")
|
|
except (PluginExecutionError, PluginRuntimeUnavailableError):
|
|
await websocket.close(code=1011, reason="Device plugin state stream failed")
|
|
|
|
|
|
for legacy_router in plugin_environment.legacy_routers:
|
|
app.include_router(legacy_router)
|
|
|
|
app.include_router(
|
|
build_session_router(
|
|
session_store,
|
|
# Production discovery belongs to the startup/background reconciler.
|
|
# HTTP list/replay paths must never rescan evidence roots inline.
|
|
catalog_refresher=None,
|
|
recording_materializer=session_recording_materializer,
|
|
recording_preparation_manager=session_recording_preparation_manager,
|
|
media_inspector=session_recorded_media_inspector,
|
|
perception_overlay_provider=session_perception_overlay_store,
|
|
perception_media_provider=session_perception_epoch_store,
|
|
point_color_renderers=plugin_environment.point_color_renderers,
|
|
)
|
|
)
|
|
app.include_router(
|
|
build_environment_router(root_provider=lambda: session_store.data_dir / "ui-environment")
|
|
)
|
|
app.include_router(build_map_router(map_gateway_proxy))
|
|
app.include_router(
|
|
build_map_view_router(
|
|
root_provider=lambda: session_store.data_dir / "map-view",
|
|
)
|
|
)
|
|
app.include_router(
|
|
build_polygon_router(
|
|
root_provider=lambda: (
|
|
configured_polygon_runs_root() or REPOSITORY_ROOT / ".runtime" / "polygon-runs"
|
|
)
|
|
)
|
|
)
|
|
app.include_router(
|
|
build_lidar_router(
|
|
root_provider=lambda: (
|
|
REPOSITORY_ROOT / ".runtime" / "compute-experiments" / "lidar-replay-v2" / "packs"
|
|
),
|
|
ground_root_provider=lambda: (
|
|
REPOSITORY_ROOT / ".runtime" / "compute-experiments" / "lidar-ground-v1" / "benchmarks"
|
|
),
|
|
field_review_root_provider=lambda: (
|
|
REPOSITORY_ROOT
|
|
/ ".runtime"
|
|
/ "compute-experiments"
|
|
/ "lidar-field-review-v1"
|
|
/ "reviews"
|
|
),
|
|
local_surface_root_provider=lambda: (
|
|
REPOSITORY_ROOT / ".runtime" / "compute-experiments" / "k1-local-surface-v1" / "models"
|
|
),
|
|
e10_source_root_provider=lambda: (
|
|
REPOSITORY_ROOT / ".runtime" / "compute-experiments" / "e10" / "lidar-packs"
|
|
),
|
|
dataset_admission_provider=lambda: (
|
|
REPOSITORY_ROOT / ".runtime" / "dataset-gateway" / "admission.json"
|
|
),
|
|
dataset_preview_provider=lambda: (
|
|
REPOSITORY_ROOT / ".runtime" / "dataset-gateway" / "preview.json"
|
|
),
|
|
dataset_rellis_preview_provider=lambda: (
|
|
REPOSITORY_ROOT / ".runtime" / "dataset-gateway" / "rellis-preview.json"
|
|
),
|
|
dataset_rellis_admission_provider=lambda: (
|
|
REPOSITORY_ROOT / ".runtime" / "dataset-gateway" / "rellis-admission.json"
|
|
),
|
|
dataset_ground_preview_provider=lambda: (
|
|
REPOSITORY_ROOT / ".runtime" / "dataset-gateway" / "ground-comparison.json"
|
|
),
|
|
local_surface_read_service=lidar_local_surface_read_service,
|
|
)
|
|
)
|
|
app.include_router(
|
|
build_laboratory_router(
|
|
e29_root_provider=lambda: (
|
|
REPOSITORY_ROOT / ".runtime" / "compute-experiments" / "e29" / "results"
|
|
),
|
|
local_surface_root_provider=lambda: (
|
|
REPOSITORY_ROOT / ".runtime" / "compute-experiments" / "k1-local-surface-v1" / "models"
|
|
),
|
|
source_pack_root_provider=lambda: (
|
|
REPOSITORY_ROOT / ".runtime" / "compute-experiments" / "e10" / "lidar-packs"
|
|
),
|
|
source_result_root_provider=lambda: (
|
|
REPOSITORY_ROOT / ".runtime" / "compute-experiments" / "e10" / "worker-results"
|
|
),
|
|
)
|
|
)
|
|
app.include_router(
|
|
build_advanced_laboratory_router(
|
|
e31_root_provider=lambda: (
|
|
REPOSITORY_ROOT / ".runtime" / "compute-experiments" / "e31" / "source-qualifications"
|
|
),
|
|
e32_root_provider=lambda: (
|
|
REPOSITORY_ROOT / ".runtime" / "compute-experiments" / "e32" / "results"
|
|
),
|
|
e33_root_provider=lambda: (
|
|
REPOSITORY_ROOT / ".runtime" / "compute-experiments" / "e33" / "results"
|
|
),
|
|
e34_root_provider=lambda: (
|
|
REPOSITORY_ROOT / ".runtime" / "compute-experiments" / "e34" / "results"
|
|
),
|
|
e35_root_provider=lambda: (
|
|
REPOSITORY_ROOT / ".runtime" / "compute-experiments" / "e35" / "results"
|
|
),
|
|
e37_root_provider=lambda: (
|
|
REPOSITORY_ROOT / ".runtime" / "compute-experiments" / "e37" / "results"
|
|
),
|
|
e38_root_provider=lambda: (
|
|
REPOSITORY_ROOT / ".runtime" / "compute-experiments" / "e38" / "results"
|
|
),
|
|
e39_root_provider=lambda: (
|
|
REPOSITORY_ROOT / ".runtime" / "compute-experiments" / "e39" / "results"
|
|
),
|
|
e40_root_provider=lambda: (
|
|
REPOSITORY_ROOT / ".runtime" / "compute-experiments" / "e40" / "results"
|
|
),
|
|
e46_root_provider=lambda: (
|
|
REPOSITORY_ROOT / ".runtime" / "compute-experiments" / "e46" / "results"
|
|
),
|
|
e46a_root_provider=lambda: (
|
|
REPOSITORY_ROOT
|
|
/ ".runtime"
|
|
/ "compute-experiments"
|
|
/ "e46a"
|
|
/ "ai-engineering-preannotations"
|
|
),
|
|
e46b_root_provider=lambda: (
|
|
REPOSITORY_ROOT / ".runtime" / "compute-experiments" / "e46b" / "temporal-motion"
|
|
),
|
|
e46c_root_provider=lambda: (
|
|
REPOSITORY_ROOT
|
|
/ ".runtime"
|
|
/ "compute-experiments"
|
|
/ "e46c"
|
|
/ "full-replay-world-tracks"
|
|
),
|
|
e46d_root_provider=lambda: (
|
|
REPOSITORY_ROOT
|
|
/ ".runtime"
|
|
/ "compute-experiments"
|
|
/ "e46d"
|
|
/ "temporal-failure-audits"
|
|
),
|
|
e46e_root_provider=lambda: (
|
|
REPOSITORY_ROOT
|
|
/ ".runtime"
|
|
/ "compute-experiments"
|
|
/ "e46e"
|
|
/ "ready-stack-results"
|
|
),
|
|
e46f_root_provider=lambda: (
|
|
REPOSITORY_ROOT
|
|
/ ".runtime"
|
|
/ "compute-experiments"
|
|
/ "e46f"
|
|
/ "dashcam-bakeoff-results"
|
|
),
|
|
e46g_root_provider=lambda: (
|
|
REPOSITORY_ROOT / ".runtime" / "compute-experiments" / "e46g" / "results"
|
|
),
|
|
e46h_root_provider=lambda: (
|
|
REPOSITORY_ROOT / ".runtime" / "compute-experiments" / "e46h" / "results"
|
|
),
|
|
e46i_root_provider=lambda: (
|
|
REPOSITORY_ROOT / ".runtime" / "compute-experiments" / "e46i" / "results"
|
|
),
|
|
e46j_root_provider=lambda: (
|
|
REPOSITORY_ROOT / ".runtime" / "compute-experiments" / "e46j" / "results"
|
|
),
|
|
l34_root_provider=lambda: (
|
|
REPOSITORY_ROOT
|
|
/ ".runtime"
|
|
/ "compute-experiments"
|
|
/ "l3"
|
|
/ "right-yolox-truth-island-freeze"
|
|
),
|
|
l34a_root_provider=lambda: (
|
|
REPOSITORY_ROOT
|
|
/ ".runtime"
|
|
/ "compute-experiments"
|
|
/ "l3"
|
|
/ "assisted-yolox-error-audits"
|
|
),
|
|
l34b_root_provider=lambda: (
|
|
REPOSITORY_ROOT
|
|
/ ".runtime"
|
|
/ "compute-experiments"
|
|
/ "l3"
|
|
/ "nested-box-consolidation-shadows"
|
|
),
|
|
l34c_root_provider=lambda: (
|
|
REPOSITORY_ROOT / ".runtime" / "compute-experiments" / "l3" / "tile-seam-stitch-shadows"
|
|
),
|
|
l34d_root_provider=lambda: (
|
|
REPOSITORY_ROOT
|
|
/ ".runtime"
|
|
/ "compute-experiments"
|
|
/ "l3"
|
|
/ "cumulative-postprocessing-candidates"
|
|
),
|
|
l34e_root_provider=lambda: (
|
|
REPOSITORY_ROOT / ".runtime" / "compute-experiments" / "l3" / "self-review-diagnostics"
|
|
),
|
|
l34f_root_provider=lambda: (
|
|
REPOSITORY_ROOT / ".runtime" / "compute-experiments" / "l3" / "adjudicated-references"
|
|
),
|
|
l3_visual_root_provider=lambda: (
|
|
REPOSITORY_ROOT / ".runtime" / "compute-experiments" / "l3" / "visual-audits"
|
|
),
|
|
l31_ravnoves_root_provider=lambda: (
|
|
REPOSITORY_ROOT / ".runtime" / "compute-experiments" / "l3" / "pointpillars-ravnoves"
|
|
),
|
|
l32_camera_review_root_provider=lambda: (
|
|
REPOSITORY_ROOT
|
|
/ ".runtime"
|
|
/ "compute-experiments"
|
|
/ "l3"
|
|
/ "pointpillars-camera-review"
|
|
),
|
|
l33_camera_first_review_root_provider=lambda: (
|
|
REPOSITORY_ROOT
|
|
/ ".runtime"
|
|
/ "compute-experiments"
|
|
/ "l3"
|
|
/ "camera-first-detector-review"
|
|
),
|
|
)
|
|
)
|
|
app.include_router(
|
|
build_l3_pointpillars_visual_router(
|
|
root_provider=lambda: (
|
|
REPOSITORY_ROOT / ".runtime" / "compute-experiments" / "l3" / "visual-audits"
|
|
)
|
|
)
|
|
)
|
|
app.include_router(
|
|
build_l31_pointpillars_ravnoves_router(
|
|
root_provider=lambda: (
|
|
REPOSITORY_ROOT / ".runtime" / "compute-experiments" / "l3" / "pointpillars-ravnoves"
|
|
)
|
|
)
|
|
)
|
|
app.include_router(
|
|
build_l32_pointpillars_camera_review_router(
|
|
root_provider=lambda: (
|
|
REPOSITORY_ROOT
|
|
/ ".runtime"
|
|
/ "compute-experiments"
|
|
/ "l3"
|
|
/ "pointpillars-camera-review"
|
|
)
|
|
)
|
|
)
|
|
app.include_router(
|
|
build_l33_camera_first_detector_review_router(
|
|
root_provider=lambda: (
|
|
REPOSITORY_ROOT
|
|
/ ".runtime"
|
|
/ "compute-experiments"
|
|
/ "l3"
|
|
/ "camera-first-detector-review"
|
|
)
|
|
)
|
|
)
|
|
app.include_router(
|
|
build_l34_right_yolox_truth_island_freeze_router(
|
|
root_provider=lambda: (
|
|
REPOSITORY_ROOT
|
|
/ ".runtime"
|
|
/ "compute-experiments"
|
|
/ "l3"
|
|
/ "right-yolox-truth-island-freeze"
|
|
),
|
|
truth_root_provider=lambda: (
|
|
REPOSITORY_ROOT / ".runtime" / "compute-experiments" / "e46" / "results"
|
|
),
|
|
evaluation_pack_root_provider=lambda: (
|
|
REPOSITORY_ROOT / ".runtime" / "compute-experiments" / "e2" / "evaluation-packs"
|
|
),
|
|
)
|
|
)
|
|
app.include_router(
|
|
build_e46_blind_review_router(
|
|
truth_root_provider=lambda: (
|
|
REPOSITORY_ROOT / ".runtime" / "compute-experiments" / "e46" / "results"
|
|
),
|
|
evaluation_pack_root_provider=lambda: (
|
|
REPOSITORY_ROOT / ".runtime" / "compute-experiments" / "e2" / "evaluation-packs"
|
|
),
|
|
annotation_root_provider=lambda: (
|
|
REPOSITORY_ROOT / ".runtime" / "laboratory-annotations" / "e46-blind"
|
|
),
|
|
submission_root_provider=lambda: (
|
|
REPOSITORY_ROOT / ".runtime" / "compute-experiments" / "e48" / "review-submissions"
|
|
),
|
|
)
|
|
)
|
|
app.include_router(
|
|
build_e46a_ai_engineering_preannotation_router(
|
|
root_provider=lambda: (
|
|
REPOSITORY_ROOT
|
|
/ ".runtime"
|
|
/ "compute-experiments"
|
|
/ "e46a"
|
|
/ "ai-engineering-preannotations"
|
|
),
|
|
annotation_root_provider=lambda: (
|
|
REPOSITORY_ROOT / ".runtime" / "laboratory-annotations" / "e46a-correction"
|
|
),
|
|
)
|
|
)
|
|
app.include_router(
|
|
build_e46b_temporal_motion_router(
|
|
root_provider=lambda: (
|
|
REPOSITORY_ROOT / ".runtime" / "compute-experiments" / "e46b" / "temporal-motion"
|
|
),
|
|
)
|
|
)
|
|
app.include_router(
|
|
build_e46c_full_replay_world_tracks_router(
|
|
root_provider=lambda: (
|
|
REPOSITORY_ROOT
|
|
/ ".runtime"
|
|
/ "compute-experiments"
|
|
/ "e46c"
|
|
/ "full-replay-world-tracks"
|
|
),
|
|
e26_root_provider=lambda: (
|
|
REPOSITORY_ROOT / ".runtime" / "compute-experiments" / "e10" / "worker-results"
|
|
),
|
|
)
|
|
)
|
|
app.include_router(
|
|
build_e46d_temporal_failure_audit_router(
|
|
root_provider=lambda: (
|
|
REPOSITORY_ROOT
|
|
/ ".runtime"
|
|
/ "compute-experiments"
|
|
/ "e46d"
|
|
/ "temporal-failure-audits"
|
|
),
|
|
)
|
|
)
|
|
app.include_router(
|
|
build_e46e_ready_stack_router(
|
|
root_provider=lambda: (
|
|
REPOSITORY_ROOT
|
|
/ ".runtime"
|
|
/ "compute-experiments"
|
|
/ "e46e"
|
|
/ "ready-stack-results"
|
|
),
|
|
)
|
|
)
|
|
app.include_router(
|
|
build_e46f_dashcam_bakeoff_router(
|
|
root_provider=lambda: (
|
|
REPOSITORY_ROOT
|
|
/ ".runtime"
|
|
/ "compute-experiments"
|
|
/ "e46f"
|
|
/ "dashcam-bakeoff-results"
|
|
),
|
|
e46e_root_provider=lambda: (
|
|
REPOSITORY_ROOT
|
|
/ ".runtime"
|
|
/ "compute-experiments"
|
|
/ "e46e"
|
|
/ "ready-stack-results"
|
|
),
|
|
)
|
|
)
|
|
app.include_router(
|
|
build_e46g_rectified_detector_bakeoff_router(
|
|
root_provider=lambda: (
|
|
REPOSITORY_ROOT / ".runtime" / "compute-experiments" / "e46g" / "results"
|
|
)
|
|
)
|
|
)
|
|
app.include_router(
|
|
build_e46h_full_rectified_front_replay_router(
|
|
root_provider=lambda: (
|
|
REPOSITORY_ROOT / ".runtime" / "compute-experiments" / "e46h" / "results"
|
|
)
|
|
)
|
|
)
|
|
app.include_router(
|
|
build_e46i_grounding_dino_full_replay_router(
|
|
root_provider=lambda: (
|
|
REPOSITORY_ROOT / ".runtime" / "compute-experiments" / "e46i" / "results"
|
|
)
|
|
)
|
|
)
|
|
app.include_router(
|
|
build_e46j_raw_fisheye_realtime_router(
|
|
root_provider=lambda: (
|
|
REPOSITORY_ROOT / ".runtime" / "compute-experiments" / "e46j" / "results"
|
|
)
|
|
)
|
|
)
|
|
app.include_router(
|
|
build_l34_annotation_router(
|
|
root_provider=lambda: (
|
|
REPOSITORY_ROOT
|
|
/ ".runtime"
|
|
/ "compute-experiments"
|
|
/ "l3"
|
|
/ "right-yolox-truth-island-freeze"
|
|
),
|
|
truth_root_provider=lambda: (
|
|
REPOSITORY_ROOT / ".runtime" / "compute-experiments" / "e46" / "results"
|
|
),
|
|
evaluation_pack_root_provider=lambda: (
|
|
REPOSITORY_ROOT / ".runtime" / "compute-experiments" / "e2" / "evaluation-packs"
|
|
),
|
|
annotation_root_provider=lambda: (
|
|
REPOSITORY_ROOT / ".runtime" / "laboratory-annotations" / "l34"
|
|
),
|
|
)
|
|
)
|
|
app.include_router(
|
|
build_l34a_assisted_yolox_error_audit_router(
|
|
audit_root_provider=lambda: (
|
|
REPOSITORY_ROOT
|
|
/ ".runtime"
|
|
/ "compute-experiments"
|
|
/ "l3"
|
|
/ "assisted-yolox-error-audits"
|
|
),
|
|
l34_root_provider=lambda: (
|
|
REPOSITORY_ROOT
|
|
/ ".runtime"
|
|
/ "compute-experiments"
|
|
/ "l3"
|
|
/ "right-yolox-truth-island-freeze"
|
|
),
|
|
truth_root_provider=lambda: (
|
|
REPOSITORY_ROOT / ".runtime" / "compute-experiments" / "e46" / "results"
|
|
),
|
|
evaluation_pack_root_provider=lambda: (
|
|
REPOSITORY_ROOT / ".runtime" / "compute-experiments" / "e2" / "evaluation-packs"
|
|
),
|
|
)
|
|
)
|
|
app.include_router(
|
|
build_l34b_nested_box_consolidation_router(
|
|
shadow_root_provider=lambda: (
|
|
REPOSITORY_ROOT
|
|
/ ".runtime"
|
|
/ "compute-experiments"
|
|
/ "l3"
|
|
/ "nested-box-consolidation-shadows"
|
|
),
|
|
l34_root_provider=lambda: (
|
|
REPOSITORY_ROOT
|
|
/ ".runtime"
|
|
/ "compute-experiments"
|
|
/ "l3"
|
|
/ "right-yolox-truth-island-freeze"
|
|
),
|
|
truth_root_provider=lambda: (
|
|
REPOSITORY_ROOT / ".runtime" / "compute-experiments" / "e46" / "results"
|
|
),
|
|
evaluation_pack_root_provider=lambda: (
|
|
REPOSITORY_ROOT / ".runtime" / "compute-experiments" / "e2" / "evaluation-packs"
|
|
),
|
|
)
|
|
)
|
|
app.include_router(
|
|
build_l34c_tile_seam_stitch_router(
|
|
shadow_root_provider=lambda: (
|
|
REPOSITORY_ROOT / ".runtime" / "compute-experiments" / "l3" / "tile-seam-stitch-shadows"
|
|
),
|
|
l34_root_provider=lambda: (
|
|
REPOSITORY_ROOT
|
|
/ ".runtime"
|
|
/ "compute-experiments"
|
|
/ "l3"
|
|
/ "right-yolox-truth-island-freeze"
|
|
),
|
|
truth_root_provider=lambda: (
|
|
REPOSITORY_ROOT / ".runtime" / "compute-experiments" / "e46" / "results"
|
|
),
|
|
evaluation_pack_root_provider=lambda: (
|
|
REPOSITORY_ROOT / ".runtime" / "compute-experiments" / "e2" / "evaluation-packs"
|
|
),
|
|
)
|
|
)
|
|
app.include_router(
|
|
build_l34d_cumulative_postprocessing_router(
|
|
candidate_root_provider=lambda: (
|
|
REPOSITORY_ROOT
|
|
/ ".runtime"
|
|
/ "compute-experiments"
|
|
/ "l3"
|
|
/ "cumulative-postprocessing-candidates"
|
|
),
|
|
l34_root_provider=lambda: (
|
|
REPOSITORY_ROOT
|
|
/ ".runtime"
|
|
/ "compute-experiments"
|
|
/ "l3"
|
|
/ "right-yolox-truth-island-freeze"
|
|
),
|
|
truth_root_provider=lambda: (
|
|
REPOSITORY_ROOT / ".runtime" / "compute-experiments" / "e46" / "results"
|
|
),
|
|
evaluation_pack_root_provider=lambda: (
|
|
REPOSITORY_ROOT / ".runtime" / "compute-experiments" / "e2" / "evaluation-packs"
|
|
),
|
|
)
|
|
)
|
|
app.include_router(
|
|
build_l34d_blind_annotation_router(
|
|
candidate_root_provider=lambda: (
|
|
REPOSITORY_ROOT
|
|
/ ".runtime"
|
|
/ "compute-experiments"
|
|
/ "l3"
|
|
/ "cumulative-postprocessing-candidates"
|
|
),
|
|
l34_root_provider=lambda: (
|
|
REPOSITORY_ROOT
|
|
/ ".runtime"
|
|
/ "compute-experiments"
|
|
/ "l3"
|
|
/ "right-yolox-truth-island-freeze"
|
|
),
|
|
truth_root_provider=lambda: (
|
|
REPOSITORY_ROOT / ".runtime" / "compute-experiments" / "e46" / "results"
|
|
),
|
|
evaluation_pack_root_provider=lambda: (
|
|
REPOSITORY_ROOT / ".runtime" / "compute-experiments" / "e2" / "evaluation-packs"
|
|
),
|
|
annotation_root_provider=lambda: (
|
|
REPOSITORY_ROOT / ".runtime" / "laboratory-annotations" / "l34d-blind"
|
|
),
|
|
)
|
|
)
|
|
app.include_router(
|
|
build_l34e_self_review_diagnostic_router(
|
|
diagnostic_root_provider=lambda: (
|
|
REPOSITORY_ROOT / ".runtime" / "compute-experiments" / "l3" / "self-review-diagnostics"
|
|
),
|
|
candidate_root_provider=lambda: (
|
|
REPOSITORY_ROOT
|
|
/ ".runtime"
|
|
/ "compute-experiments"
|
|
/ "l3"
|
|
/ "cumulative-postprocessing-candidates"
|
|
),
|
|
l34_root_provider=lambda: (
|
|
REPOSITORY_ROOT
|
|
/ ".runtime"
|
|
/ "compute-experiments"
|
|
/ "l3"
|
|
/ "right-yolox-truth-island-freeze"
|
|
),
|
|
truth_root_provider=lambda: (
|
|
REPOSITORY_ROOT / ".runtime" / "compute-experiments" / "e46" / "results"
|
|
),
|
|
evaluation_pack_root_provider=lambda: (
|
|
REPOSITORY_ROOT / ".runtime" / "compute-experiments" / "e2" / "evaluation-packs"
|
|
),
|
|
)
|
|
)
|
|
app.include_router(
|
|
build_l34f_adjudication_router(
|
|
diagnostic_root_provider=lambda: (
|
|
REPOSITORY_ROOT / ".runtime" / "compute-experiments" / "l3" / "self-review-diagnostics"
|
|
),
|
|
candidate_root_provider=lambda: (
|
|
REPOSITORY_ROOT
|
|
/ ".runtime"
|
|
/ "compute-experiments"
|
|
/ "l3"
|
|
/ "cumulative-postprocessing-candidates"
|
|
),
|
|
l34_root_provider=lambda: (
|
|
REPOSITORY_ROOT
|
|
/ ".runtime"
|
|
/ "compute-experiments"
|
|
/ "l3"
|
|
/ "right-yolox-truth-island-freeze"
|
|
),
|
|
truth_root_provider=lambda: (
|
|
REPOSITORY_ROOT / ".runtime" / "compute-experiments" / "e46" / "results"
|
|
),
|
|
evaluation_pack_root_provider=lambda: (
|
|
REPOSITORY_ROOT / ".runtime" / "compute-experiments" / "e2" / "evaluation-packs"
|
|
),
|
|
adjudication_root_provider=lambda: (
|
|
REPOSITORY_ROOT / ".runtime" / "laboratory-annotations" / "l34f-adjudication"
|
|
),
|
|
result_root_provider=lambda: (
|
|
REPOSITORY_ROOT / ".runtime" / "compute-experiments" / "l3" / "adjudicated-references"
|
|
),
|
|
)
|
|
)
|
|
app.include_router(
|
|
build_e30_review_router(
|
|
materialization_root_provider=lambda: (
|
|
REPOSITORY_ROOT / ".runtime" / "compute-experiments" / "e30" / "materializations"
|
|
),
|
|
review_pack_root_provider=lambda: (
|
|
REPOSITORY_ROOT / ".runtime" / "compute-experiments" / "e30" / "review-packs"
|
|
),
|
|
)
|
|
)
|
|
app.include_router(
|
|
build_e40_case_review_router(
|
|
e40_root_provider=lambda: (
|
|
REPOSITORY_ROOT / ".runtime" / "compute-experiments" / "e40" / "results"
|
|
),
|
|
operator_review_root_provider=lambda: (
|
|
REPOSITORY_ROOT / ".runtime" / "compute-experiments" / "e40" / "operator-reviews"
|
|
),
|
|
)
|
|
)
|
|
app.include_router(
|
|
build_e30_engineering_router(
|
|
generation_root_provider=lambda: (
|
|
REPOSITORY_ROOT / ".runtime" / "compute-experiments" / "e30" / "engineering-generations"
|
|
),
|
|
materialization_root_provider=lambda: (
|
|
REPOSITORY_ROOT / ".runtime" / "compute-experiments" / "e30" / "materializations"
|
|
),
|
|
review_pack_root_provider=lambda: (
|
|
REPOSITORY_ROOT / ".runtime" / "compute-experiments" / "e30" / "review-packs"
|
|
),
|
|
)
|
|
)
|
|
app.include_router(
|
|
build_e30_human_review_router(
|
|
materialization_root_provider=lambda: (
|
|
REPOSITORY_ROOT / ".runtime" / "compute-experiments" / "e30" / "materializations"
|
|
),
|
|
review_pack_root_provider=lambda: (
|
|
REPOSITORY_ROOT / ".runtime" / "compute-experiments" / "e30" / "review-packs"
|
|
),
|
|
engineering_generation_root_provider=lambda: (
|
|
REPOSITORY_ROOT / ".runtime" / "compute-experiments" / "e30" / "engineering-generations"
|
|
),
|
|
draft_root_provider=lambda: (
|
|
REPOSITORY_ROOT / ".runtime" / "compute-experiments" / "e30" / "human-review-drafts"
|
|
),
|
|
generation_root_provider=lambda: (
|
|
REPOSITORY_ROOT
|
|
/ ".runtime"
|
|
/ "compute-experiments"
|
|
/ "e30"
|
|
/ "human-review-generations"
|
|
),
|
|
)
|
|
)
|
|
app.include_router(
|
|
build_system_telemetry_router(
|
|
root_provider=lambda: REPOSITORY_ROOT / ".runtime" / "system",
|
|
)
|
|
)
|
|
app.include_router(
|
|
build_artifact_health_router(
|
|
runtime_root_provider=lambda: REPOSITORY_ROOT / ".runtime",
|
|
e44_results_root_provider=lambda: (
|
|
REPOSITORY_ROOT / ".runtime" / "compute-experiments" / "e44" / "results"
|
|
),
|
|
e50_results_root_provider=lambda: (
|
|
REPOSITORY_ROOT / ".runtime" / "compute-experiments" / "e50" / "results"
|
|
),
|
|
)
|
|
)
|
|
app.include_router(
|
|
build_compute_contour_router(
|
|
root_provider=lambda: REPOSITORY_ROOT / ".runtime" / "system",
|
|
)
|
|
)
|
|
app.include_router(build_viewer_diagnostics_router())
|
|
|
|
|
|
frontend_dist = REPOSITORY_ROOT / "apps" / "control-station" / "dist"
|
|
if frontend_dist.is_dir():
|
|
app.mount("/", StaticFiles(directory=frontend_dist, html=True), name="frontend")
|