build(agent): molt-y#23e5c8 iteration
This commit is contained in:
parent
0177e1cfbe
commit
b051fc81ae
24
README.md
24
README.md
|
|
@ -1,19 +1,15 @@
|
||||||
# GridVerse MVP Scaffold
|
# GridVerse MVP
|
||||||
|
|
||||||
This repository contains a minimal MVP scaffold for a cross-domain energy optimization platform inspired by the GridVerse vision. It provides a canonical graph-contract registry, a marketplace skeleton for adapters, a lightweight solver, and delta-sync primitives to enable offline/partitioned operation.
|
A minimal, testable MVP scaffold for a cross-domain energy optimization platform with:
|
||||||
|
- Local problems (Objects), shared signals (Morphisms), and adapters (Functors)
|
||||||
|
- A small graph-contract registry to version contracts and schemas
|
||||||
|
- Starter adapters for DER controllers and building heating, with a toy ADMM-lite solver placeholder
|
||||||
|
- Lightweight delta-sync primitives and governance-ish metadata scaffolding
|
||||||
|
|
||||||
What's included
|
This repository is intended as a stepping stone toward a CatOpt-style interoperability bridge and a broader MVP as described in the project brief.
|
||||||
- In-memory ContractRegistry to version and validate contracts (Objects, Morphisms, PlanDelta, etc.).
|
|
||||||
- Starter adapters (DERAdapter, HeatingAdapter) implementing a simple adapt() interface to demonstrate the Edge-to-Canonical translation path.
|
|
||||||
- Tiny ADMM-like solver (gridverse.solver.admm_solve) suitable for wiring into a larger distributed stack.
|
|
||||||
- Delta-sync primitive (gridverse.delta_sync.reconcile) for merging divergent states.
|
|
||||||
- Tests validating basic wiring and interfaces (tests/test_basic.py).
|
|
||||||
|
|
||||||
How to run
|
Usage
|
||||||
- Install dependencies via pyproject.toml (requires setuptools, wheel).
|
|
||||||
- Run tests: bash test.sh
|
- Run tests: bash test.sh
|
||||||
- Build package: python -m build
|
- Package: python -m build
|
||||||
|
|
||||||
This MVP is intentionally small but designed to be extended into a full Graph-of-Contracts and Adapter Marketplace MVP over subsequent iterations.
|
Note: This is intentionally minimal. It is designed to be extended with real adapters, TLS transport, and a fuller solver in subsequent iterations.
|
||||||
|
|
||||||
License: MIT (example)
|
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,13 @@
|
||||||
"""GridVerse MVP package root (minimal stubs for tests).
|
"""GridVerse MVP namespace initialization."""
|
||||||
"""
|
|
||||||
|
from .core_contracts import LocalProblem, SharedVariables, PlanDelta, ConstraintSet, DeviceInfo
|
||||||
|
from .registry import GraphContractRegistry
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
"registry",
|
"LocalProblem",
|
||||||
"adapter_marketplace",
|
"SharedVariables",
|
||||||
"solver",
|
"PlanDelta",
|
||||||
"delta_sync",
|
"ConstraintSet",
|
||||||
|
"DeviceInfo",
|
||||||
|
"GraphContractRegistry",
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
class DERAdapter:
|
||||||
|
def adapt(self, lp: dict) -> dict:
|
||||||
|
# Minimal translation: wrap input as adapted payload
|
||||||
|
return {"adapted": lp}
|
||||||
|
|
||||||
|
def contract(self) -> dict:
|
||||||
|
return {"name": "DERAdapter", "version": "0.1.0"}
|
||||||
|
|
||||||
|
|
||||||
|
class HeatingAdapter:
|
||||||
|
def adapt(self, lp: dict) -> dict:
|
||||||
|
return {"adapted": lp}
|
||||||
|
|
||||||
|
def contract(self) -> dict:
|
||||||
|
return {"name": "HeatingAdapter", "version": "0.1.0"}
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from gridverse.core_contracts import LocalProblem, SharedVariables
|
||||||
|
|
||||||
|
|
||||||
|
class BuildingHeatingAdapter:
|
||||||
|
"""Starter building heating controller adapter (toy implementation)."""
|
||||||
|
|
||||||
|
name = "BuildingHeater"
|
||||||
|
version = "0.1"
|
||||||
|
|
||||||
|
def adapt(self, problem: LocalProblem) -> SharedVariables:
|
||||||
|
# Toy: emit a simple slack variable for heating demand
|
||||||
|
current_temp = problem.variables.get("indoor_temp", 20)
|
||||||
|
signals = {"heating_delta": max(0.0, 22.0 - current_temp)}
|
||||||
|
return SharedVariables(signals=signals, version=1)
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from gridverse.core_contracts import LocalProblem, SharedVariables
|
||||||
|
|
||||||
|
|
||||||
|
class DERControllerAdapter:
|
||||||
|
"""Starter DER controller adapter (toy implementation).
|
||||||
|
|
||||||
|
Translates a LocalProblem into a SharedVariables payload as a placeholder
|
||||||
|
for a real device-specific adapter.
|
||||||
|
"""
|
||||||
|
|
||||||
|
name = "DERController"
|
||||||
|
version = "0.1"
|
||||||
|
|
||||||
|
def adapt(self, problem: LocalProblem) -> SharedVariables:
|
||||||
|
# Toy: echo back a single shared signal representing available power margin
|
||||||
|
signals = {"available_power": max(0.0, 1.0 - len(problem.variables))}
|
||||||
|
return SharedVariables(signals=signals, version=1)
|
||||||
|
|
@ -0,0 +1,71 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from dataclasses import dataclass, asdict
|
||||||
|
from typing import Any, Dict, Optional
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class LocalProblem:
|
||||||
|
site_id: str
|
||||||
|
description: str
|
||||||
|
variables: Dict[str, Any]
|
||||||
|
|
||||||
|
def to_dict(self) -> Dict[str, Any]:
|
||||||
|
return asdict(self)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def from_dict(d: Dict[str, Any]) -> "LocalProblem":
|
||||||
|
return LocalProblem(site_id=d["site_id"], description=d["description"], variables=d.get("variables", {}))
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class SharedVariables:
|
||||||
|
signals: Dict[str, Any]
|
||||||
|
version: int = 0
|
||||||
|
|
||||||
|
def to_dict(self) -> Dict[str, Any]:
|
||||||
|
return asdict(self)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def from_dict(d: Dict[str, Any]) -> "SharedVariables":
|
||||||
|
return SharedVariables(signals=d.get("signals", {}), version=d.get("version", 0))
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class PlanDelta:
|
||||||
|
delta_id: str
|
||||||
|
changes: Dict[str, Any]
|
||||||
|
timestamp: float
|
||||||
|
|
||||||
|
def to_dict(self) -> Dict[str, Any]:
|
||||||
|
return asdict(self)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def from_dict(d: Dict[str, Any]) -> "PlanDelta":
|
||||||
|
return PlanDelta(delta_id=d["delta_id"], changes=d.get("changes", {}), timestamp=d.get("timestamp", 0.0))
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class ConstraintSet:
|
||||||
|
constraints: Dict[str, Any]
|
||||||
|
|
||||||
|
def to_dict(self) -> Dict[str, Any]:
|
||||||
|
return asdict(self)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def from_dict(d: Dict[str, Any]) -> "ConstraintSet":
|
||||||
|
return ConstraintSet(constraints=d.get("constraints", {}))
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class DeviceInfo:
|
||||||
|
device_id: str
|
||||||
|
device_type: str
|
||||||
|
capabilities: Dict[str, Any]
|
||||||
|
|
||||||
|
def to_dict(self) -> Dict[str, Any]:
|
||||||
|
return asdict(self)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def from_dict(d: Dict[str, Any]) -> "DeviceInfo":
|
||||||
|
return DeviceInfo(device_id=d["device_id"], device_type=d["device_type"], capabilities=d.get("capabilities", {}))
|
||||||
|
|
@ -1,11 +1,46 @@
|
||||||
class ContractRegistry:
|
from __future__ import annotations
|
||||||
def __init__(self):
|
|
||||||
# store contracts as {(name, version): contract_dict}
|
|
||||||
self._store = {}
|
|
||||||
|
|
||||||
def register_contract(self, name: str, version: str, contract: dict):
|
from typing import Any, Dict, Tuple
|
||||||
key = (name, version)
|
|
||||||
self._store[key] = contract
|
|
||||||
|
|
||||||
def get_contract(self, name: str, version: str):
|
from .core_contracts import LocalProblem, SharedVariables, PlanDelta, ConstraintSet, DeviceInfo
|
||||||
return self._store.get((name, version))
|
|
||||||
|
|
||||||
|
class GraphContractRegistry:
|
||||||
|
"""Lightweight in-memory registry for contracts and adapters.
|
||||||
|
|
||||||
|
This is a minimal scaffold to support MVP development of a graph-contract
|
||||||
|
oriented interoperability bridge. It stores basic contract schemas and
|
||||||
|
adapter interface descriptors, plus a simple conformance test harness.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self) -> None:
|
||||||
|
self._contracts: Dict[str, Dict[str, Any]] = {}
|
||||||
|
self._adapters: Dict[str, Dict[str, Any]] = {}
|
||||||
|
|
||||||
|
# Contracts
|
||||||
|
def register_contract(self, name: str, version: str, schema: Dict[str, Any]) -> None:
|
||||||
|
self._contracts.setdefault(name, {})[version] = schema
|
||||||
|
|
||||||
|
def get_contract(self, name: str, version: str) -> Dict[str, Any]:
|
||||||
|
return self._contracts.get(name, {}).get(version, {})
|
||||||
|
|
||||||
|
# Adapters
|
||||||
|
def register_adapter(self, name: str, version: str, iface: Dict[str, Any]) -> None:
|
||||||
|
self._adapters.setdefault(name, {})[version] = iface
|
||||||
|
|
||||||
|
def get_adapter(self, name: str, version: str) -> Dict[str, Any]:
|
||||||
|
return self._adapters.get(name, {}).get(version, {})
|
||||||
|
|
||||||
|
# Conformance harness (stub)
|
||||||
|
def conformance_test(self, adapter_iface: Dict[str, Any], contract_schema: Dict[str, Any]) -> bool:
|
||||||
|
# Minimal check: ensure required keys exist in both sides
|
||||||
|
required_adapter_keys = {"name", "version", "interface"}
|
||||||
|
required_contract_keys = {"name", "version", "schema"}
|
||||||
|
has_adapter = required_adapter_keys.issubset(set(adapter_iface.keys()))
|
||||||
|
has_contract = required_contract_keys.issubset(set(contract_schema.keys()))
|
||||||
|
return bool(has_adapter and has_contract)
|
||||||
|
|
||||||
|
# Backwards-compatibility alias for older code/tests
|
||||||
|
# Some clients import ContractRegistry from gridverse.registry.
|
||||||
|
# Expose a stable name that maps to the new GraphContractRegistry implementation.
|
||||||
|
ContractRegistry = GraphContractRegistry
|
||||||
|
|
|
||||||
|
|
@ -1,22 +1,12 @@
|
||||||
[build-system]
|
[build-system]
|
||||||
requires = ["setuptools", "wheel"]
|
requires = ["setuptools>=42","wheel"]
|
||||||
build-backend = "setuptools.build_meta"
|
build-backend = "setuptools.build_meta"
|
||||||
|
|
||||||
[project]
|
[project]
|
||||||
name = "gridverse-core"
|
name = "gridverse-mvp"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
description = "Minimal MVP scaffold for GridVerse cross-domain optimization"
|
description = "GridVerse MVP: cross-domain energy optimization core with graph-contract registry and adapter marketplace"
|
||||||
|
readme = "README.md"
|
||||||
requires-python = ">=3.8"
|
requires-python = ">=3.8"
|
||||||
dependencies = [
|
license = {text = "MIT"}
|
||||||
"numpy>=1.26.0",
|
dependencies = ["numpy"]
|
||||||
"pytest>=8.0.0",
|
|
||||||
]
|
|
||||||
|
|
||||||
[tool.setuptools.packages.find]
|
|
||||||
where = ["."]
|
|
||||||
exclude = ["tests"]
|
|
||||||
|
|
||||||
[project.urls]
|
|
||||||
homepage = "https://example.com/gridverse"
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
7
test.sh
7
test.sh
|
|
@ -1,8 +1,7 @@
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
echo "Running tests..."
|
||||||
echo "Running GridVerse MVP tests..."
|
|
||||||
pytest -q
|
pytest -q
|
||||||
echo "Running packaging check..."
|
echo "Building package..."
|
||||||
python -m build
|
python -m build
|
||||||
echo "All tests and build completed successfully."
|
echo "All tests passed and package built successfully."
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,37 @@
|
||||||
|
from gridverse.core_contracts import LocalProblem, SharedVariables, PlanDelta, ConstraintSet, DeviceInfo
|
||||||
|
|
||||||
|
|
||||||
|
def test_local_problem_roundtrip():
|
||||||
|
lp = LocalProblem(site_id="site-1", description="test", variables={"key": "value"})
|
||||||
|
d = lp.to_dict()
|
||||||
|
assert d["site_id"] == "site-1"
|
||||||
|
lp2 = LocalProblem.from_dict(d)
|
||||||
|
assert lp2.site_id == lp.site_id
|
||||||
|
|
||||||
|
|
||||||
|
def test_shared_variables_roundtrip():
|
||||||
|
sv = SharedVariables(signals={"p": 1.0}, version=2)
|
||||||
|
d = sv.to_dict()
|
||||||
|
sv2 = SharedVariables.from_dict(d)
|
||||||
|
assert sv2.signals == sv.signals and sv2.version == sv.version
|
||||||
|
|
||||||
|
|
||||||
|
def test_plan_delta_roundtrip():
|
||||||
|
pd = PlanDelta(delta_id="d1", changes={"a": 1}, timestamp=1.23)
|
||||||
|
d = pd.to_dict()
|
||||||
|
pd2 = PlanDelta.from_dict(d)
|
||||||
|
assert pd2.delta_id == pd.delta_id and pd2.timestamp == pd.timestamp
|
||||||
|
|
||||||
|
|
||||||
|
def test_constraint_set_roundtrip():
|
||||||
|
cs = ConstraintSet(constraints={"mesh": True})
|
||||||
|
d = cs.to_dict()
|
||||||
|
cs2 = ConstraintSet.from_dict(d)
|
||||||
|
assert cs2.constraints["mesh"] is True
|
||||||
|
|
||||||
|
|
||||||
|
def test_device_info_roundtrip():
|
||||||
|
di = DeviceInfo(device_id="dev-1", device_type="inverter", capabilities={"cap": 10})
|
||||||
|
d = di.to_dict()
|
||||||
|
di2 = DeviceInfo.from_dict(d)
|
||||||
|
assert di2.device_id == di.device_id and di2.device_type == di.device_type
|
||||||
|
|
@ -1,12 +1,14 @@
|
||||||
from gridverse_open_low_code_platform_for_cro.registry import ContractRegistry
|
import math
|
||||||
|
from gridverse.registry import GraphContractRegistry
|
||||||
|
from gridverse.core_contracts import LocalProblem, SharedVariables
|
||||||
|
|
||||||
|
|
||||||
def test_registry_register_and_get():
|
def test_registry_basic_conformance():
|
||||||
reg = ContractRegistry()
|
reg = GraphContractRegistry()
|
||||||
contract = {
|
reg.register_contract("LocalProblem", "0.1", {"type": "object"})
|
||||||
"name": "LocalProblemContract",
|
reg.register_adapter("DERController", "0.1", {"name": "DERController", "interface": {}})
|
||||||
"version": "0.1.0",
|
|
||||||
"schema": {"type": "object", "properties": {"id": {"type": "string"}}},
|
adapter_iface = {"name": "DERController", "version": "0.1", "interface": {}}
|
||||||
}
|
contract_schema = {"name": "LocalProblem", "version": "0.1", "schema": {"type": "object"}}
|
||||||
reg.register("local_problem", contract)
|
|
||||||
assert reg.get("local_problem")["name"] == "LocalProblemContract"
|
assert reg.conformance_test(adapter_iface, contract_schema) is True
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue