diff --git a/.github/workflows/test-integration.yml b/.github/workflows/test-integration.yml index ff463f8..f365630 100644 --- a/.github/workflows/test-integration.yml +++ b/.github/workflows/test-integration.yml @@ -1,7 +1,7 @@ name: Integration tests -# Fast wiring/integration suite for the macromod integration layer. Runs on -# PRs and pushes that touch integration/**. It installs the base macromod +# Fast wiring/integration suite for the policyengine-macro integration layer. Runs on +# PRs and pushes that touch integration/**. It installs the base policyengine-macro # package plus the two lightweight model repos (OBR emulator + UK SVAR) so the # real OBR/SVAR wiring is exercised, but NOT the heavy PolicyEngine country # models — those (and every full model solve) are marked `slow` and skipped by @@ -31,7 +31,7 @@ jobs: - uses: actions/setup-python@v5 with: python-version: "3.13" - - name: Install macromod + the two lightweight model repos + - name: Install policyengine-macro + the two lightweight model repos run: | python -m pip install --upgrade pip # Base package (click, numpy, pandas, mcp) + pytest. diff --git a/.github/workflows/validate-deployment.yml b/.github/workflows/validate-deployment.yml index ed87a49..a0a4b44 100644 --- a/.github/workflows/validate-deployment.yml +++ b/.github/workflows/validate-deployment.yml @@ -55,7 +55,7 @@ jobs: python-version: "3.13" - name: Install remote test deps # The remote suite talks to the public MCP endpoint over HTTP; it needs - # no Modal auth and does not import the macromod package. + # no Modal auth and does not import the policyengine-macro package. run: pip install --upgrade pip && pip install "mcp[cli]" pytest anyio - name: Validate the live deployment working-directory: integration diff --git a/README.md b/README.md index c70cab0..07c1103 100644 --- a/README.md +++ b/README.md @@ -36,7 +36,7 @@ benefits for the UK and US — the same engine that powers [policyengine.org](https://policyengine.org) — complementing the macro models. The models live in their own repositories. This repo hosts the **PolicyEngine Macro -website** and the **integration layer** (`integration/`) — a `macromod` CLI +website** and the **integration layer** (`integration/`) — a `pe-macro` CLI and MCP server over the models, with CI auto-deploying the hosted MCP server to Modal on every merge — merges to the model repos (obr-macroeconomic-model, boe-var-model) trigger the same redeploy via @@ -88,11 +88,11 @@ The [connect page](https://macromod.vercel.app/connect/) covers three ways to us models: - **MCP** — the hosted Model Context Protocol server is **live** at - `https://policyengine--macromod-mcp-serve.modal.run/mcp`. Add it as a custom + `https://policyengine--policyengine-macro-mcp-serve.modal.run/mcp`. Add it as a custom connector in Claude or ChatGPT, or in Claude Code: ```bash - claude mcp add --transport http macromod https://policyengine--macromod-mcp-serve.modal.run/mcp + claude mcp add --transport http policyengine-macro https://policyengine--policyengine-macro-mcp-serve.modal.run/mcp ``` Ten tools: `score_reform` (a PolicyEngine reform — the same @@ -103,19 +103,19 @@ models: (`calculate_household`, `household_reform_impact`, `list_reform_parameters`, `population_reform_impact`). `score_reform` with `model='og'` works locally only: OG-UK is deliberately excluded from the hosted image (a score takes - tens of minutes) — use `macromod score --model og` instead; `model='obr'` + tens of minutes) — use `pe-macro score --model og` instead; `model='obr'` awaits the microsim static-costing bridge (#9), so raw shocks go through `obr_shock`. The server runs serverless and scales to zero — the first call after idle may take ~10 s to wake. -- **CLI** — the `macromod` CLI (`score`, `obr-shock`, `variables`, `forecast`, +- **CLI** — the `pe-macro` CLI (`score`, `obr-shock`, `variables`, `forecast`, `shocks`, `summary`, `household`, `household-impact`, `population-impact`, `parameters`, `og-score`) lives in [`integration/`](integration/); PyPI publish is planned. Install it — with all three hosted-model packages and their data, no clone — via: ```bash - pip install "macromod[models] @ git+https://github.com/PolicyEngine/macro#subdirectory=integration" + pip install "policyengine-macro[models] @ git+https://github.com/PolicyEngine/macro#subdirectory=integration" ``` - **Code** — drive each model's Python API yourself. @@ -165,10 +165,10 @@ non-real numbers as illustrative. ## Roadmap -- [x] `macromod` CLI (in `integration/`; PyPI publish still to come) -- [x] Local MCP server (`python -m macromod.mcp_server`) -- [x] Hosted MCP server (`https://policyengine--macromod-mcp-serve.modal.run/mcp`, auto-deployed by CI) -- [x] OG-UK steady-state scoring (`macromod score --model og` / `macromod og-score`, local only) +- [x] `pe-macro` CLI (in `integration/`; PyPI publish still to come) +- [x] Local MCP server (`python -m policyengine_macro.mcp_server`) +- [x] Hosted MCP server (`https://policyengine--policyengine-macro-mcp-serve.modal.run/mcp`, auto-deployed by CI) +- [x] OG-UK steady-state scoring (`pe-macro score --model og` / `pe-macro og-score`, local only) - [x] Population-level PolicyEngine reform scoring (`population_reform_impact`, hosted and local) - [ ] Additional macroeconomic model classes - See [#1](https://github.com/PolicyEngine/macro/issues/1) — Rust port of the solver core diff --git a/connect/index.html b/connect/index.html index e0e22dc..b5fba83 100644 --- a/connect/index.html +++ b/connect/index.html @@ -221,7 +221,7 @@

1

Copy the PolicyEngine Macro URL

Click the copy button — you'll paste it in the next step.

-
https://policyengine--macromod-mcp-serve.modal.run/mcp
+
https://policyengine--policyengine-macro-mcp-serve.modal.run/mcp
2 @@ -240,7 +240,7 @@

Connect and ask

Using Claude Code?

One command adds the same server to Claude Code:

-
claude mcp add --transport http macromod https://policyengine--macromod-mcp-serve.modal.run/mcp
+
claude mcp add --transport http policyengine-macro https://policyengine--policyengine-macro-mcp-serve.modal.run/mcp
@@ -249,7 +249,7 @@

Using Claude Code?

1

Copy the PolicyEngine Macro URL

Click the copy button — you'll paste it in the next step.

-
https://policyengine--macromod-mcp-serve.modal.run/mcp
+
https://policyengine--policyengine-macro-mcp-serve.modal.run/mcp
2 @@ -268,7 +268,7 @@

Connect and ask

Using Codex?

One command adds the same server to OpenAI's Codex CLI:

-
codex mcp add macromod --url https://policyengine--macromod-mcp-serve.modal.run/mcp
+
codex mcp add policyengine-macro --url https://policyengine--policyengine-macro-mcp-serve.modal.run/mcp
@@ -286,13 +286,13 @@

Using Codex?

1

Install

Python 3.11+. One package wraps the OBR emulator, the UK SVAR, and PolicyEngine behind a single command — models and data included.

-
pip install "macromod[models] @ git+https://github.com/PolicyEngine/macro#subdirectory=integration"
+
pip install "policyengine-macro[models] @ git+https://github.com/PolicyEngine/macro#subdirectory=integration"
2

Ask Claude Code

Claude Code runs the CLI from its terminal — just describe the reform, or run it yourself:

-
macromod obr-shock --var CGG --shock 1250 --periods 4
+
pe-macro obr-shock --var CGG --shock 1250 --periods 4
3 @@ -306,13 +306,13 @@

Read the results

1

Install

Python 3.11+. One package wraps the OBR emulator, the UK SVAR, and PolicyEngine behind a single command — models and data included.

-
pip install "macromod[models] @ git+https://github.com/PolicyEngine/macro#subdirectory=integration"
+
pip install "policyengine-macro[models] @ git+https://github.com/PolicyEngine/macro#subdirectory=integration"
2

Ask Codex

Codex runs the CLI from its terminal — just describe the reform, or run it yourself:

-
macromod obr-shock --var CGG --shock 1250 --periods 4
+
pe-macro obr-shock --var CGG --shock 1250 --periods 4
3 @@ -322,7 +322,7 @@

Read the results


-

The macromod CLI installs straight from GitHub — one pip install pulls the CLI plus all three models with their data, no cloning required. It lives in this repo's integration/ directory; a shorter pip install macromod will come with PyPI publication.

+

The pe-macro CLI installs straight from GitHub — one pip install pulls the CLI plus all three models with their data, no cloning required. It lives in this repo's integration/ directory; a shorter pip install policyengine-macro will come with PyPI publication.

coding agents with a terminal — Claude Code, Codex — can run the CLI directly, no MCP needed; or add the MCP server with one command (see the MCP tab).

@@ -668,9 +668,9 @@

Sector output, long-run

Under the hood: an 8-variable Bayesian VAR (1992Q1–2023Q2, Covid dummies) is sampled from its normal-inverse-Wishart posterior, and each draw is rotated per Arias–Rubio-Ramírez–Waggoner (2018) until the zero - and sign restrictions name six structural shocks. The macromod CLI - wraps the common outputs: macromod forecast, - macromod shocks, macromod summary. + and sign restrictions name six structural shocks. The PolicyEngine Macro CLI + wraps the common outputs: pe-macro forecast, + pe-macro shocks, pe-macro summary.

diff --git a/integration/README.md b/integration/README.md index f05cded..2e4dc36 100644 --- a/integration/README.md +++ b/integration/README.md @@ -1,6 +1,6 @@ # PolicyEngine Macro integration layer -A single Python package (`macromod`) exposing the suite's models behind +A single Python package (`policyengine-macro`) exposing the suite's models behind one CLI and one MCP server: - **OBR emulator** (`obr_macro`): runs the OBR's published model equations — @@ -13,7 +13,7 @@ one CLI and one MCP server: microdata (UK data is private: set `HUGGING_FACE_TOKEN`; the hosted deployment provisions it server-side). - **OG-UK** (`oguk`, optional/local-only): overlapping-generations - steady-state scoring through `macromod score --model og`. + steady-state scoring through `pe-macro score --model og`. `score_reform` is the one reform vocabulary across the suite: the same flat `{parameter_path: value}` dict as the microsimulation tools, dispatched to a @@ -35,11 +35,11 @@ scoring model by its declared contract: Every scoring result also carries a common `score` block — the `ScoreResult` schema ([#10](https://github.com/PolicyEngine/macro/issues/10)): model id and class, horizon, per-quantity deltas with units and basis, assumptions, -caveats, and an optional distributional block — so `macromod compare +caveats, and an optional distributional block — so `pe-macro compare --reform '...' --models microsim,obr` renders the same reform through different model classes in one table. -`src/macromod/core.py` holds the model adapters (single source of truth); +`src/policyengine_macro/core.py` holds the model adapters (single source of truth); `cli.py` and `mcp_server.py` are thin wrappers over the same functions. ## Install @@ -48,10 +48,10 @@ No clone needed — one pip install pulls the CLI plus the hosted-model packages (the OBR emulator and the SVAR ship their data as package data): ```bash -pip install "macromod[models] @ git+https://github.com/PolicyEngine/macro#subdirectory=integration" +pip install "policyengine-macro[models] @ git+https://github.com/PolicyEngine/macro#subdirectory=integration" ``` -A shorter `pip install macromod` will come with PyPI publication. +A shorter `pip install policyengine-macro` will come with PyPI publication. For development, install with the full model set (policyengine included via the `[models]` extra), then override the two model packages with local @@ -73,36 +73,36 @@ uv venv .venv-og && uv pip install -p .venv-og/bin/python -e ./integration \ "oguk @ git+https://github.com/PSLmodels/OG-UK" ``` -(`-e ./integration` gives that env the `macromod` executable; the base +(`-e ./integration` gives that env the `pe-macro` executable (and its legacy `macromod` alias); the base package pins no policyengine version, so OG-UK's own pins win there.) ## CLI ```bash -macromod variables # OBR shock variables + units -macromod score --reform '{"gov.hmrc.income_tax.rates.uk[0].rate":0.21}' \ +pe-macro variables # OBR shock variables + units +pe-macro score --reform '{"gov.hmrc.income_tax.rates.uk[0].rate":0.21}' \ --model og # PolicyEngine reform -> OG-UK (slow) -macromod score --reform '{"gov.hmrc.income_tax.rates.uk[0].rate":0.21}' \ +pe-macro score --reform '{"gov.hmrc.income_tax.rates.uk[0].rate":0.21}' \ --model obr --years 5 # static costing -> OBR second-round effects -macromod compare --reform '{"gov.hmrc.income_tax.rates.uk[0].rate":0.21}' \ +pe-macro compare --reform '{"gov.hmrc.income_tax.rates.uk[0].rate":0.21}' \ --models microsim,obr # same reform, model classes side by side -macromod obr-shock --var CGG --shock 1250 --periods 4 # £5bn/yr spending, 1 year -macromod obr-shock --var TCPRO --shock -0.05 # 5pp corp tax cut (closure auto-on) -macromod forecast --horizons 12 --draws 500 # YoY GDP & CPI, 68/90 bands -macromod shocks --draws 500 # P(sign) of latest-quarter shocks -macromod summary # instant, parses committed results +pe-macro obr-shock --var CGG --shock 1250 --periods 4 # £5bn/yr spending, 1 year +pe-macro obr-shock --var TCPRO --shock -0.05 # 5pp corp tax cut (closure auto-on) +pe-macro forecast --horizons 12 --draws 500 # YoY GDP & CPI, 68/90 bands +pe-macro shocks --draws 500 # P(sign) of latest-quarter shocks +pe-macro summary # instant, parses committed results ``` PolicyEngine tools: ```bash -macromod parameters # curated reform parameters -macromod household --country uk \ +pe-macro parameters # curated reform parameters +pe-macro household --country uk \ --people '[{"age":35,"employment_income":50000}]' -macromod household-impact --country uk \ +pe-macro household-impact --country uk \ --people '[{"age":35,"employment_income":50000}]' \ --reform '{"gov.hmrc.income_tax.rates.uk[0].rate":0.25}' -macromod population-impact --country uk \ +pe-macro population-impact --country uk \ --reform '{"gov.hmrc.cgt.basic_rate":0.20,"gov.hmrc.cgt.higher_rate":0.40}' ``` @@ -119,7 +119,7 @@ benefits the MCP server). ## MCP server -Runs over stdio via `python -m macromod.mcp_server`, exposing ten tools: +Runs over stdio via `python -m policyengine_macro.mcp_server`, exposing ten tools: `score_reform` (a PolicyEngine reform through a chosen macro model), `obr_shock` and `list_reform_variables` (raw OBR variable shocks), `forecast_uk`, `latest_shocks`, `model_summary` (SVAR), and the PolicyEngine @@ -129,7 +129,7 @@ tools `calculate_household`, `household_reform_impact`, Test locally with Claude Code: ```bash -claude mcp add macromod -- python -m macromod.mcp_server +claude mcp add policyengine-macro -- python -m policyengine_macro.mcp_server ``` Default `draws=500` keeps tool calls to tens of seconds; raise it (e.g. 2000+) @@ -142,7 +142,7 @@ The MCP server is deployed on Modal (workspace `policyengine`) over streamable HTTP: ``` -https://policyengine--macromod-mcp-serve.modal.run/mcp +https://policyengine--policyengine-macro-mcp-serve.modal.run/mcp ``` Defined in `modal_app.py`. `policyengine[models]` is installed in the image; @@ -158,8 +158,8 @@ model load. The private UK microdata credential comes from the Modal secret - Claude Code: ```bash - claude mcp add --transport http macromod-remote \ - https://policyengine--macromod-mcp-serve.modal.run/mcp + claude mcp add --transport http policyengine-macro-remote \ + https://policyengine--policyengine-macro-mcp-serve.modal.run/mcp ``` **Cost profile** — `min_containers=0` (scales to zero, $0 idle), diff --git a/integration/modal_app.py b/integration/modal_app.py index 704ac18..93fce9b 100644 --- a/integration/modal_app.py +++ b/integration/modal_app.py @@ -2,14 +2,14 @@ modal deploy integration/modal_app.py -Serves the FastMCP instance from `macromod.mcp_server` (tools: score_reform, +Serves the FastMCP instance from `policyengine_macro.mcp_server` (tools: score_reform, obr_shock, list_reform_variables, forecast_uk, latest_shocks, model_summary, calculate_household, household_reform_impact, list_reform_parameters, population_reform_impact) as an -ASGI app at https://policyengine--macromod-mcp-serve.modal.run/mcp +ASGI app at https://policyengine--policyengine-macro-mcp-serve.modal.run/mcp Both model repos resolve their data files relative to their own repo root -(`Path(__file__)`-relative); `macromod.core`'s svar_summary falls back to a +(`Path(__file__)`-relative); `policyengine_macro.core`'s svar_summary falls back to a checkout via MACROMOD_BOE_VAR_REPO only when boe_var is absent (it is installed here, so the fallback never fires). We bake the repos into the image at the SAME absolute paths and `pip install -e` them, so every path @@ -35,9 +35,12 @@ POPULATION DATA (population_reform_impact) ------------------------------------------ -- Secret "macromod-hf" provides HUGGING_FACE_TOKEN for the private UK +- Secret "macromod-hf" (legacy name kept deliberately; renaming the Modal + Secret needs maintainer action) provides HUGGING_FACE_TOKEN for the private UK enhanced-FRS microdata on HuggingFace. -- A modal.Volume ("macromod-pe-data") is mounted at /root/.cache/macromod; +- A modal.Volume ("macromod-pe-data" — legacy name kept deliberately, see + above) is mounted at /root/.cache/macromod (legacy path, matches the data + already on the volume); HF_HOME points the HuggingFace download cache inside it and MACROMOD_PE_DATA_DIR puts the derived per-year .h5 files (~92MB/year) there too, so the ~125MB download + dataset build happens once and @@ -51,7 +54,7 @@ and a reform score needs two solves. That cannot fit the 600s Modal timeout with any headroom, so oguk is excluded here; the score_reform MCP tool with model='og' will return an ImportError on the hosted server. Use the local CLI -(`macromod score --model og`) or a local MCP server instead. If it is ever added, +(`pe-macro score --model og`) or a local MCP server instead. If it is ever added, `pip install git+https://github.com/PSLmodels/OG-UK` works (hatchling build; heavy deps: ogcore, policyengine-uk==2.88.0), but calibration also downloads the enhanced FRS dataset (HUGGING_FACE_TOKEN) and UN demographics at runtime. @@ -92,7 +95,7 @@ "click", "mcp[cli]>=1.9", # needs FastMCP.streamable_http_app() # PolicyEngine microsimulation (household calculator tools). Importing - # it loads the full UK+US country models (~20s), so macromod.core + # it loads the full UK+US country models (~20s), so policyengine_macro.core # imports it lazily inside the pe_* adapters — module import at # container start stays fast; only the first policyengine tool call # in a fresh container pays the load. @@ -135,13 +138,15 @@ ) ) -app = modal.App("macromod-mcp") +app = modal.App("policyengine-macro-mcp") # Persistent cache for PolicyEngine population microdata: the HuggingFace # download cache (HF_HOME) and the derived per-year .h5 datasets both live # on this volume, so the first population_reform_impact call pays the # download/build once and every later container reuses it. +# Legacy path kept deliberately: it matches the data already on the volume. CACHE_DIR = "/root/.cache/macromod" +# Volume keeps its legacy name deliberately; renaming needs maintainer action. pe_data_volume = modal.Volume.from_name("macromod-pe-data", create_if_missing=True) @@ -156,7 +161,7 @@ min_containers=0, # scale to zero: no idle cost scaledown_window=300, # stay warm 5 min between calls, then sleep max_containers=3, # spend cap - secrets=[modal.Secret.from_name("macromod-hf")], + secrets=[modal.Secret.from_name("macromod-hf")], # legacy name kept deliberately volumes={CACHE_DIR: pe_data_volume}, ) @modal.concurrent(max_inputs=20) @@ -167,8 +172,8 @@ def serve(): if "HUGGING_FACE_TOKEN" not in os.environ and os.environ.get("HF_TOKEN"): os.environ["HUGGING_FACE_TOKEN"] = os.environ["HF_TOKEN"] - from macromod import core - from macromod.mcp_server import mcp + from policyengine_macro import core + from policyengine_macro.mcp_server import mcp # Warm the cheap in-process cache (parses committed results/*.md only — # NOT a model estimation, which would make cold starts take minutes). @@ -188,7 +193,7 @@ def serve(): from mcp.server.transport_security import TransportSecuritySettings mcp.settings.transport_security = TransportSecuritySettings( - allowed_hosts=["policyengine--macromod-mcp-serve.modal.run"], + allowed_hosts=["policyengine--policyengine-macro-mcp-serve.modal.run"], allowed_origins=["*"], ) return mcp.streamable_http_app() diff --git a/integration/pyproject.toml b/integration/pyproject.toml index c462628..d26eef1 100644 --- a/integration/pyproject.toml +++ b/integration/pyproject.toml @@ -3,9 +3,9 @@ requires = ["setuptools>=68"] build-backend = "setuptools.build_meta" [project] -name = "macromod" +name = "policyengine-macro" version = "0.1.0" -description = "MacroMod integration layer: unified CLI and MCP server over the OBR macro emulator and the UK SVAR model" +description = "PolicyEngine Macro integration layer: unified CLI and MCP server over the OBR macro emulator and the UK SVAR model" requires-python = ">=3.10" dependencies = [ "click>=8", @@ -28,7 +28,9 @@ models = [ ] [project.scripts] -macromod = "macromod.cli:main" +pe-macro = "policyengine_macro.cli:main" +# Legacy alias kept for existing docs/users. +macromod = "policyengine_macro.cli:main" [tool.setuptools.packages.find] where = ["src"] diff --git a/integration/src/macromod/__init__.py b/integration/src/policyengine_macro/__init__.py similarity index 82% rename from integration/src/macromod/__init__.py rename to integration/src/policyengine_macro/__init__.py index aeeb59c..9da0d00 100644 --- a/integration/src/macromod/__init__.py +++ b/integration/src/policyengine_macro/__init__.py @@ -8,11 +8,11 @@ ``obr_shock``. - UK SVAR / BVAR model (``boe_var``): forecasts and structural shock readings. -Same functions are exposed via a CLI (``macromod``) and an MCP server -(``python -m macromod.mcp_server``). +Same functions are exposed via a CLI (``pe-macro``) and an MCP server +(``python -m policyengine_macro.mcp_server``). """ -from macromod.core import ( +from policyengine_macro.core import ( score_reform, obr_shock, obr_list_variables, diff --git a/integration/src/macromod/cli.py b/integration/src/policyengine_macro/cli.py similarity index 97% rename from integration/src/macromod/cli.py rename to integration/src/policyengine_macro/cli.py index c00a1dd..0287443 100644 --- a/integration/src/macromod/cli.py +++ b/integration/src/policyengine_macro/cli.py @@ -6,7 +6,7 @@ import click -from macromod import core +from policyengine_macro import core def _emit_json(obj) -> None: @@ -33,7 +33,7 @@ def main() -> None: show_default=True) @click.option("--reform", required=True, help='PolicyEngine reform JSON, e.g. \'{"gov.hmrc.income_tax.rates.uk[0].rate":0.21}\' ' - "(same shape as `macromod population-impact`).") + "(same shape as `pe-macro population-impact`).") @click.option("--model", required=True, type=click.Choice(list(core.SCORE_MODELS)), help="Scoring model: og (OG-UK steady state; slow), obr (OBR " @@ -52,9 +52,9 @@ def score(country, reform, model, year, max_iter, years, dataset, as_json): """Score a PolicyEngine reform with a scoring model of the suite. One reform vocabulary: the same {parameter_path: value} dict as - `macromod population-impact`. Every result carries a common `score` - block for cross-model comparison (`macromod compare`). For raw OBR - variable shocks in model units, use `macromod obr-shock`. + `pe-macro population-impact`. Every result carries a common `score` + block for cross-model comparison (`pe-macro compare`). For raw OBR + variable shocks in model units, use `pe-macro obr-shock`. """ try: res = core.score_reform( @@ -98,7 +98,7 @@ def _echo_score_block(score: dict) -> None: @click.option("--country", type=click.Choice(["uk", "us"]), default="uk", show_default=True) @click.option("--reform", required=True, - help='PolicyEngine reform JSON (same shape as `macromod score`).') + help='PolicyEngine reform JSON (same shape as `pe-macro score`).') @click.option("--models", default="microsim,obr", show_default=True, help="Comma-separated scoring models (og, obr, microsim).") @click.option("--year", default=2026, show_default=True, help="Reform start year.") @@ -142,7 +142,7 @@ def compare(country, reform, models, year, as_json): @main.command("obr-shock") -@click.option("--var", required=True, help="Policy variable to shock (see `macromod variables`).") +@click.option("--var", required=True, help="Policy variable to shock (see `pe-macro variables`).") @click.option("--shock", required=True, type=float, help="Shock size; units depend on the variable (£m/quarter for CGG, decimal for TCPRO).") @click.option("--periods", default=12, show_default=True, help="Quarters the shock is applied.") diff --git a/integration/src/macromod/core.py b/integration/src/policyengine_macro/core.py similarity index 99% rename from integration/src/macromod/core.py rename to integration/src/policyengine_macro/core.py index 8b906d2..46edd8a 100644 --- a/integration/src/macromod/core.py +++ b/integration/src/policyengine_macro/core.py @@ -662,6 +662,8 @@ def _delta(b, r): def _pe_pop_data_folder() -> str: import os + # MACROMOD_PE_DATA_DIR env var and ~/.cache/macromod path keep their + # legacy names deliberately (the hosted Modal volume is mounted there). return os.environ.get( "MACROMOD_PE_DATA_DIR", os.path.expanduser("~/.cache/macromod/policyengine-data"), diff --git a/integration/src/macromod/mcp_server.py b/integration/src/policyengine_macro/mcp_server.py similarity index 98% rename from integration/src/macromod/mcp_server.py rename to integration/src/policyengine_macro/mcp_server.py index c747f7f..3ed4bb3 100644 --- a/integration/src/macromod/mcp_server.py +++ b/integration/src/policyengine_macro/mcp_server.py @@ -1,17 +1,17 @@ """PolicyEngine Macro MCP server (stdio transport). -Run with: python -m macromod.mcp_server +Run with: python -m policyengine_macro.mcp_server -Exposes the same adapter functions as the `macromod` CLI as MCP tools. +Exposes the same adapter functions as the `pe-macro` CLI as MCP tools. """ from __future__ import annotations from mcp.server.fastmcp import FastMCP -from macromod import core +from policyengine_macro import core -mcp = FastMCP("macromod") +mcp = FastMCP("policyengine-macro") @mcp.tool() @@ -41,7 +41,7 @@ def score_reform( general-equilibrium comparison; the reform enters through PolicyEngine-estimated tax functions. VERY SLOW (tens of minutes) and excluded from the hosted server — run it - locally via `macromod score --model og`; calling it here + locally via `pe-macro score --model og`; calling it here returns install/CLI instructions. 'obr' — OBR macroeconometric emulator via the microsim static-costing bridge: the reform is costed per year with diff --git a/integration/tests/conftest.py b/integration/tests/conftest.py index 33b5347..9b0d279 100644 --- a/integration/tests/conftest.py +++ b/integration/tests/conftest.py @@ -1,4 +1,4 @@ -"""Shared pytest configuration for the macromod integration tests. +"""Shared pytest configuration for the policyengine-macro integration tests. Tests marked `slow` run a real model estimation/solve or a full PolicyEngine import (seconds to many minutes). They are skipped by default so a plain diff --git a/integration/tests/test_contract.py b/integration/tests/test_contract.py index acaf4bb..4fbbc31 100644 --- a/integration/tests/test_contract.py +++ b/integration/tests/test_contract.py @@ -17,7 +17,7 @@ import pytest -from macromod import core +from policyengine_macro import core def _import_or_require(modname: str): diff --git a/integration/tests/test_core.py b/integration/tests/test_core.py index b138061..cd3b872 100644 --- a/integration/tests/test_core.py +++ b/integration/tests/test_core.py @@ -1,10 +1,10 @@ -"""Unit tests for the macromod adapters (small draws to keep runtime low).""" +"""Unit tests for the policyengine_macro adapters (small draws to keep runtime low).""" import json import pytest -from macromod import core +from policyengine_macro import core def test_list_variables(): @@ -158,7 +158,7 @@ def test_summary_without_boe_var_uses_env_checkout(monkeypatch, tmp_path): def test_cli_summary_without_boe_var_errors_actionably(monkeypatch): from click.testing import CliRunner - from macromod.cli import main + from policyengine_macro.cli import main _block_boe_var(monkeypatch) monkeypatch.delenv("MACROMOD_BOE_VAR_REPO", raising=False) @@ -172,7 +172,7 @@ def test_cli_summary_env_checkout_missing_files_errors(monkeypatch, tmp_path): empty headings and exit 0 (round-2 review, new finding 1).""" from click.testing import CliRunner - from macromod.cli import main + from policyengine_macro.cli import main _block_boe_var(monkeypatch) monkeypatch.setenv("MACROMOD_BOE_VAR_REPO", str(tmp_path)) diff --git a/integration/tests/test_mcp_server.py b/integration/tests/test_mcp_server.py index 94b7d94..308f304 100644 --- a/integration/tests/test_mcp_server.py +++ b/integration/tests/test_mcp_server.py @@ -8,7 +8,7 @@ from mcp import ClientSession, StdioServerParameters from mcp.client.stdio import stdio_client -SERVER = StdioServerParameters(command=sys.executable, args=["-m", "macromod.mcp_server"]) +SERVER = StdioServerParameters(command=sys.executable, args=["-m", "policyengine_macro.mcp_server"]) EXPECTED_TOOLS = { "score_reform", diff --git a/integration/tests/test_og.py b/integration/tests/test_og.py index 20b8cba..01d36c2 100644 --- a/integration/tests/test_og.py +++ b/integration/tests/test_og.py @@ -7,7 +7,7 @@ deployment image (see modal_app.py). oguk 0.3.0 calibrates its tax functions from the PolicyEngine enhanced-FRS microdata and pins policyengine-uk==2.88.0; policyengine-uk >= 2.89 renamed the dataset keys (enhanced_frs_2023_24_* -> -populace_uk_*), so calibration KeyError-fails under a newer PE. The macromod +populace_uk_*), so calibration KeyError-fails under a newer PE. The policyengine-macro package itself requires policyengine[models]>=4 (which brings pe-uk >= 2.89 for the household/population tools), so a single env cannot satisfy both. The real end-to-end solve is therefore skipped when the installed PE is incompatible @@ -20,7 +20,7 @@ import pytest -from macromod import core +from policyengine_macro import core def _oguk_calibration_skip_reason(): diff --git a/integration/tests/test_population.py b/integration/tests/test_population.py index e21dd1e..7a8d85d 100644 --- a/integration/tests/test_population.py +++ b/integration/tests/test_population.py @@ -14,7 +14,7 @@ import pytest -from macromod import core +from policyengine_macro import core # --------------------------------------------------------------------------- diff --git a/integration/tests/test_remote_mcp.py b/integration/tests/test_remote_mcp.py index 10ba955..7eb73a8 100644 --- a/integration/tests/test_remote_mcp.py +++ b/integration/tests/test_remote_mcp.py @@ -21,7 +21,7 @@ URL = os.environ.get( "MACROMOD_REMOTE_URL", - "https://policyengine--macromod-mcp-serve.modal.run/mcp", + "https://policyengine--policyengine-macro-mcp-serve.modal.run/mcp", ) EXPECTED_TOOLS = { diff --git a/integration/tests/test_score_schema.py b/integration/tests/test_score_schema.py index 0759d1c..3df3e00 100644 --- a/integration/tests/test_score_schema.py +++ b/integration/tests/test_score_schema.py @@ -13,8 +13,8 @@ import pytest -from macromod import core -from macromod.cli import main +from policyengine_macro import core +from policyengine_macro.cli import main # --------------------------------------------------------------------------- diff --git a/integration/tests/test_wiring.py b/integration/tests/test_wiring.py index 7b7e6c9..0ac77e4 100644 --- a/integration/tests/test_wiring.py +++ b/integration/tests/test_wiring.py @@ -19,8 +19,8 @@ import pytest from click.testing import CliRunner -from macromod import core, mcp_server -from macromod.cli import main +from policyengine_macro import core, mcp_server +from policyengine_macro.cli import main # The full tool surface the server must expose (README + mcp_server.py). EXPECTED_TOOLS = { diff --git a/olg/index.html b/olg/index.html index 8e7d2c8..63cb1ae 100644 --- a/olg/index.html +++ b/olg/index.html @@ -362,14 +362,14 @@

Where it plugs in today.

- OG-UK is wired into the macromod CLI as two commands: + OG-UK is wired into the PolicyEngine Macro CLI as two commands: og-baseline (baseline steady state, model units) and og-score (score a PolicyEngine reform — one or more parameter changes, baseline + reform steady states mapped to £bn). Both use the fast configuration: pooled tax functions, single sector, steady state only.

-
macromod og-baseline
-macromod og-score --reform '{"gov.hmrc.income_tax.rates.uk[0].rate": 0.21}'
+
pe-macro og-baseline
+pe-macro og-score --reform '{"gov.hmrc.income_tax.rates.uk[0].rate": 0.21}'

Two prerequisites, stated plainly. First, the calibration needs the PolicyEngine enhanced-FRS microdata, which is gated: diff --git a/pe/index.html b/pe/index.html index cff7a03..dfc328c 100644 --- a/pe/index.html +++ b/pe/index.html @@ -212,7 +212,7 @@

What it answers — and what it doesn't yet.