mirror of
https://github.com/azaion/gps-denied-onboard.git
synced 2026-06-22 16:41:13 +00:00
48ea1e2fc2
Implements the production-default ReRankStrategy: K=10 → N=3 by single-pair LightGlue inlier count, with strict drop-and-continue (INV-8) on per-candidate TileFetch / backbone / zero-inlier failures and RerankAllCandidatesFailedError on zero survivors. Composition root injects the shared LightGlueRuntime + Clock + the new FeatureExtractor helper (an L1 placeholder OpenCvOrbExtractor that unblocks AZ-343 and future C3 strategies — task scope expansion). Architectural notes: - Cross-component imports stay banned; tile_store types as `object` and the C6 TileCacheError family is duck-typed by class module prefix (same workaround AZ-348 adopted for c7_inference; proper fix is to relocate TileCacheError to _types/ in a follow-up). - Clock injection follows the replay contract (AZ-398 Invariant 2); reranked_at is sourced from clock.monotonic_ns(). - AZ-342 factory grew `feature_extractor` + `clock` + `fdr_client` parameters; existing AZ-342 conformance tests updated. Tests: 19 new AC-1..AC-12 + mixed-failure scenarios in test_inlier_count_reranker.py; existing AZ-342 suite (26) still green. Full repo sweep 1093 passed / 2 skipped (cmake/actionlint not on PATH). Co-authored-by: Cursor <cursoragent@cursor.com>
63 lines
2.0 KiB
Python
63 lines
2.0 KiB
Python
"""C2.5 ReRankStrategy config block (AZ-342).
|
||
|
||
Registered into ``config.components['c2_5_rerank']`` by the package
|
||
``__init__.py``. The composition-root factory
|
||
:func:`gps_denied_onboard.runtime_root.rerank_factory.build_rerank_strategy`
|
||
reads this block to select the strategy and configure the top-N cut.
|
||
|
||
``top_n`` is the strategy-side cap on the returned
|
||
:attr:`RerankResult.candidates` length; the composition root binds
|
||
``n`` per-frame from this value (default 3 per the epic's K=10 → N=3
|
||
spec).
|
||
"""
|
||
|
||
from __future__ import annotations
|
||
|
||
from dataclasses import dataclass
|
||
from typing import Final
|
||
|
||
from gps_denied_onboard.config.schema import ConfigError
|
||
|
||
__all__ = [
|
||
"C2_5RerankConfig",
|
||
"KNOWN_STRATEGIES",
|
||
]
|
||
|
||
KNOWN_STRATEGIES: Final[frozenset[str]] = frozenset({"inlier_count"})
|
||
|
||
|
||
@dataclass(frozen=True)
|
||
class C2_5RerankConfig:
|
||
"""Per-component config for C2.5 ReRank.
|
||
|
||
``strategy`` selects exactly one of the registered re-rankers
|
||
(today only ``inlier_count``); the composition-root factory
|
||
respects compile-time ``BUILD_RERANK_<variant>`` gating on top
|
||
of this label.
|
||
|
||
``top_n`` is the per-frame N cap (1..K-1). Default 3 (the epic's
|
||
K=10 → N=3 spec).
|
||
|
||
``debug_per_frame_log`` gates the two DEBUG events
|
||
(``c2_5.rerank.zero_inliers`` per dropped candidate and
|
||
``c2_5.rerank.frame_done`` per frame); flooding journald at
|
||
``3 Hz × K=10 = 30 events/sec`` by default would violate
|
||
description.md § 9. Operators flip this to ``True`` for the
|
||
debug-build flight binary.
|
||
"""
|
||
|
||
strategy: str = "inlier_count"
|
||
top_n: int = 3
|
||
debug_per_frame_log: bool = False
|
||
|
||
def __post_init__(self) -> None:
|
||
if self.strategy not in KNOWN_STRATEGIES:
|
||
raise ConfigError(
|
||
f"C2_5RerankConfig.strategy={self.strategy!r} not in "
|
||
f"{sorted(KNOWN_STRATEGIES)}"
|
||
)
|
||
if self.top_n < 1:
|
||
raise ConfigError(
|
||
f"C2_5RerankConfig.top_n must be >= 1; got {self.top_n}"
|
||
)
|