build(agent): new-agents-2#7e3bbc iteration

This commit is contained in:
agent-7e3bbc424e07835b 2026-04-23 22:18:50 +02:00
parent 43a7e53845
commit b26d937fef
3 changed files with 89 additions and 15 deletions

View File

@ -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 Roadmap (MVP, 812 weeks)
- Core primitives: LocalProblem, SharedVariables, DualVariables, PlanDelta, PrivacyBudget, AuditLog - Phase 0: Protocol skeleton, 2 starter adapters, TLS transport, ADMM-lite local solver, delta-sync with deterministic replay.
- Graph-of-Contracts registry for adapters and data schemas with per-message metadata - Phase 1: Governance ledger scaffold, identity management (DIDs/certs), secure aggregation for SharedVariables.
- Adapters: starter implementations (substation_meter, der_aggregator) - Phase 2: Cross-domain demo with toy cross-domain objective and EnergiBridge SDK.
- EnergiBridge: mapping functions between OpenEnergySphere primitives and a CatOpt-like IR - Phase 3: Hardware-in-the-loop validation and KPI dashboards.
- Server: FastAPI app exposing endpoints to register contracts and adapters
- Storage: in-memory by default, with potential for SQLite/PostgreSQL persistence
Getting started Data Contracts Seeds
- Install dependencies and run tests via test.sh - LocalProblem, SharedVariables, DualVariables, PlanDelta, PrivacyBudget, AuditLog; a Graph-of-Contracts registry; minimal contract example and conformance harness for adapters.
- Run API locally: uvicorn energysphere.server.main:app --reload
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.

View File

@ -51,5 +51,26 @@ class EnergiBridgeMapper:
parameters=obj.get("parameters", {}), 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"] __all__ = ["EnergiBridgeMapper"]

View File

@ -1,5 +1,7 @@
from __future__ import annotations from __future__ import annotations
import time
import secrets
from typing import Any, Dict, List from typing import Any, Dict, List
from .models import LocalProblem from .models import LocalProblem
@ -16,6 +18,36 @@ class GraphContractRegistry:
self._contracts: Dict[str, Dict[str, Any]] = {} self._contracts: Dict[str, Dict[str, Any]] = {}
def register_contract(self, contract_id: str, contract_spec: Dict[str, Any]) -> None: 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 self._contracts[contract_id] = contract_spec
def list_contracts(self) -> List[Dict[str, Any]]: def list_contracts(self) -> List[Dict[str, Any]]:
@ -26,4 +58,11 @@ class GraphContractRegistry:
# Lightweight helper to store a LocalProblem as a contract reference # Lightweight helper to store a LocalProblem as a contract reference
def register_local_problem(self, contract_id: str, lp: LocalProblem) -> None: 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)