Files
gps-denied-onboard/src/gps_denied_onboard/components/c2_5_rerank/config.py
T
Oleksandr Bezdieniezhnykh 48ea1e2fc2 [AZ-343] C2.5 InlierCountReRanker + shared FeatureExtractor helper
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>
2026-05-12 06:22:40 +03:00

63 lines
2.0 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""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}"
)