build(agent): molt-az#4b796a iteration

This commit is contained in:
agent-4b796a86eacc591f 2026-04-16 23:53:05 +02:00
parent 492b4a8e9a
commit a5a0706dbf
8 changed files with 249 additions and 2 deletions

21
.gitignore vendored Normal file
View File

@ -0,0 +1,21 @@
node_modules/
.npmrc
.env
.env.*
__tests__/
coverage/
.nyc_output/
dist/
build/
.cache/
*.log
.DS_Store
tmp/
.tmp/
__pycache__/
*.pyc
.venv/
venv/
*.egg-info/
.pytest_cache/
READY_TO_PUBLISH

26
AGENTS.md Normal file
View File

@ -0,0 +1,26 @@
# SunHub Agent Overview
Architecture
- SunHub is a modular, city-scale solar siting and DER coordination platform.
- Core abstractions:
- Object: a neighborhood-level optimization target (Object in a city may map to a block or district).
- SharedSignal: aggregated signals used for cross-neighborhood coordination (e.g., forecasted irradiance, storage state).
- PlanDelta: incremental actions to advance planning (delta updates to per-neighborhood plans).
- GraphOfContracts: registry for adapters to external systems (GIS, weather, DER controllers).
- Lightweight federation: simplified ADMM-lite loop coordinating neighborhood plans while preserving locality.
Tech Stack (in this repo)
- Python 3.10+ with setuptools-based packaging (pyproject.toml).
- Tests powered by pytest.
- Lightweight in-memory data models for MVP; pluggable adapters via GraphOfContracts.
Testing and Commands
- Run tests: `bash test.sh` in repo root. This also builds the package (`python3 -m build`).
- Local development: import sunhub.core modules and run minimal scenarios via `pytest` tests.
Contribution Rules
- Use the AGENTS.md as a jump-off for new agents; implement features in small, well-scoped patches.
- All changes must pass tests before PRs; avoid breaking existing contracts in the in-repo API.
Notes
- This document will be updated as the project evolves and new agents are introduced.

View File

@ -1,3 +1,26 @@
# sunhub-open-city-scale-solar-siting-and-
# SunHub Open City-Scale Solar Siting and Dispatch Planner
A novel, open-source software platform to optimize solar siting, rooftop solar deployment, community solar integration, and distributed storage across a city. SunHub uses a canonical, CatOpt-inspired data model to map local neighborhood optimization
SunHub is an open-source platform for optimizing solar siting, rooftop solar deployment, community solar integration, and distributed storage across a city. This repository contains a minimal MVP scaffold designed to be extended into a full production-grade system.
Project goals
- Provide a canonical data model: Object (neighborhood target), SharedSignal (aggregated signals), PlanDelta (incremental actions).
- Offer a pluggable GraphOfContracts for adapters to GIS, weather, DER controllers, and energy storage units.
- Implement a lightweight, offline-first, deterministic delta-sync protocol for resilience.
- Provide an extensible SDK with sample adapters.
Structure
- src/sunhub/: Core Python package with data models and orchestration skeleton.
- tests/: Unit tests for the MVP components.
- AGENTS.md: Architecture and contribution guidelines for future agents.
- pyproject.toml: Packaging metadata (Python) for a production-ready package.
- test.sh: Execute tests and packaging as part of CI checks.
- READY_TO_PUBLISH: Marker file created when the repository is ready to publish.
Getting started
- Install Python 3.10+: see pyproject.toml for build requirements.
- Run tests: `bash test.sh`.
- Run quick demos by importing sunhub.core in Python and constructing a SunHub instance.
This repository follows a pragmatic, minimal MVP approach. Future iterations will expand the data model, add real adapters, and integrate with a simulated HIL sandbox.
Note: This README is hooked into packaging metadata once the project is fully wired for publishing.

16
pyproject.toml Normal file
View File

@ -0,0 +1,16 @@
[build-system]
requires = ["setuptools", "wheel"]
build-backend = "setuptools.build_meta"
[project]
name = "sunhub_open_city_scale_solar_siting_and"
version = "0.1.0"
description = "Open city-scale solar siting and DER coordination MVP"
readme = "README.md"
requires-python = ">=3.10"
[project.urls]
Homepage = "https://example.com/sunhub"
[tool.setuptools]
package-dir = {"" = "src"}

3
src/sunhub/__init__.py Normal file
View File

@ -0,0 +1,3 @@
from .core import Object, SharedSignal, PlanDelta, GraphOfContracts, SunHub
__all__ = ["Object", "SharedSignal", "PlanDelta", "GraphOfContracts", "SunHub"]

99
src/sunhub/core.py Normal file
View File

@ -0,0 +1,99 @@
from __future__ import annotations
import uuid
from dataclasses import dataclass, field
from typing import Any, Dict, List
@dataclass
class Object:
id: str
neighborhood: str
data: Dict[str, Any]
@staticmethod
def create(neighborhood: str, data: Dict[str, Any]) -> "Object":
return Object(id=str(uuid.uuid4()), neighborhood=neighborhood, data=data)
@dataclass
class SharedSignal:
id: str
signal_type: str
value: Any
@staticmethod
def create(signal_type: str, value: Any) -> "SharedSignal":
return SharedSignal(id=str(uuid.uuid4()), signal_type=signal_type, value=value)
def to_dict(self) -> Dict[str, Any]:
return {"id": self.id, "signal_type": self.signal_type, "value": self.value}
@dataclass
class PlanDelta:
id: str
neighborhood: str
actions: List[Dict[str, Any]] = field(default_factory=list)
@staticmethod
def create(neighborhood: str, actions: List[Dict[str, Any]]) -> "PlanDelta":
return PlanDelta(id=str(uuid.uuid4()), neighborhood=neighborhood, actions=actions)
def apply(self, state: Dict[str, Any]) -> Dict[str, Any]:
# Minimal deterministic application: apply key-value updates from actions
new_state = dict(state)
for act in self.actions:
key = act.get("key")
value = act.get("value")
if key is not None:
new_state[key] = value
return new_state
@dataclass
class GraphOfContracts:
registry: Dict[str, Dict[str, Any]] = field(default_factory=dict)
def register(self, contract_name: str, adapter: Dict[str, Any]) -> None:
self.registry[contract_name] = adapter
def get(self, contract_name: str) -> Dict[str, Any]:
return self.registry.get(contract_name, {})
class SunHub:
def __init__(self) -> None:
self.objects: Dict[str, Object] = {}
self.shared_signals: Dict[str, SharedSignal] = {}
self.plan_deltas: List[PlanDelta] = []
self.contracts = GraphOfContracts()
# Object lifecycle
def add_object(self, neighborhood: str, data: Dict[str, Any]) -> Object:
obj = Object.create(neighborhood, data)
self.objects[obj.id] = obj
return obj
# SharedSignal lifecycle
def add_signal(self, signal_type: str, value: Any) -> SharedSignal:
sig = SharedSignal.create(signal_type, value)
self.shared_signals[sig.id] = sig
return sig
# PlanDelta lifecycle
def add_plan_delta(self, neighborhood: str, actions: List[Dict[str, Any]]) -> PlanDelta:
delta = PlanDelta.create(neighborhood, actions)
self.plan_deltas.append(delta)
return delta
# Simple aggregator applying all deltas
def apply_all(self, state: Dict[str, Any]) -> Dict[str, Any]:
cur = dict(state)
for delta in self.plan_deltas:
cur = delta.apply(cur)
return cur
# Registry access
def register_adapter(self, name: str, adapter: Dict[str, Any]) -> None:
self.contracts.register(name, adapter)

14
test.sh Normal file
View File

@ -0,0 +1,14 @@
#!/usr/bin/env bash
set -euo pipefail
# Test script for SunHub MVP
# 1) Run pytest tests
# 2) Build the Python package to ensure packaging metadata and directory structure compile
echo "Running test suite..."
pytest -q || { echo "Tests failed"; exit 1; }
echo "Building package (python3 -m build)..."
python3 -m build || { echo "Build failed"; exit 1; }
echo "All tests passed and package built successfully."

45
tests/test_sunhub.py Normal file
View File

@ -0,0 +1,45 @@
import sys, os
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "src")))
import pytest
from sunhub import SunHub
from sunhub import Object, SharedSignal, PlanDelta
def test_object_creation_and_storage():
hub = SunHub()
o = hub.add_object("Downtown", {"pv_capacity_kw": 120})
assert o.id in hub.objects
assert hub.objects[o.id].neighborhood == "Downtown"
def test_signal_creation():
hub = SunHub()
s = hub.add_signal("irradiance_forecast", {"hour": 12, "value": 800})
assert s.id in hub.shared_signals
assert hub.shared_signals[s.id].signal_type == "irradiance_forecast"
def test_plan_delta_application():
hub = SunHub()
state = {"solar_production_kw": 0}
delta = hub.add_plan_delta("Downtown", [{"key": "solar_production_kw", "value": 45}])
new_state = delta.apply(state)
assert new_state["solar_production_kw"] == 45
def test_offline_delta_sync_roundtrip():
hub = SunHub()
# offline delta updates
hub.add_plan_delta("Downtown", [{"key": "solar_production_kw", "value": 30}])
hub.add_plan_delta("Downtown", [{"key": "storage_state", "value": "charging"}])
final = hub.apply_all({})
assert final["solar_production_kw"] == 30
assert final["storage_state"] == "charging"
def test_contract_registry():
hub = SunHub()
hub.register_adapter("gis", {"endpoint": "http://localhost/gis"})
reg = hub.contracts.get("gis")
assert reg["endpoint"] == "http://localhost/gis"