feat(perception): connect full graph to external streaming source and scene receiver

This commit is contained in:
DCCONSTRUCTIONS
2026-09-02 19:04:41 +03:00
parent 191612282b
commit 6469505c41
11 changed files with 1032 additions and 90 deletions
@@ -0,0 +1,116 @@
"""Small network/controller seam test, not decoder or GPU qualification."""
import asyncio
import importlib
import threading
from pathlib import Path
from types import SimpleNamespace
import pytest
pytest.importorskip("grpc")
from test_perception_streaming_grpc import eventually, identity, tls # noqa: F401,E402
from k1link.perception.streaming_grpc import GrpcStreamClient # noqa: E402
from k1link.perception.streaming_lifecycle import StreamingLifecycle # noqa: E402
from k1link.perception.streaming_queue import StreamMailbox # noqa: E402
@pytest.fixture
def adapter(monkeypatch):
monkeypatch.syspath_prepend(
str(
Path(__file__).resolve().parents[1]
/ "experiments/perception/worker/streaming_profile_stage1"
)
)
module = importlib.import_module("pilot_grpc_graph")
control = importlib.import_module("pilot_network_control")
proof = {"boot_id": "synthetic-clock-proof", "time_namespace_offsets": ["monotonic 0 0"]}
monkeypatch.setattr(module, "kernel_clock", lambda: proof)
monkeypatch.setattr(control, "kernel_clock", lambda: proof)
return module, control
def test_adapter_rotates_grant_only_after_old_stream_drains(tmp_path, tls, adapter): # noqa: F811
module, control = adapter
cert, key = tmp_path / "cert", tmp_path / "key"
cert.write_bytes(tls[0])
key.write_bytes(tls[1])
status = tmp_path / "status"
status.mkdir()
runtime = StreamingLifecycle(
identity(),
tmp_path / "lease",
StreamMailbox(),
threading.Event(),
clock_ns=lambda: 1_000_000_000,
recover_input=True,
source_clock_ns=lambda: 1_000_000_000,
)
runtime.ready()
closed = []
bridge = module.NetworkGraphBridge(
runtime,
SimpleNamespace(close=lambda: closed.append(True)),
{},
control=tmp_path,
source_status=status,
source_zero=1,
certificate=cert,
private_key=key,
address="localhost:0",
reset_temporal=lambda: None,
)
# Capture dynamically bound port without opening another listener.
real_serve = bridge.endpoint.serve
async def serve(*args, **kwargs):
server, port = await real_serve(*args, **kwargs)
bridge.port = port
return server, port
bridge.endpoint.serve = serve
async def check():
bridge.start()
_, first = control.read_grant(tmp_path / "grant.json")
client = GrpcStreamClient(f"localhost:{bridge.port}", tls[0], first)
try:
await client.open()
await client.close() # True network disconnect, not a synthetic runtime pause.
await eventually(
lambda: control.read_grant(tmp_path / "grant.json")[1].epoch != first.epoch
)
_, second = control.read_grant(tmp_path / "grant.json")
assert runtime.continuity.phase == "synchronizing"
assert second.epoch.lease_generation == first.epoch.lease_generation
assert runtime.start == first.epoch and not runtime.stop_event.is_set()
assert bridge.endpoint.active is None
assert len(bridge.epoch_reports) == 1
finally:
await client.close()
try:
asyncio.run(check())
finally:
runtime.request_stop("completed")
assert bridge.close()
assert runtime.close()
assert runtime.mailbox.bytes == 0 and closed
def test_foreign_clock_cannot_be_silently_subtracted(tmp_path, adapter):
_, control = adapter
path = tmp_path / "grant.json"
control.write_control(path, {"clock": {"boot_id": "other"}})
with pytest.raises(ValueError, match="foreign"):
control.read_grant(path)
def test_delayed_source_start_does_not_open_an_idle_connection(adapter):
source = importlib.import_module("pilot_grpc_source")
assert source.connection_delay(3_000_000_000, 1_000_000_000) == 1.9
assert source.connection_delay(3_000_000_000, 2_950_000_000) == 0
assert source.connection_delay(3_000_000_000, 3_010_000_000) == 0
+148
View File
@@ -0,0 +1,148 @@
"""Bounded actual scene/mask codec; no models or real recordings."""
import importlib
import json
from hashlib import sha256
from pathlib import Path
import pytest
from k1link.perception.realtime_contract import StreamStart
from k1link.perception.streaming_scene_payload import (
MAX_SCENE_JSON,
decode_scene_payload,
encode_scene_payload,
)
@pytest.fixture
def sample(monkeypatch):
monkeypatch.syspath_prepend(
str(
Path(__file__).resolve().parents[1]
/ "experiments/perception/worker/streaming_profile_stage1"
)
)
pilot = importlib.import_module("pilot_freshness")
epoch = StreamStart(
"run", "source", "worker", "epoch", 1, *(["a" * 64] * 4), pilot.CLOCK_DOMAIN, "live"
)
mask = b"\x01\x02\x03\x04"
stamp = 9_007_199_254_740_993
scene = dict(
segmentation_sha256=sha256(mask).hexdigest(),
proposals=[],
observations=[],
tracks=[],
threats=[],
surface_state="valid",
range_estimator={},
costmap_states=[1],
costmap_material=[1],
policy_actions=[0],
costmap_grid=[[0.0, 0.0, 0.45, 0.45]],
policy_counts={"ALLOW_candidate": 1, "HIGH_COST": 0, "NO_GO": 0},
tgs_counts={"oldest_permissive_cell_source_ns": stamp},
commands_enabled=False,
actuation_allowed=False,
sequence=2,
original_source_ns=stamp,
runtime_binding=epoch.to_dict(),
)
bundle = dict(
sequence=2,
time_ns=stamp,
due_ns=1_000_000_000,
available=True,
lineage={
"pose_host_monotonic_ns": stamp,
"point_increments": [{"host_monotonic_ns": stamp}],
},
)
ddr = dict(state="current", source_sequence=2, source_host_monotonic_ns=stamp)
pilot.prepare_publication(scene, bundle, ddr, epoch_id="epoch", now_ns=bundle["due_ns"])
return scene, mask, epoch, bundle, pilot
def test_actual_mask_grid_and_int64_source_identity_roundtrip(sample):
scene, mask, epoch, bundle, pilot = sample
raw = encode_scene_payload(json.dumps(scene).encode(), mask, (2, 2))
received, plane, shape = decode_scene_payload(raw, epoch, 2)
assert received == scene and bytes(plane) == mask and shape == (2, 2)
assert plane.obj is raw and plane.readonly # no second retained mask allocation
freshness = pilot.validate_receipt(received, bundle, epoch_id="epoch")
view, assessed = pilot.assess_receipt(
received, freshness, bundle=bundle, now_ns=bundle["due_ns"] + 251_000_000
)
assert not assessed.fresh_complete and view["policy_actions"] == [2]
assert received["policy_actions"] == [0] # transit age never rewrites published evidence
@pytest.mark.parametrize(
"mutation",
[
"mask",
"shape",
"truncated",
"extra",
"magic",
"sequence",
"epoch",
"authority",
"digest",
"nan",
"duplicate",
],
)
def test_payload_rejects_bad_framing_binding_and_integrity(sample, mutation):
scene, mask, epoch, _, _ = sample
if mutation == "sequence":
scene["sequence"] = 3
if mutation == "epoch":
scene["runtime_binding"]["epoch_id"] = "old"
if mutation == "authority":
scene["actuation_allowed"] = True
if mutation == "digest":
scene["segmentation_sha256"] = "0" * 64
if mutation == "nan":
scene["range_estimator"] = {"range": float("nan")}
encoded = json.dumps(scene).encode()
if mutation == "duplicate":
encoded = b'{"sequence":2,' + encoded[1:]
raw = encode_scene_payload(encoded, mask, (2, 2))
if mutation == "mask":
raw = raw[:-1] + b"z"
if mutation == "shape":
raw = raw[:10] + b"\x00\x03" + raw[12:]
if mutation == "truncated":
raw = raw[:-1]
if mutation == "extra":
raw += b"z"
if mutation == "magic":
raw = b"NOPE" + raw[4:]
with pytest.raises(ValueError):
decode_scene_payload(raw, epoch, 2)
def test_grid_is_covered_by_the_costmap_layer_digest(sample):
scene, mask, epoch, bundle, pilot = sample
scene["costmap_grid"][0][0] = 500
raw = encode_scene_payload(json.dumps(scene).encode(), mask, (2, 2))
received, _, _ = decode_scene_payload(raw, epoch, 2)
with pytest.raises(ValueError, match="digest"):
pilot.validate_receipt(received, bundle, epoch_id="epoch")
@pytest.mark.parametrize(
"shape,mask,scene",
[
((0, 2), b"", b"{}"),
((True, 1), b"x", b"{}"),
((2, 2), b"abc", b"{}"),
((1, 1), b"x", b" " * (MAX_SCENE_JSON + 1)),
((1, 1), b"x", b""),
],
)
def test_encoder_bounds(shape, mask, scene):
with pytest.raises(ValueError):
encode_scene_payload(scene, mask, shape)