Download README.md from FineEnvs/retroenv: direct link, hf CLI and curl.
- Browser
- Download file 7.55 kB
-
https://huggingface.co/spaces/FineEnvs/retroenv/resolve/main/README.md
- Command line
-
hf download hf://spaces/FineEnvs/retroenv/README.md
-
curl -L -o README.md https://huggingface.co/spaces/FineEnvs/retroenv/resolve/main/README.md
title: RetroEnv
emoji: ⚗️
colorFrom: gray
colorTo: green
sdk: docker
app_port: 8000
base_path: /web
pinned: false
tags:
- openenv
- chemistry
- retrosynthesis
- tool-use
datasets:
- LiteFold/RetroEnv
short_description: Plan retrosynthesis routes with verifier-backed rewards.
RetroEnv: OpenEnv server
An agent gets a target molecule and works back to purchasable starting materials. It uses tools to inspect molecules, search a fixed stock and training precedents, and check disconnections. It ends the episode by submitting molecule/reaction trees with emit_routes, and the verifier judges every step against a frozen reaction library; matching the patent route only adds a bonus.
The same server is used for training, evaluation and the browser playground at /web.
Read the code
retroenv_openenv/environment.py: the MCP tools, the Task API,reset, and the terminal reward.retroenv_openenv/client.py:RetroEnvClientfor one episode per WebSocket session, andRemoteRetroRouteEnvfor TRL'senvironment_factory.retroenv_openenv/agent.py,agent_anthropic.py,agent_responses.py: model loops for OpenAI-compatible chat, the Claude Messages API and the OpenAI Responses API.retroenv_openenv/ui.py: the playground.../core/retroenv: chemistry, stock search, tasks and the verifier. Every front-end shares it.
The episode
| Step | What happens |
|---|---|
reset(split, index) |
Returns the prompt, target, depth budget, route count, constraints and tool names. It never returns known routes, patents or the stock list. |
| tool calls | MCP call_tool. Up to 32 calls per episode (RETROENV_MAX_TOOL_CALLS); emit_routes always stays available. |
emit_routes(submission) |
Scores the trees. That step returns done=True with the dense reward, once. |
| Tool | Answers from |
|---|---|
inspect_molecule, pubchem_lookup |
RDKit on the given SMILES |
stock_retrieve |
The task's stock (excluded building blocks absent): exact, InChIKey, class, SMARTS or similarity search, capped at 20 results |
reaction_precedent_search, search_literature, reaction_conditions_search |
Train-visible corpus reactions only |
validate_disconnection |
Train-visible reactions and frequent templates, never this task's hidden routes. Removed by RETROENV_TOOLSET=unaided. |
reaction_class_lookup |
The verifier's own reaction classifier |
emit_routes |
The verifier |
The Task API lists splits and public task rows without references: GET /retro_route/splits, and POST /retro_route/task, /tasks, /task_range and /num_tasks. The API docs are at /docs.
Data and credits
The tasks, known routes, difficulty labels, stock and reaction library are LiteFold/RetroEnv, the dataset built and released by LiteFold. This server serves it unchanged.
It derives from PaRoutes v2: S. Genheden and E. Bjerrum, PaRoutes: towards a framework for benchmarking retrosynthesis route predictions, Digital Discovery 2022 (doi:10.1039/D2DD00015F); data on Zenodo 7341155, CC BY 4.0. PaRoutes takes its reactions from D. Lowe, Chemical reactions from US patents (1976 to Sep 2016) (figshare), CC0. Software: RDKit, rdchiral, 3Dmol.js and OpenEnv.
Data and splits
The server serves a pinned snapshot of LiteFold/RetroEnv
from the public bucket FineEnvs/retroenv-bucket.
corpus-manifest.json, beside the server code, pins it: the release revision, the serving index and the
SHA-256 of every file a server reads.
| Split | Tasks | Use |
|---|---|---|
train |
75,224 | RL rollouts |
dev |
1,079 | checkpoint selection |
test_id |
1,083 | unseen targets, training distribution |
test_hard |
1,119 | longer, novel or rarer chemistry |
final_eval |
50 | 17 easy, 17 medium, 16 hard from the two test splits (dataset/build_final_eval.py) |
final_eval names tasks of the test splits, so they score exactly as there.
Run locally
From the project folder (09-retroenv):
uv sync --extra dev --extra eval
uv run bash envs/retro_route/openenv/start.sh
start.sh runs prepare.py, which downloads the pinned snapshot from the bucket (about 115 MB,
once), checks every file's SHA-256, and starts the server; a restart reuses the checked files.
Startup then takes about 10 s. To serve a local release directory without an index instead:
RETROENV_BENCHMARK_DIR=data/release/RetroEnv-RL uv run uvicorn retroenv_openenv.server:app --port 8000
Open http://localhost:8000/web/ for the playground. Its Plan tab works the way the task does: pick
the molecule still to make, take a cut the rule library proposes (or type one), check it and its
pieces with the real tools, and add it to a route drawn as it grows; "Fill from the plan" turns that
route into the emit_routes submission. Each molecule is drawn in 2D and, from an RDKit conformer,
in 3D (vendored 3Dmol.js, so it works offline). To run one model
episode against the server:
uv run python envs/retro_route/openenv/rollout.py --server http://127.0.0.1:8000 \
--split final_eval --index 0 --provider anthropic --model claude-opus-5-5
Docker and Spaces
The image holds code and corpus-manifest.json only. On a Space the bucket is mounted read-only at
/data and prepare.py copies the snapshot from there; with nothing mounted it downloads it.
python deploy.py --stage-only --stage-dir /tmp/retroenv-space
docker build -t retroenv /tmp/retroenv-space
docker run --rm -p 8000:8000 retroenv # downloads the pinned snapshot
FineEnvs/retroenv is deployed with:
python deploy.py --repo FineEnvs/retroenv --public
deploy.py refuses to deploy unless every file the manifest names is already in the bucket, mounts
the bucket, and sets the variables below. A new snapshot comes from dataset/publish_bucket.py,
which also rewrites corpus-manifest.json.
| Variable | Default | Meaning |
|---|---|---|
RETROENV_CORPUS_MANIFEST |
corpus-manifest.json here |
The serving snapshot to fetch and check |
RETROENV_BUCKET_ROOT |
/data in the image |
A mounted copy of the bucket; files missing there are downloaded |
RETROENV_BUCKET_ID |
the manifest's | The bucket to download from, if it was renamed |
RETROENV_PREPARED_DIR |
prepared |
Where the checked snapshot is kept between restarts |
RETROENV_BENCHMARK_DIR |
none | A local release or prepared directory, used as is |
RETROENV_TASKS_REPO |
none | A release dataset org/name[@revision], served without an index |
RETROENV_TOOLSET |
full |
unaided removes validate_disconnection (an ablation) |
RETROENV_MAX_TOOL_CALLS |
32 |
Tool budget per episode |
RETROENV_DEFAULT_SPLIT |
train |
Split used by a reset that names none |
MAX_CONCURRENT_ENVS |
64 |
WebSocket sessions per process |
ENABLE_WEB_INTERFACE |
true |
Mount the playground at /web |