[AZ-240] [AZ-241] [AZ-242] Add native retrieval remediation

Implement the product remediation paths required before greenfield
code testability revision: native VIO backend selection, local
VPR descriptor index retrieval, and computed anchor matching gates.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Oleksandr Bezdieniezhnykh
2026-05-05 06:05:10 +03:00
parent 44c19ed117
commit 70f786f2d1
26 changed files with 869 additions and 114 deletions
+10 -1
View File
@@ -1,9 +1,15 @@
"""Anchor verification component."""
from .interfaces import AnchorVerifier, GeometryGatedAnchorVerifier
from .interfaces import (
AnchorVerifier,
FeatureMatcher,
GeometryGatedAnchorVerifier,
KeypointRansacMatcher,
)
from .types import (
AnchorFrame,
AnchorVerificationResult,
CandidateTile,
GeometryGateConfig,
MatchEvidence,
MatcherBenchmarkReport,
@@ -15,8 +21,11 @@ __all__ = [
"AnchorFrame",
"AnchorVerificationResult",
"AnchorVerifier",
"CandidateTile",
"FeatureMatcher",
"GeometryGateConfig",
"GeometryGatedAnchorVerifier",
"KeypointRansacMatcher",
"MatchEvidence",
"MatcherBenchmarkReport",
"MatcherBenchmarkResult",
+112 -1
View File
@@ -1,5 +1,7 @@
"""Public anchor verification interfaces."""
from statistics import median
from time import perf_counter
from typing import Protocol
from shared.contracts import AnchorDecision
@@ -7,10 +9,12 @@ from shared.contracts import AnchorDecision
from .types import (
AnchorFrame,
AnchorVerificationResult,
CandidateTile,
GeometryGateConfig,
MatchEvidence,
MatcherBenchmarkReport,
MatcherBenchmarkResult,
MatcherProfile,
)
@@ -21,11 +25,98 @@ class AnchorVerifier(Protocol):
"""Return an anchor decision for one candidate."""
class FeatureMatcher(Protocol):
"""Computes correspondence evidence from local frame and tile inputs."""
def compute(
self,
frame: AnchorFrame,
tile: CandidateTile,
matcher_profile: MatcherProfile,
) -> MatchEvidence:
"""Return matcher and geometry evidence for one candidate tile."""
class KeypointRansacMatcher:
"""Small CPU matcher for keypoint fixtures and dependency-gated runs."""
def __init__(self, inlier_threshold_px: float = 2.0) -> None:
self._inlier_threshold_px = inlier_threshold_px
def compute(
self,
frame: AnchorFrame,
tile: CandidateTile,
matcher_profile: MatcherProfile,
) -> MatchEvidence:
started = perf_counter()
correspondences = tuple(zip(frame.keypoints, tile.keypoints))
if len(correspondences) < 4:
return MatchEvidence(
candidate=tile.candidate,
matcher_profile=matcher_profile,
inliers=0,
mean_reprojection_error_px=self._inlier_threshold_px + 1.0,
homography=None,
runtime_ms=(perf_counter() - started) * 1000.0,
provenance_trusted=tile.provenance_trusted,
evidence_source="computed_geometry",
)
dx_values = tuple(tile_point[0] - frame_point[0] for frame_point, tile_point in correspondences)
dy_values = tuple(tile_point[1] - frame_point[1] for frame_point, tile_point in correspondences)
dx = median(dx_values)
dy = median(dy_values)
residuals = tuple(
((frame_point[0] + dx - tile_point[0]) ** 2 + (frame_point[1] + dy - tile_point[1]) ** 2)
** 0.5
for frame_point, tile_point in correspondences
)
inlier_residuals = tuple(
residual for residual in residuals if residual <= self._inlier_threshold_px
)
mean_error = (
sum(inlier_residuals) / len(inlier_residuals)
if inlier_residuals
else self._inlier_threshold_px + 1.0
)
homography = (
{
"h00": 1.0,
"h01": 0.0,
"h02": dx,
"h10": 0.0,
"h11": 1.0,
"h12": dy,
"h20": 0.0,
"h21": 0.0,
"h22": 1.0,
}
if inlier_residuals
else None
)
return MatchEvidence(
candidate=tile.candidate,
matcher_profile=matcher_profile,
inliers=len(inlier_residuals),
mean_reprojection_error_px=mean_error,
homography=homography,
runtime_ms=(perf_counter() - started) * 1000.0,
provenance_trusted=tile.provenance_trusted,
evidence_source="computed_geometry",
)
class GeometryGatedAnchorVerifier:
"""Converts matcher evidence into accepted/rejected anchor decisions."""
def __init__(self, gates: GeometryGateConfig | None = None) -> None:
def __init__(
self,
gates: GeometryGateConfig | None = None,
matcher: FeatureMatcher | None = None,
) -> None:
self._gates = gates or GeometryGateConfig()
self._matcher = matcher or KeypointRansacMatcher()
def verify(self, frame: AnchorFrame, evidence: MatchEvidence) -> AnchorVerificationResult:
accepted, reason = self._classify(frame, evidence)
@@ -45,6 +136,15 @@ class GeometryGatedAnchorVerifier:
freshness_status=evidence.candidate.freshness_status,
)
def verify_candidate(
self,
frame: AnchorFrame,
tile: CandidateTile,
matcher_profile: MatcherProfile = "sift_orb",
) -> AnchorVerificationResult:
evidence = self._matcher.compute(frame, tile, matcher_profile)
return self.verify(frame, evidence)
def benchmark(
self, frame: AnchorFrame, evidences: tuple[MatchEvidence, ...]
) -> MatcherBenchmarkReport:
@@ -65,6 +165,17 @@ class GeometryGatedAnchorVerifier:
results=tuple(results),
)
def benchmark_candidates(
self,
frame: AnchorFrame,
tiles: tuple[CandidateTile, ...],
matcher_profile: MatcherProfile = "sift_orb",
) -> MatcherBenchmarkReport:
return self.benchmark(
frame,
tuple(self._matcher.compute(frame, tile, matcher_profile) for tile in tiles),
)
def _classify(self, frame: AnchorFrame, evidence: MatchEvidence) -> tuple[bool, str]:
if not frame.usable_for_anchor:
return False, "frame_not_usable"
+2 -2
View File
@@ -1,3 +1,3 @@
# Anchor Verification Native Bridge
# Anchor Verification Matcher Package
Reserved for native feature extraction, matching, and RANSAC acceleration code owned by `anchor_verification`.
Exports local feature matching and geometry verification boundaries owned by `anchor_verification`.
@@ -0,0 +1,5 @@
"""Anchor feature matching package exports."""
from anchor_verification.interfaces import FeatureMatcher, KeypointRansacMatcher
__all__ = ["FeatureMatcher", "KeypointRansacMatcher"]
+9
View File
@@ -18,6 +18,14 @@ class AnchorFrame(AnchorVerificationModel):
frame_id: str = Field(min_length=1)
image_ref: str = Field(min_length=1)
usable_for_anchor: bool = True
keypoints: tuple[tuple[float, float], ...] = ()
class CandidateTile(AnchorVerificationModel):
candidate: VprCandidate
image_ref: str = Field(min_length=1)
keypoints: tuple[tuple[float, float], ...] = ()
provenance_trusted: bool = True
class GeometryGateConfig(AnchorVerificationModel):
@@ -33,6 +41,7 @@ class MatchEvidence(AnchorVerificationModel):
homography: dict[str, float] | None = None
runtime_ms: NonNegativeFloat
provenance_trusted: bool = True
evidence_source: Literal["computed_geometry", "external_evidence"] = "external_evidence"
class AnchorVerificationResult(AnchorVerificationModel):
+9 -1
View File
@@ -1,6 +1,12 @@
"""Offline satellite retrieval and synchronization component."""
from .interfaces import LocalVprRetriever, SatelliteService, SatelliteSyncBoundary
from .interfaces import (
CpuFaissDescriptorIndex,
DescriptorIndex,
LocalVprRetriever,
SatelliteService,
SatelliteSyncBoundary,
)
from .types import (
DescriptorFidelityReport,
GeneratedTileUploadRecord,
@@ -18,7 +24,9 @@ from .types import (
)
__all__ = [
"CpuFaissDescriptorIndex",
"DescriptorFidelityReport",
"DescriptorIndex",
"GeneratedTileUploadRecord",
"LocalVprIndexPackage",
"LocalVprRetriever",
+165 -78
View File
@@ -1,7 +1,9 @@
"""Public satellite service interfaces."""
from collections.abc import Callable
from math import sqrt
from collections.abc import Callable
from pathlib import Path
from time import perf_counter
from typing import Protocol
from shared.contracts import VprCandidate
@@ -19,6 +21,7 @@ from .types import (
SatelliteSyncResult,
SatelliteSyncStatus,
UploadOutcome,
VprDescriptorRecord,
VprReadinessReport,
VprRetrievalResult,
)
@@ -34,95 +37,47 @@ class SatelliteService(Protocol):
"""Return candidate anchor records for one frame."""
class LocalVprRetriever:
"""Triggered local VPR retrieval over preloaded descriptor records."""
class DescriptorIndex(Protocol):
"""Search boundary for local descriptor packages."""
def __init__(self) -> None:
self._index: LocalVprIndexPackage | None = None
@property
def record_count(self) -> int:
"""Return the number of loaded descriptor records."""
def load_index(self, package: LocalVprIndexPackage) -> VprReadinessReport:
self._index = package
return VprReadinessReport(
ready=True,
engine=package.engine,
loaded_records=len(package.records),
)
def search(
self,
query_descriptor: tuple[float, ...],
top_k: int,
) -> tuple[tuple[float, VprDescriptorRecord], ...]:
"""Return scored descriptor records in descending score order."""
def readiness(self) -> VprReadinessReport:
if self._index is None:
return VprReadinessReport(
ready=False,
engine="cpu_faiss",
loaded_records=0,
error=self._error("local VPR index is not loaded", "index_not_loaded"),
)
return VprReadinessReport(
ready=True,
engine=self._index.engine,
loaded_records=len(self._index.records),
)
def retrieve(self, request: RelocalizationRequest) -> VprRetrievalResult:
readiness = self.readiness()
if not readiness.ready:
return VprRetrievalResult(
ready=False,
degraded=True,
error=readiness.error,
)
class CpuFaissDescriptorIndex:
"""CPU vector index with a FAISS-compatible search contract."""
assert self._index is not None
query_descriptor = request.query_descriptor or self._extract_descriptor(request.image_ref)
def __init__(self, records: tuple[VprDescriptorRecord, ...]) -> None:
self._records = tuple(record for record in records if record.freshness_status != "rejected")
@property
def record_count(self) -> int:
return len(self._records)
def search(
self,
query_descriptor: tuple[float, ...],
top_k: int,
) -> tuple[tuple[float, VprDescriptorRecord], ...]:
scored = sorted(
(
(self._similarity(query_descriptor, record.descriptor), record)
for record in self._index.records
if record.freshness_status != "rejected"
(self._cosine_similarity(query_descriptor, record.descriptor), record)
for record in self._records
),
key=lambda item: item[0],
reverse=True,
)
candidates = tuple(
VprCandidate(
chunk_id=record.chunk_id,
tile_id=record.tile_id,
score=score,
footprint=record.footprint,
freshness_status=record.freshness_status,
)
for score, record in scored[: request.top_k]
)
if not candidates:
return VprRetrievalResult(
ready=True,
degraded=True,
error=self._error("local VPR index produced no valid candidates", "no_candidates"),
)
return tuple(scored[:top_k])
return VprRetrievalResult(ready=True, degraded=False, candidates=candidates)
def verify_descriptor_fidelity(
self,
reference_descriptor: tuple[float, ...],
optimized_descriptor: tuple[float, ...],
max_l2_delta: float,
) -> DescriptorFidelityReport:
observed_delta = self._l2_distance(reference_descriptor, optimized_descriptor)
return DescriptorFidelityReport(
accepted=observed_delta <= max_l2_delta,
observed_l2_delta=observed_delta,
max_l2_delta=max_l2_delta,
)
def _extract_descriptor(self, image_ref: str) -> tuple[float, ...]:
encoded = image_ref.encode("utf-8")
buckets = [0.0, 0.0, 0.0, 0.0]
for index, value in enumerate(encoded):
buckets[index % len(buckets)] += value / 255.0
magnitude = sqrt(sum(value * value for value in buckets)) or 1.0
return tuple(value / magnitude for value in buckets)
def _similarity(
def _cosine_similarity(
self,
query_descriptor: tuple[float, ...],
record_descriptor: tuple[float, ...],
@@ -138,6 +93,138 @@ class LocalVprRetriever:
record_norm = sqrt(sum(value * value for value in padded_record)) or 1.0
return max(0.0, min(1.0, dot_product / (query_norm * record_norm)))
class LocalVprRetriever:
"""Triggered local VPR retrieval over mission-cache descriptor indexes."""
def __init__(self) -> None:
self._package: LocalVprIndexPackage | None = None
self._index: DescriptorIndex | None = None
self._load_error: ErrorEnvelope | None = None
def load_index(self, package: LocalVprIndexPackage) -> VprReadinessReport:
self._package = package
self._index = CpuFaissDescriptorIndex(package.records)
self._load_error = None
return VprReadinessReport(
ready=self._index.record_count > 0,
engine=package.engine,
loaded_records=self._index.record_count,
package_id=package.package_id,
descriptor_model=package.descriptor_model,
error=None
if self._index.record_count > 0
else self._error("local descriptor index has no searchable records", "empty_index"),
)
def load_index_from_path(self, package_path: str | Path) -> VprReadinessReport:
try:
return self.load_index(LocalVprIndexPackage.from_json_file(package_path))
except (FileNotFoundError, OSError, ValueError) as exc:
self._package = None
self._index = None
self._load_error = self._error(
f"local descriptor index package could not be loaded: {exc}",
"index_package_invalid",
)
return VprReadinessReport(
ready=False,
engine="cpu_faiss",
loaded_records=0,
error=self._load_error,
)
def readiness(self) -> VprReadinessReport:
if self._load_error is not None:
return VprReadinessReport(
ready=False,
engine="cpu_faiss",
loaded_records=0,
error=self._load_error,
)
if self._index is None or self._package is None:
return VprReadinessReport(
ready=False,
engine="cpu_faiss",
loaded_records=0,
error=self._error("local descriptor index is not loaded", "index_not_loaded"),
)
return VprReadinessReport(
ready=True,
engine=self._package.engine,
loaded_records=self._index.record_count,
package_id=self._package.package_id,
descriptor_model=self._package.descriptor_model,
)
def retrieve(self, request: RelocalizationRequest) -> VprRetrievalResult:
started = perf_counter()
readiness = self.readiness()
if not readiness.ready:
return VprRetrievalResult(
ready=False,
degraded=True,
retrieval_path="unavailable",
error=readiness.error,
)
if request.query_descriptor is None:
return VprRetrievalResult(
ready=True,
degraded=True,
retrieval_path="unavailable",
error=self._error(
"query descriptor is required for local descriptor index retrieval",
"query_descriptor_missing",
),
)
assert self._index is not None
scored = self._index.search(request.query_descriptor, request.top_k)
candidates = tuple(
VprCandidate(
chunk_id=record.chunk_id,
tile_id=record.tile_id,
score=score,
footprint=record.footprint,
freshness_status=record.freshness_status,
)
for score, record in scored[: request.top_k]
)
latency_ms = (perf_counter() - started) * 1000.0
if not candidates:
return VprRetrievalResult(
ready=True,
degraded=True,
retrieval_path="local_descriptor_index",
latency_ms=latency_ms,
error=self._error(
"local descriptor index produced no valid candidates",
"no_candidates",
),
)
return VprRetrievalResult(
ready=True,
degraded=False,
candidates=candidates,
retrieval_path="local_descriptor_index",
latency_ms=latency_ms,
)
def verify_descriptor_fidelity(
self,
reference_descriptor: tuple[float, ...],
optimized_descriptor: tuple[float, ...],
max_l2_delta: float,
) -> DescriptorFidelityReport:
observed_delta = self._l2_distance(reference_descriptor, optimized_descriptor)
return DescriptorFidelityReport(
accepted=observed_delta <= max_l2_delta,
observed_l2_delta=observed_delta,
max_l2_delta=max_l2_delta,
)
def _l2_distance(
self,
reference_descriptor: tuple[float, ...],
+2 -2
View File
@@ -1,3 +1,3 @@
# Satellite Service Native Bridge
# Satellite Service Descriptor Index Package
Reserved for ONNX/TensorRT descriptor inference integrations owned by `satellite_service`.
Exports local descriptor index search boundaries owned by `satellite_service`.
+5
View File
@@ -0,0 +1,5 @@
"""Local descriptor index package exports."""
from satellite_service.interfaces import CpuFaissDescriptorIndex, DescriptorIndex
__all__ = ["CpuFaissDescriptorIndex", "DescriptorIndex"]
+14 -1
View File
@@ -1,8 +1,10 @@
"""Public satellite service models."""
import json
from pathlib import Path
from typing import Literal
from pydantic import BaseModel, ConfigDict, Field, PositiveInt
from pydantic import BaseModel, ConfigDict, Field, PositiveInt, ValidationError
from shared.contracts import VprCandidate
from shared.errors import ErrorEnvelope
@@ -57,8 +59,14 @@ class VprDescriptorRecord(SatelliteServiceModel):
class LocalVprIndexPackage(SatelliteServiceModel):
package_id: str = Field(min_length=1)
engine: Literal["cpu_faiss"] = "cpu_faiss"
descriptor_model: str = Field(default="dinov2_vlad", min_length=1)
records: tuple[VprDescriptorRecord, ...] = Field(min_length=1)
@classmethod
def from_json_file(cls, package_path: str | Path) -> "LocalVprIndexPackage":
payload = json.loads(Path(package_path).read_text(encoding="utf-8"))
return cls.model_validate(payload)
class RelocalizationRequest(SatelliteServiceModel):
frame_id: str = Field(min_length=1)
@@ -72,6 +80,8 @@ class VprReadinessReport(SatelliteServiceModel):
ready: bool
engine: Literal["cpu_faiss"]
loaded_records: int = Field(ge=0)
package_id: str | None = None
descriptor_model: str | None = None
error: ErrorEnvelope | None = None
@@ -79,6 +89,8 @@ class VprRetrievalResult(SatelliteServiceModel):
ready: bool
degraded: bool
candidates: tuple[VprCandidate, ...] = ()
retrieval_path: Literal["local_descriptor_index", "unavailable"] = "unavailable"
latency_ms: float | None = Field(default=None, ge=0.0)
error: ErrorEnvelope | None = None
@@ -90,3 +102,4 @@ class DescriptorFidelityReport(SatelliteServiceModel):
RuntimePhase = Literal["pre_flight", "in_flight", "post_flight"]
UploadOutcome = Literal["success", "retryable_failure", "rejected"]
IndexLoadError = FileNotFoundError | json.JSONDecodeError | ValidationError | OSError
+13 -2
View File
@@ -1,13 +1,24 @@
"""Replaceable VIO adapter component."""
from .interfaces import DeterministicVioBackend, LocalVioAdapter, VioAdapter, VioBackend
from .interfaces import (
LocalVioAdapter,
NativeVioBackend,
NativeVioRunner,
ReplayVioBackend,
VioAdapter,
VioBackend,
VioBackendError,
)
from .types import VioBackendEstimate, VioHealthReport, VioInputPacket, VioProcessingResult
__all__ = [
"DeterministicVioBackend",
"LocalVioAdapter",
"NativeVioBackend",
"NativeVioRunner",
"ReplayVioBackend",
"VioAdapter",
"VioBackend",
"VioBackendError",
"VioBackendEstimate",
"VioHealthReport",
"VioInputPacket",
+105 -8
View File
@@ -1,6 +1,7 @@
"""Public VIO adapter interfaces."""
from typing import Any, Protocol
from time import perf_counter
from typing import Any, Protocol, runtime_checkable
from shared.contracts import VioStatePacket
from shared.errors import ErrorEnvelope
@@ -28,7 +29,7 @@ class VioAdapter(Protocol):
class VioBackend(Protocol):
"""Backend-neutral native bridge boundary."""
"""Backend-neutral VIO execution boundary."""
def initialize(self) -> None:
"""Initialize native backend resources."""
@@ -37,8 +38,61 @@ class VioBackend(Protocol):
"""Return one relative VIO estimate."""
class DeterministicVioBackend:
"""Small deterministic backend used until a native bridge is attached."""
@runtime_checkable
class NativeVioRunner(Protocol):
"""Runtime object supplied by the selected VIO engine package."""
def initialize(self) -> None:
"""Prepare engine resources."""
def estimate(self, frame: Any, telemetry_window: tuple[Any, ...]) -> VioBackendEstimate | dict[str, Any]:
"""Return an estimate payload for one synchronized replay frame."""
class VioBackendError(RuntimeError):
"""Raised when the configured VIO engine cannot produce an estimate."""
class NativeVioBackend:
"""Configurable backend adapter for native VIO engine packages."""
def __init__(self, runner: NativeVioRunner, backend_name: str = "native_vio") -> None:
self._runner = runner
self.backend_name = backend_name
def initialize(self) -> None:
try:
self._runner.initialize()
except Exception as exc:
raise VioBackendError(f"{self.backend_name} initialization failed") from exc
def estimate(self, frame: Any, telemetry_window: tuple[Any, ...]) -> VioBackendEstimate:
started = perf_counter()
try:
estimate = self._runner.estimate(frame, telemetry_window)
except Exception as exc:
raise VioBackendError(f"{self.backend_name} estimate failed") from exc
try:
if isinstance(estimate, VioBackendEstimate):
if estimate.processing_latency_ms is not None:
return estimate
payload = estimate.model_dump()
else:
payload = dict(estimate)
payload.setdefault("timestamp_ns", frame.timestamp_ns)
payload["processing_latency_ms"] = payload.get("processing_latency_ms") or (
perf_counter() - started
) * 1000.0
return VioBackendEstimate.model_validate(payload)
except Exception as exc:
raise VioBackendError(f"{self.backend_name} returned invalid estimate") from exc
class ReplayVioBackend:
"""Small local backend for replay smoke tests when no engine is configured."""
backend_name = "replay_vio"
def initialize(self) -> None:
return None
@@ -74,7 +128,8 @@ class LocalVioAdapter:
timestamp_tolerance_ns: int = 5_000_000,
degraded_quality_threshold: float = 0.35,
) -> None:
self._backend = backend or DeterministicVioBackend()
self._backend = backend or ReplayVioBackend()
self._backend_name = getattr(self._backend, "backend_name", self._backend.__class__.__name__)
self._timestamp_tolerance_ns = timestamp_tolerance_ns
self._degraded_quality_threshold = degraded_quality_threshold
self._initialized = False
@@ -82,20 +137,35 @@ class LocalVioAdapter:
initialized=False,
state="not_initialized",
tracking_quality=0.0,
backend_name=self._backend_name,
)
def initialize(self) -> None:
self._backend.initialize()
try:
self._backend.initialize()
except Exception as exc:
self._initialized = False
self._health = VioHealthReport(
initialized=False,
state="failed",
tracking_quality=0.0,
backend_name=self._backend_name,
error=self._backend_error(str(exc), "backend_initialization_failed", "error"),
)
return None
self._initialized = True
self._health = VioHealthReport(
initialized=True,
state="ready",
tracking_quality=1.0,
backend_name=self._backend_name,
)
def process(self, packet: VioInputPacket) -> VioProcessingResult:
if not self._initialized:
self.initialize()
if self._health.state == "failed":
return VioProcessingResult(health=self._health, error=self._health.error)
telemetry_timestamps = [sample.timestamp_ns for sample in packet.telemetry_samples]
time_window = select_time_window(
@@ -116,6 +186,7 @@ class LocalVioAdapter:
initialized=True,
state="degraded",
tracking_quality=0.0,
backend_name=self._backend_name,
error=error,
)
return VioProcessingResult(health=self._health, error=error)
@@ -125,7 +196,18 @@ class LocalVioAdapter:
for sample in packet.telemetry_samples
if sample.timestamp_ns in set(time_window.sample_timestamps_ns)
)
estimate = self._backend.estimate(packet.frame, telemetry_window)
try:
estimate = self._backend.estimate(packet.frame, telemetry_window)
except Exception as exc:
error = self._backend_error(str(exc), "backend_runtime_failed", "error")
self._health = VioHealthReport(
initialized=True,
state="failed",
tracking_quality=0.0,
backend_name=self._backend_name,
error=error,
)
return VioProcessingResult(health=self._health, error=error)
state_packet = VioStatePacket(
timestamp_ns=estimate.timestamp_ns,
relative_pose=estimate.relative_pose,
@@ -141,8 +223,23 @@ class LocalVioAdapter:
initialized=True,
state=health_state,
tracking_quality=estimate.tracking_quality,
backend_name=self._backend_name,
)
return VioProcessingResult(
state_packet=state_packet,
health=self._health,
processing_latency_ms=estimate.processing_latency_ms,
)
return VioProcessingResult(state_packet=state_packet, health=self._health)
def health(self) -> VioHealthReport:
return self._health
def _backend_error(self, message: str, cause: str, severity: str) -> ErrorEnvelope:
return ErrorEnvelope(
component="vio_adapter",
category="runtime",
message=message,
severity=severity,
retryable=False,
cause=cause,
)
+2 -2
View File
@@ -1,3 +1,3 @@
# VIO Native Bridge
# VIO Native Backend Package
Reserved for native VIO backend integration code owned by `vio_adapter`.
Exports the configured VIO backend package boundary owned by `vio_adapter`.
+5
View File
@@ -0,0 +1,5 @@
"""Native VIO backend package exports."""
from vio_adapter.interfaces import NativeVioBackend, NativeVioRunner, VioBackendError
__all__ = ["NativeVioBackend", "NativeVioRunner", "VioBackendError"]
+3
View File
@@ -21,12 +21,14 @@ class VioHealthReport(VioAdapterModel):
initialized: bool
state: Literal["not_initialized", "ready", "degraded", "failed"]
tracking_quality: float = Field(ge=0.0, le=1.0)
backend_name: str | None = None
error: ErrorEnvelope | None = None
class VioProcessingResult(VioAdapterModel):
state_packet: VioStatePacket | None = None
health: VioHealthReport
processing_latency_ms: float | None = Field(default=None, ge=0.0)
error: ErrorEnvelope | None = None
@@ -37,3 +39,4 @@ class VioBackendEstimate(VioAdapterModel):
tracking_quality: float = Field(ge=0.0, le=1.0)
bias_estimate: dict[str, float] | None = None
covariance_hint: list[list[float]] | None = None
processing_latency_ms: float | None = Field(default=None, ge=0.0)