Добавление канонического графа M4.7

This commit is contained in:
DCCONSTRUCTIONS
2026-08-23 20:03:40 +03:00
parent 51bb1369eb
commit d50d3bb3d3
18 changed files with 2538 additions and 110 deletions
+80 -1
View File
@@ -19,7 +19,11 @@ from .contracts import (
)
REFERENCE_GRAPH_CONFIG_SCHEMA: Final = "missioncore.reference-perception-graph-config/v1"
REFERENCE_GRAPH_CONFIG_SCHEMA_V2: Final = "missioncore.reference-perception-graph-config/v2"
REFERENCE_GRAPH_STAGE_IDS: Final = frozenset({"detector", "geometry", "temporal", "threat"})
REFERENCE_GRAPH_STAGE_IDS_V2: Final = frozenset(
{"detector", "geometry", "temporal", "rolling", "threat"}
)
_SHA256 = re.compile(r"^[a-f0-9]{64}$")
_IDENTIFIER = re.compile(r"^[A-Za-z0-9][A-Za-z0-9._:/-]{0,159}$")
@@ -34,9 +38,23 @@ class ProviderRole(StrEnum):
GEOMETRY = "geometry"
TEMPORAL = "temporal"
MOTION = "motion"
ROLLING = "rolling"
THREAT = "threat"
REFERENCE_GRAPH_PROVIDER_ROLES: Final = frozenset(
{
ProviderRole.SOURCE,
ProviderRole.DETECTOR,
ProviderRole.GEOMETRY,
ProviderRole.TEMPORAL,
ProviderRole.MOTION,
ProviderRole.THREAT,
}
)
REFERENCE_GRAPH_PROVIDER_ROLES_V2: Final = frozenset(ProviderRole)
@dataclass(frozen=True, slots=True)
class SourcePacket:
"""Execution-only carrier; the graph never interprets opaque sensor payloads."""
@@ -211,7 +229,7 @@ class ReferencePerceptionGraphConfig:
_identifier(self.graph_id, "graph id")
_identifier(self.source_profile_id, "source profile id")
roles = [provider.role for provider in self.providers]
if len(set(roles)) != len(roles) or set(roles) != set(ProviderRole):
if len(set(roles)) != len(roles) or set(roles) != REFERENCE_GRAPH_PROVIDER_ROLES:
raise ProviderContractError("graph must pin each provider role exactly once")
stage_ids = [queue.stage_id for queue in self.queues]
if len(set(stage_ids)) != len(stage_ids):
@@ -248,6 +266,57 @@ class ReferencePerceptionGraphConfig:
)
@dataclass(frozen=True, slots=True)
class ReferencePerceptionGraphConfigV2:
"""Final M4 graph contract with explicit retained rolling occupancy."""
graph_id: str
source_profile_id: str
providers: tuple[ProviderPin, ...]
queues: tuple[QueuePolicy, ...]
authority: GraphAuthority = GraphAuthority()
def __post_init__(self) -> None:
_identifier(self.graph_id, "graph id")
_identifier(self.source_profile_id, "source profile id")
roles = [provider.role for provider in self.providers]
if len(set(roles)) != len(roles) or set(roles) != REFERENCE_GRAPH_PROVIDER_ROLES_V2:
raise ProviderContractError("graph v2 must pin each provider role exactly once")
stage_ids = [queue.stage_id for queue in self.queues]
if len(set(stage_ids)) != len(stage_ids):
raise ProviderContractError("graph v2 queue policies must be unique")
if set(stage_ids) != REFERENCE_GRAPH_STAGE_IDS_V2:
raise ProviderContractError("graph v2 must bound each reference stage exactly once")
def to_dict(self) -> dict[str, object]:
return {
"schema_version": REFERENCE_GRAPH_CONFIG_SCHEMA_V2,
"graph_id": self.graph_id,
"source_profile_id": self.source_profile_id,
"providers": [provider.to_dict() for provider in self.providers],
"queues": [queue.to_dict() for queue in self.queues],
"authority": self.authority.to_dict(),
}
@classmethod
def from_dict(cls, value: object) -> ReferencePerceptionGraphConfigV2:
document = _object(value, "reference graph v2 config")
_exact_keys(
document,
{"schema_version", "graph_id", "source_profile_id", "providers", "queues", "authority"},
"reference graph v2 config",
)
if document.get("schema_version") != REFERENCE_GRAPH_CONFIG_SCHEMA_V2:
raise ProviderContractError("reference graph v2 config schema is incompatible")
return cls(
graph_id=_string(document, "graph_id"),
source_profile_id=_string(document, "source_profile_id"),
providers=tuple(ProviderPin.from_dict(item) for item in _array(document, "providers")),
queues=tuple(QueuePolicy.from_dict(item) for item in _array(document, "queues")),
authority=GraphAuthority.from_dict(document.get("authority")),
)
class SourceProvider(Protocol):
provider_id: str
@@ -290,6 +359,16 @@ class MotionProvider(Protocol):
) -> tuple[TemporalObstacle, ...]: ...
class RollingMapProvider(Protocol):
provider_id: str
def update(
self,
packet: SourcePacket,
obstacles: tuple[TemporalObstacle, ...],
) -> tuple[TemporalObstacle, ...]: ...
class ThreatProvider(Protocol):
provider_id: str