build(agent): new-agents-3#dd492b iteration
This commit is contained in:
parent
34fa24c97e
commit
7b63a2ddd7
|
|
@ -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,22 @@
|
||||||
|
# AGENTS
|
||||||
|
|
||||||
|
Architecture overview for AudioLedger Studio.
|
||||||
|
|
||||||
|
- Language: Python 3.9+ (pyproject configured)
|
||||||
|
- Core modules:
|
||||||
|
- core.py: DSL parsing and IR data classes (LocalProblemIR, SharedVariableIR, PlanDeltaIR, AuditLog)
|
||||||
|
- runtime.py: ExecutionGraph and a minimal deterministic allocator
|
||||||
|
- delta.py: DeltaStore for offline delta-synchronization
|
||||||
|
- signer.py: Lightweight HMAC-based attestations for audit logs
|
||||||
|
- registry.py: GraphRegistry primitive with adapters support
|
||||||
|
- adapters.py: PriceFeedAdapter as a sample adapter
|
||||||
|
- sonifier.py: Audio cue mapping from risk/allocation state
|
||||||
|
- tests/ (unit tests)
|
||||||
|
|
||||||
|
- Testing: pytest-based tests to cover DSL parsing, runtime allocation, delta syncing, signing, and registry;
|
||||||
|
a test.sh script will run tests and Python packaging to verify build integrity.
|
||||||
|
|
||||||
|
- How to contribute: follow these steps
|
||||||
|
1) Implement or modify a feature in the core, then run tests via test.sh
|
||||||
|
2) Update AGENTS.md if you introduce new components or interfaces
|
||||||
|
3) Ensure READY_TO_PUBLISH is created only after the repository is fully ready for publishing
|
||||||
34
README.md
34
README.md
|
|
@ -1,3 +1,33 @@
|
||||||
# idea81-audioledger-studio-verifiable
|
# idea81_audioledger_studio_verifiable
|
||||||
|
|
||||||
Source logic for Idea #81
|
Open-source platform to express financial market scenarios via a math-friendly DSL, compile to a portable execution graph, and execute offline-first with delta-sync to governance hubs.
|
||||||
|
|
||||||
|
Highlights
|
||||||
|
- Algebraic DSL for assets, objectives, risk budgets, and policies
|
||||||
|
- Canonical IR: LocalProblem, SharedVariable, PlanDelta, DualVariables, AuditLog
|
||||||
|
- WebAssembly-ready runtime (stubbed here for Python-based prototyping)
|
||||||
|
- Tamper-evident, attestable governance logs via a Signer
|
||||||
|
- Lightweight Graph-of-Contracts registry with adapters (price feeds, brokers)
|
||||||
|
- Audio sonification layer mapping risk/allocation/state to audible cues
|
||||||
|
- Offline-first operation with deterministic delta-sync
|
||||||
|
|
||||||
|
Architecture
|
||||||
|
- Python-based core with modular separation:
|
||||||
|
- core.py: DSL parsing and IR data classes
|
||||||
|
- runtime.py: Execution graph and allocation logic
|
||||||
|
- delta.py: Delta store and application logic
|
||||||
|
- signer.py: Attestation helper
|
||||||
|
- registry.py: Registry for contracts
|
||||||
|
- adapters.py: Adapters for external data sources
|
||||||
|
- sonifier.py: Audio cue generation
|
||||||
|
|
||||||
|
Quick Start
|
||||||
|
- Install: pytest and build tooling (Python packaging)
|
||||||
|
- Run tests: bash test.sh
|
||||||
|
- Extend: Add new adapters, DSL features, and testing coverage
|
||||||
|
|
||||||
|
Packaging
|
||||||
|
- Python package name: idea81_audioledger_studio_verifiable
|
||||||
|
- pyproject.toml defines build metadata and long_description hook to README.md
|
||||||
|
|
||||||
|
For more details, see AGENTS.md and the unit tests in tests/.
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
# Idea
|
||||||
|
|
||||||
|
This is a placeholder to assist tooling in this sprint. The real README will be upgraded in subsequent tasks.
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
"""__init__ for idea81_audioledger_studio_verifiable"""
|
||||||
|
from .core import parse_dsl, LocalProblemIR, SharedVariableIR, PlanDeltaIR, AuditLog
|
||||||
|
from .runtime import ExecutionGraph, Node
|
||||||
|
from .delta import DeltaStore
|
||||||
|
from .signer import Signer
|
||||||
|
from .registry import GraphRegistry
|
||||||
|
from .sonifier import Sonifier
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
"parse_dsl",
|
||||||
|
"LocalProblemIR",
|
||||||
|
"SharedVariableIR",
|
||||||
|
"PlanDeltaIR",
|
||||||
|
"AuditLog",
|
||||||
|
"ExecutionGraph",
|
||||||
|
"Node",
|
||||||
|
"DeltaStore",
|
||||||
|
"Signer",
|
||||||
|
"GraphRegistry",
|
||||||
|
"Sonifier",
|
||||||
|
]
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from typing import Dict
|
||||||
|
|
||||||
|
|
||||||
|
class PriceFeedAdapter:
|
||||||
|
"""Simple in-memory price feed adapter."""
|
||||||
|
def __init__(self, prices: Dict[str, float] | None = None) -> None:
|
||||||
|
self._prices = prices or {"AAPL": 150.0, "BTC": 60000.0, "USDC": 1.0}
|
||||||
|
|
||||||
|
def get_prices(self) -> Dict[str, float]:
|
||||||
|
return dict(self._prices)
|
||||||
|
|
@ -0,0 +1,91 @@
|
||||||
|
import time
|
||||||
|
from dataclasses import dataclass, field
|
||||||
|
from typing import Any, Dict, List
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class LocalProblemIR:
|
||||||
|
assets: Dict[str, float] = field(default_factory=dict)
|
||||||
|
objectives: Dict[str, Any] = field(default_factory=dict)
|
||||||
|
risk_budgets: Dict[str, Any] = field(default_factory=dict)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class SharedVariableIR:
|
||||||
|
variables: Dict[str, Any] = field(default_factory=dict)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class PlanDeltaIR:
|
||||||
|
changes: Dict[str, Any] = field(default_factory=dict)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class AuditLogEntry:
|
||||||
|
timestamp: float
|
||||||
|
action: str
|
||||||
|
details: str
|
||||||
|
|
||||||
|
|
||||||
|
class AuditLog:
|
||||||
|
def __init__(self) -> None:
|
||||||
|
self.entries: List[AuditLogEntry] = []
|
||||||
|
|
||||||
|
def add(self, action: str, details: str) -> None:
|
||||||
|
self.entries.append(AuditLogEntry(time.time(), action, details))
|
||||||
|
|
||||||
|
def to_json(self) -> str:
|
||||||
|
# minimal JSON-like representation
|
||||||
|
items = [
|
||||||
|
{"timestamp": e.timestamp, "action": e.action, "details": e.details}
|
||||||
|
for e in self.entries
|
||||||
|
]
|
||||||
|
return str(items)
|
||||||
|
|
||||||
|
|
||||||
|
def parse_dsl(text: str) -> Dict[str, Any]:
|
||||||
|
assets: Dict[str, float] = {}
|
||||||
|
objectives: Dict[str, Any] = {}
|
||||||
|
risk_budgets: Dict[str, Any] = {}
|
||||||
|
current: str | None = None
|
||||||
|
for raw_line in text.splitlines():
|
||||||
|
line = raw_line.strip()
|
||||||
|
if not line or line.startswith("#"):
|
||||||
|
continue
|
||||||
|
if line.lower().startswith("assets:"):
|
||||||
|
current = "assets"
|
||||||
|
continue
|
||||||
|
if line.lower().startswith("objectives:"):
|
||||||
|
current = "objectives"
|
||||||
|
continue
|
||||||
|
if line.lower().startswith("risk_budgets:") or line.lower().startswith("risk budgets:"):
|
||||||
|
current = "risk_budgets"
|
||||||
|
continue
|
||||||
|
if line.endswith(":"):
|
||||||
|
# section placeholder
|
||||||
|
current = line[:-1].lower()
|
||||||
|
continue
|
||||||
|
# Parse key: value lines within sections
|
||||||
|
if ":" in line:
|
||||||
|
key, val = [p.strip() for p in line.split(":", 1)]
|
||||||
|
try:
|
||||||
|
num = float(val)
|
||||||
|
v = num
|
||||||
|
except ValueError:
|
||||||
|
v = val
|
||||||
|
if current == "assets":
|
||||||
|
assets[key] = v
|
||||||
|
elif current == "objectives":
|
||||||
|
objectives[key] = v
|
||||||
|
elif current == "risk_budgets" or current == "risk budgets":
|
||||||
|
risk_budgets[key] = v
|
||||||
|
else:
|
||||||
|
# default to assets-like mapping
|
||||||
|
assets[key] = v
|
||||||
|
ir = {
|
||||||
|
"objects": LocalProblemIR(assets=assets, objectives=objectives, risk_budgets=risk_budgets),
|
||||||
|
"morphisms": SharedVariableIR(variables={}),
|
||||||
|
"plan_delta": PlanDeltaIR(changes={}),
|
||||||
|
"audit_log": AuditLog(),
|
||||||
|
}
|
||||||
|
return ir
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from typing import Any, Dict
|
||||||
|
|
||||||
|
|
||||||
|
class DeltaStore:
|
||||||
|
def __init__(self) -> None:
|
||||||
|
self.last_full_state: Dict[str, Any] = {}
|
||||||
|
|
||||||
|
def compute_delta(self, current_state: Dict[str, Any], previous_state: Dict[str, Any]) -> Dict[str, Any]:
|
||||||
|
delta: Dict[str, Any] = {}
|
||||||
|
# naive shallow diff
|
||||||
|
keys = set(current_state.keys()) | set(previous_state.keys())
|
||||||
|
for k in keys:
|
||||||
|
cur = current_state.get(k)
|
||||||
|
prev = previous_state.get(k)
|
||||||
|
if cur != prev:
|
||||||
|
delta[k] = cur
|
||||||
|
return delta
|
||||||
|
|
||||||
|
def apply_delta(self, state: Dict[str, Any], delta: Dict[str, Any]) -> Dict[str, Any]:
|
||||||
|
new_state = dict(state)
|
||||||
|
for k, v in delta.items():
|
||||||
|
new_state[k] = v
|
||||||
|
return new_state
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from typing import Any, Dict
|
||||||
|
|
||||||
|
|
||||||
|
class GraphRegistry:
|
||||||
|
def __init__(self) -> None:
|
||||||
|
self._contracts: Dict[str, Any] = {}
|
||||||
|
|
||||||
|
def register_contract(self, name: str, contract: Any) -> None:
|
||||||
|
self._contracts[name] = contract
|
||||||
|
|
||||||
|
def get_contract(self, name: str) -> Any | None:
|
||||||
|
return self._contracts.get(name)
|
||||||
|
|
||||||
|
def list_contracts(self) -> Dict[str, Any]:
|
||||||
|
return dict(self._contracts)
|
||||||
|
|
@ -0,0 +1,40 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from dataclasses import dataclass, field
|
||||||
|
from typing import Dict, Any
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Node:
|
||||||
|
id: str
|
||||||
|
action: str
|
||||||
|
inputs: Dict[str, Any] = field(default_factory=dict)
|
||||||
|
outputs: Dict[str, Any] = field(default_factory=dict)
|
||||||
|
|
||||||
|
|
||||||
|
class ExecutionGraph:
|
||||||
|
def __init__(self) -> None:
|
||||||
|
self.nodes: Dict[str, Node] = {}
|
||||||
|
self.edges = [] # list of (src, dst)
|
||||||
|
|
||||||
|
def add_node(self, node: Node) -> None:
|
||||||
|
self.nodes[node.id] = node
|
||||||
|
|
||||||
|
def add_edge(self, src_id: str, dst_id: str) -> None:
|
||||||
|
self.edges.append((src_id, dst_id))
|
||||||
|
|
||||||
|
def compile_from_ir(self, ir: Dict[str, Any]) -> None:
|
||||||
|
# Minimal compilation: create a single allocation node if assets present
|
||||||
|
assets = getattr(ir.get("objects"), "assets", {}) if isinstance(ir, dict) else {}
|
||||||
|
total = sum(assets.values()) if isinstance(assets, dict) else 0.0
|
||||||
|
self.nodes["allocate"] = Node(id="allocate", action="allocate", inputs={"assets_total": total})
|
||||||
|
|
||||||
|
def run(self, assets: Dict[str, float], target_total: float) -> Dict[str, float]:
|
||||||
|
# Simple deterministic allocation: scale assets to meet target_total while preserving ratios
|
||||||
|
if not assets:
|
||||||
|
return {}
|
||||||
|
total = sum(assets.values())
|
||||||
|
if total == 0:
|
||||||
|
return {k: 0.0 for k in assets}
|
||||||
|
scale = target_total / total if target_total is not None else 1.0
|
||||||
|
return {k: v * scale for k, v in assets.items()}
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
import time
|
||||||
|
import hmac
|
||||||
|
import hashlib
|
||||||
|
from dataclasses import dataclass
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class AuditLogEntry:
|
||||||
|
timestamp: float
|
||||||
|
action: str
|
||||||
|
details: str
|
||||||
|
|
||||||
|
|
||||||
|
class Signer:
|
||||||
|
def __init__(self, key: str) -> None:
|
||||||
|
self.key = key.encode("utf-8")
|
||||||
|
|
||||||
|
def sign(self, message: str) -> str:
|
||||||
|
return hmac.new(self.key, message.encode("utf-8"), hashlib.sha256).hexdigest()
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from typing import List, Dict, Any
|
||||||
|
|
||||||
|
|
||||||
|
class Sonifier:
|
||||||
|
def map_to_cues(self, risk: float, allocation: Dict[str, float], constraints: Dict[str, Any] | None = None) -> List[Dict[str, Any]]:
|
||||||
|
cues: List[Dict[str, Any]] = []
|
||||||
|
# Simple mapping: higher risk -> alert cue, otherwise steady cue per asset
|
||||||
|
base_note = 60 # MIDI note C4
|
||||||
|
if risk > 0.6:
|
||||||
|
cues.append({"note": base_note + 12, "duration": 0.5, "type": "alert", "message": "High risk"})
|
||||||
|
for i, (asset, val) in enumerate(allocation.items()):
|
||||||
|
note = base_note + i
|
||||||
|
cues.append({"note": note, "duration": 0.25, "type": "beat", "asset": asset, "value": val})
|
||||||
|
return cues
|
||||||
|
|
@ -0,0 +1,2 @@
|
||||||
|
This repository is a multi-component Python prototype for AudioLedger Studio.
|
||||||
|
It demonstrates a minimal but production-minded architecture (DSL, IR, runtime, delta-sync, signing, registry, adapters, and sonification) suitable for further expansion.
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
[build-system]
|
||||||
|
requires = ["setuptools>=42", "wheel"]
|
||||||
|
build-backend = "setuptools.build_meta"
|
||||||
|
|
||||||
|
[project]
|
||||||
|
name = "idea81_audioledger_studio_verifiable"
|
||||||
|
version = "0.1.0"
|
||||||
|
description = "AudioLedger Studio: Verifiable Algebraic Market Scenarios with Sonified Governance"
|
||||||
|
readme = "README.md"
|
||||||
|
requires-python = ">=3.9"
|
||||||
|
license = {text = "MIT"}
|
||||||
|
authors = [{name = "OpenCode AI", email = "noreply@example.com"}]
|
||||||
|
dependencies = ["pytest"]
|
||||||
|
|
||||||
|
[tool.setuptools.packages.find]
|
||||||
|
where = ["."]
|
||||||
|
include = ["idea81_audioledger_studio_verifiable"]
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
echo "Running tests..."
|
||||||
|
pytest -q
|
||||||
|
|
||||||
|
echo "Building package (python -m build)..."
|
||||||
|
python3 -m build
|
||||||
|
|
||||||
|
echo "All tests passed and package built."
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
"""Tests package init."""
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
from idea81_audioledger_studio_verifiable.delta import DeltaStore
|
||||||
|
|
||||||
|
|
||||||
|
def test_delta_and_apply():
|
||||||
|
ds = DeltaStore()
|
||||||
|
prev = {"A": 1, "B": 2}
|
||||||
|
curr = {"A": 1, "B": 3}
|
||||||
|
delta = ds.compute_delta(curr, prev)
|
||||||
|
assert delta == {"B": 3}
|
||||||
|
applied = ds.apply_delta(prev, delta)
|
||||||
|
assert applied == curr
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
import math
|
||||||
|
from idea81_audioledger_studio_verifiable.core import parse_dsl, LocalProblemIR
|
||||||
|
|
||||||
|
|
||||||
|
def test_parse_dsl_basic_assets_and_risk():
|
||||||
|
text = """
|
||||||
|
assets:
|
||||||
|
AAPL: 100
|
||||||
|
BTC: 2
|
||||||
|
objectives:
|
||||||
|
maximize_return: true
|
||||||
|
risk_budgets:
|
||||||
|
CVaR: 0.05
|
||||||
|
"""
|
||||||
|
ir = parse_dsl(text)
|
||||||
|
assert "objects" in ir
|
||||||
|
obj: LocalProblemIR = ir["objects"] # type: ignore
|
||||||
|
assert isinstance(obj, LocalProblemIR)
|
||||||
|
assert obj.assets["AAPL"] == 100
|
||||||
|
assert obj.assets["BTC"] == 2
|
||||||
|
assert obj.risk_budgets.get("CVaR") == 0.05
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
from idea81_audioledger_studio_verifiable.registry import GraphRegistry
|
||||||
|
from idea81_audioledger_studio_verifiable.adapters import PriceFeedAdapter
|
||||||
|
|
||||||
|
|
||||||
|
def test_registry_and_adapter():
|
||||||
|
reg = GraphRegistry()
|
||||||
|
adapter = PriceFeedAdapter({"AAPL": 170.0})
|
||||||
|
reg.register_contract("price-feed", adapter)
|
||||||
|
got = reg.get_contract("price-feed")
|
||||||
|
assert isinstance(got, PriceFeedAdapter)
|
||||||
|
assert got.get_prices()["AAPL"] == 170.0
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
from idea81_audioledger_studio_verifiable.runtime import ExecutionGraph
|
||||||
|
|
||||||
|
|
||||||
|
def test_runtime_allocation_scaling():
|
||||||
|
g = ExecutionGraph()
|
||||||
|
assets = {"A": 100.0, "B": 300.0}
|
||||||
|
g.compile_from_ir({"objects": type("O", (), {"assets": assets})()})
|
||||||
|
result = g.run(assets, target_total=400.0)
|
||||||
|
# Ratios preserved, sum equals target
|
||||||
|
assert abs(sum(result.values()) - 400.0) < 1e-6
|
||||||
|
assert result["A"] == 100.0 * (400.0 / 400.0)
|
||||||
|
assert result["B"] == 300.0 * (400.0 / 400.0)
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
from idea81_audioledger_studio_verifiable.signer import Signer
|
||||||
|
|
||||||
|
|
||||||
|
def test_signer_basic():
|
||||||
|
signer = Signer("super-secret-key")
|
||||||
|
sig = signer.sign("audit-log-entry")
|
||||||
|
assert isinstance(sig, str) and len(sig) >= 64
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
from idea81_audioledger_studio_verifiable.sonifier import Sonifier
|
||||||
|
|
||||||
|
|
||||||
|
def test_sonifier_basic():
|
||||||
|
s = Sonifier()
|
||||||
|
cues = s.map_to_cues(0.75, {"A": 10.0, "B": 20.0})
|
||||||
|
assert isinstance(cues, list)
|
||||||
|
assert len(cues) >= 1
|
||||||
Loading…
Reference in New Issue