fix(platform): bind AI runs to canonical correlation
Some checks failed
CD Pipeline / select-latest-carrier (push) Successful in 51s
CD Pipeline / workflow-shape (push) Successful in 0s
CD Pipeline / cancel-stale-cd (push) Has been skipped
CD Pipeline / tests (push) Successful in 2m45s
CD Pipeline / revalidate-deploy-carrier (push) Successful in 33s
CD Pipeline / revalidate-post-deploy-carrier (push) Has been cancelled
CD Pipeline / build-and-deploy (push) Has been cancelled
CD Pipeline / post-deploy-checks (push) Has been cancelled
Some checks failed
CD Pipeline / select-latest-carrier (push) Successful in 51s
CD Pipeline / workflow-shape (push) Successful in 0s
CD Pipeline / cancel-stale-cd (push) Has been skipped
CD Pipeline / tests (push) Successful in 2m45s
CD Pipeline / revalidate-deploy-carrier (push) Successful in 33s
CD Pipeline / revalidate-post-deploy-carrier (push) Has been cancelled
CD Pipeline / build-and-deploy (push) Has been cancelled
CD Pipeline / post-deploy-checks (push) Has been cancelled
This commit is contained in:
117
apps/api/src/services/runtime_correlation.py
Normal file
117
apps/api/src/services/runtime_correlation.py
Normal file
@@ -0,0 +1,117 @@
|
||||
"""Canonical identity helpers for durable AwoooP runtime runs.
|
||||
|
||||
The durable ``awooop_run_state.run_id`` is the root identity. Trace and work
|
||||
item identifiers are derived from it so retries, API readback, channel mirrors,
|
||||
and downstream receipts cannot silently invent unrelated correlation values.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import hashlib
|
||||
import re
|
||||
import secrets
|
||||
import time
|
||||
from dataclasses import dataclass
|
||||
from uuid import UUID
|
||||
|
||||
_PROJECT_ID_RE = re.compile(r"^[A-Za-z0-9][A-Za-z0-9_.-]{0,63}$")
|
||||
_TRACEPARENT_RE = re.compile(
|
||||
r"^00-(?!0{32})[0-9a-f]{32}-(?!0{16})[0-9a-f]{16}-01$"
|
||||
)
|
||||
|
||||
|
||||
@dataclass(frozen=True, slots=True)
|
||||
class RuntimeCorrelation:
|
||||
"""Public-safe identity shared by every node in one runtime run."""
|
||||
|
||||
run_id: UUID
|
||||
trace_id: str
|
||||
work_item_id: str
|
||||
|
||||
def public_dict(self) -> dict[str, str]:
|
||||
return {
|
||||
"run_id": str(self.run_id),
|
||||
"trace_id": self.trace_id,
|
||||
"work_item_id": self.work_item_id,
|
||||
}
|
||||
|
||||
|
||||
def new_uuid7(
|
||||
*,
|
||||
timestamp_ms: int | None = None,
|
||||
entropy: int | None = None,
|
||||
) -> UUID:
|
||||
"""Return an RFC 9562 UUIDv7 with the RFC 4122 variant bits set.
|
||||
|
||||
Optional inputs make the bit layout deterministic in focused tests. They
|
||||
are not accepted from API callers.
|
||||
"""
|
||||
|
||||
observed_ms = (
|
||||
int(time.time_ns() // 1_000_000)
|
||||
if timestamp_ms is None
|
||||
else int(timestamp_ms)
|
||||
)
|
||||
if not 0 <= observed_ms < (1 << 48):
|
||||
raise ValueError("uuid7_timestamp_out_of_range")
|
||||
|
||||
random_bits = secrets.randbits(74) if entropy is None else int(entropy)
|
||||
if not 0 <= random_bits < (1 << 74):
|
||||
raise ValueError("uuid7_entropy_out_of_range")
|
||||
|
||||
rand_a = random_bits >> 62
|
||||
rand_b = random_bits & ((1 << 62) - 1)
|
||||
value = (
|
||||
(observed_ms << 80)
|
||||
| (0x7 << 76)
|
||||
| (rand_a << 64)
|
||||
| (0b10 << 62)
|
||||
| rand_b
|
||||
)
|
||||
return UUID(int=value)
|
||||
|
||||
|
||||
def canonical_traceparent(run_id: UUID) -> str:
|
||||
"""Derive a stable W3C traceparent from one durable run identifier."""
|
||||
|
||||
trace_hex = hashlib.sha256(
|
||||
f"awooop-runtime-trace:{run_id}".encode("utf-8")
|
||||
).hexdigest()[:32]
|
||||
span_hex = hashlib.sha256(
|
||||
f"awooop-runtime-root-span:{run_id}".encode("utf-8")
|
||||
).hexdigest()[:16]
|
||||
return f"00-{trace_hex}-{span_hex}-01"
|
||||
|
||||
|
||||
def is_canonical_traceparent(value: str, *, run_id: UUID | None = None) -> bool:
|
||||
"""Validate shape and, when supplied, binding to the durable run."""
|
||||
|
||||
candidate = str(value or "").strip()
|
||||
if _TRACEPARENT_RE.fullmatch(candidate) is None:
|
||||
return False
|
||||
return run_id is None or candidate == canonical_traceparent(run_id)
|
||||
|
||||
|
||||
def canonical_work_item_id(project_id: str, run_id: UUID) -> str:
|
||||
"""Return the only generic work-item identity for one platform run."""
|
||||
|
||||
project = str(project_id or "").strip()
|
||||
if _PROJECT_ID_RE.fullmatch(project) is None:
|
||||
raise ValueError("runtime_correlation_project_id_invalid")
|
||||
return f"platform-run:{project}:{run_id}"
|
||||
|
||||
|
||||
def correlation_for_run(project_id: str, run_id: UUID) -> RuntimeCorrelation:
|
||||
"""Build the canonical public-safe correlation projection for a run."""
|
||||
|
||||
return RuntimeCorrelation(
|
||||
run_id=run_id,
|
||||
trace_id=canonical_traceparent(run_id),
|
||||
work_item_id=canonical_work_item_id(project_id, run_id),
|
||||
)
|
||||
|
||||
|
||||
def new_runtime_correlation(project_id: str) -> RuntimeCorrelation:
|
||||
"""Allocate one new run root and all identifiers derived from it."""
|
||||
|
||||
return correlation_for_run(project_id, new_uuid7())
|
||||
Reference in New Issue
Block a user