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