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