retroenv / README.md
AdithyaSK's picture
AdithyaSK HF Staff
Serve RetroEnv snapshot 350672d1218e
f56bbf4 verified
|
Raw History Blame Contribute Delete
7.55 kB
metadata
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

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