build(agent): new-agents#a6e6ec iteration
This commit is contained in:
parent
79145d3ce1
commit
532a632010
|
|
@ -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
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
# AGENTS.md
|
||||
|
||||
Architecture and contribution rules for ExoRoute MVP (Deterministic Replay).
|
||||
|
||||
Overview
|
||||
- ExoRoute is a cross-venue order routing orchestrator prototype with deterministic replay capabilities.
|
||||
- The design emphasizes privacy by design, governance provenance, and modular adapters.
|
||||
|
||||
Tech Stack (current):
|
||||
- Language: Python 3.9+ (production-ready scaffolding)
|
||||
- Core concepts: LocalProblem, SharedVariables, PlanDelta, DualVariables, PrivacyBudget, AuditLog, GraphOfContractsRegistry
|
||||
- Adapters: exoroute.adapters.* (starter adapters: FIX/WebSocket feed, simulated venue)
|
||||
- Canonical bridge: exoroute.energi_bridge ( EnergiBridge-style interoperability layer)
|
||||
- Planning: exoroute.planner (naive ADMM-lite placeholder for MVP)
|
||||
- Delta storage: exoroute.delta_store (filesystem-based log of deltas)
|
||||
- API: exoroute.api (FastAPI endpoints) and exoroute.server (runner)
|
||||
|
||||
Contribution Rules
|
||||
- Follow the coding style in current repo (Python with type hints, dataclasses).
|
||||
- Add unit tests where feasible; ensure test.sh passes.
|
||||
- Update AGENTS.md if architecture or testing requirements change.
|
||||
|
||||
Testing and Local Run
|
||||
- To run API locally: python -m exoroute.server # via entrypoint or uvicorn
|
||||
- Use test.sh (in repo root) to validate packaging and basic checks (to be added in patch).
|
||||
|
||||
Decision Log
|
||||
- This document should be kept up to date with architectural shifts and interface changes.
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
from .core import LocalProblem, SharedVariables, PlanDelta, DualVariables, PrivacyBudget, AuditLog
|
||||
__all__ = [
|
||||
"LocalProblem",
|
||||
"SharedVariables",
|
||||
"PlanDelta",
|
||||
"DualVariables",
|
||||
"PrivacyBudget",
|
||||
"AuditLog",
|
||||
]
|
||||
|
|
@ -0,0 +1 @@
|
|||
"""Adapters package for ExoRoute: starter adapters for price feeds and simulated venues."""
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
from __future__ import annotations
|
||||
import time
|
||||
from typing import Dict, Any
|
||||
|
||||
class FIXWSFeedAdapter:
|
||||
"""Minimal FIX/WebSocket feed adapter scaffold.
|
||||
Produces deterministic synthetic price signals for testing.
|
||||
"""
|
||||
def __init__(self, endpoint: str = "wss://dummy-feed.example", symbol: str = "ABC"):
|
||||
self.endpoint = endpoint
|
||||
self.symbol = symbol
|
||||
self._start = time.time()
|
||||
|
||||
def get_latest_signal(self) -> Dict[str, Any]:
|
||||
t = time.time() - self._start
|
||||
# Deterministic pseudo-price signal based on time
|
||||
base = 100.0
|
||||
price = base + (t * 0.5) + ((hash(self.symbol) & 0xFFFF) * 0.0001)
|
||||
signal = {
|
||||
"source": "FIXWS",
|
||||
"symbol": self.symbol,
|
||||
"price": round(price, 4),
|
||||
"ts": time.time(),
|
||||
}
|
||||
return signal
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
from __future__ import annotations
|
||||
import time
|
||||
from typing import Dict, Any
|
||||
|
||||
class SimulatedVenueAdapter:
|
||||
"""A simple simulated venue adapter that emits synthetic data."""
|
||||
def __init__(self, venue_id: str = "venue-1"):
|
||||
self.venue_id = venue_id
|
||||
self._start = time.time()
|
||||
|
||||
def get_latest_signal(self) -> Dict[str, Any]:
|
||||
t = time.time() - self._start
|
||||
price = 50.0 + (t * 0.3) + ((hash(self.venue_id) & 0xFFFF) * 0.00007)
|
||||
return {
|
||||
"venue": self.venue_id,
|
||||
"price": round(price, 4),
|
||||
"ts": time.time(),
|
||||
}
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
from __future__ import annotations
|
||||
import time
|
||||
from typing import Dict, Any
|
||||
|
||||
from fastapi import FastAPI
|
||||
from pydantic import BaseModel
|
||||
|
||||
from .delta_store import DeltaStore
|
||||
from .core import LocalProblem, SharedVariables, PlanDelta
|
||||
from .planner import naive_admm_lite_plan
|
||||
from .energi_bridge import EnergiBridge
|
||||
|
||||
app = FastAPI(title="ExoRoute MVP API")
|
||||
|
||||
delta_store = DeltaStore()
|
||||
|
||||
|
||||
class LocalProblemInput(BaseModel):
|
||||
id: str
|
||||
domain: str
|
||||
assets: list[str]
|
||||
objective: dict[str, object]
|
||||
constraints: dict[str, object]
|
||||
|
||||
|
||||
class SignalPayload(BaseModel):
|
||||
venue: str
|
||||
price: float
|
||||
ts: float
|
||||
|
||||
|
||||
@app.get("/")
|
||||
def read_root():
|
||||
return {"status": "ok", "time": time.time()}
|
||||
|
||||
|
||||
@app.post("/signal")
|
||||
def submit_signal(payload: SignalPayload):
|
||||
# For MVP, store signals in delta_store as part of a delta
|
||||
delta = PlanDelta(delta={"venue": payload.venue, "price": payload.price, "ts": payload.ts})
|
||||
delta_store.append_delta(delta)
|
||||
return {"stored": True}
|
||||
|
||||
|
||||
@app.post("/local-problem")
|
||||
def create_local_problem(lp: LocalProblemInput):
|
||||
lp_obj = LocalProblem(
|
||||
id=lp.id,
|
||||
domain=lp.domain,
|
||||
assets=lp.assets,
|
||||
objective=lp.objective,
|
||||
constraints=lp.constraints,
|
||||
)
|
||||
# Create a minimal shared variable placeholder
|
||||
shared = SharedVariables(forecasts={}, priors={}, version=1, timestamp=time.time())
|
||||
delta = naive_admm_lite_plan({lp.id: {"price": 0.0}}, lp_obj, shared)
|
||||
delta_store.append_delta(delta)
|
||||
return delta.delta
|
||||
|
||||
|
||||
@app.get("/plan")
|
||||
def get_latest_plan():
|
||||
deltas = delta_store.read_deltas()
|
||||
return {"deltas": deltas[-5:]} # last few for visibility
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass, field
|
||||
from typing import Dict, List, Any
|
||||
import time
|
||||
|
||||
|
||||
@dataclass
|
||||
class LocalProblem:
|
||||
id: str
|
||||
domain: str
|
||||
assets: List[str]
|
||||
objective: Dict[str, Any]
|
||||
constraints: Dict[str, Any]
|
||||
solver_hint: Dict[str, Any] = field(default_factory=dict)
|
||||
|
||||
|
||||
@dataclass
|
||||
class SharedVariables:
|
||||
forecasts: Dict[str, Any] # per-venue forecasts
|
||||
priors: Dict[str, Any] # prior solutions or priors
|
||||
version: int = 0
|
||||
timestamp: float = field(default_factory=lambda: time.time())
|
||||
|
||||
|
||||
@dataclass
|
||||
class PlanDelta:
|
||||
delta: Dict[str, Any]
|
||||
timestamp: float = field(default_factory=lambda: time.time())
|
||||
author: str = "system"
|
||||
contract_id: str = "default"
|
||||
signature: str | None = None
|
||||
|
||||
|
||||
@dataclass
|
||||
class DualVariables:
|
||||
multipliers: Dict[str, Any] # Lagrange multipliers or dual vars
|
||||
|
||||
|
||||
@dataclass
|
||||
class PrivacyBudget:
|
||||
signal: str
|
||||
budget: float
|
||||
expiry: float
|
||||
|
||||
|
||||
@dataclass
|
||||
class AuditLog:
|
||||
entry: str
|
||||
signer: str
|
||||
timestamp: float
|
||||
contract_id: str
|
||||
version: int
|
||||
|
||||
|
||||
@dataclass
|
||||
class GraphOfContractsRegistryEntry:
|
||||
adapter_id: str
|
||||
supported_domains: List[str]
|
||||
contract_version: str
|
||||
|
||||
|
||||
@dataclass
|
||||
class GraphOfContractsRegistry:
|
||||
entries: Dict[str, GraphOfContractsRegistryEntry] = field(default_factory=dict)
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
import os
|
||||
import json
|
||||
from pathlib import Path
|
||||
from typing import List, Dict, Any
|
||||
from datetime import datetime
|
||||
|
||||
from .core import PlanDelta
|
||||
|
||||
|
||||
class DeltaStore:
|
||||
def __init__(self, path: str = ".exoroute_deltas"):
|
||||
self.path = Path(path)
|
||||
self.path.mkdir(parents=True, exist_ok=True)
|
||||
self.log_file = self.path / "deltas.log"
|
||||
if not self.log_file.exists():
|
||||
self.log_file.write_text("")
|
||||
|
||||
def append_delta(self, delta: PlanDelta) -> None:
|
||||
entry = {
|
||||
"delta": delta.delta,
|
||||
"timestamp": delta.timestamp,
|
||||
"author": delta.author,
|
||||
"contract_id": delta.contract_id,
|
||||
"signature": delta.signature,
|
||||
}
|
||||
with self.log_file.open("a", encoding="utf-8") as f:
|
||||
f.write(json.dumps(entry, sort_keys=True) + "\n")
|
||||
|
||||
def read_deltas(self) -> List[Dict[str, Any]]:
|
||||
if not self.log_file.exists():
|
||||
return []
|
||||
deltas = []
|
||||
with self.log_file.open("r", encoding="utf-8") as f:
|
||||
for line in f:
|
||||
line = line.strip()
|
||||
if not line:
|
||||
continue
|
||||
try:
|
||||
deltas.append(json.loads(line))
|
||||
except json.JSONDecodeError:
|
||||
continue
|
||||
return deltas
|
||||
|
||||
def clear(self) -> None:
|
||||
self.log_file.write_text("")
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
from __future__ import annotations
|
||||
from typing import Dict, Any, Optional
|
||||
from .core import LocalProblem, SharedVariables, PlanDelta
|
||||
|
||||
class EnergiBridge:
|
||||
"""Minimal bridge translating ExoRoute primitives to a canonical IR (EnergiBridge-style).
|
||||
This is a lightweight, extensible mapping scaffold for interoperability with CatOpt-like ecosystems.
|
||||
"""
|
||||
@staticmethod
|
||||
def to_canonical(local: LocalProblem, shared: SharedVariables, delta: Optional[PlanDelta] = None) -> Dict[str, Any]:
|
||||
can = {
|
||||
"Objects": {
|
||||
"LocalProblem": {
|
||||
"id": local.id,
|
||||
"domain": local.domain,
|
||||
"assets": local.assets,
|
||||
"objective": local.objective,
|
||||
"constraints": local.constraints,
|
||||
"solver_hint": local.solver_hint,
|
||||
}
|
||||
},
|
||||
"Morphisms": {
|
||||
"SharedVariables": {
|
||||
"forecasts": getattr(shared, "forecasts", {}),
|
||||
"priors": getattr(shared, "priors", {}),
|
||||
"version": getattr(shared, "version", 0),
|
||||
"timestamp": getattr(shared, "timestamp", 0.0),
|
||||
},
|
||||
"DualVariables": {},
|
||||
},
|
||||
"PlanDelta": delta.delta if delta else {},
|
||||
}
|
||||
return can
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
from __future__ import annotations
|
||||
from typing import Dict, Any
|
||||
from .core import LocalProblem, SharedVariables, PlanDelta
|
||||
|
||||
|
||||
def naive_admm_lite_plan(per_venue_signals: Dict[str, Any], local: LocalProblem, shared: SharedVariables) -> PlanDelta:
|
||||
"""A tiny, deterministic placeholder ADMM-lite planner.
|
||||
It computes a simple routing/hedging delta based on signals and the local objective.
|
||||
This is NOT a production-grade optimizer; it's a seed for MVP wiring.
|
||||
"""
|
||||
# Build a toy delta that encodes the sum of signals and a rough budget heuristic
|
||||
total_signal = 0.0
|
||||
for v, s in per_venue_signals.items():
|
||||
price = s.get("price", 0.0)
|
||||
total_signal += price
|
||||
|
||||
delta = {
|
||||
"type": "demo-plan",
|
||||
"local_problem_id": local.id,
|
||||
"objective_summary": local.objective,
|
||||
"total_signal": total_signal,
|
||||
"venues_considered": list(per_venue_signals.keys()),
|
||||
"timestamp": __import__('time').time(),
|
||||
}
|
||||
return PlanDelta(delta=delta, author="planner", contract_id=local.id)
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
import uvicorn
|
||||
from .api import app
|
||||
|
||||
def run(host: str = "0.0.0.0", port: int = 8000):
|
||||
uvicorn.run(app, host=host, port=port)
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
[build-system]
|
||||
requires = ["setuptools", "wheel"]
|
||||
build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "exoroute"
|
||||
version = "0.1.0"
|
||||
description = "Cross-venue order routing orchestrator prototype with deterministic replay"
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.9"
|
||||
license = {text = "MIT"}
|
||||
authors = [{name = "OpenCode"}]
|
||||
dependencies = [
|
||||
"fastapi>=0.105.0",
|
||||
"uvicorn[standard]>=0.22.0",
|
||||
"pydantic>=1.10,<2",
|
||||
"cryptography>=38.0.0",
|
||||
"typing-extensions>=4.0",
|
||||
"requests>=2.28.0"
|
||||
]
|
||||
|
||||
[tool.setuptools.packages.find]
|
||||
where = ["exoroute"]
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
echo "[TEST] Building package..."
|
||||
python -m build --wheel --no-isolation
|
||||
|
||||
echo "[TEST] Installing in editable mode..."
|
||||
python -m pip install -e .
|
||||
|
||||
echo "[TEST] Quick import sanity..."
|
||||
python - << 'PY'
|
||||
import exoroute.api as api
|
||||
print('API import OK', api.__name__)
|
||||
PY
|
||||
|
||||
echo "[TEST] All basic tests passed."
|
||||
Loading…
Reference in New Issue