SDK + CLI (mrg) for local synth/PnR feedback, sandboxed agents, and running FPGA designs on real silicon.
Project description
Manhattan Reasoning Gym
Python SDK and CLI (mrg) for hardware design: iterate locally with fast
synthesis / place-and-route feedback, run untrusted agents in a sandbox, and
program real cloud FPGAs — all from one package.
| Surface | What it does | Needs |
|---|---|---|
mrg.build |
local synth / place-and-route reports (no board) | Docker |
mrg.Sandbox |
run an agent in a locked container that promotes to silicon | Docker |
mrg.cloud |
program + drive a real ECP5 over the cloud | API key |
Cloud FPGA access requires an allowlisted API key (
mrg login). Local synth/PnR and sandboxing need only Docker — no toolchain to install.
Install
pip install manhattan-reasoning-gym
For local synth/PnR and sandboxing, pull the pinned toolchain image once (the SDK runs it for you — you never install yosys/nextpnr/LiteX):
docker pull ghcr.io/manhattanreasoning/mrg-sandbox:latest
If your system Python is broken or externally managed, the robust path is
uv venv && uv pip install manhattan-reasoning-gym.
Agent quickstart (headless)
Copy-paste path for coding agents and CI — no browser, no prompts:
export MRG_API_KEY=... # or: mrg login --github-token <no-scope PAT>
mrg synth examples/design.py # JSON report on stdout, ~seconds
mrg pnr examples/design.py # JSON report (fmax_mhz, timing_met), ~tens of seconds
mrg run examples/app.py # programs a real FPGA, ~2-3 min first time
mrg reset 0 # free the board when done (async, ~1 min)
- All bus addresses are byte addresses: word n in your design's register
map is byte address
4*nforapp.read/app.writeandmrg read/mrg write. - After
mrg runthe board stays reserved to you — reuse it withmrg run --no-program/mrg read/mrg write, and release it withmrg reset <fpga_id>when done. Reset is an async queued job that reflashes the base SoC (~1 min); pollmrg status --jsonrather than expecting instantidle. mrg synth/mrg pnrprint machine-readable JSON on stdout and exit non-zero when the build fails;mrg status --jsonandmrg job --jsonemit the raw orchestrator response — parse JSON, don't scrape tables.- Rough time budget:
synthseconds ·pnrtens of seconds ·mrg run~2-3 min for the first program ·--no-programseconds ·reset~1 min. mrg loginwith no flags is an interactive GitHub device flow. Headless, prefer$MRG_API_KEY, ormrg login --github-token <PAT>(also reads$GITHUB_TOKEN) with a no-scope PAT — it's only used to read your username. If you must run the device flow from a non-tty context, relay the printed URL + code to a human.
Local feedback — mrg.build
import manhattan_reasoning_gym as mrg
rep = mrg.build.synth("examples/design.py") # fast: resource utilization
rep = mrg.build.pnr("examples/design.py") # full SoC: real Fmax + timing
print(rep.fmax_mhz, rep.timing_met, rep.util.dsp.used)
CLI: mrg synth design.py · mrg pnr design.py [--target-mhz N].
synth is the cheap "is it getting bigger?" signal (core-only util); pnr
places & routes the full SoC for the truthful system-clock Fmax and timing.
Sandboxed agents — mrg.Sandbox
Run untrusted agent code in a locked container (--network none, no key) that
iterates locally and promotes vetted candidates to silicon:
import manhattan_reasoning_gym as mrg
result = mrg.Sandbox(files=["examples/design.py", "examples/agent.py"]).run("agent.py")
for promo in result.promotions:
print(promo)
The container never holds a key or touches the network; the promote is a file
handoff the trusted host brokers to silicon. No promote gating by default —
the agent gates itself; pass guard= for an opt-in host-side check. With no API
key, silicon resolves to a no-op backend; set one (and silicon="cloud") for
real hardware.
Real silicon — mrg.cloud
import manhattan_reasoning_gym as mrg
app = mrg.cloud.App("my_design", design="examples/design.py")
with app: # programs the FPGA on first use, releases on exit
app.write(0x0, 3) # byte address: word n in the design => 4*n
print("read:", app.read(0x8))
Or drive it from the CLI with a file that defines an App and an
@app.local_entrypoint() — see examples/app.py, which
exercises the example MAC design end-to-end:
mrg run examples/app.py
Name your registers by subclassing mrg.RegisterMap (byte addresses).
Board lifecycle: the with app: form releases the board on exit, but
mrg run does not — the reservation persists so you can keep iterating
with mrg run --no-program, mrg read, and mrg write. Free the board with
mrg reset <fpga_id> (an async job that reflashes the base SoC, ~1 min — poll
mrg status).
Authentication (cloud only)
API requests authenticate with an opaque API key sent as X-API-Key. You obtain
one by exchanging a GitHub identity — the orchestrator verifies you against its
allowlist and mints a key.
mrg login # GitHub device flow (or paste a no-scope PAT), then you're set
The key is saved to ~/.config/mrg/credentials.json (mode 0600, keyed by
orchestrator URL). Every command resolves the key in this order:
--api-keyflag$MRG_API_KEY- the stored login (
mrg login)
mrg logout revokes the key on the server and clears it locally.
Tutorials
Two runnable notebooks in examples/notebooks/:
01_build_and_run— local synth/pnr feedback → program a real FPGA.02_sandboxing— run an agent in a locked sandbox that promotes to silicon.
The shared examples/design.py is a tiny tunable multiply-accumulate; tweak its
WIDTH and watch DSP/Fmax move. examples/agent.py is the sandboxed agent, and
examples/app.py is the mrg run app that drives the MAC on real silicon.
CLI reference
mrg login | logout
mrg synth <design.py> [--target-mhz N] # local synthesis report
mrg pnr <design.py> [--target-mhz N] # local full-SoC place & route
mrg run <file.py> [--fpga-id N] [--no-program]
mrg status [fpga_id] [--json]
mrg job <fpga_id> <job_id> [--json]
mrg logs | cancel <fpga_id> <job_id>
mrg reset <fpga_id>
mrg read <fpga_id> <addr> [--count N]
mrg write <fpga_id> <addr> <value>
Development
pip install -e ".[dev]"
ruff check . && pytest -q
License
MIT — see LICENSE.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file manhattan_reasoning_gym-0.1.2.tar.gz.
File metadata
- Download URL: manhattan_reasoning_gym-0.1.2.tar.gz
- Upload date:
- Size: 39.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c4d75b2a3bfe3cb463ba25ef668370dacd034dc7201e7d7967057c5b41754691
|
|
| MD5 |
73dae2c9cafda5bdaf57b6241a3c1ef2
|
|
| BLAKE2b-256 |
f6b619a8be0d5b439c21ed3e29896c75c016cd5b5a5084192f9c56d77e16ed20
|
Provenance
The following attestation bundles were made for manhattan_reasoning_gym-0.1.2.tar.gz:
Publisher:
publish.yml on ManhattanReasoning/manhattan-reasoning-gym
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
manhattan_reasoning_gym-0.1.2.tar.gz -
Subject digest:
c4d75b2a3bfe3cb463ba25ef668370dacd034dc7201e7d7967057c5b41754691 - Sigstore transparency entry: 2043240963
- Sigstore integration time:
-
Permalink:
ManhattanReasoning/manhattan-reasoning-gym@578811288cb9c6d7bb97737d923a4f3b05dc6b08 -
Branch / Tag:
refs/tags/v0.1.2 - Owner: https://github.com/ManhattanReasoning
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@578811288cb9c6d7bb97737d923a4f3b05dc6b08 -
Trigger Event:
push
-
Statement type:
File details
Details for the file manhattan_reasoning_gym-0.1.2-py3-none-any.whl.
File metadata
- Download URL: manhattan_reasoning_gym-0.1.2-py3-none-any.whl
- Upload date:
- Size: 36.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
04ee95219639b1a29589e0e035014a483576df8cbd99269c58306fd0f2f6e757
|
|
| MD5 |
a2c532a283fa3b5bb5e4c555c7550891
|
|
| BLAKE2b-256 |
5356a2fc498dd85ba1ae8315b0d424019d1be5230aa55a5bab04f36168ecfbe7
|
Provenance
The following attestation bundles were made for manhattan_reasoning_gym-0.1.2-py3-none-any.whl:
Publisher:
publish.yml on ManhattanReasoning/manhattan-reasoning-gym
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
manhattan_reasoning_gym-0.1.2-py3-none-any.whl -
Subject digest:
04ee95219639b1a29589e0e035014a483576df8cbd99269c58306fd0f2f6e757 - Sigstore transparency entry: 2043241033
- Sigstore integration time:
-
Permalink:
ManhattanReasoning/manhattan-reasoning-gym@578811288cb9c6d7bb97737d923a4f3b05dc6b08 -
Branch / Tag:
refs/tags/v0.1.2 - Owner: https://github.com/ManhattanReasoning
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@578811288cb9c6d7bb97737d923a4f3b05dc6b08 -
Trigger Event:
push
-
Statement type: