idea172-bevault-verifiable-.../idea172_bevault_verifiable_.../bridge.py

97 lines
3.1 KiB
Python

"""Bridge: canonical IR mapper for BeVault.
This module introduces a minimal, canonical representation mapping BeVault
primitives into a single, serializable IR (Objects/Morphisms/PlanDelta).
It's intentionally lightweight and is designed to support plug-and-play adapters
without vendor lock-in. The goal is to enable reproducible cross-adapter
interactions and deterministic backtests later in the MVP.
"""
from __future__ import annotations
from dataclasses import asdict, dataclass
from typing import Any, Dict, Optional
from .core import AuditLog, HedgeDelta, LocalArbProblem, PrivacyBudget, SharedSignals
@dataclass
class CanonicalIR:
"""A tiny, serializable canonical IR representation.
This is not a full-fledged IR. It captures the core primitives in a
structured, versioned form suitable for signing, auditing, and replay.
"""
version: str
objects: Dict[str, Any]
morphisms: Dict[str, Any]
plan_delta: Optional[Dict[str, Any]] = None
audit_log: Optional[Dict[str, Any]] = None
privacy_budget: Optional[Dict[str, Any]] = None
def to_dict(self) -> dict:
return {
"version": self.version,
"objects": self.objects,
"morphisms": self.morphisms,
"plan_delta": self.plan_delta,
"audit_log": self.audit_log,
"privacy_budget": self.privacy_budget,
}
def to_canonical_ir(
local: LocalArbProblem,
signals: SharedSignals,
delta: Optional[HedgeDelta] = None,
audit_log: Optional[AuditLog] = None,
privacy_budget: Optional[PrivacyBudget] = None,
) -> CanonicalIR:
"""Map BeVault MVP primitives to a CanonicalIR structure.
This function provides a tiny, deterministic bridge from the MVP domain
objects to a stable representation that can be serialized, signed, and
replayed by adapters.
"""
objects = {
"LocalArbProblem": asdict_safe(local),
}
morphisms = {
"SharedSignals": asdict_safe(signals),
}
plan_delta = asdict_safe(delta) if delta is not None else None
audit_payload = asdict_safe(audit_log) if audit_log is not None else None
privacy_payload = asdict_safe(privacy_budget) if privacy_budget is not None else None
return CanonicalIR(
version="1.0",
objects=objects,
morphisms=morphisms,
plan_delta=plan_delta,
audit_log=audit_payload,
privacy_budget=privacy_payload,
)
def asdict_safe(obj) -> dict:
"""Safely convert a dataclass instance to a dict, handling None gracefully."""
if obj is None:
return {}
# Use dataclasses.asdict for clean conversion; if input isn't a dataclass,
# fall back to its string representation to avoid raising.
try:
return asdict(obj)
except Exception:
return {"value": obj}
def canonical_payload(ir: CanonicalIR) -> str:
"""Serialize a CanonicalIR in a stable form for signing and replay."""
import json
return json.dumps(ir.to_dict(), sort_keys=True, separators=(",", ":"))
__all__ = ["CanonicalIR", "to_canonical_ir", "canonical_payload"]