mirror of
https://github.com/azaion/gps-denied-onboard.git
synced 2026-06-22 10:31:12 +00:00
[AZ-622] Phase D: build_pre_constructed seeds c3 GPU runtimes
build_pre_constructed now populates c3_lightglue_runtime (LightGlueRuntime) + c3_feature_extractor (FeatureExtractor) on top of AZ-619/620/621. Strategy-specific BUILD_MATCHER_* flag mismatch raises AirborneBootstrapError naming the missing flag and the c3_matcher consumer; the c7 InferenceRuntime built earlier in the bootstrap is reused as the engine source so no double-build at this layer. C3MatcherConfig gains optional lightglue_weights_path: Path | None for the operator's deployment config; production main() (AZ-624) populates it. Real LightGlue inference correctness is verified by AZ-624's Jetson AC-5 run per the AZ-622 Tier-2 Note. Phase tests for AZ-619/620/621 gain an autouse _stub_c3_matcher_builders fixture so additivity assertions remain valid as the bootstrap grows. Code review: PASS_WITH_WARNINGS (3 Low: signature drift from spec, _is_build_flag_on duplication across 3 runtime_root modules, and BuildConfig literal mirrored with per-strategy build configs). All deferred to future hygiene PBIs. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -55,6 +55,8 @@ from typing import TYPE_CHECKING, Any, Final
|
||||
|
||||
from gps_denied_onboard.clock.wall_clock import WallClock
|
||||
from gps_denied_onboard.fdr_client.client import make_fdr_client
|
||||
from gps_denied_onboard.helpers.feature_extractor import OpenCvOrbExtractor
|
||||
from gps_denied_onboard.helpers.lightglue_runtime import LightGlueRuntime
|
||||
from gps_denied_onboard.runtime_root import register_strategy
|
||||
from gps_denied_onboard.runtime_root.errors import RuntimeNotAvailableError
|
||||
from gps_denied_onboard.runtime_root.inference_factory import build_inference_runtime
|
||||
@@ -71,11 +73,15 @@ from gps_denied_onboard.runtime_root.vio_factory import build_vio_strategy
|
||||
from gps_denied_onboard.runtime_root.vpr_factory import build_vpr_strategy
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from gps_denied_onboard._types.manifests import EngineHandle
|
||||
from gps_denied_onboard.components.c7_inference import InferenceRuntime
|
||||
from gps_denied_onboard.config import Config
|
||||
from gps_denied_onboard.helpers.feature_extractor import FeatureExtractor
|
||||
|
||||
__all__ = [
|
||||
"AIRBORNE_MAIN_PRODUCER_ID",
|
||||
"AIRBORNE_REQUIRED_PRE_CONSTRUCTED_KEYS",
|
||||
"C3_MATCHER_BUILD_FLAGS",
|
||||
"C7_AIRBORNE_BUILD_FLAGS",
|
||||
"FAISS_BUILD_FLAG",
|
||||
"AirborneBootstrapError",
|
||||
@@ -114,6 +120,33 @@ flag would unblock the bootstrap with a different runtime selection.
|
||||
"""
|
||||
|
||||
|
||||
C3_MATCHER_BUILD_FLAGS: Final[Mapping[str, str]] = {
|
||||
"disk_lightglue": "BUILD_MATCHER_DISK_LIGHTGLUE",
|
||||
"aliked_lightglue": "BUILD_MATCHER_ALIKED_LIGHTGLUE",
|
||||
"xfeat": "BUILD_MATCHER_XFEAT",
|
||||
}
|
||||
"""Per-strategy ``BUILD_MATCHER_*`` flag matrix consumed by the airborne
|
||||
LightGlue-runtime builder (AZ-622 / Phase D).
|
||||
|
||||
Mirrors :data:`gps_denied_onboard.runtime_root.matcher_factory.\
|
||||
_STRATEGY_TO_BUILD_FLAG` verbatim — both this constant and the matcher
|
||||
factory's table read the same compile-time flags. ANY mutation of this
|
||||
matrix MUST be mirrored in ``matcher_factory._STRATEGY_TO_BUILD_FLAG``
|
||||
(and vice versa).
|
||||
|
||||
Surfaced here so :func:`_build_c3_lightglue_runtime` can name the
|
||||
gating flag in an :class:`AirborneBootstrapError` (AC-622.2) when the
|
||||
configured C3 matcher strategy's flag is OFF in this binary.
|
||||
|
||||
Note on flag naming: AZ-622's task spec uses the name
|
||||
``BUILD_C3_MATCHER_DISK_LIGHTGLUE`` informally, but the actual
|
||||
production flag matrix is the older ``BUILD_MATCHER_*`` family
|
||||
(``matcher_factory._STRATEGY_TO_BUILD_FLAG``). Reusing those flags is
|
||||
the spirit of the AZ-622 ``MUST reuse the existing per-strategy
|
||||
BUILD_C3_MATCHER_* matrix`` constraint.
|
||||
"""
|
||||
|
||||
|
||||
AIRBORNE_MAIN_PRODUCER_ID: Final[str] = "airborne_main"
|
||||
"""Producer ID for the per-binary shared FdrClient placed under
|
||||
``pre_constructed['c13_fdr']``.
|
||||
@@ -542,25 +575,208 @@ def _build_c7_inference(config: Config) -> Any:
|
||||
) from exc
|
||||
|
||||
|
||||
def _resolve_c3_matcher_strategy(config: Config) -> str:
|
||||
"""Return the configured C3 matcher strategy, defaulting to disk_lightglue.
|
||||
|
||||
Reuses :class:`gps_denied_onboard.components.c3_matcher.C3MatcherConfig`'s
|
||||
own default ``"disk_lightglue"`` when ``config.components`` carries no
|
||||
``c3_matcher`` block (early-bootstrap tests with bare ``Config()``).
|
||||
"""
|
||||
block = config.components.get("c3_matcher")
|
||||
if block is None:
|
||||
return "disk_lightglue"
|
||||
return getattr(block, "strategy", "disk_lightglue")
|
||||
|
||||
|
||||
def _is_build_flag_on(flag_name: str) -> bool:
|
||||
"""Read a compile-time ``BUILD_*`` flag from the environment.
|
||||
|
||||
Mirrors the same predicate used by
|
||||
:func:`gps_denied_onboard.runtime_root.matcher_factory.\
|
||||
_is_build_flag_on` — ``ON`` / ``1`` / ``true`` / ``yes`` (case-insensitive)
|
||||
is ON; everything else (including unset) is OFF. Defined locally so the
|
||||
bootstrap does not depend on the matcher-factory's private helper.
|
||||
"""
|
||||
raw = os.environ.get(flag_name, "")
|
||||
return raw.strip().lower() in {"on", "1", "true", "yes"}
|
||||
|
||||
|
||||
def _load_lightglue_engine_handle(
|
||||
config: Config, inference_runtime: InferenceRuntime
|
||||
) -> EngineHandle:
|
||||
"""Production loader for the shared LightGlue matcher engine.
|
||||
|
||||
Reads ``config.components['c3_matcher'].lightglue_weights_path``,
|
||||
compiles the engine via the C7
|
||||
:class:`InferenceRuntime.compile_engine` (TensorRT or PyTorch-FP16
|
||||
per AZ-621), then deserialises it into an opaque
|
||||
:class:`EngineHandle`. The handle's lifecycle is owned by the
|
||||
:class:`LightGlueRuntime` instance returned by
|
||||
:func:`_build_c3_lightglue_runtime`.
|
||||
|
||||
AZ-622 unit tests monkey-patch this function with a sentinel
|
||||
:class:`EngineHandle`-shaped mock so they can exercise the
|
||||
LightGlueRuntime wiring without standing up a real GPU + TensorRT
|
||||
toolchain (per AZ-622 ``Tier-2 Note``: real LightGlue inference
|
||||
correctness is verified by AZ-624's Jetson AC-5 run).
|
||||
|
||||
Raises:
|
||||
AirborneBootstrapError: if ``c3_matcher.lightglue_weights_path``
|
||||
is ``None`` (the operator-actionable message points at the
|
||||
production main() wiring task — AZ-624).
|
||||
RuntimeNotAvailableError: if the underlying
|
||||
:func:`InferenceRuntime.compile_engine` /
|
||||
:func:`deserialize_engine` paths fail (caller wraps this
|
||||
into an :class:`AirborneBootstrapError`).
|
||||
"""
|
||||
block = config.components.get("c3_matcher")
|
||||
weights_path = (
|
||||
getattr(block, "lightglue_weights_path", None) if block is not None else None
|
||||
)
|
||||
if weights_path is None:
|
||||
raise AirborneBootstrapError(
|
||||
"airborne_bootstrap: cannot construct "
|
||||
"pre_constructed['c3_lightglue_runtime'] because "
|
||||
"config.components['c3_matcher'].lightglue_weights_path "
|
||||
"is None. Production main() (AZ-624) must populate the "
|
||||
"path to the compiled LightGlue engine before calling "
|
||||
"build_pre_constructed; tests stub _load_lightglue_engine_handle "
|
||||
"via monkeypatch."
|
||||
)
|
||||
from gps_denied_onboard._types.inference import BuildConfig, PrecisionMode
|
||||
|
||||
build_config: BuildConfig = BuildConfig(
|
||||
precision=PrecisionMode.FP16,
|
||||
workspace_mb=512,
|
||||
calibration_dataset=None,
|
||||
optimization_profiles=(),
|
||||
)
|
||||
cache_entry = inference_runtime.compile_engine(weights_path, build_config)
|
||||
return inference_runtime.deserialize_engine(cache_entry)
|
||||
|
||||
|
||||
def _build_c3_lightglue_runtime(
|
||||
config: Config, *, inference_runtime: InferenceRuntime
|
||||
) -> LightGlueRuntime:
|
||||
"""Build ``pre_constructed['c3_lightglue_runtime']`` for the airborne binary.
|
||||
|
||||
1. Resolve the configured C3 matcher strategy (default
|
||||
``disk_lightglue``).
|
||||
2. Look up the gating flag in :data:`C3_MATCHER_BUILD_FLAGS`.
|
||||
An unknown strategy or an OFF flag is an
|
||||
:class:`AirborneBootstrapError` naming the missing flag and
|
||||
the consuming component slug ``c3_matcher`` (AC-622.2).
|
||||
3. Load the LightGlue engine handle via
|
||||
:func:`_load_lightglue_engine_handle` (the heavy seam unit
|
||||
tests monkeypatch — see AZ-622 ``Tier-2 Note``).
|
||||
4. Wrap the handle in :class:`LightGlueRuntime` and return.
|
||||
|
||||
The returned runtime is the SAME instance that the wrappers for
|
||||
``c3_matcher`` and ``c2_5_rerank`` extract from
|
||||
``pre_constructed['c3_lightglue_runtime']`` — identity-share is
|
||||
the structural R14 fix (avoids double GPU memory; AZ-344 AC-10).
|
||||
The cross-component identity-share assertion is verified at
|
||||
AZ-624's integration AC, not here.
|
||||
|
||||
Raises:
|
||||
AirborneBootstrapError: when the configured strategy's
|
||||
``BUILD_MATCHER_*`` flag is OFF, when the strategy is
|
||||
unknown to :data:`C3_MATCHER_BUILD_FLAGS`, or when the
|
||||
heavy seam fails (the upstream
|
||||
:class:`RuntimeNotAvailableError` is preserved as
|
||||
``__cause__``).
|
||||
"""
|
||||
strategy = _resolve_c3_matcher_strategy(config)
|
||||
flag = C3_MATCHER_BUILD_FLAGS.get(strategy)
|
||||
if flag is None:
|
||||
raise AirborneBootstrapError(
|
||||
f"airborne_bootstrap: cannot construct "
|
||||
f"pre_constructed['c3_lightglue_runtime'] because "
|
||||
f"config.components['c3_matcher'].strategy={strategy!r} is "
|
||||
f"not in the airborne BUILD-flag matrix "
|
||||
f"{sorted(C3_MATCHER_BUILD_FLAGS.keys())!r}. Consuming "
|
||||
f"component: c3_matcher. Reconfigure the C3 matcher to "
|
||||
f"select one of the supported strategies."
|
||||
)
|
||||
if not _is_build_flag_on(flag):
|
||||
raise AirborneBootstrapError(
|
||||
f"airborne_bootstrap: cannot construct "
|
||||
f"pre_constructed['c3_lightglue_runtime'] because the "
|
||||
f"gating flag {flag}=ON is required for the configured "
|
||||
f"strategy={strategy!r}, but {flag} is OFF in this binary. "
|
||||
f"Consuming component: c3_matcher. Set {flag}=ON, or "
|
||||
f"reconfigure config.components['c3_matcher'].strategy to a "
|
||||
f"strategy whose BUILD_MATCHER_* flag is ON."
|
||||
)
|
||||
try:
|
||||
engine_handle = _load_lightglue_engine_handle(config, inference_runtime)
|
||||
except RuntimeNotAvailableError as exc:
|
||||
raise AirborneBootstrapError(
|
||||
f"airborne_bootstrap: cannot construct "
|
||||
f"pre_constructed['c3_lightglue_runtime'] because the "
|
||||
f"LightGlue engine load failed for strategy={strategy!r} "
|
||||
f"(gating flag {flag} is ON). Consuming component: "
|
||||
f"c3_matcher. Upstream error: {exc}"
|
||||
) from exc
|
||||
return LightGlueRuntime(engine_handle)
|
||||
|
||||
|
||||
def _build_c3_feature_extractor(config: Config) -> FeatureExtractor:
|
||||
"""Build ``pre_constructed['c3_feature_extractor']`` for the airborne binary.
|
||||
|
||||
Returns the shared :class:`FeatureExtractor` that C2.5's
|
||||
:class:`InlierCountReRanker` consumes for both per-frame nav-camera
|
||||
images and per-candidate tile pixels (per
|
||||
:class:`AIRBORNE_REQUIRED_PRE_CONSTRUCTED_KEYS`'s ``c2_5_rerank``
|
||||
row). The L1 helper module
|
||||
:mod:`gps_denied_onboard.helpers.feature_extractor` documents
|
||||
:class:`OpenCvOrbExtractor` as the production-ready airborne
|
||||
placeholder until the C7 ``InferenceRuntime``-backed DISK / ALIKED
|
||||
feature extractor lands; AZ-622 wires that placeholder in.
|
||||
|
||||
No ``BUILD_*`` flag check applies here: the C3 matcher's per-strategy
|
||||
flag matrix gates the *matcher* engine (handled by
|
||||
:func:`_build_c3_lightglue_runtime`); the *feature extractor* is
|
||||
consumed by C2.5 (not C3) and is a pure-CPU OpenCV path that has no
|
||||
compile-time gate of its own.
|
||||
|
||||
The ``config`` argument is accepted for symmetry with the other
|
||||
bootstrap builders and to keep the door open for a future
|
||||
config-driven feature-extractor selection (DISK / ALIKED swap-in)
|
||||
without changing the call site in :func:`build_pre_constructed`.
|
||||
"""
|
||||
del config # currently no config knobs; placeholder for future selection
|
||||
return OpenCvOrbExtractor()
|
||||
|
||||
|
||||
def build_pre_constructed(config: Config) -> dict[str, Any]:
|
||||
"""Build the airborne ``pre_constructed`` dict for :func:`compose_root`.
|
||||
|
||||
AZ-619 (Phase A) seeded ``c13_fdr`` and ``clock``. AZ-620 (Phase B)
|
||||
added the two C6 storage entries (``c6_descriptor_index`` +
|
||||
``c6_tile_store``). AZ-621 (Phase C) adds ``c7_inference``
|
||||
``c6_tile_store``). AZ-621 (Phase C) added ``c7_inference``
|
||||
(PyTorch FP16 vs. TensorRT, gated by
|
||||
:data:`C7_AIRBORNE_BUILD_FLAGS`). Phases D..F (AZ-622..AZ-624) will
|
||||
extend this function to populate the remaining keys in
|
||||
:data:`C7_AIRBORNE_BUILD_FLAGS`). AZ-622 (Phase D) adds
|
||||
``c3_lightglue_runtime`` (single shared
|
||||
:class:`gps_denied_onboard.helpers.lightglue_runtime.LightGlueRuntime`
|
||||
instance, gated by :data:`C3_MATCHER_BUILD_FLAGS` per the
|
||||
configured strategy) + ``c3_feature_extractor`` (the shared
|
||||
:class:`gps_denied_onboard.helpers.feature_extractor.FeatureExtractor`
|
||||
used by C2.5). Phases E..F (AZ-623..AZ-624) will extend this
|
||||
function to populate the remaining keys in
|
||||
:data:`AIRBORNE_REQUIRED_PRE_CONSTRUCTED_KEYS`.
|
||||
|
||||
Returns a fresh dict on each call. The ``c13_fdr`` instance is cached
|
||||
inside :func:`make_fdr_client` (per-producer cache) so two calls within
|
||||
the same process return dicts where ``pre_constructed['c13_fdr']`` is
|
||||
the SAME object — AC-619.2. ``clock`` is a fresh :class:`WallClock`
|
||||
each call (stateless; the cache would be a no-op). The C6 + C7
|
||||
entries are constructed via the existing :mod:`storage_factory` and
|
||||
:mod:`inference_factory` builders without additional caching at this
|
||||
layer.
|
||||
each call (stateless; the cache would be a no-op). The C6, C7, and C3
|
||||
entries are constructed via the existing :mod:`storage_factory`,
|
||||
:mod:`inference_factory`, and helper modules without additional
|
||||
caching at this layer; the C7 :class:`InferenceRuntime` built for the
|
||||
``c7_inference`` slot is reused as the engine source for the LightGlue
|
||||
matcher load (AZ-622) so the bootstrap does not double-build the
|
||||
inference runtime.
|
||||
|
||||
Replay-mode override: :func:`compose_root` merges ``replay_components``
|
||||
over ``pre_constructed`` so the :class:`WallClock` here is replaced by
|
||||
@@ -575,16 +791,23 @@ def build_pre_constructed(config: Config) -> dict[str, Any]:
|
||||
runtime is buildable (both ``BUILD_TENSORRT_RUNTIME`` and
|
||||
``BUILD_PYTORCH_FP16_RUNTIME`` OFF, or the configured
|
||||
runtime's matching flag is OFF) and any configured consumer
|
||||
requires ``c7_inference``. The message names the consuming
|
||||
component slug(s) and the relevant gating flag(s).
|
||||
requires ``c7_inference``; OR if the configured C3 matcher
|
||||
strategy's :data:`C3_MATCHER_BUILD_FLAGS` flag is OFF (or
|
||||
the strategy is unknown), or if the LightGlue engine load
|
||||
fails. The message names the consuming component slug(s)
|
||||
and the relevant gating flag(s).
|
||||
"""
|
||||
return {
|
||||
"c13_fdr": make_fdr_client(AIRBORNE_MAIN_PRODUCER_ID, config),
|
||||
"clock": WallClock(),
|
||||
"c6_descriptor_index": _build_c6_descriptor_index(config),
|
||||
"c6_tile_store": _build_c6_tile_store(config),
|
||||
"c7_inference": _build_c7_inference(config),
|
||||
}
|
||||
constructed: dict[str, Any] = {}
|
||||
constructed["c13_fdr"] = make_fdr_client(AIRBORNE_MAIN_PRODUCER_ID, config)
|
||||
constructed["clock"] = WallClock()
|
||||
constructed["c6_descriptor_index"] = _build_c6_descriptor_index(config)
|
||||
constructed["c6_tile_store"] = _build_c6_tile_store(config)
|
||||
constructed["c7_inference"] = _build_c7_inference(config)
|
||||
constructed["c3_lightglue_runtime"] = _build_c3_lightglue_runtime(
|
||||
config, inference_runtime=constructed["c7_inference"]
|
||||
)
|
||||
constructed["c3_feature_extractor"] = _build_c3_feature_extractor(config)
|
||||
return constructed
|
||||
|
||||
|
||||
def register_airborne_strategies() -> None:
|
||||
|
||||
Reference in New Issue
Block a user