build(agent): new-agents-2#7e3bbc iteration
This commit is contained in:
parent
5ab4009b6b
commit
b459f560e9
|
|
@ -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,19 @@
|
||||||
|
# AGENTS
|
||||||
|
|
||||||
|
Architecture overview for the PolyPort Studio MVP.
|
||||||
|
|
||||||
|
- Language: Python (src/idea74_polyport_studio_interactive)
|
||||||
|
- Core: DSL parser and canonical bridge (LocalProblem) with a minimal skeleton for future adapters.
|
||||||
|
- Visualization: Placeholder scaffolding (3D frontend to be wired later).
|
||||||
|
- Persistence/Verifiability: Basic AuditLog placeholder to outline intent; signing and delta structures to be expanded.
|
||||||
|
- Testing: Pytest-based tests under tests/.
|
||||||
|
|
||||||
|
How to contribute:
|
||||||
|
- Run tests with `bash test.sh`.
|
||||||
|
- Use `pytest -q` for focused test runs.
|
||||||
|
- Docs and architecture decisions live in README.md and AGENTS.md.
|
||||||
|
|
||||||
|
Code organization:
|
||||||
|
- src/idea74_polyport_studio_interactive/
|
||||||
|
- tests/
|
||||||
|
- README.md
|
||||||
23
README.md
23
README.md
|
|
@ -1,3 +1,22 @@
|
||||||
# idea74-polyport-studio-interactive
|
# PolyPort Studio: Interactive, Verifiable Algebraic Portfolio Design (MVP)
|
||||||
|
|
||||||
Source logic for Idea #74
|
This repository implements a production-oriented MVP for a graphical, algebraic DSL-driven portfolio design tool.
|
||||||
|
|
||||||
|
- Language: Python (Core). See `pyproject.toml` for packaging and project metadata.
|
||||||
|
- Core functionality: A minimal DSL parser that converts a simple domain-specific language into a canonical LocalProblem-like representation.
|
||||||
|
- Extensibility: Designed for adapters (Graph-of-Contracts) to map real-world data feeds into the canonical form.
|
||||||
|
- Testing: Pytest-based tests; an executable `test.sh` orchestrates tests and packaging checks.
|
||||||
|
|
||||||
|
How to run locally:
|
||||||
|
- Install dependencies (if any) with your Python environment.
|
||||||
|
- Run tests: `bash test.sh`.
|
||||||
|
|
||||||
|
Project structure:
|
||||||
|
- src/idea74_polyport_studio_interactive/ # Python package with the DSL core
|
||||||
|
- tests/ # Pytest test-suite
|
||||||
|
- AGENTS.md # Architecture and contribution guide
|
||||||
|
- README.md # This file
|
||||||
|
- pyproject.toml # Packaging metadata
|
||||||
|
- test.sh # Test runner (also builds)
|
||||||
|
|
||||||
|
Note: This is an MVP that focuses on correctness, testability, and a clean path toward a production-grade, offline-first architecture.
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
[build-system]
|
||||||
|
requires = ["setuptools>=42", "wheel"]
|
||||||
|
build-backend = "setuptools.build_meta"
|
||||||
|
|
||||||
|
[project]
|
||||||
|
name = "idea74-polyport-studio-interactive"
|
||||||
|
version = "0.1.0"
|
||||||
|
description = "Interactive, verifiable algebraic portfolio design DSL core (MVP)"
|
||||||
|
readme = "README.md"
|
||||||
|
requires-python = ">=3.9"
|
||||||
|
|
||||||
|
[tool.setuptools]
|
||||||
|
packages = ["idea74_polyport_studio_interactive"]
|
||||||
|
package-dir = {"idea74_polyport_studio_interactive" = "src/idea74_polyport_studio_interactive"}
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
"""Idea74 PolyPort Studio Interactive - Python package init"""
|
||||||
|
|
||||||
|
from .dsl import parse_dsl # Public entry point for parsing DSL strings
|
||||||
|
|
||||||
|
__all__ = ["parse_dsl"]
|
||||||
|
|
@ -0,0 +1,133 @@
|
||||||
|
"""Minimal DSL parser for PolyPort Studio MVP.
|
||||||
|
|
||||||
|
This module provides a small, well-defined DSL that enables declaration of
|
||||||
|
assets, objectives, and constraints. It produces a canonical JSON-like
|
||||||
|
dictionary structure that can be consumed by downstream adapters and visualizers.
|
||||||
|
"""
|
||||||
|
from __future__ import annotations
|
||||||
|
from typing import Any, Dict, List
|
||||||
|
|
||||||
|
def _parse_assets(section_lines: List[str]) -> List[str]:
|
||||||
|
# Expect a single line of comma-separated asset names, e.g.:
|
||||||
|
# ASSETS: AAPL, MSFT, GOOGL
|
||||||
|
if not section_lines:
|
||||||
|
return []
|
||||||
|
line = section_lines[0].strip()
|
||||||
|
if line.upper().startswith("ASSETS:"):
|
||||||
|
content = line.split(":", 1)[1].strip()
|
||||||
|
else:
|
||||||
|
content = line
|
||||||
|
if not content:
|
||||||
|
return []
|
||||||
|
# Split by comma and strip spaces
|
||||||
|
return [a.strip() for a in content.split(",") if a.strip()]
|
||||||
|
|
||||||
|
def _parse_objectives(section_lines: List[str]) -> List[str]:
|
||||||
|
# Expect a single line like: OBJECTIVES: maximize_return
|
||||||
|
if not section_lines:
|
||||||
|
return []
|
||||||
|
line = section_lines[0].strip()
|
||||||
|
if line.upper().startswith("OBJECTIVES:"):
|
||||||
|
content = line.split(":", 1)[1].strip()
|
||||||
|
else:
|
||||||
|
content = line
|
||||||
|
if not content:
|
||||||
|
return []
|
||||||
|
return [c.strip() for c in content.split(",") if c.strip()]
|
||||||
|
|
||||||
|
def _parse_constraints(section_lines: List[str]) -> Dict[str, Any]:
|
||||||
|
# Constraints in a simple key=value format, separated by semicolons or commas.
|
||||||
|
# Example: "budget=1.0; max_risk=0.05; liquidity=0.2"
|
||||||
|
if not section_lines:
|
||||||
|
return {}
|
||||||
|
line = section_lines[0].strip()
|
||||||
|
if line.upper().startswith("CONSTRAINTS:"):
|
||||||
|
content = line.split(":", 1)[1].strip()
|
||||||
|
else:
|
||||||
|
content = line
|
||||||
|
if not content:
|
||||||
|
return {}
|
||||||
|
items = [p.strip() for p in content.replace(";", ",").split(",") if p.strip()]
|
||||||
|
result: Dict[str, Any] = {}
|
||||||
|
for item in items:
|
||||||
|
if "=" in item:
|
||||||
|
k, v = item.split("=", 1)
|
||||||
|
k = k.strip()
|
||||||
|
v = v.strip()
|
||||||
|
# Try to parse as float/int, otherwise leave as string
|
||||||
|
if "." in v:
|
||||||
|
try:
|
||||||
|
val: Any = float(v)
|
||||||
|
except ValueError:
|
||||||
|
val = v
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
val = int(v)
|
||||||
|
except ValueError:
|
||||||
|
try:
|
||||||
|
val = float(v)
|
||||||
|
except ValueError:
|
||||||
|
val = v
|
||||||
|
result[k] = val
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def _split_into_sections(text: str) -> Dict[str, List[str]]:
|
||||||
|
# Very lightweight section splitter for three known sections.
|
||||||
|
sections: Dict[str, List[str]] = {"assets": [], "objectives": [], "constraints": []}
|
||||||
|
current: str | None = None
|
||||||
|
for raw_line in text.splitlines():
|
||||||
|
line = raw_line.strip()
|
||||||
|
if not line:
|
||||||
|
continue
|
||||||
|
upper = line.upper()
|
||||||
|
if upper.startswith("ASSETS:"):
|
||||||
|
current = "assets"
|
||||||
|
sections[current].append(line)
|
||||||
|
continue
|
||||||
|
if upper.startswith("OBJECTIVES:"):
|
||||||
|
current = "objectives"
|
||||||
|
sections[current].append(line)
|
||||||
|
continue
|
||||||
|
if upper.startswith("CONSTRAINTS:"):
|
||||||
|
current = "constraints"
|
||||||
|
sections[current].append(line)
|
||||||
|
continue
|
||||||
|
if current is None:
|
||||||
|
# Ignore stray lines until a section header is found
|
||||||
|
continue
|
||||||
|
sections[current].append(line)
|
||||||
|
return sections
|
||||||
|
|
||||||
|
|
||||||
|
def parse_dsl(text: str) -> Dict[str, Any]:
|
||||||
|
"""Parse a tiny DSL into a canonical LocalProblem-like dict.
|
||||||
|
|
||||||
|
The DSL supports three sections in a single textual block:
|
||||||
|
- ASSETS: comma-separated asset names
|
||||||
|
- OBJECTIVES: comma-separated objective names
|
||||||
|
- CONSTRAINTS: key=value pairs separated by semicolons or commas
|
||||||
|
|
||||||
|
Example DSL:
|
||||||
|
ASSETS: AAPL, MSFT, GOOGL
|
||||||
|
OBJECTIVES: maximize_return, minimize_risk
|
||||||
|
CONSTRAINTS: budget=1.0; max_risk=0.05
|
||||||
|
"""
|
||||||
|
sections = _split_into_sections(text)
|
||||||
|
assets = _parse_assets(sections.get("assets", []))
|
||||||
|
objectives = _parse_objectives(sections.get("objectives", []))
|
||||||
|
constraints = _parse_constraints(sections.get("constraints", []))
|
||||||
|
|
||||||
|
canonical: Dict[str, Any] = {
|
||||||
|
"LocalProblem": {
|
||||||
|
"Assets": assets,
|
||||||
|
"Objectives": objectives,
|
||||||
|
"Constraints": constraints,
|
||||||
|
},
|
||||||
|
"SharedVariables": {}, # placeholder for future delta/state sharing
|
||||||
|
"PlanDelta": {}, # placeholder for change-tracking deltas
|
||||||
|
}
|
||||||
|
return canonical
|
||||||
|
|
||||||
|
|
||||||
|
__all__ = ["parse_dsl"]
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# Test script for PolyPort Studio MVP
|
||||||
|
# - Runs pytest tests
|
||||||
|
# - Builds the Python package (PEP 517/518) to verify packaging metadata
|
||||||
|
|
||||||
|
echo "==> Running pytest tests..."
|
||||||
|
pytest -q
|
||||||
|
|
||||||
|
echo "==> Building the package to verify packaging metadata..."
|
||||||
|
python3 -m build
|
||||||
|
|
||||||
|
echo "All tests and build completed successfully."
|
||||||
|
|
@ -0,0 +1,30 @@
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
|
||||||
|
# Ensure the src package is on the Python path for tests
|
||||||
|
ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
|
||||||
|
SRC = os.path.join(ROOT, "src")
|
||||||
|
if SRC not in sys.path:
|
||||||
|
sys.path.insert(0, SRC)
|
||||||
|
|
||||||
|
from idea74_polyport_studio_interactive import parse_dsl
|
||||||
|
|
||||||
|
|
||||||
|
def test_parse_basic_dsl():
|
||||||
|
dsl = (
|
||||||
|
"ASSETS: AAPL, MSFT, GOOGL\n"
|
||||||
|
"OBJECTIVES: maximize_return\n"
|
||||||
|
"CONSTRAINTS: budget=1.0; max_risk=0.05; liquidity=0.2"
|
||||||
|
)
|
||||||
|
result = parse_dsl(dsl)
|
||||||
|
# Basic shape checks
|
||||||
|
assert "LocalProblem" in result
|
||||||
|
lp = result["LocalProblem"]
|
||||||
|
assert isinstance(lp, dict)
|
||||||
|
assert lp.get("Assets") == ["AAPL", "MSFT", "GOOGL"]
|
||||||
|
assert lp.get("Objectives") == ["maximize_return"]
|
||||||
|
constraints = lp.get("Constraints")
|
||||||
|
assert isinstance(constraints, dict)
|
||||||
|
# Check a couple of parsed values
|
||||||
|
assert constraints.get("budget") == 1.0
|
||||||
|
assert constraints.get("max_risk") == 0.05
|
||||||
Loading…
Reference in New Issue