[project] name = "portfolio-optimiser" version = "1.0.0" description = "Generic framework on Microsoft Agent Framework for per-project cost-savings optimization" readme = "README.md" requires-python = ">=3.10" dependencies = [ # MAF — GA-pakker, IKKE meta-pakken `agent-framework`. Metaen drar `agent-framework-core[all]`, # og `[all]` trekker inn de fortsatt-beta integrasjonene (azure-ai-search/cosmos/ollama/…) som # tvinger pre-releases og drar med en ALPHA pydantic. Offisiell guide: installer kun det du trenger. # Beta-integrasjoner legges til per-fase (med snevert pre-release-scope) når de faktisk trengs. "agent-framework-core>=1.9.0,<2", # kjerne (GA) — to-sidig pin (S2.5): major-bump krever re-verifisering av privat-API-premissene (test_maf_version_guard) "agent-framework-foundry>=1.8.2", # Azure/Foundry-profil: FoundryChatClient (GA) "azure-identity>=1.25", # S4.1: AzureFoundryBackend passes an explicit AzureCliCredential (Foundry requires it); already transitive via foundry — promoted to a declared direct dep (zero new install weight) "agent-framework-openai>=1.8.2", # OpenAI + OpenAI-kompatible lokale endpoints (GA) → lokal profil # Promotert dev→core i Fase 2 (MVP-runtime, ikke lenger spike-only): "agent-framework-orchestrations>=1.0.0", # GA orchestration builders (GroupChat/Concurrent/Magentic); resolves med core 1.9.0 "pulp>=2.8", # deterministisk validator-solver; PuLP bundler CBC i wheelen (R2). Installert 3.3.2. # PuLP 4.0 vil kreve `pip install pulp[cbc]` + COIN_CMD (Fase-migrasjonsnotat). "mcp>=1.28.0", # tynn lokal-mappe MCP-server (Step 7) — GA (resolverte 1.28.0) per Step 1-beslutning "anyio>=4.14", # kø-(z): ingest_mcp.py bruker anyio.fail_after direkte (MCP-timeout-stien); allerede transitiv via mcp — promotert til deklarert direkte dep (zero new install weight, resolverte 4.14.0) "pydantic>=2.11,<3", # IR/validering (B1) — eksplisitt pin til STABIL 2.x, aldri alpha # S3.1: brute-force cosine for the hybrid verdict retriever — MAF-free, offline (D-C). # Upper bound is <2.3, NOT <3, and it is load-bearing twice over: numpy 2.3+ requires # Python >=3.11 (2.4+ requires >=3.12), so a wider range silently contradicts this project's # `requires-python = ">=3.10"`; and numpy 2.3+ stubs use PEP 695 `type` statements, which # mypy refuses to parse under `python_version = 3.10` (python/mypy#18701) — breaking # `uv run mypy src` outright. Raise both floors together or not at all. "numpy>=2.0,<2.3", "llm-ingestion-okf", # Door A ingest (§4–§6) — the shared implementation of shared/ingest-spec.md; zero runtime deps, MAF-free (D7) "llm-ingestion-guard", # Door A content gate — scans materialized concepts before they reach the bundle; zero runtime deps, MAF-free (D7) ] # Console entry points — the install surface a fresh clone gets from `uv sync` (P4 pkt. 5). # Deliberately TWO, not five: `run` is the framework CLI (three documented modes) and `simulation` # is the offline end-to-end proof the README points a newcomer at. `costsim` / `hitl` / `preflight` # keep the `python -m` form — they are operator utilities, not the product's front door, and every # name here is a name the freeze has to carry. Pinned by tests/test_console_entry_points.py. [project.scripts] portfolio-optimiser = "portfolio_optimiser.run:main" portfolio-optimiser-demo = "portfolio_optimiser.simulation:main" # Distribution channel for the shared ingest library (mirrors portfolio-optimiser-claude, # verified in consumer CI): git pin against the public Forgejo repo — reproducible for every # consumer, uv.lock pins the exact commit behind the tag. Bump the rev on a new library tag. [tool.uv.sources] llm-ingestion-okf = { git = "https://git.fromaitochitta.com/open/llm-ingestion-okf.git", rev = "v0.3.2" } # Pinned to a TAG, never a floating ref — a security component whose version can move under a # `uv sync` is a gate that can stop gating without a local diff (the okf `generated`-literal # lesson, measured 2026-08-09). Bumping this is a decision, not maintenance. llm-ingestion-guard = { git = "https://git.fromaitochitta.com/open/llm-ingestion-pipeline-security.git", rev = "v0.3.4" } # Dev tooling as a PEP 735 dependency-group (uv includes it by default in `uv sync`/`uv run`), # so the documented bare `uv sync` + `uv run pytest` workflow installs it without `--extra`. # # INTENTIONAL, RECORDED DEVIATION (Fase 2 review F4): the original plan Step 1 authorized only # moving `agent-framework-orchestrations` + `pulp` from the dev extra into core. Migrating the # dev group from `[project.optional-dependencies]` to this PEP 735 `[dependency-groups]` block # went beyond that stated scope, but is KEPT (not reverted) because it is what makes the brief's # Success Criterion SC1 ("bare `uv sync` + `uv run pytest`") install dev tooling without # `--extra` — reverting would break SC1. Logged here so the deviation is no longer silent. [dependency-groups] dev = [ "pytest>=8", "ruff>=0.6", "mypy>=1.11", "pytest-asyncio>=0.24", # MAF orchestrations are async (await workflow.run(...)) — test-only ] [build-system] requires = ["hatchling"] build-backend = "hatchling.build" [tool.hatch.build.targets.wheel] packages = ["src/portfolio_optimiser"] [tool.ruff] line-length = 100 src = ["src", "tests", "spikes"] [tool.pytest.ini_options] pythonpath = ["src", "."] testpaths = ["tests"] asyncio_mode = "auto" [tool.mypy] python_version = "3.10" # Third-party libs without bundled type stubs (py.typed) — analysed as untyped, not errors. [[tool.mypy.overrides]] module = ["pulp.*", "agent_framework_foundry.*", "llm_ingestion_guard.*"] ignore_missing_imports = true # NOTE on `llm_ingestion_guard`: the override alone would make the seam type-BLIND, not # type-safe — every symbol arrives as `Any`, so a field rename upstream would type-check # happily and fail at runtime. `ingest.materialize_gated` therefore coerces each value it # reads off the guard's result objects to a concrete type at the boundary (the okf precedent), # and the seam's behaviour is pinned by tests/test_ingest_content_gate_loadbearing.py.