build(agent): new-agents-3#dd492b iteration

This commit is contained in:
agent-dd492b85242a98c5 2026-04-20 14:16:15 +02:00
parent 7cfae65f0c
commit 3eb87a9f94
3 changed files with 116 additions and 1 deletions

View File

@ -9,6 +9,12 @@ Offline-first cross-domain orchestration for disaster-resilient grids.
- Simulation and hardware-in-the-loop testing with KPI dashboards. - Simulation and hardware-in-the-loop testing with KPI dashboards.
- Governance ledger and event sourcing for auditability. - Governance ledger and event sourcing for auditability.
### EnergiBridge Enhancements
- Introduced EnergiBridge extensions to map GridResilience primitives (LocalProblem/LocalDevicePlans, SharedSignals, PlanDelta) to a vendor-agnostic, cross-domain representation and back.
- Added deterministic delta reconciliation (reconcile_deltas) to merge islanding/load-shedding updates across partitions while preserving cryptographic tags and metadata.
- Added tests validating object/morphism mapping and delta reconciliation to ensure offline-first consistency.
- Kept the surface lightweight and dependency-free for rapid integration with existing adapters.
Project structure and packaging Project structure and packaging
- Python-based core with adapters under src/gridresilience_studio/adapters. - Python-based core with adapters under src/gridresilience_studio/adapters.
- Core primitives live in src/gridresilience_studio/core.py. - Core primitives live in src/gridresilience_studio/core.py.

View File

@ -8,7 +8,7 @@ tests fast and focused on integration surface.
""" """
from __future__ import annotations from __future__ import annotations
from dataclasses import dataclass, field from dataclasses import dataclass, field
from typing import Any, Dict from typing import Any, Dict, List
from .core import Object, Morphism, PlanDelta from .core import Object, Morphism, PlanDelta
@ -49,6 +49,74 @@ class EnergiBridge:
"""Identity mapping for PlanDelta (pass-through in this sketch).""" """Identity mapping for PlanDelta (pass-through in this sketch)."""
return delta return delta
# ---------------------------------------------------------------------
# Cross-domain canonical representations (GoC-style)
# ---------------------------------------------------------------------
@staticmethod
def to_goc_object(obj: Object) -> Dict[str, Any]:
"""Translate a GridResilience Object into a simple GoC payload."""
return {
"type": "Object",
"id": obj.id,
"class": obj.type,
"properties": obj.properties,
}
@staticmethod
def to_goc_morphism(morph: Morphism) -> Dict[str, Any]:
"""Translate a GridResilience Morphism into a simple GoC payload."""
return {
"type": "Morphism",
"id": morph.id,
"source": morph.source,
"target": morph.target,
"signals": morph.signals,
"version": morph.version,
}
@staticmethod
def reconcile_deltas(local_deltas: List[PlanDelta], remote_deltas: List[PlanDelta]) -> List[PlanDelta]:
"""Deterministically reconcile two sets of PlanDelta objects.
- Delta IDs are used as the canonical key.
- Actions from both sides are merged (without duplicating identical actions).
- islanded flag is OR'ed; tags are merged shallowly.
- Returns a new list of PlanDelta instances representing the merged view.
"""
merged: dict[str, PlanDelta] = {}
# seed with local deltas
for d in local_deltas:
merged[d.delta_id] = PlanDelta(
delta_id=d.delta_id,
islanded=d.islanded,
actions=list(d.actions),
tags=dict(d.tags),
)
# merge remote deltas
for d in remote_deltas:
if d.delta_id in merged:
base = merged[d.delta_id]
# merge actions without duplicates
existing = list(base.actions)
for act in d.actions:
if act not in existing:
existing.append(act)
base.actions = existing
base.islanded = base.islanded or d.islanded
# shallow merge of tags
base.tags = {**base.tags, **d.tags}
else:
merged[d.delta_id] = PlanDelta(
delta_id=d.delta_id,
islanded=d.islanded,
actions=list(d.actions),
tags=dict(d.tags),
)
return list(merged.values())
__all__ = ["LocalProblem", "SharedSignals", "EnergiBridge"] __all__ = ["LocalProblem", "SharedSignals", "EnergiBridge"]

View File

@ -0,0 +1,41 @@
import pytest
from gridresilience_studio.energi_bridge import EnergiBridge, LocalProblem, SharedSignals
from gridresilience_studio.core import Object, Morphism, PlanDelta
def test_goC_object_mapping():
lp = LocalProblem(id="LP1", description="Test LocalProblem", resources={"foo": "bar"})
obj = EnergiBridge.map_object(lp)
assert isinstance(obj, Object)
goc = EnergiBridge.to_goc_object(obj)
assert goc["type"] == "Object"
assert goc["id"] == "LP1"
assert goc["class"] == "LocalProblem" or goc["class"] == obj.type
assert isinstance(goc["properties"], dict)
def test_goC_morphism_mapping():
sig = SharedSignals(id="SIG1", source="LP1", target="LP2", signals={"voltage": 1.0})
morph = EnergiBridge.map_signals(sig)
assert isinstance(morph, Morphism)
gocm = EnergiBridge.to_goc_morphism(morph)
assert gocm["type"] == "Morphism"
assert gocm["id"] == "SIG1"
assert gocm["source"] == "LP1"
assert gocm["signals"] == {"voltage": 1.0}
def test_delta_reconciliation():
d_local = PlanDelta(delta_id="D1", islanded=False, actions=[{"type": "noop"}], tags={})
d_remote = PlanDelta(delta_id="D1", islanded=True, actions=[{"type": "toggle"}], tags={"t": "v1"})
merged = EnergiBridge.reconcile_deltas([d_local], [d_remote])
assert len(merged) == 1
merged_delta = merged[0]
assert merged_delta.delta_id == "D1"
# both actions should be merged without duplicates
actions = merged_delta.actions
assert any(a.get("type") == "noop" for a in actions)
assert any(a.get("type") == "toggle" for a in actions)
# islanded should be True due to OR
assert merged_delta.islanded is True