build(agent): new-agents-2#7e3bbc iteration
This commit is contained in:
parent
43a7e53845
commit
b26d937fef
42
README.md
42
README.md
|
|
@ -1,19 +1,33 @@
|
|||
# OpenEnergySphere EnergiBridge (EnergiBridge MVP)
|
||||
OpenEnergySphere EnergiBridge MVP
|
||||
|
||||
This repository implements a production-ready skeleton for OpenEnergySphere's federated, contract-driven cross-domain energy planning platform. The MVP focuses on the EnergiBridge interoperability layer, mapping OpenEnergySphere primitives to a canonical IR and enabling plug-and-play adapters across DERs, meters, pumps, and buildings while preserving offline-first operation and governance.
|
||||
Overview
|
||||
- EnergiBridge provides a canonical, vendor-agnostic interoperability layer for cross-domain energy planning. It translates domain primitives into a compact IR (Object, SharedVariables, PlanDelta) and enables plug-and-play adapters across DERs, meters, pumps, and buildings while preserving offline-first operation and governance hooks.
|
||||
|
||||
Note: Added a minimal EnergiBridge mapper (EnergiBridgeMapper) to illustrate canonical mappings between LocalProblem objects and a vendor-agnostic IR. This enables straightforward extension to more complex cross-domain workflows and adapters.
|
||||
Architecture
|
||||
- Core primitives: LocalProblem (per-site optimization), SharedVariables (signals and priors), PlanDelta (incremental actions with provenance), and a Graph-of-Contracts (GoC) registry for adapters and data schemas.
|
||||
- EnergiBridgeMapper (in src/energysphere/energi_bridge.py) maps Energysphere primitives to a canonical IR representation and supports round-tripping LocalProblem objects.
|
||||
- Registry: In-memory GraphContractRegistry with metadata for auditing and replay protection. LocalProblems get stored with version, timestamp, and nonce metadata.
|
||||
- Adapters: Starter adapters implemented under energysphere/adapters/ (SubstationMeterAdapter, DERAggregatorAdapter).
|
||||
- Server API: Lightweight FastAPI app in energysphere/server/main.py exposing contract and adapter endpoints.
|
||||
|
||||
Architecture highlights
|
||||
- Core primitives: LocalProblem, SharedVariables, DualVariables, PlanDelta, PrivacyBudget, AuditLog
|
||||
- Graph-of-Contracts registry for adapters and data schemas with per-message metadata
|
||||
- Adapters: starter implementations (substation_meter, der_aggregator)
|
||||
- EnergiBridge: mapping functions between OpenEnergySphere primitives and a CatOpt-like IR
|
||||
- Server: FastAPI app exposing endpoints to register contracts and adapters
|
||||
- Storage: in-memory by default, with potential for SQLite/PostgreSQL persistence
|
||||
Roadmap (MVP, 8–12 weeks)
|
||||
- Phase 0: Protocol skeleton, 2 starter adapters, TLS transport, ADMM-lite local solver, delta-sync with deterministic replay.
|
||||
- Phase 1: Governance ledger scaffold, identity management (DIDs/certs), secure aggregation for SharedVariables.
|
||||
- Phase 2: Cross-domain demo with toy cross-domain objective and EnergiBridge SDK.
|
||||
- Phase 3: Hardware-in-the-loop validation and KPI dashboards.
|
||||
|
||||
Getting started
|
||||
- Install dependencies and run tests via test.sh
|
||||
- Run API locally: uvicorn energysphere.server.main:app --reload
|
||||
Data Contracts Seeds
|
||||
- LocalProblem, SharedVariables, DualVariables, PlanDelta, PrivacyBudget, AuditLog; a Graph-of-Contracts registry; minimal contract example and conformance harness for adapters.
|
||||
|
||||
This README intentionally provides a compact architectural overview and a concrete MVP workflow to accelerate collaboration and iteration.
|
||||
Security & Governance
|
||||
- Hardware-backed attestation for adapters; DID/short-lived certs; attestation for plan deltas; optional local DP budgets.
|
||||
|
||||
How to run (current MVP)
|
||||
- Install dependencies and run tests:
|
||||
- bash test.sh
|
||||
- Start API (dev):
|
||||
- uvicorn energysphere.server.main:app --reload --port 8000
|
||||
|
||||
Notes
|
||||
- This project uses a pyproject.toml with a proper build-system. Packaging metadata is wired to README.md via readme = "README.md" in pyproject.toml.
|
||||
- The goal is to keep a tight MVP surface while enabling gradual expansion into a production-grade, privacy-preserving federation platform.
|
||||
|
|
|
|||
|
|
@ -51,5 +51,26 @@ class EnergiBridgeMapper:
|
|||
parameters=obj.get("parameters", {}),
|
||||
)
|
||||
|
||||
# --- Additional helpers for richer IR coverage ---
|
||||
def map_shared_variable(self, sv: "SharedVariable") -> Dict[str, Any]: # type: ignore[name-defined]
|
||||
"""Map a SharedVariable (or similar) to a canonical IR representation."""
|
||||
return {
|
||||
"type": "SharedVariable",
|
||||
"name": getattr(sv, "name", ""),
|
||||
"value": getattr(sv, "value", None),
|
||||
"version": getattr(sv, "version", 1),
|
||||
"privacy_bound": getattr(sv, "privacy_bound", None),
|
||||
}
|
||||
|
||||
def map_plan_delta(self, delta: "PlanDelta") -> Dict[str, Any]: # type: ignore[name-defined]
|
||||
"""Map a PlanDelta to a canonical IR representation."""
|
||||
return {
|
||||
"type": "PlanDelta",
|
||||
"delta_id": getattr(delta, "delta_id", ""),
|
||||
"changes": getattr(delta, "changes", {}),
|
||||
"timestamp": getattr(delta, "timestamp", 0.0),
|
||||
"author": getattr(delta, "delta_id", ""),
|
||||
}
|
||||
|
||||
|
||||
__all__ = ["EnergiBridgeMapper"]
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import time
|
||||
import secrets
|
||||
from typing import Any, Dict, List
|
||||
|
||||
from .models import LocalProblem
|
||||
|
|
@ -16,6 +18,36 @@ class GraphContractRegistry:
|
|||
self._contracts: Dict[str, Dict[str, Any]] = {}
|
||||
|
||||
def register_contract(self, contract_id: str, contract_spec: Dict[str, Any]) -> None:
|
||||
"""Register a contract specification with optional metadata.
|
||||
|
||||
This is a very light-weight in-memory store. Contracts can carry
|
||||
arbitrary metadata under a dedicated _meta key, but we preserve the
|
||||
contract_spec as-is for backward-compatibility.
|
||||
"""
|
||||
self._contracts[contract_id] = contract_spec
|
||||
|
||||
def register_contract_with_meta(
|
||||
self,
|
||||
contract_id: str,
|
||||
contract_spec: Dict[str, Any],
|
||||
version: int | None = None,
|
||||
timestamp: float | None = None,
|
||||
nonce: str | None = None,
|
||||
) -> None:
|
||||
"""Register a contract with explicit metadata for auditing/replay protection."""
|
||||
if version is None:
|
||||
version = 1
|
||||
if timestamp is None:
|
||||
timestamp = time.time()
|
||||
if nonce is None:
|
||||
nonce = secrets.token_hex(8)
|
||||
meta = {
|
||||
"version": int(version),
|
||||
"timestamp": float(timestamp),
|
||||
"nonce": nonce,
|
||||
}
|
||||
contract_spec = dict(contract_spec) # shallow copy
|
||||
contract_spec["_meta"] = meta
|
||||
self._contracts[contract_id] = contract_spec
|
||||
|
||||
def list_contracts(self) -> List[Dict[str, Any]]:
|
||||
|
|
@ -26,4 +58,11 @@ class GraphContractRegistry:
|
|||
|
||||
# Lightweight helper to store a LocalProblem as a contract reference
|
||||
def register_local_problem(self, contract_id: str, lp: LocalProblem) -> None:
|
||||
self.register_contract(contract_id, lp.dict())
|
||||
"""Register a LocalProblem with default metadata."""
|
||||
spec = lp.dict()
|
||||
spec["_meta"] = {
|
||||
"version": 1,
|
||||
"timestamp": time.time(),
|
||||
"nonce": secrets.token_hex(8),
|
||||
}
|
||||
self.register_contract(contract_id, spec)
|
||||
|
|
|
|||
Loading…
Reference in New Issue