Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .github/workflows/test-integration.yml
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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.
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/validate-deployment.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
20 changes: 10 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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.

Expand Down Expand Up @@ -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
Expand Down
24 changes: 12 additions & 12 deletions connect/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,7 @@ <h1 class="reveal page-title" style="--d:1;">
<span class="step-n mono">1</span>
<h3>Copy the PolicyEngine Macro URL</h3>
<p>Click the copy button — you'll paste it in the next step.</p>
<div class="copyblock"><code>https://policyengine--macromod-mcp-serve.modal.run/mcp</code><button data-copy>copy</button></div>
<div class="copyblock"><code>https://policyengine--policyengine-macro-mcp-serve.modal.run/mcp</code><button data-copy>copy</button></div>
</div>
<div class="step">
<span class="step-n mono">2</span>
Expand All @@ -240,7 +240,7 @@ <h3>Connect and ask</h3>
<div class="step" style="grid-column: 1 / -1;">
<h3>Using Claude Code?</h3>
<p>One command adds the same server to Claude Code:</p>
<div class="copyblock"><code>claude mcp add --transport http macromod https://policyengine--macromod-mcp-serve.modal.run/mcp</code><button data-copy>copy</button></div>
<div class="copyblock"><code>claude mcp add --transport http policyengine-macro https://policyengine--policyengine-macro-mcp-serve.modal.run/mcp</code><button data-copy>copy</button></div>
</div>
</div>

Expand All @@ -249,7 +249,7 @@ <h3>Using Claude Code?</h3>
<span class="step-n mono">1</span>
<h3>Copy the PolicyEngine Macro URL</h3>
<p>Click the copy button — you'll paste it in the next step.</p>
<div class="copyblock"><code>https://policyengine--macromod-mcp-serve.modal.run/mcp</code><button data-copy>copy</button></div>
<div class="copyblock"><code>https://policyengine--policyengine-macro-mcp-serve.modal.run/mcp</code><button data-copy>copy</button></div>
</div>
<div class="step">
<span class="step-n mono">2</span>
Expand All @@ -268,7 +268,7 @@ <h3>Connect and ask</h3>
<div class="step" style="grid-column: 1 / -1;">
<h3>Using Codex?</h3>
<p>One command adds the same server to OpenAI's Codex CLI:</p>
<div class="copyblock"><code>codex mcp add macromod --url https://policyengine--macromod-mcp-serve.modal.run/mcp</code><button data-copy>copy</button></div>
<div class="copyblock"><code>codex mcp add policyengine-macro --url https://policyengine--policyengine-macro-mcp-serve.modal.run/mcp</code><button data-copy>copy</button></div>
</div>
</div>

Expand All @@ -286,13 +286,13 @@ <h3>Using Codex?</h3>
<span class="step-n mono">1</span>
<h3>Install</h3>
<p>Python 3.11+. One package wraps the OBR emulator, the UK SVAR, and PolicyEngine behind a single command — models and data included.</p>
<div class="copyblock"><code>pip install "macromod[models] @ git+https://github.com/PolicyEngine/macro#subdirectory=integration"</code><button data-copy>copy</button></div>
<div class="copyblock"><code>pip install "policyengine-macro[models] @ git+https://github.com/PolicyEngine/macro#subdirectory=integration"</code><button data-copy>copy</button></div>
</div>
<div class="step">
<span class="step-n mono">2</span>
<h3>Ask Claude Code</h3>
<p>Claude Code runs the CLI from its terminal — just describe the reform, or run it yourself:</p>
<div class="copyblock"><code>macromod obr-shock --var CGG --shock 1250 --periods 4</code><button data-copy>copy</button></div>
<div class="copyblock"><code>pe-macro obr-shock --var CGG --shock 1250 --periods 4</code><button data-copy>copy</button></div>
</div>
<div class="step">
<span class="step-n mono">3</span>
Expand All @@ -306,13 +306,13 @@ <h3>Read the results</h3>
<span class="step-n mono">1</span>
<h3>Install</h3>
<p>Python 3.11+. One package wraps the OBR emulator, the UK SVAR, and PolicyEngine behind a single command — models and data included.</p>
<div class="copyblock"><code>pip install "macromod[models] @ git+https://github.com/PolicyEngine/macro#subdirectory=integration"</code><button data-copy>copy</button></div>
<div class="copyblock"><code>pip install "policyengine-macro[models] @ git+https://github.com/PolicyEngine/macro#subdirectory=integration"</code><button data-copy>copy</button></div>
</div>
<div class="step">
<span class="step-n mono">2</span>
<h3>Ask Codex</h3>
<p>Codex runs the CLI from its terminal — just describe the reform, or run it yourself:</p>
<div class="copyblock"><code>macromod obr-shock --var CGG --shock 1250 --periods 4</code><button data-copy>copy</button></div>
<div class="copyblock"><code>pe-macro obr-shock --var CGG --shock 1250 --periods 4</code><button data-copy>copy</button></div>
</div>
<div class="step">
<span class="step-n mono">3</span>
Expand All @@ -322,7 +322,7 @@ <h3>Read the results</h3>
</div>

<hr class="panel-divider" />
<p class="soon-hint" style="max-width: none;">The <code class="mono">macromod</code> CLI installs straight from GitHub — one <code class="mono">pip install</code> pulls the CLI plus all three models with their data, no cloning required. It lives in this repo's <a href="https://github.com/PolicyEngine/macro/tree/main/integration"><code class="mono">integration/</code></a> directory; a shorter <code class="mono">pip install macromod</code> will come with PyPI publication.</p>
<p class="soon-hint" style="max-width: none;">The <code class="mono">pe-macro</code> CLI installs straight from GitHub — one <code class="mono">pip install</code> pulls the CLI plus all three models with their data, no cloning required. It lives in this repo's <a href="https://github.com/PolicyEngine/macro/tree/main/integration"><code class="mono">integration/</code></a> directory; a shorter <code class="mono">pip install policyengine-macro</code> will come with PyPI publication.</p>
<p class="panel-foot mono" style="color: var(--paper-faint);">
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).
</p>
Expand Down Expand Up @@ -668,9 +668,9 @@ <h4>Sector output, long-run</h4>
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: <code>macromod forecast</code>,
<code>macromod shocks</code>, <code>macromod summary</code>.
and sign restrictions name six structural shocks. The PolicyEngine Macro CLI
wraps the common outputs: <code>pe-macro forecast</code>,
<code>pe-macro shocks</code>, <code>pe-macro summary</code>.
</p>

<div class="dials">
Expand Down
50 changes: 25 additions & 25 deletions integration/README.md
Original file line number Diff line number Diff line change
@@ -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 —
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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}'
```

Expand All @@ -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
Expand All @@ -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+)
Expand All @@ -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;
Expand All @@ -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),
Expand Down
Loading
Loading