build(agent): molt-x#ed374b iteration
This commit is contained in:
parent
79f8277d02
commit
7e891bda72
23
README.md
23
README.md
|
|
@ -1,16 +1,17 @@
|
||||||
# CatOpt-Graph MVP
|
# CatOpt-Graph MVP
|
||||||
|
|
||||||
This repository hosts a minimal, testable MVP of CatOpt-Graph: a graph-calculus-inspired orchestration layer for compositional optimization across edge devices.
|
A minimal, Graph-Calculus-inspired orchestration studio for compositional optimization across edge meshes.
|
||||||
|
|
||||||
- Core ontology: Objects, Morphisms, Functors, and a versioned ContractRegistry to manage data contracts.
|
- Core ontology: Objects, Morphisms, Functors, and a versioned ContractRegistry.
|
||||||
- Bridge: simple to_canonical/from_canonical bridge to map local problems into a canonical representation.
|
- Bridge: a lightweight to_canonical / from_canonical mapper to connect domain models to a canonical form.
|
||||||
- ADMM-lite: a tiny asynchronous, delta-sync solver skeleton for two agents with a simple global constraint.
|
- ADMM-lite: simple, asynchronous-like solver for distributed optimization with delta-sync semantics.
|
||||||
- Adapters: scaffolded rover and habitat adapters ready for extension.
|
- Adapters: rover and habitat starter adapters are included; transport is mocked for MVP.
|
||||||
- Tests: unit tests for contract registry, bridge mapping, and ADMM-lite core.
|
- Governance: lightweight audit trail scaffolding.
|
||||||
|
|
||||||
How to run
|
How to run tests
|
||||||
- Prerequisites: Python 3.10+, pip, and a POSIX shell.
|
- Ensure Python 3.10+ is installed
|
||||||
- Run tests: bash test.sh
|
- Run: bash test.sh
|
||||||
- Build package: python3 -m build
|
|
||||||
|
|
||||||
This MVP is intentionally small and opinionated to enable rapid iteration and interoperability testing with other ecosystems.
|
Notes
|
||||||
|
- This MVP focuses on minimal, well-scoped components to enable end-to-end interoperability with adapters and the ADMM-lite solver.
|
||||||
|
- See core/bridge.py for the canonical mapping primitives and tests for contract registry and bridge in tests/.
|
||||||
|
|
|
||||||
|
|
@ -2,19 +2,33 @@ from __future__ import annotations
|
||||||
|
|
||||||
from typing import Dict
|
from typing import Dict
|
||||||
|
|
||||||
from .contracts import LocalProblem, SharedVariables, DualVariables, PlanDelta
|
from .contracts import LocalProblem
|
||||||
|
|
||||||
|
|
||||||
class CatOptBridge:
|
|
||||||
"""Minimal bridge translating between domain LocalProblem and canonical form."""
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def to_canonical(lp: LocalProblem) -> Dict[str, object]:
|
def to_canonical(lp: LocalProblem) -> Dict[str, object]:
|
||||||
# Very lightweight translation: wrap payload with id
|
"""Map a LocalProblem into a tiny canonical representation.
|
||||||
return {"object_id": lp.asset_id, "payload": lp.payload}
|
|
||||||
|
This is a minimal, MVP-friendly bridge primitive that preserves
|
||||||
|
the essential fields needed for cross-domain orchestration:
|
||||||
|
- type: a string tag for the canonical object
|
||||||
|
- object_id: asset_id of the local problem
|
||||||
|
- data: the payload dictionary
|
||||||
|
"""
|
||||||
|
return {
|
||||||
|
"type": "LocalProblem",
|
||||||
|
"object_id": lp.asset_id,
|
||||||
|
"data": dict(lp.payload),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def from_canonical(data: Dict[str, object]) -> LocalProblem:
|
def from_canonical(data: Dict[str, object]) -> LocalProblem:
|
||||||
asset_id = str(data.get("object_id", "unknown"))
|
"""Inverse of to_canonical for LocalProblem objects.
|
||||||
payload = dict(data.get("payload", {}))
|
|
||||||
|
Expects a dict produced by to_canonical or a compatible canonical form.
|
||||||
|
"""
|
||||||
|
asset_id = str(data.get("object_id"))
|
||||||
|
payload = data.get("data", {})
|
||||||
|
# Safety: ensure payload is a dict
|
||||||
|
if not isinstance(payload, dict):
|
||||||
|
payload = {"payload": payload}
|
||||||
return LocalProblem(asset_id=asset_id, payload=payload)
|
return LocalProblem(asset_id=asset_id, payload=payload)
|
||||||
|
|
|
||||||
|
|
@ -20,3 +20,21 @@ class CatOptBridge:
|
||||||
payload = dict(data.get("payload", {}))
|
payload = dict(data.get("payload", {}))
|
||||||
# Return canonical LocalProblem type defined in core.contracts (tests shim)
|
# Return canonical LocalProblem type defined in core.contracts (tests shim)
|
||||||
return CanonicalLocalProblem(asset_id=asset_id, payload=payload)
|
return CanonicalLocalProblem(asset_id=asset_id, payload=payload)
|
||||||
|
|
||||||
|
# Minimal, standalone bridge API compatible with tests.
|
||||||
|
def to_canonical(lp):
|
||||||
|
return {
|
||||||
|
"type": "LocalProblem",
|
||||||
|
"object_id": lp.asset_id,
|
||||||
|
"data": lp.payload,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def from_canonical(data):
|
||||||
|
asset_id = str(data.get("object_id", "unknown"))
|
||||||
|
payload = dict(data.get("data", {}))
|
||||||
|
# Import the LocalProblem type from the tests contract shim to ensure
|
||||||
|
# consistency with test expectations
|
||||||
|
from .contracts import LocalProblem as CanonicalLocalProblem
|
||||||
|
|
||||||
|
return CanonicalLocalProblem(asset_id=asset_id, payload=payload)
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
from core.bridge import to_canonical, from_canonical
|
||||||
|
from core.contracts import LocalProblem
|
||||||
|
|
||||||
|
|
||||||
|
def test_bridge_round_trip_local_problem():
|
||||||
|
lp = LocalProblem(asset_id="robot-01", payload={"speed": 1.5, "mode": "auto"})
|
||||||
|
canon = to_canonical(lp)
|
||||||
|
assert canon["type"] == "LocalProblem"
|
||||||
|
assert canon["object_id"] == lp.asset_id
|
||||||
|
assert canon["data"] == lp.payload
|
||||||
|
|
||||||
|
lp_back = from_canonical(canon)
|
||||||
|
assert isinstance(lp_back, LocalProblem)
|
||||||
|
assert lp_back.asset_id == lp.asset_id
|
||||||
|
assert lp_back.payload == lp.payload
|
||||||
|
|
@ -1,16 +1,24 @@
|
||||||
import pytest
|
|
||||||
|
|
||||||
from core.contracts import ContractRegistry
|
from core.contracts import ContractRegistry
|
||||||
|
|
||||||
|
|
||||||
def test_contract_registry_basic():
|
def test_contract_registry_basic_operations():
|
||||||
reg = ContractRegistry()
|
reg = ContractRegistry()
|
||||||
reg.add_contract("LocalProblem", "v1", {"fields": ["asset_id", "payload"]})
|
|
||||||
reg.add_contract("SharedVariables", "v1", {"fields": ["iter_id", "values"]})
|
|
||||||
|
|
||||||
c1 = reg.get_contract("LocalProblem", "v1")
|
# initially empty
|
||||||
c2 = reg.get_contract("SharedVariables", "v1")
|
assert reg.list_contracts() == {}
|
||||||
assert c1 is not None
|
|
||||||
assert c2 is not None
|
# add a contract and verify retrieval
|
||||||
assert c1.name == "LocalProblem" and c1.version == "v1"
|
reg.add_contract("LocalProblem", "v1", {"fields": ["asset_id", "payload"]})
|
||||||
assert c2.name == "SharedVariables" and c2.version == "v1"
|
c = reg.get_contract("LocalProblem", "v1")
|
||||||
|
assert c is not None
|
||||||
|
assert c.name == "LocalProblem" # type: ignore[attr-defined]
|
||||||
|
assert c.version == "v1" # type: ignore[attr-defined]
|
||||||
|
assert c.schema["fields"] == ["asset_id", "payload"] # type: ignore[attr-defined]
|
||||||
|
|
||||||
|
# unknown contract should return None
|
||||||
|
assert reg.get_contract("Unknown", "v1") is None
|
||||||
|
|
||||||
|
# list_contracts should include the contract under the right keys
|
||||||
|
all_contracts = reg.list_contracts()
|
||||||
|
assert "LocalProblem" in all_contracts
|
||||||
|
assert "v1" in all_contracts["LocalProblem"]
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue