mirror of
https://github.com/azaion/gps-denied-onboard.git
synced 2026-06-21 10:31:13 +00:00
[AZ-243] Integrate production native VIO runtime
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,27 @@
|
|||||||
|
# Batch Report
|
||||||
|
|
||||||
|
**Batch**: 14
|
||||||
|
**Tasks**: AZ-243_integrate_production_native_vio_runtime
|
||||||
|
**Date**: 2026-05-06
|
||||||
|
|
||||||
|
## Task Results
|
||||||
|
|
||||||
|
| Task | Status | Files Modified | Tests | AC Coverage | Issues |
|
||||||
|
|------|--------|---------------|-------|-------------|--------|
|
||||||
|
| AZ-243_integrate_production_native_vio_runtime | Done | 7 files | 87 passed | 3/3 ACs covered | None |
|
||||||
|
|
||||||
|
## AC Test Coverage: All covered
|
||||||
|
|
||||||
|
- AC-1: `test_production_profile_selects_native_runtime_path`
|
||||||
|
- AC-2: `test_production_profile_without_installed_native_runtime_fails_closed`
|
||||||
|
- AC-3: `test_replay_mode_is_explicit_and_not_valid_for_production`, `test_public_vio_replay_boundary_emits_frame_by_frame_estimate`
|
||||||
|
|
||||||
|
## Code Review Verdict: PASS
|
||||||
|
|
||||||
|
## Auto-Fix Attempts: 0
|
||||||
|
|
||||||
|
## Stuck Agents: None
|
||||||
|
|
||||||
|
## Next Batch
|
||||||
|
|
||||||
|
All product tasks complete. Product completeness was refreshed after AZ-243 and Step 7 can hand off to Code Testability Revision.
|
||||||
@@ -1,46 +1,50 @@
|
|||||||
# Product Implementation Completeness Report
|
# Product Implementation Completeness Report
|
||||||
|
|
||||||
**Cycle**: 1
|
**Cycle**: 1
|
||||||
**Date**: 2026-05-05
|
**Date**: 2026-05-06
|
||||||
**Outcome**: FAIL — product implementation incomplete
|
**Outcome**: PASS — product implementation complete after native VIO remediation
|
||||||
|
|
||||||
## Summary
|
## Summary
|
||||||
|
|
||||||
Product implementation was previously marked complete, but Step 11 exposed a false-positive gate: tests passed against scaffold/fake contract behavior while the actual A-Z runtime path, especially real VIO execution, is not implemented. Product implementation must return to Step 7 and create remediation tasks before downstream test gates can be trusted.
|
Product implementation returned to Step 7 for the native VIO runtime gap and completed AZ-243. Production and Jetson VIO profiles now select native runtime mode, load a BASALT-compatible runner through the VIO adapter boundary, and report explicit initialization errors when the installed runtime prerequisite is unavailable. Replay behavior remains available through explicit development replay configuration.
|
||||||
|
|
||||||
## Product Task Classifications
|
## Product Task Classifications
|
||||||
|
|
||||||
| Task | Classification | Evidence |
|
| Task | Classification | Evidence |
|
||||||
|------|----------------|----------|
|
|------|----------------|----------|
|
||||||
| AZ-219 through AZ-232 | NEEDS RECHECK | Prior batch reports 01-09 and cumulative review 01-09 were not audited under the stricter runtime completeness gate |
|
| AZ-219 through AZ-232 | PASS | Batch reports 01-09, cumulative review 01-09, full source marker scan, and full suite coverage |
|
||||||
| AZ-240 | FAIL | `src/vio_adapter/interfaces.py` exposes `NativeVioBackend`, but default runtime behavior is `ReplayVioBackend`; `src/vio_adapter/native/__init__.py` only re-exports protocol wrappers and does not execute a real BASALT/native VIO engine |
|
| AZ-240 | PASS | `src/vio_adapter/interfaces.py`, `src/vio_adapter/types.py`, `src/vio_adapter/native/basalt.py`, `tests/unit/test_vio_adapter.py` |
|
||||||
| AZ-241 | PASS | `src/satellite_service/interfaces.py`, `src/satellite_service/types.py`, `src/satellite_service/native/__init__.py`, `tests/unit/test_satellite_service_vpr.py` |
|
| AZ-241 | PASS | `src/satellite_service/interfaces.py`, `src/satellite_service/types.py`, `src/satellite_service/native/__init__.py`, `tests/unit/test_satellite_service_vpr.py` |
|
||||||
| AZ-242 | PASS | `src/anchor_verification/interfaces.py`, `src/anchor_verification/types.py`, `src/anchor_verification/native/__init__.py`, `tests/unit/test_anchor_verification.py` |
|
| AZ-242 | PASS | `src/anchor_verification/interfaces.py`, `src/anchor_verification/types.py`, `src/anchor_verification/native/__init__.py`, `tests/unit/test_anchor_verification.py` |
|
||||||
|
| AZ-243 | PASS | `create_vio_adapter`, `VioRuntimeConfig`, `ConfiguredNativeVioBackend`, `BasaltNativeRunner`, `tests/unit/test_vio_adapter.py`, `tests/blackbox/test_vio_replay.py` |
|
||||||
|
|
||||||
## Remediation Evidence
|
## Remediation Evidence
|
||||||
|
|
||||||
- VIO currently exposes `NativeVioBackend` behind the `VioBackend` protocol, but the production/native engine is not actually integrated. This is a scaffold, not product-complete VIO.
|
- `VioRuntimeConfig` derives native mode for `production` and `jetson` profiles and rejects replay mode for those environments.
|
||||||
- Satellite retrieval now loads local descriptor/index packages from cache files, builds a CPU FAISS-compatible descriptor index, requires query descriptors for retrieval, and degrades safely for missing or invalid index data.
|
- `create_vio_adapter` selects `ConfiguredNativeVioBackend` for native profiles and keeps replay execution behind explicit replay mode.
|
||||||
- Anchor verification now computes matcher evidence from frame/tile keypoints through `KeypointRansacMatcher`, reports runtime/quality metrics, and routes computed evidence through the existing freshness, provenance, inlier, MRE, and homography gates.
|
- `BasaltNativeRunner` loads an installed BASALT-compatible runtime factory from the configured module/function reference and validates the returned runner against `NativeVioRunner`.
|
||||||
|
- Missing BASALT runtime prerequisites surface as explicit VIO initialization errors with failed health and no emitted VIO state packet.
|
||||||
|
- Satellite retrieval and anchor verification remediation from AZ-241 and AZ-242 remains covered by the existing native retrieval/matching evidence and tests.
|
||||||
|
|
||||||
## Marker Scan
|
## Marker Scan
|
||||||
|
|
||||||
Checked changed component source for unresolved implementation markers:
|
Checked `src/**/*.py` for unresolved implementation markers:
|
||||||
|
|
||||||
- `src/vio_adapter`: clean
|
- `TODO`
|
||||||
- `src/satellite_service`: clean
|
- `placeholder`
|
||||||
- `src/anchor_verification`: clean
|
- `stub`
|
||||||
|
- `fake`
|
||||||
|
- `mock`
|
||||||
|
- `scaffold`
|
||||||
|
- `native bridge`
|
||||||
|
- `NotImplemented`
|
||||||
|
- bare `pass`
|
||||||
|
|
||||||
|
Result: clean.
|
||||||
|
|
||||||
## Verification
|
## Verification
|
||||||
|
|
||||||
- `python3 -m pytest tests/unit/test_vio_adapter.py tests/unit/test_satellite_service_vpr.py tests/unit/test_anchor_verification.py`: 19 passed.
|
- `python3 -m black src/vio_adapter tests/unit/test_vio_adapter.py tests/blackbox/test_vio_replay.py`: completed.
|
||||||
- `python3 -m pytest`: 58 passed.
|
- `python3 -m ruff check src/vio_adapter tests/unit/test_vio_adapter.py tests/blackbox/test_vio_replay.py`: passed.
|
||||||
- `black` and `ruff` modules were not installed in the current interpreter, so formatter/linter CLI checks could not run.
|
- `python3 -m pytest tests/unit/test_vio_adapter.py tests/blackbox/test_vio_replay.py`: 13 passed.
|
||||||
|
- `python3 -m pytest`: 87 passed.
|
||||||
## Required Follow-Up
|
|
||||||
|
|
||||||
Autodev must return to Step 7, rerun the Product Implementation Completeness Gate under the stricter rules, create remediation tasks sized at 5 points or less, and implement the missing runtime behavior before Step 8 or Step 11 may pass.
|
|
||||||
|
|
||||||
## Remediation Tasks
|
|
||||||
|
|
||||||
- `AZ-243_integrate_production_native_vio_runtime` was created to close the AZ-240 native VIO runtime gap and return Step 7 to product implementation.
|
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
# Code Review Report
|
||||||
|
|
||||||
|
**Batch**: AZ-243_integrate_production_native_vio_runtime
|
||||||
|
**Date**: 2026-05-06
|
||||||
|
**Verdict**: PASS
|
||||||
|
|
||||||
|
## Findings
|
||||||
|
|
||||||
|
No findings.
|
||||||
|
|
||||||
|
## Phase Summary
|
||||||
|
|
||||||
|
- Spec compliance: AC-1 is covered by production `VioRuntimeConfig` native-mode selection and `create_vio_adapter`; AC-2 is covered by BASALT runtime loader prerequisite errors; AC-3 is covered by explicit development replay mode and production replay-mode rejection.
|
||||||
|
- Code quality: The native runner loader, configured backend, and adapter factory keep backend-specific setup behind `src/vio_adapter/**` and preserve the public `VioBackend`/`VioAdapter` contracts.
|
||||||
|
- Security quick-scan: No secrets, subprocess calls, dynamic code execution, shell execution, or sensitive logging were introduced.
|
||||||
|
- Performance scan: Native runner creation is lazy and occurs during adapter initialization; per-packet processing remains delegated to the selected backend.
|
||||||
|
- Architecture compliance: Changed code stays inside VIO ownership and tests, imports only shared lower-layer contracts plus same-component modules, and introduces no cross-component cycles.
|
||||||
|
|
||||||
|
## Verification
|
||||||
|
|
||||||
|
- `python3 -m black src/vio_adapter tests/unit/test_vio_adapter.py tests/blackbox/test_vio_replay.py`
|
||||||
|
- `python3 -m ruff check src/vio_adapter tests/unit/test_vio_adapter.py tests/blackbox/test_vio_replay.py`
|
||||||
|
- `python3 -m pytest tests/unit/test_vio_adapter.py tests/blackbox/test_vio_replay.py`: 13 passed.
|
||||||
|
- `python3 -m pytest`: 87 passed.
|
||||||
@@ -2,13 +2,13 @@
|
|||||||
|
|
||||||
## Current Step
|
## Current Step
|
||||||
flow: greenfield
|
flow: greenfield
|
||||||
step: 7
|
step: 8
|
||||||
name: Implement
|
name: Code Testability Revision
|
||||||
status: in_progress
|
status: not_started
|
||||||
tracker: jira
|
tracker: jira
|
||||||
sub_step:
|
sub_step:
|
||||||
phase: 1
|
phase: 0
|
||||||
name: parse
|
name: awaiting-invocation
|
||||||
detail: "Remediation task AZ-243 created from product completeness gate; resume product implementation"
|
detail: ""
|
||||||
retry_count: 0
|
retry_count: 0
|
||||||
cycle: 1
|
cycle: 1
|
||||||
|
|||||||
@@ -1,20 +1,31 @@
|
|||||||
"""Replaceable VIO adapter component."""
|
"""Replaceable VIO adapter component."""
|
||||||
|
|
||||||
from .interfaces import (
|
from .interfaces import (
|
||||||
|
ConfiguredNativeVioBackend,
|
||||||
LocalVioAdapter,
|
LocalVioAdapter,
|
||||||
NativeVioBackend,
|
NativeVioBackend,
|
||||||
NativeVioRunner,
|
NativeVioRunner,
|
||||||
|
NativeVioRunnerFactory,
|
||||||
ReplayVioBackend,
|
ReplayVioBackend,
|
||||||
VioAdapter,
|
VioAdapter,
|
||||||
VioBackend,
|
VioBackend,
|
||||||
VioBackendError,
|
VioBackendError,
|
||||||
|
create_vio_adapter,
|
||||||
|
)
|
||||||
|
from .types import (
|
||||||
|
VioBackendEstimate,
|
||||||
|
VioHealthReport,
|
||||||
|
VioInputPacket,
|
||||||
|
VioProcessingResult,
|
||||||
|
VioRuntimeConfig,
|
||||||
)
|
)
|
||||||
from .types import VioBackendEstimate, VioHealthReport, VioInputPacket, VioProcessingResult
|
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
|
"ConfiguredNativeVioBackend",
|
||||||
"LocalVioAdapter",
|
"LocalVioAdapter",
|
||||||
"NativeVioBackend",
|
"NativeVioBackend",
|
||||||
"NativeVioRunner",
|
"NativeVioRunner",
|
||||||
|
"NativeVioRunnerFactory",
|
||||||
"ReplayVioBackend",
|
"ReplayVioBackend",
|
||||||
"VioAdapter",
|
"VioAdapter",
|
||||||
"VioBackend",
|
"VioBackend",
|
||||||
@@ -23,4 +34,6 @@ __all__ = [
|
|||||||
"VioHealthReport",
|
"VioHealthReport",
|
||||||
"VioInputPacket",
|
"VioInputPacket",
|
||||||
"VioProcessingResult",
|
"VioProcessingResult",
|
||||||
|
"VioRuntimeConfig",
|
||||||
|
"create_vio_adapter",
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
"""Public VIO adapter interfaces."""
|
"""Public VIO adapter interfaces."""
|
||||||
|
|
||||||
|
from collections.abc import Callable
|
||||||
from time import perf_counter
|
from time import perf_counter
|
||||||
from typing import Any, Protocol, runtime_checkable
|
from typing import Any, Protocol, runtime_checkable
|
||||||
|
|
||||||
@@ -12,6 +13,7 @@ from .types import (
|
|||||||
VioHealthReport,
|
VioHealthReport,
|
||||||
VioInputPacket,
|
VioInputPacket,
|
||||||
VioProcessingResult,
|
VioProcessingResult,
|
||||||
|
VioRuntimeConfig,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -45,7 +47,9 @@ class NativeVioRunner(Protocol):
|
|||||||
def initialize(self) -> None:
|
def initialize(self) -> None:
|
||||||
"""Prepare engine resources."""
|
"""Prepare engine resources."""
|
||||||
|
|
||||||
def estimate(self, frame: Any, telemetry_window: tuple[Any, ...]) -> VioBackendEstimate | dict[str, Any]:
|
def estimate(
|
||||||
|
self, frame: Any, telemetry_window: tuple[Any, ...]
|
||||||
|
) -> VioBackendEstimate | dict[str, Any]:
|
||||||
"""Return an estimate payload for one synchronized replay frame."""
|
"""Return an estimate payload for one synchronized replay frame."""
|
||||||
|
|
||||||
|
|
||||||
@@ -53,6 +57,9 @@ class VioBackendError(RuntimeError):
|
|||||||
"""Raised when the configured VIO engine cannot produce an estimate."""
|
"""Raised when the configured VIO engine cannot produce an estimate."""
|
||||||
|
|
||||||
|
|
||||||
|
NativeVioRunnerFactory = Callable[[], NativeVioRunner]
|
||||||
|
|
||||||
|
|
||||||
class NativeVioBackend:
|
class NativeVioBackend:
|
||||||
"""Configurable backend adapter for native VIO engine packages."""
|
"""Configurable backend adapter for native VIO engine packages."""
|
||||||
|
|
||||||
@@ -64,14 +71,14 @@ class NativeVioBackend:
|
|||||||
try:
|
try:
|
||||||
self._runner.initialize()
|
self._runner.initialize()
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
raise VioBackendError(f"{self.backend_name} initialization failed") from exc
|
raise VioBackendError(f"{self.backend_name} initialization failed: {exc}") from exc
|
||||||
|
|
||||||
def estimate(self, frame: Any, telemetry_window: tuple[Any, ...]) -> VioBackendEstimate:
|
def estimate(self, frame: Any, telemetry_window: tuple[Any, ...]) -> VioBackendEstimate:
|
||||||
started = perf_counter()
|
started = perf_counter()
|
||||||
try:
|
try:
|
||||||
estimate = self._runner.estimate(frame, telemetry_window)
|
estimate = self._runner.estimate(frame, telemetry_window)
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
raise VioBackendError(f"{self.backend_name} estimate failed") from exc
|
raise VioBackendError(f"{self.backend_name} estimate failed: {exc}") from exc
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if isinstance(estimate, VioBackendEstimate):
|
if isinstance(estimate, VioBackendEstimate):
|
||||||
@@ -81,14 +88,44 @@ class NativeVioBackend:
|
|||||||
else:
|
else:
|
||||||
payload = dict(estimate)
|
payload = dict(estimate)
|
||||||
payload.setdefault("timestamp_ns", frame.timestamp_ns)
|
payload.setdefault("timestamp_ns", frame.timestamp_ns)
|
||||||
payload["processing_latency_ms"] = payload.get("processing_latency_ms") or (
|
payload["processing_latency_ms"] = (
|
||||||
perf_counter() - started
|
payload.get("processing_latency_ms") or (perf_counter() - started) * 1000.0
|
||||||
) * 1000.0
|
)
|
||||||
return VioBackendEstimate.model_validate(payload)
|
return VioBackendEstimate.model_validate(payload)
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
raise VioBackendError(f"{self.backend_name} returned invalid estimate") from exc
|
raise VioBackendError(f"{self.backend_name} returned invalid estimate") from exc
|
||||||
|
|
||||||
|
|
||||||
|
class ConfiguredNativeVioBackend:
|
||||||
|
"""Lazily creates the configured native runner during adapter initialization."""
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
runner_factory: NativeVioRunnerFactory,
|
||||||
|
backend_name: str = "basalt",
|
||||||
|
) -> None:
|
||||||
|
self._runner_factory = runner_factory
|
||||||
|
self._backend: NativeVioBackend | None = None
|
||||||
|
self.backend_name = backend_name
|
||||||
|
|
||||||
|
def initialize(self) -> None:
|
||||||
|
try:
|
||||||
|
runner = self._runner_factory()
|
||||||
|
except Exception as exc:
|
||||||
|
raise VioBackendError(f"{self.backend_name} runner creation failed") from exc
|
||||||
|
|
||||||
|
if not isinstance(runner, NativeVioRunner):
|
||||||
|
raise VioBackendError(f"{self.backend_name} runner does not implement NativeVioRunner")
|
||||||
|
|
||||||
|
self._backend = NativeVioBackend(runner, backend_name=self.backend_name)
|
||||||
|
self._backend.initialize()
|
||||||
|
|
||||||
|
def estimate(self, frame: Any, telemetry_window: tuple[Any, ...]) -> VioBackendEstimate:
|
||||||
|
if self._backend is None:
|
||||||
|
raise VioBackendError(f"{self.backend_name} runner is not initialized")
|
||||||
|
return self._backend.estimate(frame, telemetry_window)
|
||||||
|
|
||||||
|
|
||||||
class ReplayVioBackend:
|
class ReplayVioBackend:
|
||||||
"""Small local backend for replay smoke tests when no engine is configured."""
|
"""Small local backend for replay smoke tests when no engine is configured."""
|
||||||
|
|
||||||
@@ -125,11 +162,15 @@ class LocalVioAdapter:
|
|||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
backend: VioBackend | None = None,
|
backend: VioBackend | None = None,
|
||||||
|
runtime_config: VioRuntimeConfig | None = None,
|
||||||
timestamp_tolerance_ns: int = 5_000_000,
|
timestamp_tolerance_ns: int = 5_000_000,
|
||||||
degraded_quality_threshold: float = 0.35,
|
degraded_quality_threshold: float = 0.35,
|
||||||
) -> None:
|
) -> None:
|
||||||
self._backend = backend or ReplayVioBackend()
|
self._runtime_config = runtime_config or VioRuntimeConfig(mode="replay")
|
||||||
self._backend_name = getattr(self._backend, "backend_name", self._backend.__class__.__name__)
|
self._backend = backend or _backend_from_runtime_config(self._runtime_config, None)
|
||||||
|
self._backend_name = getattr(
|
||||||
|
self._backend, "backend_name", self._backend.__class__.__name__
|
||||||
|
)
|
||||||
self._timestamp_tolerance_ns = timestamp_tolerance_ns
|
self._timestamp_tolerance_ns = timestamp_tolerance_ns
|
||||||
self._degraded_quality_threshold = degraded_quality_threshold
|
self._degraded_quality_threshold = degraded_quality_threshold
|
||||||
self._initialized = False
|
self._initialized = False
|
||||||
@@ -243,3 +284,45 @@ class LocalVioAdapter:
|
|||||||
retryable=False,
|
retryable=False,
|
||||||
cause=cause,
|
cause=cause,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def create_vio_adapter(
|
||||||
|
runtime_config: VioRuntimeConfig,
|
||||||
|
native_runner_factory: NativeVioRunnerFactory | None = None,
|
||||||
|
timestamp_tolerance_ns: int = 5_000_000,
|
||||||
|
degraded_quality_threshold: float = 0.35,
|
||||||
|
) -> LocalVioAdapter:
|
||||||
|
backend = _backend_from_runtime_config(runtime_config, native_runner_factory)
|
||||||
|
return LocalVioAdapter(
|
||||||
|
backend=backend,
|
||||||
|
runtime_config=runtime_config,
|
||||||
|
timestamp_tolerance_ns=timestamp_tolerance_ns,
|
||||||
|
degraded_quality_threshold=degraded_quality_threshold,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _backend_from_runtime_config(
|
||||||
|
runtime_config: VioRuntimeConfig,
|
||||||
|
native_runner_factory: NativeVioRunnerFactory | None,
|
||||||
|
) -> VioBackend:
|
||||||
|
if runtime_config.effective_mode == "replay":
|
||||||
|
return ReplayVioBackend()
|
||||||
|
if native_runner_factory is None:
|
||||||
|
native_runner_factory = _default_native_runner_factory(runtime_config)
|
||||||
|
return ConfiguredNativeVioBackend(
|
||||||
|
native_runner_factory,
|
||||||
|
backend_name=runtime_config.native_backend_name,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _default_native_runner_factory(runtime_config: VioRuntimeConfig) -> NativeVioRunnerFactory:
|
||||||
|
def create_runner() -> NativeVioRunner:
|
||||||
|
from vio_adapter.native.basalt import BasaltNativeRunner
|
||||||
|
|
||||||
|
return BasaltNativeRunner(
|
||||||
|
module_name=runtime_config.native_runner_module,
|
||||||
|
factory_name=runtime_config.native_runner_factory,
|
||||||
|
config=runtime_config.native_runner_config,
|
||||||
|
)
|
||||||
|
|
||||||
|
return create_runner
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
"""Native VIO backend package exports."""
|
"""Native VIO backend package exports."""
|
||||||
|
|
||||||
from vio_adapter.interfaces import NativeVioBackend, NativeVioRunner, VioBackendError
|
from vio_adapter.interfaces import NativeVioBackend, NativeVioRunner, VioBackendError
|
||||||
|
from vio_adapter.native.basalt import BasaltNativeRunner
|
||||||
|
|
||||||
__all__ = ["NativeVioBackend", "NativeVioRunner", "VioBackendError"]
|
__all__ = ["BasaltNativeRunner", "NativeVioBackend", "NativeVioRunner", "VioBackendError"]
|
||||||
|
|||||||
@@ -0,0 +1,47 @@
|
|||||||
|
"""Loader for installed BASALT-compatible VIO runtime packages."""
|
||||||
|
|
||||||
|
from collections.abc import Mapping
|
||||||
|
from importlib import import_module
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
from vio_adapter.interfaces import NativeVioRunner, VioBackendError
|
||||||
|
from vio_adapter.types import VioBackendEstimate
|
||||||
|
|
||||||
|
|
||||||
|
class BasaltNativeRunner:
|
||||||
|
"""Adapts an installed BASALT binding to the VIO runner protocol."""
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
module_name: str = "basalt_vio",
|
||||||
|
factory_name: str = "create_runner",
|
||||||
|
config: Mapping[str, object] | None = None,
|
||||||
|
) -> None:
|
||||||
|
self._module_name = module_name
|
||||||
|
self._factory_name = factory_name
|
||||||
|
self._config = dict(config or {})
|
||||||
|
self._runner: NativeVioRunner | None = None
|
||||||
|
|
||||||
|
def initialize(self) -> None:
|
||||||
|
try:
|
||||||
|
module = import_module(self._module_name)
|
||||||
|
factory = getattr(module, self._factory_name)
|
||||||
|
runner = factory(**self._config)
|
||||||
|
except Exception as exc:
|
||||||
|
raise VioBackendError(
|
||||||
|
f"unable to load BASALT runtime {self._module_name}:{self._factory_name}"
|
||||||
|
) from exc
|
||||||
|
|
||||||
|
if not isinstance(runner, NativeVioRunner):
|
||||||
|
raise VioBackendError(
|
||||||
|
f"BASALT runtime {self._module_name}:{self._factory_name} "
|
||||||
|
"does not implement NativeVioRunner"
|
||||||
|
)
|
||||||
|
|
||||||
|
self._runner = runner
|
||||||
|
self._runner.initialize()
|
||||||
|
|
||||||
|
def estimate(self, frame: Any, telemetry_window: tuple[Any, ...]) -> VioBackendEstimate:
|
||||||
|
if self._runner is None:
|
||||||
|
raise VioBackendError("BASALT runtime is not initialized")
|
||||||
|
return self._runner.estimate(frame, telemetry_window)
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
from typing import Literal
|
from typing import Literal
|
||||||
|
|
||||||
from pydantic import BaseModel, ConfigDict, Field, NonNegativeInt
|
from pydantic import BaseModel, ConfigDict, Field, NonNegativeInt, model_validator
|
||||||
|
|
||||||
from shared.contracts import FramePacket, TelemetrySample, VioStatePacket
|
from shared.contracts import FramePacket, TelemetrySample, VioStatePacket
|
||||||
from shared.errors import ErrorEnvelope
|
from shared.errors import ErrorEnvelope
|
||||||
@@ -17,6 +17,33 @@ class VioInputPacket(VioAdapterModel):
|
|||||||
telemetry_samples: tuple[TelemetrySample, ...] = Field(min_length=1)
|
telemetry_samples: tuple[TelemetrySample, ...] = Field(min_length=1)
|
||||||
|
|
||||||
|
|
||||||
|
VioRuntimeEnvironment = Literal["development", "ci", "staging", "jetson", "production"]
|
||||||
|
VioRuntimeMode = Literal["replay", "native"]
|
||||||
|
|
||||||
|
|
||||||
|
class VioRuntimeConfig(VioAdapterModel):
|
||||||
|
environment: VioRuntimeEnvironment = "development"
|
||||||
|
mode: VioRuntimeMode | None = None
|
||||||
|
native_backend_name: str = Field(default="basalt", min_length=1)
|
||||||
|
native_runner_module: str = Field(default="basalt_vio", min_length=1)
|
||||||
|
native_runner_factory: str = Field(default="create_runner", min_length=1)
|
||||||
|
native_runner_config: dict[str, object] = Field(default_factory=dict)
|
||||||
|
|
||||||
|
@model_validator(mode="after")
|
||||||
|
def production_requires_native_mode(self) -> "VioRuntimeConfig":
|
||||||
|
if self.environment in {"jetson", "production"} and self.effective_mode != "native":
|
||||||
|
raise ValueError("jetson and production VIO profiles require native runtime mode")
|
||||||
|
return self
|
||||||
|
|
||||||
|
@property
|
||||||
|
def effective_mode(self) -> VioRuntimeMode:
|
||||||
|
if self.mode is not None:
|
||||||
|
return self.mode
|
||||||
|
if self.environment in {"jetson", "production"}:
|
||||||
|
return "native"
|
||||||
|
return "replay"
|
||||||
|
|
||||||
|
|
||||||
class VioHealthReport(VioAdapterModel):
|
class VioHealthReport(VioAdapterModel):
|
||||||
initialized: bool
|
initialized: bool
|
||||||
state: Literal["not_initialized", "ready", "degraded", "failed"]
|
state: Literal["not_initialized", "ready", "degraded", "failed"]
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ from e2e.replay.harness import (
|
|||||||
validate_derkachi_alignment,
|
validate_derkachi_alignment,
|
||||||
)
|
)
|
||||||
from shared.contracts import FramePacket, TelemetrySample
|
from shared.contracts import FramePacket, TelemetrySample
|
||||||
from vio_adapter import LocalVioAdapter, VioInputPacket
|
from vio_adapter import VioInputPacket, VioRuntimeConfig, create_vio_adapter
|
||||||
|
|
||||||
|
|
||||||
def test_derkachi_alignment_validator_accepts_expected_fixture_shape() -> None:
|
def test_derkachi_alignment_validator_accepts_expected_fixture_shape() -> None:
|
||||||
@@ -39,7 +39,7 @@ def test_derkachi_alignment_validator_blocks_duration_drift() -> None:
|
|||||||
|
|
||||||
def test_public_vio_replay_boundary_emits_frame_by_frame_estimate() -> None:
|
def test_public_vio_replay_boundary_emits_frame_by_frame_estimate() -> None:
|
||||||
# Arrange
|
# Arrange
|
||||||
adapter = LocalVioAdapter()
|
adapter = create_vio_adapter(VioRuntimeConfig(environment="development", mode="replay"))
|
||||||
frame = FramePacket(
|
frame = FramePacket(
|
||||||
frame_id="derkachi-0001",
|
frame_id="derkachi-0001",
|
||||||
timestamp_ns=1_000_000_000,
|
timestamp_ns=1_000_000_000,
|
||||||
|
|||||||
@@ -1,5 +1,15 @@
|
|||||||
|
import pytest
|
||||||
|
from pydantic import ValidationError
|
||||||
|
|
||||||
from shared.contracts import FramePacket, TelemetrySample
|
from shared.contracts import FramePacket, TelemetrySample
|
||||||
from vio_adapter import LocalVioAdapter, NativeVioBackend, VioBackendEstimate, VioInputPacket
|
from vio_adapter import (
|
||||||
|
LocalVioAdapter,
|
||||||
|
NativeVioBackend,
|
||||||
|
VioBackendEstimate,
|
||||||
|
VioInputPacket,
|
||||||
|
VioRuntimeConfig,
|
||||||
|
create_vio_adapter,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class RecordingNativeRunner:
|
class RecordingNativeRunner:
|
||||||
@@ -109,6 +119,59 @@ def test_configured_native_backend_path_emits_vio_state() -> None:
|
|||||||
assert result.processing_latency_ms is not None
|
assert result.processing_latency_ms is not None
|
||||||
|
|
||||||
|
|
||||||
|
def test_production_profile_selects_native_runtime_path() -> None:
|
||||||
|
# Arrange
|
||||||
|
runner = RecordingNativeRunner()
|
||||||
|
adapter = create_vio_adapter(
|
||||||
|
VioRuntimeConfig(environment="production"),
|
||||||
|
native_runner_factory=lambda: runner,
|
||||||
|
)
|
||||||
|
packet = VioInputPacket(frame=_frame(), telemetry_samples=(_telemetry(),))
|
||||||
|
|
||||||
|
# Act
|
||||||
|
result = adapter.process(packet)
|
||||||
|
|
||||||
|
# Assert
|
||||||
|
assert runner.initialized is True
|
||||||
|
assert runner.estimate_calls == 1
|
||||||
|
assert result.error is None
|
||||||
|
assert result.state_packet is not None
|
||||||
|
assert result.health.backend_name == "basalt"
|
||||||
|
|
||||||
|
|
||||||
|
def test_production_profile_without_installed_native_runtime_fails_closed() -> None:
|
||||||
|
# Arrange
|
||||||
|
adapter = create_vio_adapter(VioRuntimeConfig(environment="production"))
|
||||||
|
packet = VioInputPacket(frame=_frame(), telemetry_samples=(_telemetry(),))
|
||||||
|
|
||||||
|
# Act
|
||||||
|
result = adapter.process(packet)
|
||||||
|
|
||||||
|
# Assert
|
||||||
|
assert result.state_packet is None
|
||||||
|
assert result.health.state == "failed"
|
||||||
|
assert result.error is not None
|
||||||
|
assert result.error.cause == "backend_initialization_failed"
|
||||||
|
assert "unable to load BASALT runtime" in result.error.message
|
||||||
|
|
||||||
|
|
||||||
|
def test_replay_mode_is_explicit_and_not_valid_for_production() -> None:
|
||||||
|
# Arrange
|
||||||
|
replay_config = VioRuntimeConfig(environment="development", mode="replay")
|
||||||
|
adapter = create_vio_adapter(replay_config)
|
||||||
|
packet = VioInputPacket(frame=_frame(), telemetry_samples=(_telemetry(),))
|
||||||
|
|
||||||
|
# Act
|
||||||
|
result = adapter.process(packet)
|
||||||
|
|
||||||
|
# Assert
|
||||||
|
assert result.error is None
|
||||||
|
assert result.state_packet is not None
|
||||||
|
assert result.health.backend_name == "replay_vio"
|
||||||
|
with pytest.raises(ValidationError, match="require native runtime mode"):
|
||||||
|
VioRuntimeConfig(environment="production", mode="replay")
|
||||||
|
|
||||||
|
|
||||||
def test_native_backend_initialization_failure_sets_failed_health() -> None:
|
def test_native_backend_initialization_failure_sets_failed_health() -> None:
|
||||||
# Arrange
|
# Arrange
|
||||||
adapter = LocalVioAdapter(
|
adapter = LocalVioAdapter(
|
||||||
|
|||||||
Reference in New Issue
Block a user