[AZ-621] Phase C: build_pre_constructed seeds c7_inference

Third subtask of AZ-618. Extends airborne_bootstrap.build_pre_constructed
additively with c7_inference (GPU InferenceRuntime). Wraps the existing
inference_factory.build_inference_runtime so a BUILD_TENSORRT_RUNTIME /
BUILD_PYTORCH_FP16_RUNTIME mismatch surfaces a clear operator-facing
AirborneBootstrapError naming BOTH airborne C7 flags plus the consuming
component slug, rather than bubbling up RuntimeNotAvailableError with no
context.

New public const C7_AIRBORNE_BUILD_FLAGS pairs each airborne runtime
with its gating env flag (onnx_trt_ep deliberately omitted — research
only). Tests stub at the factory boundary; real GPU/TensorRT load
remains Tier-2 only (consolidated at AZ-624). AZ-619 and AZ-620 test
files extended with a _stub_c7_inference_builder autouse fixture
mirroring the AZ-620 pattern for _build_c6_*.

18/18 runtime_root unit tests pass.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Oleksandr Bezdieniezhnykh
2026-05-19 06:47:05 +03:00
parent 1ab93fe0c7
commit 680ba29ae6
8 changed files with 398 additions and 11 deletions
@@ -67,6 +67,19 @@ def _stub_c6_builders(monkeypatch: pytest.MonkeyPatch) -> None:
)
@pytest.fixture(autouse=True)
def _stub_c7_inference_builder(monkeypatch: pytest.MonkeyPatch) -> None:
# Arrange: stub the AZ-621 Phase C C7 inference builder so the AZ-619
# tests stay focused on Phase A. Without this the bare Config() below
# would hit KeyError inside inference_factory's
# config.components["c7_inference"] lookup, AND the airborne BUILD_*
# flags are typically unset in the test env. The sentinel is opaque
# on purpose — AZ-619 assertions never inspect it.
monkeypatch.setattr(
airborne_bootstrap, "_build_c7_inference", lambda _config: object()
)
def test_ac_619_1_default_config_seeds_c13_fdr_and_clock() -> None:
# Arrange
config = Config()
@@ -60,6 +60,20 @@ def _isolated_fdr_cache() -> Iterator[None]:
fdr_client_module._reset_for_tests()
@pytest.fixture(autouse=True)
def _stub_c7_inference_builder(monkeypatch: pytest.MonkeyPatch) -> None:
# Arrange: stub the AZ-621 Phase C C7 inference builder so AZ-620
# tests stay focused on the Phase B contract. Without this the
# configs used below would hit KeyError inside inference_factory's
# config.components["c7_inference"] lookup. The sentinel is opaque
# on purpose — AZ-620 assertions never inspect it.
monkeypatch.setattr(
airborne_bootstrap,
"_build_c7_inference",
lambda _config: MagicMock(name="InferenceRuntime"),
)
def test_ac_620_1_adds_c6_descriptor_index_and_c6_tile_store(
monkeypatch: pytest.MonkeyPatch,
) -> None:
@@ -0,0 +1,180 @@
"""AZ-621 — Phase C of AZ-618: ``build_pre_constructed`` seeds c7_inference.
Verifies the contract at
``_docs/02_tasks/todo/AZ-621_pre_constructed_phase_c_c7_inference.md``:
* AC-621.1: with a default airborne ``Config`` (and one of the airborne
C7 ``BUILD_*`` flags ON in spirit — here stubbed at the factory
boundary), ``build_pre_constructed(config)`` additionally contains
``c7_inference`` (InferenceRuntime instance) on top of AZ-619 + AZ-620.
* AC-621.2: when BOTH ``BUILD_TENSORRT_RUNTIME=OFF`` AND
``BUILD_PYTORCH_FP16_RUNTIME=OFF`` AND a config selects a C2 / C3
strategy that needs c7 (here: ``c3_matcher.strategy="disk_lightglue"``),
``build_pre_constructed`` raises ``AirborneBootstrapError`` whose
message names both airborne flags AND the consuming component slug.
AC-621.3 (this file exists with the above tests) is satisfied by the
existence of this module.
The tests stub the heavy ``build_inference_runtime`` factory at the
airborne_bootstrap import boundary — exactly mirroring the AZ-620
pattern for the C6 factories. The factory itself has its own
component-level unit tests under ``tests/unit/c7_inference/`` for the
real engine-load paths; real GPU model load is Tier-2 only (consolidated
under AZ-624 per the umbrella's Tier-2 testing strategy).
"""
from __future__ import annotations
from collections.abc import Iterator
from dataclasses import dataclass
from unittest.mock import MagicMock
import pytest
from gps_denied_onboard.config import Config
from gps_denied_onboard.fdr_client import client as fdr_client_module
from gps_denied_onboard.runtime_root import airborne_bootstrap
from gps_denied_onboard.runtime_root.airborne_bootstrap import (
C7_AIRBORNE_BUILD_FLAGS,
AirborneBootstrapError,
build_pre_constructed,
)
from gps_denied_onboard.runtime_root.errors import RuntimeNotAvailableError
@dataclass(frozen=True)
class _C3MatcherBlock:
"""Minimal c3_matcher config block — only the field the test needs."""
strategy: str = "disk_lightglue"
@pytest.fixture(autouse=True)
def _isolated_fdr_cache() -> Iterator[None]:
# Arrange: every test starts with an empty FdrClient cache so the
# bootstrap's make_fdr_client call doesn't accidentally pick up a
# stale instance from a prior test in the same process.
fdr_client_module._reset_for_tests()
yield
fdr_client_module._reset_for_tests()
@pytest.fixture(autouse=True)
def _stub_c6_builders(monkeypatch: pytest.MonkeyPatch) -> None:
# Arrange: stub the AZ-620 Phase B C6 builders so AZ-621 stays
# focused on the Phase C contract. The configs used below either
# omit c6 entirely or use bare Config()-derived blocks.
monkeypatch.setattr(
airborne_bootstrap,
"_build_c6_descriptor_index",
lambda _config: MagicMock(name="DescriptorIndex"),
)
monkeypatch.setattr(
airborne_bootstrap,
"_build_c6_tile_store",
lambda _config: MagicMock(name="TileStore"),
)
def test_ac_621_1_adds_c7_inference(monkeypatch: pytest.MonkeyPatch) -> None:
# Arrange: stub build_inference_runtime to a sentinel so we can
# assert wiring without standing up real GPU/TensorRT/PyTorch.
inference_runtime_sentinel = MagicMock(name="InferenceRuntime")
monkeypatch.setattr(
airborne_bootstrap,
"build_inference_runtime",
lambda _config: inference_runtime_sentinel,
)
config = Config()
# Act
pre_constructed = build_pre_constructed(config)
# Assert: AZ-621 key is present and references the exact factory
# return. AZ-619 + AZ-620 keys are still present (additivity contract).
assert "c7_inference" in pre_constructed
assert pre_constructed["c7_inference"] is inference_runtime_sentinel
assert {"c13_fdr", "clock", "c6_descriptor_index", "c6_tile_store"}.issubset(
pre_constructed.keys()
)
def test_ac_621_2_both_build_flags_off_with_configured_consumer_raises_named_error(
monkeypatch: pytest.MonkeyPatch,
) -> None:
# Arrange: simulate both BUILD_TENSORRT_RUNTIME=OFF and
# BUILD_PYTORCH_FP16_RUNTIME=OFF by making build_inference_runtime
# raise RuntimeNotAvailableError the same way inference_factory does
# in production when neither flag is ON.
monkeypatch.setattr(
airborne_bootstrap,
"build_inference_runtime",
_raise_no_c7_runtime_available,
)
config = Config.with_blocks(
c3_matcher=_C3MatcherBlock(strategy="disk_lightglue")
)
# Act + Assert
with pytest.raises(AirborneBootstrapError) as excinfo:
build_pre_constructed(config)
message = str(excinfo.value)
assert "c7_inference" in message
# Both airborne C7 BUILD_* flags must be named in the message so the
# operator sees the production-default AND the Tier-0 fallback flag.
for _runtime, flag in C7_AIRBORNE_BUILD_FLAGS:
assert flag in message, f"{flag} missing from error: {message!r}"
# Consuming component slug is named.
assert "c3_matcher" in message
# The original factory error is preserved as the cause chain so the
# operator can still see the upstream reason.
assert isinstance(excinfo.value.__cause__, RuntimeNotAvailableError)
def test_ac_621_2_no_configured_consumer_still_raises_with_full_set(
monkeypatch: pytest.MonkeyPatch,
) -> None:
"""Defence-in-depth: even with no consumer configured, AZ-621 still
fails loudly when no airborne C7 runtime is buildable.
Mirrors the AZ-620 defence-in-depth test. ``build_pre_constructed``
is the airborne contract's single seam — silently returning a dict
without ``c7_inference`` would let a later AZ-622..AZ-624 phase or a
downstream caller misread the state. The error message lists the
FULL theoretical consumer set so the operator still gets actionable
information.
"""
# Arrange
monkeypatch.setattr(
airborne_bootstrap,
"build_inference_runtime",
_raise_no_c7_runtime_available,
)
config = Config() # empty components
# Act + Assert
with pytest.raises(AirborneBootstrapError) as excinfo:
build_pre_constructed(config)
message = str(excinfo.value)
assert "c7_inference" in message
for _runtime, flag in C7_AIRBORNE_BUILD_FLAGS:
assert flag in message
# When no consumer is configured, ALL theoretical consumers of
# c7_inference are surfaced — per AIRBORNE_REQUIRED_PRE_CONSTRUCTED_KEYS
# these are c2_vpr, c3_matcher, c3_5_adhop.
for slug in ("c2_vpr", "c3_matcher", "c3_5_adhop"):
assert slug in message, f"{slug} missing from error: {message!r}"
def _raise_no_c7_runtime_available(_config: Config) -> None:
# Mirrors the message that inference_factory.build_inference_runtime
# raises when the configured runtime's BUILD_* flag is OFF — except
# phrased to cover the "both flags off, nothing left" scenario the
# bootstrap is supposed to wrap into an AirborneBootstrapError.
raise RuntimeNotAvailableError(
"InferenceRuntime runtime 'tensorrt' requires "
"BUILD_TENSORRT_RUNTIME=ON in this binary; the flag is OFF."
)