refactor(perception): isolate detector runtime

This commit is contained in:
DCCONSTRUCTIONS
2026-08-05 16:05:26 +03:00
parent 8e161a197f
commit 8bd632509e
14 changed files with 81 additions and 263 deletions
@@ -139,7 +139,7 @@
},
"wheel": {
"name": "nodedc_mission_core-0.1.0-py3-none-any.whl",
"sha256": "df756938f2c212fb6d1770c83569e5366c708d70d74e0434895a21be55a16102"
"sha256": "4158784fd40b70c7213b629ed39d664fd2f12eab1c9242ec9a6ebbb366e3dd5a"
}
},
"rollback": {
+3 -3
View File
@@ -28,9 +28,9 @@
"admission": "reuse"
},
{
"module": "k1link.compute.yolox_object_detector",
"role": "frozen source-neutral YOLOX preprocess, Triton transport and postprocess",
"admission": "adapt-behind-provider"
"module": "k1link.perception.yolox_object_detector",
"role": "product-owned frozen YOLOX preprocess, Triton transport and postprocess",
"admission": "product-owned"
}
],
"historical_wrappers": [
@@ -686,7 +686,7 @@ duplicate development server or Worker 006 mutation was introduced.
The product detector seam is implemented without importing the immutable E46J
wrapper:
- `k1link.compute.yolox_object_detector` contains only the frozen raw-KB4
- `k1link.perception.yolox_object_detector` contains only the frozen raw-KB4
valid-FOV fill, top-left bilinear letterbox, Triton V2 binary HTTP transport,
standard YOLOX decode, fixed score/class-wise NMS and FOV/box validation;
- `k1link.perception.detector.FrozenYoloxDetectorProvider` turns exactly one
+1 -1
View File
@@ -25,7 +25,7 @@ WHEEL_NAME = "nodedc_mission_core-0.1.0-py3-none-any.whl"
RUNNER_NAME = RUNNER.name
PATCH_ID = re.compile(r"^[A-Za-z0-9._-]{1,96}$")
EXPECTED_BASELINE_SHA256 = "ea10359339e6cce31b5780a2710299771cab7cc0c1c2a2b56a1621f786b31fa8"
EXPECTED_WHEEL_SHA256 = "df756938f2c212fb6d1770c83569e5366c708d70d74e0434895a21be55a16102"
EXPECTED_WHEEL_SHA256 = "4158784fd40b70c7213b629ed39d664fd2f12eab1c9242ec9a6ebbb366e3dd5a"
PAYLOAD_FILES = (
RUNNER_NAME,
WHEEL_NAME,
+5 -212
View File
@@ -1,213 +1,6 @@
"""Object-centric, source-neutral Mission Core perception product boundary."""
"""Object-centric, source-neutral Mission Core perception product boundary.
from .adapters import (
PerceptionAdapterError,
observations_from_track_geometry,
temporal_obstacles_from_e34_projection,
)
from .baseline import (
BASELINE_PROFILE_ID,
BASELINE_SCHEMA,
BaselineContractError,
BaselineProfile,
BaselineVerification,
load_m4_baseline,
validate_reuse_inventory,
verify_m4_baseline,
verify_m4_execution_source,
)
from .contracts import (
LOCAL_OBSTACLE_MAP_SCHEMA,
OBJECT_PROPOSAL_SCHEMA,
OBSTACLE_OBSERVATION_SCHEMA,
SOURCE_ENVELOPE_SCHEMA,
TEMPORAL_OBSTACLE_SCHEMA,
THREAT_ASSESSMENT_SCHEMA,
BoundingRegion2D,
ClockBasis,
CorridorIntersection,
EvidenceBasis,
EvidenceCurrentness,
FalseAuthority,
GridCell,
HistorySample,
LocalObstacleMap,
MetricGeometry,
ModalityOutcome,
ModalityStatus,
MotionState,
ObjectProposal2D,
ObstacleObservation,
PerceptionContractError,
QualificationState,
SourceAccounting,
SourceEnvelope,
TemporalObstacle,
TemporalState,
ThreatAssessment,
ThreatAuthority,
ThreatDecision,
TimestampBundle,
validate_exclusive_point_ownership,
)
from .detector import (
FROZEN_YOLOX_MODEL_ID,
FROZEN_YOLOX_PREPROCESS_ID,
FROZEN_YOLOX_PROVIDER_ID,
DetectorProviderError,
DetectorProviderSnapshot,
FrozenYoloxDetectorProvider,
proposals_from_detections,
)
from .detector_replay import run_detector_replay
from .detector_replay_result import (
DETECTOR_REPLAY_FRAME_SCHEMA,
DETECTOR_REPLAY_RESULT_SCHEMA,
DETECTOR_RUNTIME_IDENTITY_SCHEMA,
M4_DETECTOR_REPLAY_GATE,
DetectorReplayFrame,
DetectorReplayGate,
DetectorReplayMetrics,
DetectorReplayResult,
DetectorReplayResultError,
DetectorRuntimeIdentity,
read_detector_replay_result,
require_m4_detector_replay_acceptance,
)
from .graph import (
GRAPH_RESULT_SCHEMA,
REFERENCE_GRAPH_ID,
TERMINAL_OUTCOME_SCHEMA,
DeliveredFrame,
GraphExecutionError,
GraphRunResult,
GraphState,
ReferencePerceptionGraphV1,
TerminalOutcome,
TerminalOutcomeType,
)
from .providers import (
REFERENCE_GRAPH_CONFIG_SCHEMA,
DetectorProvider,
GeometryAssociationProvider,
GraphAuthority,
MotionProvider,
ProviderContractError,
ProviderPin,
ProviderRole,
QueuePolicy,
ReferencePerceptionGraphConfig,
SourcePacket,
SourceProvider,
TemporalStateProvider,
ThreatProvider,
)
from .recorded_source import (
DecodedRecordedSource,
LiveSourceAdapter,
PyAvRecordedImageDecoder,
RecordedFrameReference,
RecordedImageDecoder,
RecordedRavnoves00Source,
RecordedSourceError,
ReplayPacing,
)
__all__ = [
"BASELINE_PROFILE_ID",
"BASELINE_SCHEMA",
"BaselineContractError",
"BaselineProfile",
"BaselineVerification",
"load_m4_baseline",
"validate_reuse_inventory",
"verify_m4_execution_source",
"verify_m4_baseline",
"PerceptionAdapterError",
"observations_from_track_geometry",
"temporal_obstacles_from_e34_projection",
"LOCAL_OBSTACLE_MAP_SCHEMA",
"OBJECT_PROPOSAL_SCHEMA",
"OBSTACLE_OBSERVATION_SCHEMA",
"SOURCE_ENVELOPE_SCHEMA",
"TEMPORAL_OBSTACLE_SCHEMA",
"THREAT_ASSESSMENT_SCHEMA",
"BoundingRegion2D",
"ClockBasis",
"CorridorIntersection",
"EvidenceBasis",
"EvidenceCurrentness",
"FalseAuthority",
"GridCell",
"HistorySample",
"LocalObstacleMap",
"MetricGeometry",
"ModalityOutcome",
"ModalityStatus",
"MotionState",
"ObjectProposal2D",
"ObstacleObservation",
"PerceptionContractError",
"QualificationState",
"SourceAccounting",
"SourceEnvelope",
"TemporalObstacle",
"TemporalState",
"ThreatAssessment",
"ThreatAuthority",
"ThreatDecision",
"TimestampBundle",
"validate_exclusive_point_ownership",
"FROZEN_YOLOX_MODEL_ID",
"FROZEN_YOLOX_PREPROCESS_ID",
"FROZEN_YOLOX_PROVIDER_ID",
"DetectorProviderError",
"DetectorProviderSnapshot",
"FrozenYoloxDetectorProvider",
"proposals_from_detections",
"DETECTOR_REPLAY_FRAME_SCHEMA",
"DETECTOR_REPLAY_RESULT_SCHEMA",
"DETECTOR_RUNTIME_IDENTITY_SCHEMA",
"M4_DETECTOR_REPLAY_GATE",
"DetectorReplayFrame",
"DetectorReplayGate",
"DetectorReplayMetrics",
"DetectorReplayResult",
"DetectorReplayResultError",
"DetectorRuntimeIdentity",
"read_detector_replay_result",
"require_m4_detector_replay_acceptance",
"run_detector_replay",
"REFERENCE_GRAPH_CONFIG_SCHEMA",
"DetectorProvider",
"GeometryAssociationProvider",
"GraphAuthority",
"MotionProvider",
"ProviderContractError",
"ProviderPin",
"ProviderRole",
"QueuePolicy",
"ReferencePerceptionGraphConfig",
"SourcePacket",
"SourceProvider",
"TemporalStateProvider",
"ThreatProvider",
"GRAPH_RESULT_SCHEMA",
"REFERENCE_GRAPH_ID",
"TERMINAL_OUTCOME_SCHEMA",
"DeliveredFrame",
"GraphExecutionError",
"GraphRunResult",
"GraphState",
"ReferencePerceptionGraphV1",
"TerminalOutcome",
"TerminalOutcomeType",
"LiveSourceAdapter",
"DecodedRecordedSource",
"PyAvRecordedImageDecoder",
"RecordedFrameReference",
"RecordedImageDecoder",
"RecordedRavnoves00Source",
"RecordedSourceError",
"ReplayPacing",
]
The package initializer is deliberately side-effect free. Runtime code imports
the concrete contract, provider, graph or source module it needs; importing the
product namespace must not initialize legacy compute, device, web or MQTT code.
"""
+3 -4
View File
@@ -12,7 +12,9 @@ from typing import Final
import numpy as np
from numpy.typing import NDArray
from k1link.compute.yolox_object_detector import (
from .contracts import BoundingRegion2D, ObjectProposal2D
from .providers import SourcePacket
from .yolox_object_detector import (
FROZEN_YOLOX_CONFIG,
YOLOX_MODEL_ID,
YOLOX_MODEL_VERSION,
@@ -24,9 +26,6 @@ from k1link.compute.yolox_object_detector import (
preprocess_raw_kb4,
)
from .contracts import BoundingRegion2D, ObjectProposal2D
from .providers import SourcePacket
FROZEN_YOLOX_PROVIDER_ID: Final = "triton-yolox-s-raw-kb4/v1"
FROZEN_YOLOX_MODEL_ID: Final = f"{YOLOX_MODEL_ID}:{YOLOX_MODEL_VERSION}"
FROZEN_YOLOX_PREPROCESS_ID: Final = "raw-kb4-valid-fov-letterbox/v1"
+4 -5
View File
@@ -9,11 +9,6 @@ import urllib.parse
from pathlib import Path
from threading import Event
from k1link.compute.yolox_object_detector import (
TritonHttpInferenceBackend,
load_valid_fov_mask,
)
from .baseline import load_m4_baseline, verify_m4_execution_source
from .detector import FrozenYoloxDetectorProvider
from .detector_replay import run_detector_replay
@@ -28,6 +23,10 @@ from .recorded_source import (
RecordedRavnoves00Source,
ReplayPacing,
)
from .yolox_object_detector import (
TritonHttpInferenceBackend,
load_valid_fov_mask,
)
def _arguments() -> argparse.Namespace:
@@ -8,12 +8,6 @@ from dataclasses import dataclass
from pathlib import Path
from typing import Final
from k1link.compute.yolox_object_detector import (
YOLOX_CONFIG_SHA256,
YOLOX_MODEL_SHA256,
YOLOX_VALID_FOV_SHA256,
)
from .contracts import ObjectProposal2D, SourceEnvelope
from .detector import (
FROZEN_YOLOX_MODEL_ID,
@@ -21,6 +15,11 @@ from .detector import (
FROZEN_YOLOX_PROVIDER_ID,
DetectorProviderSnapshot,
)
from .yolox_object_detector import (
YOLOX_CONFIG_SHA256,
YOLOX_MODEL_SHA256,
YOLOX_VALID_FOV_SHA256,
)
DETECTOR_REPLAY_RESULT_SCHEMA: Final = "missioncore.perception-detector-replay-result/v1"
DETECTOR_REPLAY_RECEIPT_SCHEMA: Final = "missioncore.perception-detector-replay-receipt/v1"
@@ -10,14 +10,6 @@ import uuid
from datetime import UTC, datetime
from pathlib import Path
from k1link.compute.yolox_object_detector import (
FROZEN_YOLOX_CONFIG,
YOLOX_CONFIG_SHA256,
YOLOX_MODEL_SHA256,
YOLOX_MODEL_VERSION,
YOLOX_VALID_FOV_SHA256,
)
from .baseline import (
BASELINE_CAMERA_STREAM_SHA256,
BASELINE_FILE_SHA256,
@@ -57,6 +49,13 @@ from .detector_replay_validation import (
)
from .detector_replay_validation import validate_frame_accounting
from .recorded_source import RECORDED_SOURCE_PROVIDER_ID
from .yolox_object_detector import (
FROZEN_YOLOX_CONFIG,
YOLOX_CONFIG_SHA256,
YOLOX_MODEL_SHA256,
YOLOX_MODEL_VERSION,
YOLOX_VALID_FOV_SHA256,
)
def seal_detector_replay_result(
@@ -6,14 +6,6 @@ import hashlib
import json
from pathlib import Path
from k1link.compute.yolox_object_detector import (
FROZEN_YOLOX_CONFIG,
YOLOX_CONFIG_SHA256,
YOLOX_MODEL_SHA256,
YOLOX_MODEL_VERSION,
YOLOX_VALID_FOV_SHA256,
)
from .baseline import (
BASELINE_CAMERA_STREAM_SHA256,
BASELINE_FILE_SHA256,
@@ -53,6 +45,13 @@ from .detector_replay_contracts import (
detector_replay_accepted,
)
from .recorded_source import RECORDED_SOURCE_PROVIDER_ID
from .yolox_object_detector import (
FROZEN_YOLOX_CONFIG,
YOLOX_CONFIG_SHA256,
YOLOX_MODEL_SHA256,
YOLOX_MODEL_VERSION,
YOLOX_VALID_FOV_SHA256,
)
def read_detector_replay_result(root: Path) -> DetectorReplayResult:
@@ -1,4 +1,4 @@
"""Source-neutral frozen YOLOX preprocessing, inference transport and postprocessing."""
"""Perception-owned frozen YOLOX preprocessing, transport and postprocessing."""
from __future__ import annotations
+5 -5
View File
@@ -11,11 +11,6 @@ import numpy as np
import pytest
from numpy.typing import NDArray
from k1link.compute.yolox_object_detector import (
YOLOX_CONFIG_SHA256,
YOLOX_MODEL_SHA256,
YOLOX_VALID_FOV_SHA256,
)
from k1link.perception.contracts import (
ClockBasis,
ModalityOutcome,
@@ -34,6 +29,11 @@ from k1link.perception.detector_replay_result import (
require_m4_detector_replay_acceptance,
)
from k1link.perception.providers import SourcePacket
from k1link.perception.yolox_object_detector import (
YOLOX_CONFIG_SHA256,
YOLOX_MODEL_SHA256,
YOLOX_VALID_FOV_SHA256,
)
class _Source:
+30
View File
@@ -19,6 +19,20 @@ REPOSITORY_ROOT = Path(__file__).resolve().parents[1]
PERCEPTION_ROOT = REPOSITORY_ROOT / "src" / "k1link" / "perception"
BASELINE_PATH = REPOSITORY_ROOT / "config" / "perception" / "m4-recorded-realtime-baseline-v1.json"
REUSE_PATH = REPOSITORY_ROOT / "config" / "perception" / "m4-reuse-inventory-v1.json"
DETECTOR_RUNTIME_MODULES = (
"__init__.py",
"baseline.py",
"contracts.py",
"detector.py",
"detector_replay.py",
"detector_replay_cli.py",
"detector_replay_contracts.py",
"detector_replay_result.py",
"detector_replay_validation.py",
"providers.py",
"recorded_source.py",
"yolox_object_detector.py",
)
def _imports(path: Path) -> set[str]:
@@ -151,6 +165,22 @@ def test_perception_contracts_import_no_compute_lab_graph_or_web_module() -> Non
assert forbidden == set()
def test_perception_package_initializer_is_side_effect_free() -> None:
assert _imports(PERCEPTION_ROOT / "__init__.py") == set()
def test_detector_runtime_closure_imports_no_legacy_compute_package() -> None:
violations = {
name: sorted(
module
for module in _imports(PERCEPTION_ROOT / name)
if module.startswith("k1link.compute")
)
for name in DETECTOR_RUNTIME_MODULES
}
assert {name: modules for name, modules in violations.items() if modules} == {}
def test_new_perception_boundary_has_no_experiment_specific_imports() -> None:
violations: dict[str, str] = {}
for path in PERCEPTION_ROOT.glob("*.py"):
+8 -8
View File
@@ -9,14 +9,6 @@ import numpy as np
import pytest
from numpy.typing import NDArray
from k1link.compute.yolox_object_detector import (
FrozenYoloxConfig,
TritonHttpInferenceBackend,
YoloxDetection,
YoloxDetectorError,
postprocess_yolox,
preprocess_raw_kb4,
)
from k1link.perception.contracts import (
ClockBasis,
ModalityOutcome,
@@ -31,6 +23,14 @@ from k1link.perception.detector import (
proposals_from_detections,
)
from k1link.perception.providers import SourcePacket
from k1link.perception.yolox_object_detector import (
FrozenYoloxConfig,
TritonHttpInferenceBackend,
YoloxDetection,
YoloxDetectorError,
postprocess_yolox,
preprocess_raw_kb4,
)
REPOSITORY_ROOT = Path(__file__).resolve().parents[1]
E46J_FRAMES = (