Initial public release
GeoIntel release gates / Compile, test, contracts and builds (push) Successful in 1m49s
GeoIntel release gates / Python and npm vulnerability policy (push) Successful in 21s
GeoIntel release gates / Production AI image, SBOM and container scan (push) Successful in 5m39s
GeoIntel release gates / Deploy exact gated revision to Unraid (push) Failing after 58m43s

This commit is contained in:
Jens
2026-08-31 21:56:53 +02:00
commit faeb58ef6d
1386 changed files with 263203 additions and 0 deletions
@@ -0,0 +1,144 @@
"""What is promoted must be what the workbench then runs.
The candidate evaluation freezes its post-processing before the protected test
— NMS IoU and a containment threshold selected during calibration. The runtime
applied its own hardcoded containment value, so a model gated at one setting
was served at another and suppressed detections the gate had counted. The
difference is invisible in both reports.
Containment is therefore configuration, recorded with every run, and two runs
that post-processed differently are not comparable however good their numbers
look.
"""
from __future__ import annotations
import pytest
from shapely.geometry import box
from app.core.config import Settings
from app.services.detection_comparison_service import DetectionComparisonService
from app.services.detection_service import DetectionService
def _candidate(name: str, geometry, confidence: float):
return {
"class_name": "building",
"confidence": confidence,
"geometry": geometry,
"bbox": [0.0, 0.0, 1.0, 1.0],
"source_tile_path": f"/tiles/{name}.tif",
"properties": {"name": name},
}
class TestConfigurableContainment:
def test_the_runtime_threshold_comes_from_settings(self) -> None:
assert Settings(_env_file=None).yolo_containment_nms_threshold == pytest.approx(0.85)
assert Settings(
_env_file=None, yolo_containment_nms_threshold=1.0
).yolo_containment_nms_threshold == pytest.approx(1.0)
def test_a_strict_threshold_suppresses_only_a_fully_nested_box(self) -> None:
outer = _candidate("outer", box(0, 0, 10, 10), 0.9)
# 90% of the smaller box lies inside the larger one, but their IoU is
# only 0.09 — so only the containment rule can act on this pair.
mostly_nested = _candidate("mostly", box(8.2, 1, 10.2, 6), 0.5)
kept = DetectionService._suppress_duplicate_candidates(
[outer, mostly_nested], iou_threshold=0.5, containment_threshold=1.0
)
assert [item["properties"]["name"] for item in kept] == ["outer", "mostly"]
def test_a_looser_threshold_suppresses_it(self) -> None:
outer = _candidate("outer", box(0, 0, 10, 10), 0.9)
mostly_nested = _candidate("mostly", box(8.2, 1, 10.2, 6), 0.5)
kept = DetectionService._suppress_duplicate_candidates(
[outer, mostly_nested], iou_threshold=0.5, containment_threshold=0.7
)
assert [item["properties"]["name"] for item in kept] == ["outer"]
def test_the_default_matches_the_documented_runtime_value(self) -> None:
outer = _candidate("outer", box(0, 0, 10, 10), 0.9)
nested = _candidate("nested", box(1, 1, 9, 9), 0.5)
kept = DetectionService._suppress_duplicate_candidates([outer, nested], iou_threshold=0.5)
assert [item["properties"]["name"] for item in kept] == ["outer"]
class TestComparabilityOfPostProcessing:
def _entry(self, *, dataset_id, reference_id, containment: float, duplicate_iou: float = 0.5):
from uuid import uuid4
return {
"analysis_run_id": uuid4(),
"dataset_id": dataset_id,
"model_id": "yolo-configured",
"model_asset_id": "asset",
"reference_dataset_id": reference_id,
"coverage_mode": "persisted_tile_manifest_union",
"reference_evaluated_count": 100,
"containment_suppression_threshold": containment,
"duplicate_iou_threshold": duplicate_iou,
}
def test_runs_with_the_same_post_processing_stay_comparable(self) -> None:
from uuid import uuid4
dataset_id, reference_id = uuid4(), uuid4()
report = DetectionComparisonService.assess_comparability(
[
self._entry(dataset_id=dataset_id, reference_id=reference_id, containment=0.85),
self._entry(dataset_id=dataset_id, reference_id=reference_id, containment=0.85),
]
)
assert report["comparable"] is True
def test_a_different_containment_threshold_blocks_the_comparison(self) -> None:
from uuid import uuid4
dataset_id, reference_id = uuid4(), uuid4()
report = DetectionComparisonService.assess_comparability(
[
self._entry(dataset_id=dataset_id, reference_id=reference_id, containment=0.85),
self._entry(dataset_id=dataset_id, reference_id=reference_id, containment=1.0),
]
)
assert report["comparable"] is False
assert "different_post_processing" in report["blocking_reasons"]
def test_a_different_duplicate_iou_blocks_the_comparison(self) -> None:
from uuid import uuid4
dataset_id, reference_id = uuid4(), uuid4()
report = DetectionComparisonService.assess_comparability(
[
self._entry(dataset_id=dataset_id, reference_id=reference_id, containment=0.85, duplicate_iou=0.5),
self._entry(dataset_id=dataset_id, reference_id=reference_id, containment=0.85, duplicate_iou=0.7),
]
)
assert report["comparable"] is False
assert "different_post_processing" in report["blocking_reasons"]
def test_runs_from_before_the_setting_existed_do_not_block(self) -> None:
"""Older runs recorded no threshold; absence is not a difference."""
from uuid import uuid4
dataset_id, reference_id = uuid4(), uuid4()
entries = [
self._entry(dataset_id=dataset_id, reference_id=reference_id, containment=0.85),
self._entry(dataset_id=dataset_id, reference_id=reference_id, containment=0.85),
]
for entry in entries:
entry.pop("containment_suppression_threshold")
entry.pop("duplicate_iou_threshold")
assert DetectionComparisonService.assess_comparability(entries)["comparable"] is True