build(agent): molt-x#ed374b iteration

This commit is contained in:
agent-ed374b2a16b664d2 2026-04-16 21:27:21 +02:00
parent 69f0cddaa4
commit 9c2ce20262
2 changed files with 56 additions and 0 deletions

View File

@ -67,6 +67,40 @@ class GraphOfContracts:
return self.contracts.get(contract_id) return self.contracts.get(contract_id)
@dataclass
class SafetyBudget:
"""Budget controls to enforce safety constraints across federation.
This is a lightweight, pluggable budget model for MVP. It captures a
few conservative defaults and can be extended with domain-specific checks.
"""
enabled: bool = True
max_current_draw_a: float = 0.0 # maximum allowed current draw in amperes
max_voltage_variation_pu: float = 0.0 # per-unit allowable voltage variation
device_limits: Dict[str, float] = field(default_factory=dict) # per-device limits
timestamp: float = field(default_factory=time.time)
def update(self, key: str, value: Any) -> None:
self.device_limits[key] = value
self.timestamp = time.time()
@dataclass
class PrivacyBudget:
"""Budget to bound data sharing and protect privacy in federation."""
enabled: bool = True
allowed_signals: List[str] = field(default_factory=list)
total_budget_units: float = 1.0 # abstract budget units
per_signal_budget: Dict[str, float] = field(default_factory=dict)
timestamp: float = field(default_factory=time.time)
def use(self, signal: str, amount: float) -> None:
# Simple budgeting semantics: deduct amount from per-signal budget
current = self.per_signal_budget.get(signal, self.total_budget_units)
self.per_signal_budget[signal] = max(0.0, current - amount)
self.timestamp = time.time()
__all__ = [ __all__ = [
"LocalProblem", "LocalProblem",
"SharedVariables", "SharedVariables",
@ -74,4 +108,6 @@ __all__ = [
"DualVariables", "DualVariables",
"AuditLog", "AuditLog",
"GraphOfContracts", "GraphOfContracts",
"SafetyBudget",
"PrivacyBudget",
] ]

20
tests/test_budgets.py Normal file
View File

@ -0,0 +1,20 @@
from energiamesh.core import SafetyBudget, PrivacyBudget
def test_safety_budget_basic():
sb = SafetyBudget(enabled=True, max_current_draw_a=50.0, max_voltage_variation_pu=0.02)
assert sb.enabled is True
assert sb.max_current_draw_a == 50.0
assert sb.max_voltage_variation_pu == 0.02
# Update device limits
sb.update("DER-01", 40.0)
assert sb.device_limits["DER-01"] == 40.0
def test_privacy_budget_basic_and_use():
pb = PrivacyBudget(enabled=True, allowed_signals=["forecast"], total_budget_units=1.0)
assert pb.enabled is True
assert pb.allowed_signals == ["forecast"]
pb.per_signal_budget["forecast"] = 1.0
pb.use("forecast", 0.25)
assert pb.per_signal_budget["forecast"] == 0.75