build(agent): new-agents-2#7e3bbc iteration

This commit is contained in:
agent-7e3bbc424e07835b 2026-04-23 22:33:04 +02:00
parent 1fed725255
commit e11963ffb2
12 changed files with 313 additions and 137 deletions

BIN
0_delta.pkl Normal file

Binary file not shown.

BIN
1_delta.pkl Normal file

Binary file not shown.

BIN
2_delta.pkl Normal file

Binary file not shown.

BIN
99_delta.pkl Normal file

Binary file not shown.

BIN
cache/0_delta.pkl vendored Normal file

Binary file not shown.

BIN
cache/1_delta.pkl vendored Normal file

Binary file not shown.

View File

@ -1,18 +1,17 @@
"""
Public package for Interplanetary Edge Orchestrator (Privacy MVP).
"""Interplanetary Edge Orchestrator - Privacy Package (Minimal UAV/Robotics MVP)
This package provides a tiny, production-oriented skeleton for
privacy-preserving federated optimization with offline-first capability.
It is intentionally minimal but well-structured to support future expansion
into the full EnergiBridge/CatOpt و NovaPlan interop surface described in
the MVP roadmap.
"""
from .core import LocalProblem, SharedVariables, PlanDelta, DualVariables, PrivacyBudget, AuditLog, PolicyBlock
from .federated import Client, Server
from .core import Client, Server, OfflineCache, update_model
__all__ = [
"LocalProblem",
"SharedVariables",
"PlanDelta",
"DualVariables",
"PrivacyBudget",
"AuditLog",
"PolicyBlock",
"Client",
"Server",
"OfflineCache",
"update_model",
]

View File

@ -0,0 +1,27 @@
"""Adapters scaffold for canonical/interop bridging (toy implementation)."""
from __future__ import annotations
import json
from typing import Dict, Any
from .core import PlanDelta
def to_contract_payload(plan_delta: PlanDelta) -> Dict[str, Any]:
payload = plan_delta.to_dict()
# attach a minimal signature-like tag for governance traceability
payload["_interop"] = {
"version": "0.1.0",
"timestamp": plan_delta.timestamp,
"signature_present": bool(plan_delta.signature),
}
return payload
def from_contract_payload(payload: Dict[str, Any]) -> PlanDelta:
delta = payload.get("delta", {})
ts = payload.get("timestamp", None)
author = payload.get("author", "")
contract_id = payload.get("contract_id", "")
signature = payload.get("signature", "")
return PlanDelta(delta=delta, timestamp=ts, author=author, contract_id=contract_id, signature=signature)

View File

@ -1,62 +1,93 @@
"""Minimal NumPy-free core primitives for the tests.
This module implements a lightweight, test-focused API surface for
privacy-preserving federated optimization. It avoids external dependencies
like NumPy and provides a deterministic, offline-capable workflow that the
tests exercise.
"""
from __future__ import annotations
from dataclasses import dataclass, field
from typing import Any, Dict, Optional, List
import os
import pickle
import time
import json
import random
import math
from typing import List, Optional
def _clip_norm_vec(vec: List[float], max_norm: Optional[float]) -> List[float]:
if max_norm is None or max_norm <= 0:
return vec
norm = math.sqrt(sum(v * v for v in vec))
if norm <= max_norm:
return vec
scale = max_norm / (norm + 1e-12)
return [v * scale for v in vec]
# Canonical IR primitives (minimal)
@dataclass
class LocalProblem:
id: str
domain: str
assets: List[str]
objective: str
constraints: Optional[Dict[str, Any]] = field(default_factory=dict)
solver_hint: Optional[str] = None
"""A minimal LocalProblem: id, domain, assets, objective, constraints."""
def to_dict(self) -> Dict[str, Any]:
def __init__(self, id: str, domain: str, assets: List[str], objective: str, constraints: Optional[str] = None):
self.id = id
self.domain = domain
self.assets = assets
self.objective = objective
self.constraints = constraints or ""
def to_dict(self) -> dict:
return {
"id": self.id,
"domain": self.domain,
"assets": self.assets,
"objective": self.objective,
"constraints": self.constraints or {},
"solver_hint": self.solver_hint,
"constraints": self.constraints,
}
@dataclass
class SharedVariables:
# Lightweight, versioned signals
versions: Dict[str, int] = field(default_factory=dict)
payloads: Dict[str, Any] = field(default_factory=dict)
encryption_schema: Optional[str] = None
"""Versioned shared signals between domains (test-focused placeholder)."""
def __init__(self, versions: Optional[dict] = None):
self.versions = versions or {}
self.payloads = {}
def update(self, key: str, value):
self.versions[key] = value
def bump_version(self, key: str) -> int:
self.versions[key] = self.versions.get(key, 0) + 1
return self.versions[key]
cur = self.versions.get(key, 0)
cur += 1
self.versions[key] = cur
return cur
def to_dict(self) -> dict:
return {"payloads": self.payloads}
def to_dict(self) -> Dict[str, Any]:
return {
"versions": self.versions,
"payloads": self.payloads,
"encryption_schema": self.encryption_schema,
}
@dataclass
class PlanDelta:
delta: Dict[str, Any]
timestamp: float = field(default_factory=lambda: time.time())
author: Optional[str] = None
contract_id: Optional[str] = None
signature: Optional[str] = None
"""Incremental plan delta with provenance fields."""
def sign(self, signer: str) -> None:
# Simple deterministic "signature" for demo purposes
payload = json.dumps({"delta": self.delta, "timestamp": self.timestamp, "author": signer}, sort_keys=True)
self.signature = f"sig-{abs(hash(payload))}"
self.author = signer
def __init__(
self,
delta: dict,
timestamp: Optional[float] = None,
author: str = "",
contract_id: str = "",
signature: str = "",
):
self.delta = delta
self.timestamp = timestamp or float("nan")
self.author = author
self.contract_id = contract_id
self.signature = signature
def to_dict(self) -> Dict[str, Any]:
def sign(self, author: str) -> str:
self.author = author
self.signature = f"signed-by-{author}"
return self.signature
def to_dict(self) -> dict:
return {
"delta": self.delta,
"timestamp": self.timestamp,
@ -65,37 +96,48 @@ class PlanDelta:
"signature": self.signature,
}
@dataclass
class DualVariables:
multipliers: Dict[str, float] = field(default_factory=dict)
"""Multipliers or dual variables for optimization (placeholder)."""
def set(self, name: str, value: float) -> None:
self.multipliers[name] = value
def __init__(self, multipliers: Optional[dict] = None):
self.multipliers = multipliers or {}
def to_dict(self) -> Dict[str, Any]:
def set(self, key: str, value):
self.multipliers[key] = value
def to_dict(self) -> dict:
return {"multipliers": self.multipliers}
@dataclass
class PrivacyBudget:
signal: str
budget: float
expiry: float # epoch
"""Budget for privacy budget per signal (placeholder)."""
def is_expired(self) -> bool:
return time.time() > self.expiry
def __init__(self, signal: str, budget: float, expiry: Optional[float] = None):
self.signal = signal
self.budget = budget
self.expiry = expiry
def to_dict(self) -> Dict[str, Any]:
def to_dict(self) -> dict:
return {"signal": self.signal, "budget": self.budget, "expiry": self.expiry}
@dataclass
class AuditLog:
entry: str
signer: str
timestamp: float
contract_id: Optional[str] = None
version: Optional[str] = None
def is_expired(self) -> bool:
if self.expiry is None:
return False
return time.time() > self.expiry
def to_dict(self) -> Dict[str, Any]:
class AuditLog:
"""Provenance/log of operations for governance (placeholder)."""
def __init__(self, entry: str, signer: str, timestamp: Optional[float] = None, contract_id: str = "", version: str = ""):
self.entry = entry
self.signer = signer
self.timestamp = timestamp or float("nan")
self.contract_id = contract_id
self.version = version
def to_dict(self) -> dict:
return {
"entry": self.entry,
"signer": self.signer,
@ -104,19 +146,123 @@ class AuditLog:
"version": self.version,
}
@dataclass
class PolicyBlock:
safety: Optional[str] = None
exposure_controls: Optional[Dict[str, Any]] = field(default_factory=dict)
def to_dict(self) -> Dict[str, Any]:
return {
"safety": self.safety,
"exposure_controls": self.exposure_controls or {},
}
class OfflineCache:
"""Simple disk-backed cache for offline updates per client."""
# Tiny helper for serialization (to aid tests)
def serialize(obj: Any) -> str:
if hasattr(obj, "to_dict"):
return json.dumps(obj.to_dict(), sort_keys=True)
return json.dumps(obj, default=lambda o: o.__dict__, sort_keys=True)
def __init__(self, base_dir: Optional[str] = None):
self.base = base_dir or "."
os.makedirs(self.base, exist_ok=True)
self.cache_dir = self.base
def cache_update(self, client_id: str, delta: List[float]) -> str:
os.makedirs(self.cache_dir, exist_ok=True)
fname = os.path.join(self.cache_dir, f"{client_id}_delta.pkl")
with open(fname, "wb") as f:
pickle.dump(delta, f)
return fname
def load_update(self, client_id: str) -> Optional[List[float]]:
fname = os.path.join(self.cache_dir, f"{client_id}_delta.pkl")
if not os.path.exists(fname):
return None
with open(fname, "rb") as f:
return pickle.load(f)
def clear(self, client_id: str) -> None:
fname = os.path.join(self.cache_dir, f"{client_id}_delta.pkl")
if os.path.exists(fname):
os.remove(fname)
class Server:
"""Simple server that aggregates deltas from clients with optional clipping and noise."""
def __init__(self, dim: int):
self.dim = dim
self.w = [0.0 for _ in range(dim)]
def aggregate(
self,
deltas: List[List[float]],
clip_norm: Optional[float] = None,
noise_scale: float = 0.0,
seed: Optional[int] = None,
) -> List[float]:
if not deltas:
return self.w
total = [0.0 for _ in range(self.dim)]
for d in deltas:
for i in range(self.dim):
total[i] += d[i]
if clip_norm is not None:
total = _clip_norm_vec(total, clip_norm)
if noise_scale and noise_scale > 0:
rnd = random.Random(seed)
total = [v + rnd.gauss(0.0, noise_scale) for v in total]
self.w = [self.w[i] + total[i] for i in range(self.dim)]
return self.w
class Client:
"""Lightweight client for local training on a toy dataset."""
def __init__(self, client_id, data_X, data_y, connected: bool = True, cache_dir: Optional[str] = None):
self.client_id = client_id
self.X = data_X
self.y = data_y
self.connected = connected
self.cache_dir = cache_dir or "."
self.n_features = None
self.w = None
# Simple per-client cache
self.cache = OfflineCache(self.cache_dir)
# Backward-compatible alias used by tests
def initialize(self, n_features: int):
self.n_features = int(n_features)
self.w = [0.0 for _ in range(self.n_features)]
def train(self, model, lr: float = 0.01, epochs: int = 1, clip_norm: Optional[float] = None) -> List[float]:
if self.n_features is None:
# Infer from provided model if not initialized yet
if model is not None:
self.initialize(len(model))
else:
self.initialize(len(self.X[0]) if isinstance(self.X, list) and len(self.X) > 0 else 0)
# Local model start point
w = list(model)
delta_total = [0.0 for _ in range(self.n_features)]
N = len(self.X) if self.X else 0
for _ in range(epochs):
if N == 0:
break
grad = [0.0 for _ in range(self.n_features)]
for i in range(N):
xi = self.X[i]
yi = self.y[i]
pred = sum(xi[j] * w[j] for j in range(self.n_features))
residual = pred - yi
for j in range(self.n_features):
grad[j] += xi[j] * residual
grad = [g / float(N) for g in grad]
delta_epoch = [-lr * g for g in grad]
if clip_norm is not None:
delta_epoch = _clip_norm_vec(delta_epoch, clip_norm)
delta_total = [delta_total[i] + delta_epoch[i] for i in range(self.n_features)]
w = [w[i] + delta_epoch[i] for i in range(self.n_features)]
self.w = w
# Persist delta for offline caching as required by tests
self.cache.cache_update(self.client_id, delta_total)
return delta_total
def load_update(self):
return self.cache.load_update(self.client_id)
def update_model(model, delta):
"""Apply a delta to a model (minimal list-like semantics)."""
if model is None:
return delta
return [m + d for m, d in zip(model, delta)]

View File

@ -1,71 +1,43 @@
"""Minimal DSL sketch for LocalProblem / SharedVariables / PlanDelta.
This is intentionally lightweight and dependency-free. It provides a
canonical, vendor-agnostic contract surface that adapters can map to/from
their internal representations.
"""
"""Tiny DSL seeds for LocalProblem and related primitives."""
from __future__ import annotations
from dataclasses import dataclass, field
from typing import Any, Dict, List, Optional
from dataclasses import dataclass
from typing import List, Optional, Dict
@dataclass
class LocalProblem:
"""Represents a per-agent optimization task.
- problem_id: unique identifier for the local problem
- features: simple feature vector or dictionary describing the task
- objective: optional objective descriptor (string or structured)
- metadata: extensible metadata for compatibility checks
"""
problem_id: str
features: List[float] | Dict[str, Any] = field(default_factory=list)
objective: Optional[str] = None
metadata: Dict[str, Any] = field(default_factory=dict)
class LocalProblemDSL:
id: str
domain: str
assets: List[str]
objective: str
constraints: Optional[str] = None
@dataclass
class SharedVariables:
"""Represents shared summaries, priors, or signals exchanged between agents."""
version: int
data: Dict[str, Any] = field(default_factory=dict)
class SharedVariablesDSL:
versions: Dict[str, int]
@dataclass
class PlanDeltaDSL:
delta: Dict
timestamp: Optional[float] = None
author: str = ""
contract_id: str = ""
signature: str = ""
@dataclass
class PlanDelta:
"""Represents an incremental plan change derived from optimization.
Extended with optional provenance fields to support auditing and offline replay:
- timestamp: when the delta was created
- author: identifier of the delta creator
- contract_id: contract/session identifier
- signature: cryptographic signature proving integrity
"""
version: int
delta: Dict[str, Any] = field(default_factory=dict)
insight: Optional[str] = None
# Provenance / governance fields (optional)
timestamp: Optional[float] = None
author: Optional[str] = None
contract_id: Optional[str] = None
signature: Optional[str] = None
class DualVariablesDSL:
multipliers: Dict[str, float]
@dataclass
class PrivacyBudget:
"""Governance/privacy budget block for a contract message."""
class PrivacyBudgetDSL:
signal: str
budget: float
expiry: Optional[float] = None
@dataclass
class AuditLog:
"""Tamper-evident audit log entry for governance provenance."""
class AuditLogDSL:
entry: str
signer: str
timestamp: float
contract_id: str
version: str
timestamp: Optional[float] = None
contract_id: str = ""
version: str = ""

View File

@ -0,0 +1,22 @@
"""Toy example/demo runner for the privacy MVP."""
from __future__ import annotations
import numpy as np
from .core import Client, Server
def run_toy_demo():
# Simple synthetic dataset: y = 2x with noise
rng = np.random.default_rng(42)
X = rng.normal(size=(100, 3))
true_w = np.array([1.5, -2.0, 0.5])
y = X @ true_w + rng.normal(scale=0.5, size=100)
client = Client("client-1", X, y, seed=7)
server = Server(dim=X.shape[1])
# simulate three rounds of local training
for rnd in range(3):
delta = client.train(learning_rate=0.05, clip_norm=1.0, noise_scale=0.01)
server.aggregate([delta], clip_norm=None, noise_scale=0.0)
print("Toy demo finished. Global model:", server.global_model)

View File

@ -0,0 +1,10 @@
"""Utility helpers for the toy MVP."""
from __future__ import annotations
import numpy as np
def gaussian_noise(size, scale: float):
if scale <= 0:
return np.zeros(size)
return np.random.normal(0.0, scale, size)