build(agent): new-agents-3#dd492b iteration
This commit is contained in:
parent
781a87a0b7
commit
a4ed925bef
31
README.md
31
README.md
|
|
@ -46,3 +46,34 @@ This repo ships a production-like MVP rather than a toy example. Expect incremen
|
||||||
|
|
||||||
## Migration and Evolution
|
## Migration and Evolution
|
||||||
- Contract migration: The contract registry now supports migrating a contract from an older version to a newer version. This enables adapters to evolve data schemas over time while marking the legacy version as migrated. The old version will have a migrated_to field pointing to the new version, and the new version will register its updated schema. This helps maintain backward-compatibility during MVP-driven evolution of the canonical IR.
|
- Contract migration: The contract registry now supports migrating a contract from an older version to a newer version. This enables adapters to evolve data schemas over time while marking the legacy version as migrated. The old version will have a migrated_to field pointing to the new version, and the new version will register its updated schema. This helps maintain backward-compatibility during MVP-driven evolution of the canonical IR.
|
||||||
|
|
||||||
|
## MVP Roadmap
|
||||||
|
|
||||||
|
- Phase 0: Core interop skeleton (1–2 weeks)
|
||||||
|
- Solidify Objects/Morphisms/Functors with a versioned ContractRegistry (already scaffolded).
|
||||||
|
- Ship DSL seeds: LocalProblem, SharedVariables, PlanDelta, DualVariables, PrivacyBudget, AuditLog, PolicyBlock.
|
||||||
|
- Implement two starter adapters (rover_planner, habitat_module) connected via TLS, plus a minimal ADMM-lite solver integration.
|
||||||
|
|
||||||
|
- Phase 1: Governance & identity (2–3 weeks)
|
||||||
|
- Lightweight governance ledger and per-message conformance checks.
|
||||||
|
- Identity layer (DID or short-lived certs) for adapters and channels.
|
||||||
|
- Hardened contract conformance tests and deterministic replay on islanding.
|
||||||
|
|
||||||
|
- Phase 2: Cross-domain demo (2–3 weeks)
|
||||||
|
- Simulated two-agent end-to-end demo with a toy energy/task-allocation objective.
|
||||||
|
- SDK bindings (Python/C++) for the GoC registry and EnergiBridge-like mappings.
|
||||||
|
- Publish a minimal, stable GoC registry surface for onboarding adapters.
|
||||||
|
|
||||||
|
- Phase 3: Hardware-in-the-loop (2–3 weeks)
|
||||||
|
- HIL demo with Gazebo/ROS integration, KPI dashboards for convergence and delta-sync latency.
|
||||||
|
- Documentation updates and a minimal DSL tutorial for rapid prototyping.
|
||||||
|
|
||||||
|
- Success metrics
|
||||||
|
- Convergence speed to near-optimal, plan optimality gap vs centralized baseline.
|
||||||
|
- Delta-sync latency and replay accuracy during islanding/recovery.
|
||||||
|
- Adapter conformance and overall interoperability scoring.
|
||||||
|
|
||||||
|
- Deliverable artifacts
|
||||||
|
- DSL sketch docs, toy adapters, EnergiBridge mapping examples, and a publish-ready README.
|
||||||
|
|
||||||
|
If helpful, I can draft concrete toy adapters and a minimal EnergiBridge mapping to accelerate first-to-MVP progress.
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
"""CatOpt-Graph MVP package init"""
|
"""CatOpt-Graph MVP package init"""
|
||||||
from .core import Object, Morphism, Functor, ContractRegistry
|
from .core import Object, Morphism, Functor, ContractRegistry
|
||||||
|
from .dsl import LocalProblem, SharedVariables, PlanDelta, DualVariables, PrivacyBudget, AuditLog, PolicyBlock, to_json
|
||||||
from .admm_lite import AdmmLite
|
from .admm_lite import AdmmLite
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
|
|
@ -7,5 +8,13 @@ __all__ = [
|
||||||
"Morphism",
|
"Morphism",
|
||||||
"Functor",
|
"Functor",
|
||||||
"ContractRegistry",
|
"ContractRegistry",
|
||||||
|
"LocalProblem",
|
||||||
|
"SharedVariables",
|
||||||
|
"PlanDelta",
|
||||||
|
"DualVariables",
|
||||||
|
"PrivacyBudget",
|
||||||
|
"AuditLog",
|
||||||
|
"PolicyBlock",
|
||||||
|
"to_json",
|
||||||
"AdmmLite",
|
"AdmmLite",
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,150 @@
|
||||||
|
"""Lightweight DSL primitives for CatOpt-Graph interoperability.
|
||||||
|
|
||||||
|
This module provides minimal, well-typed representations of the canonical
|
||||||
|
graph-interop primitives used in MVP wiring:
|
||||||
|
- LocalProblem (Object-like task description)
|
||||||
|
- SharedVariables (signals, priors, forecasts)
|
||||||
|
- PlanDelta (incremental plan changes)
|
||||||
|
- DualVariables (Lagrange multipliers / dual variables)
|
||||||
|
- PrivacyBudget (privacy budget proxy for governance)
|
||||||
|
- AuditLog (tamper-evident provenance entries)
|
||||||
|
- PolicyBlock (safety/exposure controls)
|
||||||
|
|
||||||
|
The goal is to offer a small, framework-agnostic surface that adapters can map
|
||||||
|
to/from vendor-specific representations via the EnergiBridge-like interop spine.
|
||||||
|
"""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from dataclasses import dataclass, asdict
|
||||||
|
from typing import Any, Dict, Optional
|
||||||
|
import json
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class LocalProblem:
|
||||||
|
id: str
|
||||||
|
domain: str
|
||||||
|
assets: Dict[str, Any]
|
||||||
|
objective: Dict[str, Any]
|
||||||
|
constraints: Optional[Dict[str, Any]] = None
|
||||||
|
solver_hint: Optional[str] = None
|
||||||
|
|
||||||
|
def to_json(self) -> str:
|
||||||
|
return json.dumps(asdict(self))
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def from_json(payload: str) -> "LocalProblem":
|
||||||
|
data = json.loads(payload)
|
||||||
|
return LocalProblem(**data)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class SharedVariables:
|
||||||
|
version: str
|
||||||
|
forecasts: Dict[str, Any]
|
||||||
|
priors: Optional[Dict[str, Any]] = None
|
||||||
|
encryption_schema: Optional[str] = None
|
||||||
|
|
||||||
|
def to_json(self) -> str:
|
||||||
|
return json.dumps(asdict(self))
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def from_json(payload: str) -> "SharedVariables":
|
||||||
|
data = json.loads(payload)
|
||||||
|
return SharedVariables(**data)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class PlanDelta:
|
||||||
|
delta: Dict[str, Any]
|
||||||
|
timestamp: float
|
||||||
|
author: str
|
||||||
|
contract_id: Optional[str] = None
|
||||||
|
signature: Optional[str] = None
|
||||||
|
|
||||||
|
def to_json(self) -> str:
|
||||||
|
return json.dumps(asdict(self))
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def from_json(payload: str) -> "PlanDelta":
|
||||||
|
data = json.loads(payload)
|
||||||
|
return PlanDelta(**data)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class DualVariables:
|
||||||
|
multipliers: Dict[str, float]
|
||||||
|
center: Optional[Dict[str, Any]] = None
|
||||||
|
|
||||||
|
def to_json(self) -> str:
|
||||||
|
return json.dumps(asdict(self))
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def from_json(payload: str) -> "DualVariables":
|
||||||
|
data = json.loads(payload)
|
||||||
|
return DualVariables(**data)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class PrivacyBudget:
|
||||||
|
signal: str
|
||||||
|
budget: float
|
||||||
|
expiry: Optional[str] = None
|
||||||
|
|
||||||
|
def to_json(self) -> str:
|
||||||
|
return json.dumps(asdict(self))
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def from_json(payload: str) -> "PrivacyBudget":
|
||||||
|
data = json.loads(payload)
|
||||||
|
return PrivacyBudget(**data)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class AuditLog:
|
||||||
|
entry: str
|
||||||
|
signer: str
|
||||||
|
timestamp: float
|
||||||
|
contract_id: Optional[str] = None
|
||||||
|
version: Optional[str] = None
|
||||||
|
|
||||||
|
def to_json(self) -> str:
|
||||||
|
return json.dumps(asdict(self))
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def from_json(payload: str) -> "AuditLog":
|
||||||
|
data = json.loads(payload)
|
||||||
|
return AuditLog(**data)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class PolicyBlock:
|
||||||
|
safety: Dict[str, Any]
|
||||||
|
exposure_controls: Optional[Dict[str, Any]] = None
|
||||||
|
|
||||||
|
def to_json(self) -> str:
|
||||||
|
return json.dumps(asdict(self))
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def from_json(payload: str) -> "PolicyBlock":
|
||||||
|
data = json.loads(payload)
|
||||||
|
return PolicyBlock(**data)
|
||||||
|
|
||||||
|
|
||||||
|
# Simple utility to illustrate interop mapping patterns
|
||||||
|
def to_json(obj: Any) -> str:
|
||||||
|
if hasattr(obj, "to_json"):
|
||||||
|
return obj.to_json()
|
||||||
|
return json.dumps(asdict(obj))
|
||||||
|
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
"LocalProblem",
|
||||||
|
"SharedVariables",
|
||||||
|
"PlanDelta",
|
||||||
|
"DualVariables",
|
||||||
|
"PrivacyBudget",
|
||||||
|
"AuditLog",
|
||||||
|
"PolicyBlock",
|
||||||
|
"to_json",
|
||||||
|
]
|
||||||
|
|
@ -0,0 +1,39 @@
|
||||||
|
import json
|
||||||
|
from catopt_graph.dsl import LocalProblem, SharedVariables, PlanDelta, DualVariables, PrivacyBudget, AuditLog, PolicyBlock, to_json
|
||||||
|
|
||||||
|
|
||||||
|
def test_local_problem_json_roundtrip():
|
||||||
|
lp = LocalProblem(
|
||||||
|
id="lp-1",
|
||||||
|
domain="energy",
|
||||||
|
assets={"battery": 2, "solar": 5},
|
||||||
|
objective={"maximize": "util"},
|
||||||
|
constraints={"budget": 100},
|
||||||
|
solver_hint="fast-convex",
|
||||||
|
)
|
||||||
|
s = lp.to_json()
|
||||||
|
assert isinstance(s, str)
|
||||||
|
lp2 = LocalProblem.from_json(s)
|
||||||
|
assert lp2.id == lp.id
|
||||||
|
assert lp2.domain == lp.domain
|
||||||
|
|
||||||
|
|
||||||
|
def test_shared_variables_json_roundtrip():
|
||||||
|
sv = SharedVariables(version="1.0.0", forecasts={"demand": 10}, priors={"mean": 0.0})
|
||||||
|
s = sv.to_json()
|
||||||
|
sv2 = SharedVariables.from_json(s)
|
||||||
|
assert sv2.version == sv.version
|
||||||
|
|
||||||
|
|
||||||
|
def test_to_json_generic():
|
||||||
|
lp = LocalProblem(
|
||||||
|
id="lp-2",
|
||||||
|
domain="robotics",
|
||||||
|
assets={"robot": "rover-1"},
|
||||||
|
objective={"minimize": "time"},
|
||||||
|
constraints=None,
|
||||||
|
)
|
||||||
|
j = to_json(lp)
|
||||||
|
# ensures a JSON string is produced and decodable
|
||||||
|
data = json.loads(j)
|
||||||
|
assert data["id"] == lp.id
|
||||||
Loading…
Reference in New Issue