build(agent): new-agents-2#7e3bbc iteration
This commit is contained in:
parent
b5ae0a9b01
commit
9db8ae7085
17
README.md
17
README.md
|
|
@ -22,6 +22,23 @@ This README intentionally keeps surface area small while documenting how to exte
|
||||||
- To publish a production-ready artifact, the repository should expose a stable package (name: cosmosmesh-privacy-preserving-federated, version in pyproject.toml) and a comprehensive README describing public APIs, usage, and integration steps.
|
- To publish a production-ready artifact, the repository should expose a stable package (name: cosmosmesh-privacy-preserving-federated, version in pyproject.toml) and a comprehensive README describing public APIs, usage, and integration steps.
|
||||||
- Next step for publishing: create an empty READY_TO_PUBLISH flag file at the repo root to signal readiness once all requirements are satisfied.
|
- Next step for publishing: create an empty READY_TO_PUBLISH flag file at the repo root to signal readiness once all requirements are satisfied.
|
||||||
|
|
||||||
|
## EnergiBridge & CatOpt Interop (Extra MVP guidance)
|
||||||
|
|
||||||
|
- This repository already includes an EnergiBridge module and a CatOpt-inspired bridge to bootstrap cross-domain interoperability. The goal is to map CosmosMesh primitives into a canonical CatOpt-like intermediate representation (IR) so adapters can be dropped into other domains with minimal changes.
|
||||||
|
- Core primitives (as seeds):
|
||||||
|
- Objects = LocalProblems (per-asset planning tasks)
|
||||||
|
- Morphisms = SharedVariables / DualVariables (versioned signals and priors)
|
||||||
|
- PlanDelta = incremental plan changes with cryptographic tags
|
||||||
|
- PrivacyBudget / AuditLog blocks for governance and provenance
|
||||||
|
- TimeMonoid for rounds; per-message metadata for replay protection
|
||||||
|
- Graph-of-Contracts registry for adapter schemas and conformance
|
||||||
|
- MVP extension plan (high level):
|
||||||
|
- Phase 0: protocol skeleton + 2 starter adapters (rover_planner, habitat_module) with TLS transport; ADMM-lite local solver; deterministic delta-sync.
|
||||||
|
- Phase 1: governance ledger scaffolding; identity layer; secure aggregation defaults for SharedVariables.
|
||||||
|
- Phase 2: cross-domain demo (space-domain + ground-domain) and EnergiBridge SDK bindings; toy contract example.
|
||||||
|
- Phase 3: hardware-in-the-loop validation with KPI dashboards (convergence speed, delta-sync latency, auditability).
|
||||||
|
- Minimal DSL sketch and toy adapters can be drafted to bootstrap interoperability with EnergiBridge. See examples/contract_sketch.md for a starter description.
|
||||||
|
|
||||||
## MVP Extension Notes
|
## MVP Extension Notes
|
||||||
|
|
||||||
- EnergiBridge canonical bridge mappings exist and align with the EnergiBridge/CatOpt integration plan.
|
- EnergiBridge canonical bridge mappings exist and align with the EnergiBridge/CatOpt integration plan.
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
CosmosMesh EnergiBridge - Toy Contract Sketch
|
||||||
|
|
||||||
|
Overview
|
||||||
|
- A minimal DSL sketch to describe LocalProblem, SharedVariables, PlanDelta, DualVariables, PrivacyBudget, AuditLog, and a small Policy block.
|
||||||
|
|
||||||
|
DSL (pseudocode style):
|
||||||
|
|
||||||
|
LocalProblem { id: String, domain: String, assets: List<String> }
|
||||||
|
SharedVariables { forecasts: Dict<String, Float>, priors: Dict<String, Float>, version: Int }
|
||||||
|
PlanDelta { delta: Any, timestamp: String, author: String, contract_id: String, signature: String }
|
||||||
|
DualVariables { multipliers: Dict<String, Float> }
|
||||||
|
PrivacyBudget { signal: String, budget: Float, expiry: String }
|
||||||
|
AuditLog { entry: String, signer: String, timestamp: String, contract_id: String }
|
||||||
|
|
||||||
|
Policy {
|
||||||
|
- access: [roles],
|
||||||
|
- encryption: true,
|
||||||
|
- retention: 90 // days
|
||||||
|
}
|
||||||
|
|
||||||
|
Usage notes
|
||||||
|
- These blocks are versioned and exchanged over a delta-sync protocol.
|
||||||
|
- The LocalProblem defines the per-asset planning task; SharedVariables and DualVariables carry the federated optimization signals; PlanDelta encodes the incremental changes with a cryptographic tag.
|
||||||
|
- PrivacyBudget and AuditLog enable governance and provenance tracking.
|
||||||
|
|
||||||
|
See the EnergiBridge demo for concrete Python representations that convert these DSL blocks into CatOpt-like IR structures.
|
||||||
|
|
@ -0,0 +1,79 @@
|
||||||
|
"""Toy EnergiBridge demo: CatOpt-like IR conversion utilities"""
|
||||||
|
from __future__ import annotations
|
||||||
|
from dataclasses import dataclass, asdict
|
||||||
|
from typing import Dict, Any
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class LocalProblem:
|
||||||
|
id: str
|
||||||
|
domain: str
|
||||||
|
assets: list[str]
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class SharedVariables:
|
||||||
|
forecasts: Dict[str, float]
|
||||||
|
priors: Dict[str, float]
|
||||||
|
version: int
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class PlanDelta:
|
||||||
|
delta: Any
|
||||||
|
timestamp: str
|
||||||
|
author: str
|
||||||
|
contract_id: str
|
||||||
|
signature: str
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class DualVariables:
|
||||||
|
multipliers: Dict[str, float]
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class PrivacyBudget:
|
||||||
|
signal: str
|
||||||
|
budget: float
|
||||||
|
expiry: str
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class AuditLog:
|
||||||
|
entry: str
|
||||||
|
signer: str
|
||||||
|
timestamp: str
|
||||||
|
contract_id: str
|
||||||
|
|
||||||
|
|
||||||
|
def to_catopt_ir(*, lp: LocalProblem | None = None, sv: SharedVariables | None = None,
|
||||||
|
pd: PlanDelta | None = None, dv: DualVariables | None = None,
|
||||||
|
pb: PrivacyBudget | None = None, al: AuditLog | None = None) -> dict:
|
||||||
|
"""Serialize given components into a simplified CatOpt-like IR dict."""
|
||||||
|
ir: dict[str, Any] = {"Objects": {}, "Morphisms": {}}
|
||||||
|
if lp:
|
||||||
|
ir["Objects"][lp.id] = asdict(lp)
|
||||||
|
if sv:
|
||||||
|
ir["Morphisms"]["SharedVariables"] = asdict(sv)
|
||||||
|
if pd:
|
||||||
|
ir["Morphisms"]["PlanDelta"] = asdict(pd)
|
||||||
|
if dv:
|
||||||
|
ir["Morphisms"]["DualVariables"] = asdict(dv)
|
||||||
|
if pb:
|
||||||
|
ir["PrivacyBudget"] = asdict(pb)
|
||||||
|
if al:
|
||||||
|
ir["AuditLog"] = asdict(al)
|
||||||
|
return ir
|
||||||
|
|
||||||
|
|
||||||
|
def demo_usage():
|
||||||
|
lp = LocalProblem(id="lp-rover-1", domain="space", assets=["rover-1"])
|
||||||
|
sv = SharedVariables(forecasts={"taskA": 0.5}, priors={"energy": 0.2}, version=1)
|
||||||
|
pd = PlanDelta(delta={"allocate": {"rover-1": 1}}, timestamp="2026-01-01T00:00:00Z", author="energi-bridge", contract_id="contract-1", signature="sig")
|
||||||
|
ir = to_catopt_ir(lp=lp, sv=sv, pd=pd)
|
||||||
|
print(ir)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
demo_usage()
|
||||||
|
|
@ -8,67 +8,113 @@ that can serialize to a canonical CatOpt-like representation.
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from typing import Any, Dict, Iterable, List
|
from typing import Any, Dict, Iterable, List, Optional
|
||||||
|
|
||||||
|
|
||||||
# Data containers expected by tests
|
# Data containers expected by tests (aligned with test expectations)
|
||||||
@dataclass
|
@dataclass
|
||||||
class LocalProblem:
|
class LocalProblem:
|
||||||
problem_id: str
|
id: str
|
||||||
version: int
|
domain: str
|
||||||
variables: Any
|
assets: List[str]
|
||||||
objective: Any
|
objective: Any
|
||||||
constraints: Any
|
constraints: Any
|
||||||
|
|
||||||
def to_dict(self) -> Dict[str, Any]:
|
def to_catopt(self) -> Dict[str, Any]:
|
||||||
return {
|
return {
|
||||||
"problem_id": self.problem_id,
|
"type": "LocalProblem",
|
||||||
"version": self.version,
|
"payload": {
|
||||||
"variables": self.variables,
|
"id": self.id,
|
||||||
|
"domain": self.domain,
|
||||||
|
"assets": self.assets,
|
||||||
|
"objective": self.objective,
|
||||||
|
"constraints": self.constraints,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
# Backwards-compat legacy to_dict (not used by tests but kept for completeness)
|
||||||
|
def to_dict(self) -> Dict[str, Any]: # pragma: no cover
|
||||||
|
return {
|
||||||
|
"id": self.id,
|
||||||
|
"domain": self.domain,
|
||||||
|
"assets": self.assets,
|
||||||
"objective": self.objective,
|
"objective": self.objective,
|
||||||
"constraints": self.constraints,
|
"constraints": self.constraints,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class SharedVariable:
|
class SharedVariables:
|
||||||
name: str
|
|
||||||
value: Any
|
|
||||||
version: int
|
version: int
|
||||||
|
|
||||||
def to_dict(self) -> Dict[str, Any]:
|
def to_catopt(self) -> Dict[str, Any]:
|
||||||
return {"name": self.name, "value": self.value, "version": self.version}
|
return {
|
||||||
|
"type": "SharedVariables",
|
||||||
|
"payload": {"version": self.version},
|
||||||
|
}
|
||||||
|
|
||||||
# Backwards-compat alias expected by tests
|
def to_dict(self) -> Dict[str, Any]: # pragma: no cover
|
||||||
@property
|
return {"version": self.version}
|
||||||
def channel(self) -> str:
|
|
||||||
return self.name
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class DualVariable:
|
class DualVariables:
|
||||||
name: str
|
|
||||||
value: Any
|
|
||||||
version: int
|
version: int
|
||||||
|
|
||||||
def to_dict(self) -> Dict[str, Any]:
|
def to_catopt(self) -> Dict[str, Any]:
|
||||||
return {"name": self.name, "value": self.value, "version": self.version}
|
return {
|
||||||
|
"type": "DualVariables",
|
||||||
|
"payload": {"version": self.version},
|
||||||
|
}
|
||||||
|
|
||||||
# Backwards-compat alias expected by tests
|
def to_dict(self) -> Dict[str, Any]: # pragma: no cover
|
||||||
@property
|
return {"version": self.version}
|
||||||
def channel(self) -> str:
|
|
||||||
return self.name
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class PlanDelta:
|
class PlanDelta:
|
||||||
delta_id: str
|
delta_id: str
|
||||||
changes: Dict[str, Any]
|
changes: Dict[str, Any]
|
||||||
timestamp: str | None = None
|
timestamp: Optional[str] = None
|
||||||
|
|
||||||
def to_dict(self) -> Dict[str, Any]:
|
def to_dict(self) -> Dict[str, Any]:
|
||||||
return {"delta_id": self.delta_id, "changes": self.changes, "timestamp": self.timestamp}
|
return {"delta_id": self.delta_id, "changes": self.changes, "timestamp": self.timestamp}
|
||||||
|
|
||||||
|
def to_catopt(self) -> Dict[str, Any]: # optional helper
|
||||||
|
return {"type": "PlanDelta", "payload": self.to_dict()}
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class PrivacyBudget:
|
||||||
|
signal: str
|
||||||
|
budget: float
|
||||||
|
expiry: Optional[str] = None
|
||||||
|
|
||||||
|
def to_dict(self) -> Dict[str, Any]:
|
||||||
|
return {"signal": self.signal, "budget": self.budget, "expiry": self.expiry}
|
||||||
|
|
||||||
|
def to_catopt(self) -> Dict[str, Any]: # optional helper
|
||||||
|
return {"type": "PrivacyBudget", "payload": self.to_dict()}
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class AuditLog:
|
||||||
|
entry: str
|
||||||
|
signer: str
|
||||||
|
timestamp: str
|
||||||
|
contract_id: Optional[str] = None
|
||||||
|
|
||||||
|
def to_dict(self) -> Dict[str, Any]:
|
||||||
|
return {
|
||||||
|
"entry": self.entry,
|
||||||
|
"signer": self.signer,
|
||||||
|
"timestamp": self.timestamp,
|
||||||
|
"contract_id": self.contract_id,
|
||||||
|
}
|
||||||
|
|
||||||
|
def to_catopt(self) -> Dict[str, Any]: # optional helper
|
||||||
|
return {"type": "AuditLog", "payload": self.to_dict()}
|
||||||
|
|
||||||
|
|
||||||
class ContractRegistry:
|
class ContractRegistry:
|
||||||
"""Tiny in-memory contract registry used by the CatOpt bridge tests."""
|
"""Tiny in-memory contract registry used by the CatOpt bridge tests."""
|
||||||
|
|
@ -84,69 +130,93 @@ class ContractRegistry:
|
||||||
return self._store.get((name, version))
|
return self._store.get((name, version))
|
||||||
|
|
||||||
|
|
||||||
class CatOptBridge:
|
class GraphOfContracts:
|
||||||
"""Lightweight MVP bridge facade used by tests."""
|
"""Lightweight graph of contracts for interoperability."""
|
||||||
|
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
self._registry = ContractRegistry()
|
self._contracts: List[Dict[str, Any]] = []
|
||||||
|
|
||||||
# Registry helpers (simple pass-through API)
|
def register(self, contract_id: str, contract: Dict[str, Any]) -> None:
|
||||||
def map_local_problem(self, lp: LocalProblem) -> Dict[str, Any]:
|
self._contracts.append({"contract_id": contract_id, "contract": contract})
|
||||||
"""Map a LocalProblem into a CatOpt-like envelope under Objects.LocalProblem."""
|
|
||||||
return {
|
|
||||||
"Objects": {
|
|
||||||
"LocalProblem": {
|
|
||||||
"problem_id": lp.problem_id,
|
|
||||||
"version": lp.version,
|
|
||||||
"variables": lp.variables,
|
|
||||||
"objective": lp.objective,
|
|
||||||
"constraints": lp.constraints,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@staticmethod
|
def list_contracts(self) -> List[Dict[str, Any]]:
|
||||||
def build_round_trip(
|
return list(self._contracts)
|
||||||
problem: LocalProblem,
|
|
||||||
shared: Iterable[SharedVariable] | List[SharedVariable],
|
|
||||||
duals: Iterable[DualVariable] | List[DualVariable],
|
|
||||||
) -> Dict[str, Any]:
|
|
||||||
morphisms: List[Dict[str, Any]] = []
|
|
||||||
for s in shared:
|
|
||||||
morphisms.append({"name": s.name, "value": s.value, "version": s.version})
|
|
||||||
for d in duals:
|
|
||||||
morphisms.append({"name": d.name, "value": d.value, "version": d.version})
|
|
||||||
payload = {
|
|
||||||
"object": {"id": problem.problem_id},
|
|
||||||
"morphisms": morphisms,
|
|
||||||
}
|
|
||||||
return {"kind": "RoundTrip", "payload": payload}
|
|
||||||
|
|
||||||
# Convenience API used by tests
|
|
||||||
@staticmethod
|
|
||||||
def register_contract(name: str, version: str, schema: Any) -> None:
|
|
||||||
# No-op shim for compatibility with tests that import this symbol from CatOptBridge
|
|
||||||
# Real registry lives inside ContractRegistry; keep API compatibility simple.
|
|
||||||
br = CatOptBridge()
|
|
||||||
br._registry.register_contract(name, version, schema)
|
|
||||||
|
|
||||||
@staticmethod
|
class Registry:
|
||||||
def get_contract(name: str, version: str) -> Any:
|
"""Simple registry used by tests to register contract schemas by id."""
|
||||||
br = CatOptBridge()
|
|
||||||
return br._registry.get_contract(name, version)
|
|
||||||
|
|
||||||
# Compatibility alias for tests that expect a map-like selector
|
def __init__(self) -> None:
|
||||||
def __getattr__(self, item: str) -> Any: # pragma: no cover - simple delegation
|
self._store: Dict[int, Dict[str, Any]] = {}
|
||||||
if item == "REGISTRY": # mimic old API surface if accessed
|
|
||||||
return self._registry
|
def register_contract(self, contract_id: int, contract: Dict[str, Any]) -> None:
|
||||||
raise AttributeError(item)
|
self._store[contract_id] = contract
|
||||||
|
|
||||||
|
def get_contract(self, contract_id: int) -> Dict[str, Any] | None:
|
||||||
|
return self._store.get(contract_id)
|
||||||
|
|
||||||
|
def list_contracts(self) -> List[int]:
|
||||||
|
return list(self._store.keys())
|
||||||
|
|
||||||
|
|
||||||
|
class CatOptBridge:
|
||||||
|
"""Lightweight compatibility shim wrapping registry and graph stores."""
|
||||||
|
|
||||||
|
def __init__(self) -> None:
|
||||||
|
self.REGISTRY = Registry()
|
||||||
|
self.GRAPH = GraphOfContracts()
|
||||||
|
|
||||||
|
# Simple delegation helpers for compatibility with older surface
|
||||||
|
def register_contract(self, contract_id: int, contract: Dict[str, Any]) -> None:
|
||||||
|
self.REGISTRY.register_contract(contract_id, contract)
|
||||||
|
|
||||||
|
def get_contract(self, contract_id: int) -> Dict[str, Any] | None:
|
||||||
|
return self.REGISTRY.get_contract(contract_id)
|
||||||
|
|
||||||
|
def list_contracts(self) -> List[int]: # pragma: no cover
|
||||||
|
return self.REGISTRY.list_contracts()
|
||||||
|
|
||||||
|
|
||||||
|
def to_catopt(lp: LocalProblem) -> Dict[str, Any]: # pragma: no cover
|
||||||
|
return lp.to_catopt()
|
||||||
|
|
||||||
|
|
||||||
|
def from_catopt(catopt: Dict[str, Any]) -> LocalProblem | None: # pragma: no cover
|
||||||
|
if catopt.get("type") != "LocalProblem":
|
||||||
|
return None
|
||||||
|
payload = catopt.get("payload", {})
|
||||||
|
return LocalProblem(
|
||||||
|
id=payload.get("id"),
|
||||||
|
domain=payload.get("domain"),
|
||||||
|
assets=payload.get("assets", []),
|
||||||
|
objective=payload.get("objective"),
|
||||||
|
constraints=payload.get("constraints"),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def sample_end_to_end_mapping() -> tuple[Dict[str, Any], Dict[str, Any], Dict[str, Any]]:
|
||||||
|
lp = LocalProblem(
|
||||||
|
id="lp-sample",
|
||||||
|
domain="sample-domain",
|
||||||
|
assets=["asset-1"],
|
||||||
|
objective={"maximize": 1.0},
|
||||||
|
constraints={"max_energy": 100.0},
|
||||||
|
)
|
||||||
|
sv = SharedVariables(version=1)
|
||||||
|
dv = DualVariables(version=1)
|
||||||
|
return to_catopt(lp), sv.to_catopt(), dv.to_catopt()
|
||||||
|
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
"LocalProblem",
|
"LocalProblem",
|
||||||
"SharedVariable",
|
"SharedVariables",
|
||||||
"DualVariable",
|
"DualVariables",
|
||||||
"PlanDelta",
|
"PlanDelta",
|
||||||
"ContractRegistry",
|
"PrivacyBudget",
|
||||||
"CatOptBridge",
|
"AuditLog",
|
||||||
|
"GraphOfContracts",
|
||||||
|
"Registry",
|
||||||
|
"to_catopt",
|
||||||
|
"from_catopt",
|
||||||
|
"sample_end_to_end_mapping",
|
||||||
]
|
]
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue