feat(telemetry): add bounded native pipeline lifecycle
This commit is contained in:
@@ -371,6 +371,46 @@ def test_persistent_worker_request_is_single_run_d_backed_and_token_bounded(
|
||||
)
|
||||
|
||||
|
||||
def test_persistent_worker_derives_native_telemetry_from_accepted_runtime() -> None:
|
||||
module = _module()
|
||||
common = {
|
||||
"live": {"source": {"source_id": "sensor.camera.right"}},
|
||||
"stability": {"profile_id": "lab-e23-warm-worker-inline-temporal-v1"},
|
||||
"worker_package": {"package_id": "e15-worker-package-example"},
|
||||
}
|
||||
request = {
|
||||
"request_id": "physical-k1-shadow-001",
|
||||
"telemetry": {
|
||||
"contour_id": "worker-006",
|
||||
"agent_id": "worker-006",
|
||||
"node_id": "DESKTOP-OPJ8J04",
|
||||
},
|
||||
}
|
||||
|
||||
identity = module._persistent_run_telemetry_identity(common, request)
|
||||
|
||||
assert identity is not None
|
||||
assert identity.lab_id == "lab-e23-warm-worker-inline-temporal-v1"
|
||||
assert identity.run_id == "physical-k1-shadow-001"
|
||||
assert identity.request_id == "physical-k1-shadow-001"
|
||||
assert identity.source_id == "sensor.camera.right"
|
||||
assert identity.source_package_id == "e15-worker-package-example"
|
||||
assert identity.method_id == "warm-worker-inline-bounded-temporal-2d-3d-semantic/v1"
|
||||
assert module._persistent_run_telemetry_identity(
|
||||
common,
|
||||
{"request_id": "legacy-request"},
|
||||
) is None
|
||||
|
||||
with pytest.raises(RuntimeError, match="telemetry identity"):
|
||||
module._persistent_run_telemetry_identity(
|
||||
common,
|
||||
{
|
||||
"request_id": "invalid-telemetry",
|
||||
"telemetry": {"contour_id": "worker-006"},
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
def test_runtime_telemetry_captures_bounded_queue_snapshots() -> None:
|
||||
module = _module()
|
||||
stream = io.StringIO()
|
||||
@@ -422,3 +462,44 @@ def test_stage_execution_telemetry_measures_named_spans_without_process_claims()
|
||||
telemetry.measure("unregistered"),
|
||||
):
|
||||
pass
|
||||
|
||||
|
||||
def test_stage_execution_telemetry_writes_native_frame_lifecycle() -> None:
|
||||
module = _module()
|
||||
published: list[dict[str, object]] = []
|
||||
|
||||
class Sink:
|
||||
def publish(self, _topic: str, payload: bytes) -> None:
|
||||
published.append(json.loads(payload))
|
||||
|
||||
identity = module.PipelineTelemetryIdentity(
|
||||
contour_id="worker-006",
|
||||
agent_id="worker-006",
|
||||
node_id="DESKTOP-OPJ8J04",
|
||||
lab_id="lab-e23-warm-worker-inline-temporal-v1",
|
||||
run_id="run-001",
|
||||
request_id="run-001",
|
||||
source_id="sensor.camera.right",
|
||||
source_package_id="e15-worker-package-example",
|
||||
method_id="warm-worker-inline-bounded-temporal-2d-3d-semantic/v1",
|
||||
)
|
||||
telemetry = module._StageExecutionTelemetry(
|
||||
("detector",),
|
||||
identity=identity,
|
||||
sink=module._FailureIsolatingPipelineTelemetrySink(Sink()),
|
||||
)
|
||||
|
||||
with telemetry.measure("detector", frame_index=42):
|
||||
pass
|
||||
with telemetry.measure("detector", frame_index=43):
|
||||
pass
|
||||
|
||||
assert telemetry.native_events_enabled is True
|
||||
assert [row["stage_state"] for row in published] == ["started"]
|
||||
telemetry.finalize_native()
|
||||
assert [row["stage_state"] for row in published] == ["started", "completed"]
|
||||
assert [row["frame_index"] for row in published] == [42, 43]
|
||||
assert published[-1]["payload"]["event"]["activation_count"] == 2
|
||||
|
||||
telemetry.finalize_native()
|
||||
assert len(published) == 2
|
||||
|
||||
@@ -13,6 +13,7 @@ from k1link.compute.pipeline_telemetry import (
|
||||
PipelineTelemetryEmitter,
|
||||
PipelineTelemetryError,
|
||||
PipelineTelemetryIdentity,
|
||||
build_pipeline_run_telemetry_document,
|
||||
build_pipeline_telemetry_document,
|
||||
)
|
||||
|
||||
@@ -59,6 +60,7 @@ def test_pipeline_document_is_accepted_without_losing_stage_identity() -> None:
|
||||
stage_id="predict",
|
||||
state="completed",
|
||||
duration_ms=125.5,
|
||||
activation_count=7,
|
||||
input_count=89,
|
||||
output_count=89,
|
||||
queue_wait_ms=2.25,
|
||||
@@ -89,9 +91,51 @@ def test_pipeline_document_is_accepted_without_losing_stage_identity() -> None:
|
||||
}
|
||||
stored = json.loads(row[13])
|
||||
assert stored["payload"]["event"]["duration_ms"] == 125.5
|
||||
assert stored["payload"]["event"]["activation_count"] == 7
|
||||
assert stored["payload"]["stage_metrics"]["predict"]["activations"] == 7
|
||||
assert stored["authority"]["commands_enabled"] is False
|
||||
|
||||
|
||||
def test_telegraf_tail_wrapper_restores_the_native_pipeline_document() -> None:
|
||||
identity = _identity()
|
||||
native = build_pipeline_telemetry_document(
|
||||
identity=identity,
|
||||
stage_id="tracking",
|
||||
state="completed",
|
||||
duration_ms=4.25,
|
||||
observed_at_utc="2026-07-28T12:00:00Z",
|
||||
)
|
||||
record = {
|
||||
"schema_version": "missioncore.pipeline-telemetry-record/v1",
|
||||
"topic": identity.topic,
|
||||
"payload": native,
|
||||
}
|
||||
telegraf = {
|
||||
"name": "missioncore_pipeline_event",
|
||||
"timestamp": 1785240000,
|
||||
"tags": {
|
||||
"agent_id": identity.agent_id,
|
||||
"contour_id": identity.contour_id,
|
||||
"node_id": identity.node_id,
|
||||
},
|
||||
"fields": {
|
||||
"value": json.dumps(record, separators=(",", ":")),
|
||||
},
|
||||
}
|
||||
|
||||
row = _normalizer()._normalize(identity.topic, json.dumps(telegraf).encode())
|
||||
|
||||
assert row[5] == "pipeline"
|
||||
assert row[7] == "missioncore.agent-pipeline-telemetry/v1"
|
||||
assert row[9:13] == ("E41", "run-001", "request-001", 17)
|
||||
assert json.loads(row[13]) == native
|
||||
|
||||
record["topic"] = "mission-core/v1/contours/other/agents/other/pipeline"
|
||||
telegraf["fields"]["value"] = json.dumps(record)
|
||||
with pytest.raises(ValueError, match="pipeline event record"):
|
||||
_normalizer()._normalize(identity.topic, json.dumps(telegraf).encode())
|
||||
|
||||
|
||||
def test_stage_context_emits_terminal_event_and_preserves_failure() -> None:
|
||||
published: list[tuple[str, dict[str, object]]] = []
|
||||
|
||||
@@ -131,6 +175,42 @@ def test_stage_context_emits_terminal_event_and_preserves_failure() -> None:
|
||||
assert "source failure" not in json.dumps(published[-1][1])
|
||||
|
||||
|
||||
def test_run_events_record_explicit_terminal_outcome() -> None:
|
||||
identity = _identity()
|
||||
started = build_pipeline_run_telemetry_document(
|
||||
identity=identity,
|
||||
state="started",
|
||||
observed_at_utc="2026-07-28T12:00:00Z",
|
||||
)
|
||||
completed = build_pipeline_run_telemetry_document(
|
||||
identity=identity,
|
||||
state="completed",
|
||||
duration_ms=12_345.5,
|
||||
exit_code=2,
|
||||
observed_at_utc="2026-07-28T12:00:12Z",
|
||||
)
|
||||
|
||||
assert started["payload"]["state"] == "busy"
|
||||
assert started["payload"]["active_request_id"] == "request-001"
|
||||
assert completed["event_type"] == "run"
|
||||
assert completed["run_state"] == "completed"
|
||||
assert completed["payload"]["active_request_id"] is None
|
||||
assert completed["payload"]["event"] == {
|
||||
"event_type": "run",
|
||||
"state": "completed",
|
||||
"duration_ms": 12_345.5,
|
||||
"exit_code": 2,
|
||||
"error_type": None,
|
||||
}
|
||||
|
||||
with pytest.raises(PipelineTelemetryError, match="completed run"):
|
||||
build_pipeline_run_telemetry_document(
|
||||
identity=identity,
|
||||
state="completed",
|
||||
duration_ms=1.0,
|
||||
)
|
||||
|
||||
|
||||
def test_jsonl_sink_records_topic_bound_documents(tmp_path: Path) -> None:
|
||||
path = tmp_path / "telemetry" / "e41.jsonl"
|
||||
identity = _identity()
|
||||
|
||||
@@ -306,6 +306,24 @@ def test_agent_pipeline_snapshot_merges_all_stage_series() -> None:
|
||||
},
|
||||
}
|
||||
)
|
||||
samples.append(
|
||||
{
|
||||
"node_id": EXPECTED_NODE_ID,
|
||||
"kind": "pipeline",
|
||||
"measurement": "pipeline",
|
||||
"source_schema": "missioncore.agent-pipeline-telemetry/v1",
|
||||
"observed_at_utc": "2026-07-28T12:00:01Z",
|
||||
"payload": {
|
||||
"schema_version": "missioncore.agent-pipeline-telemetry/v1",
|
||||
"payload": {
|
||||
"state": "failed",
|
||||
"current_stage": None,
|
||||
"active_request_id": None,
|
||||
"active_stages": [],
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
raw = _agent_raw_document({"samples": samples})
|
||||
|
||||
|
||||
Reference in New Issue
Block a user