Source code for jeevesagent.core.ids
"""ID and hash helpers used across the harness.
ULIDs are preferred over UUIDs because they are time-sortable, which makes
journal scans, audit log queries, and episode timelines cheap.
"""
from __future__ import annotations
import hashlib
import json
from typing import Any
from ulid import ULID
[docs]
def new_id(prefix: str = "") -> str:
"""Return a fresh ULID, optionally prefixed for readability.
>>> new_id("ep").startswith("ep_")
True
"""
raw = str(ULID())
return f"{prefix}_{raw}" if prefix else raw
[docs]
def deterministic_hash(*parts: Any) -> str:
"""Stable hash of arbitrary JSON-serializable parts.
Used as an idempotency key for journaled steps. The hash is stable
across processes and Python versions because the input is canonicalised
via ``json.dumps(..., sort_keys=True)``.
"""
payload = json.dumps(parts, sort_keys=True, default=_default)
return hashlib.sha256(payload.encode("utf-8")).hexdigest()
def _default(obj: Any) -> Any:
# Pydantic models, datetimes, sets — make them representable.
if hasattr(obj, "model_dump"):
return obj.model_dump()
if hasattr(obj, "isoformat"):
return obj.isoformat()
if isinstance(obj, set | frozenset):
return sorted(obj, key=str)
return repr(obj)