SynCraft Skill
May 25, 2026 · View on GitHub
Edit unsynthesizable drug candidates into synthesizable, target-engaging analogs.
A self-contained Anthropic Agent Skill implementing the SynCraft pipeline from:
Li, Junren et al. "SynCraft: Guiding Large Language Models to Predict Edit Sequences for Molecular Synthesizability Optimization." Nature Machine Intelligence (2026, under revision).
What it does
You give it:
- a list of computationally-generated SMILES (from Pocket2Mol, ResGen, TargetDiff, DiffSBDD, LIGAN, AR, or any other source)
- a protein receptor (PDB id or
.pdbfile)
It returns:
- the same molecules, edited to be synthesizable, with Vina docking re-validated against the receptor pocket, ranked by binding strength × similarity.
INPUT OUTPUT
───────────────── ─────────────────────────────────
list[SMILES] (generated) ─→ list[EditResult]
PDB id or .pdb file ─→ ├── original / edited SMILES
├── original / edited Vina score
├── Tanimoto similarity
├── edit operations
└── LLM reasoning
Why
Generative models for structure-based drug design (Pocket2Mol, TargetDiff, …) routinely produce molecules that score well in docking but cannot be synthesized — they sit on the "synthesis cliff". SynCraft uses a reasoning LLM (Gemini-2.5-Pro, Gemini-3.1-Pro, or DeepSeek-V4-Pro [open-weight, MIT licensed]) to predict minimal atom-level edits that move each molecule off the cliff while preserving its binding interactions.
This skill is the reproducible recipe version of the paper.
Quick start
# 1. clone + install
git clone https://github.com/jrli/syncraft-skill
cd syncraft-skill
pip install -e .[all]
# 2. set up endpoint (one of)
export DSV4_BASE_URL="http://localhost:8443/ds-eval" # local vLLM for deepseek-v4-pro
export DSV4_API_KEY="..."
# or:
export GEMINI_API_KEY="..." # for gemini-*-pro
# 3. run
python -c "
from syncraft import edit_for_synthesizability
results = edit_for_synthesizability(
molecules=['NC(=O)[C@@H]1CCC(C(=O)N[C@H]2OC(=O)[C@@H](O)[C@@H](O)[C@H]2NNC2C=CC=CC=C2Cl)C1'],
protein_pdb='7L12', # SARS-CoV-2 Mpro
rescue_llm='deepseek-v4-pro',
pass_k=5,
)
print(results[0])
"
Pipeline
┌──────────────┐
input ─→│ 1. Vina dock │─→ original pose + Vina score
└──────┬───────┘
▼
┌──────────────┐
│ 2. PLIP │─→ source interaction profile
└──────┬───────┘
▼
┌────────────────────────┐
│ 3. LLM edit prediction │─→ {DEL_ATOM, ADD_BOND, …} × pass_k
│ (preserves PLIP) │
└──────┬─────────────────┘
▼
┌──────────────────┐
│ 4. Vina re-dock │─→ edited pose + Vina score
└──────┬───────────┘
▼
list[EditResult] ← ranked best-of-K per source
Supported reasoning LLMs
| Model | License | Local | Default? |
|---|---|---|---|
gemini-2.5-pro | API only | no | paper headline |
gemini-3.1-pro | API only | no | |
deepseek-v4-pro | MIT (open weights) | yes | ✓ default (reproducibility) |
Configuring the LLM endpoint
The skill never bundles model weights. You pick which backend to use via the
rescue_llm argument and expose the corresponding endpoint through environment
variables.
Option A — Gemini (closed API)
rescue_llm="gemini-2.5-pro" or "gemini-3.1-pro". Get an API key from
Google AI Studio and set:
export GEMINI_API_KEY="sk-..."
That is the only configuration step; the skill will route through litellm.
Option B — DeepSeek-V4-Pro (open-weight, self-hosted)
rescue_llm="deepseek-v4-pro" is the default because the weights are MIT and
guarantee long-term reproducibility, but you have to serve them yourself —
the skill talks to an OpenAI-compatible HTTP endpoint, not to a hosted service.
1. Pull the weights:
huggingface-cli download deepseek-ai/DeepSeek-V4-Pro --local-dir ./dsv4-pro
2. Serve them with vLLM (or SGLang / TensorRT-LLM — any OpenAI-compatible server works). Pick a reasoning-mode-aware serve command; example with vLLM:
vllm serve ./dsv4-pro \
--port 8443 \
--served-model-name deepseek-v4-pro \
--tensor-parallel-size 8 \
--enable-reasoning \
--reasoning-parser deepseek-r1
This requires a node with enough VRAM (typically 8×H100 80 GB for the Pro variant; the lighter Flash variant fits on 1×H100).
3. Point the skill at it:
export DSV4_BASE_URL="http://localhost:8443/v1" # or wherever vLLM is bound
export DSV4_API_KEY="any-non-empty-string" # vLLM ignores the value
export NO_PROXY="127.0.0.1,localhost" # bypass corporate HTTP_PROXY
The endpoint is hit through openai.OpenAI(base_url=DSV4_BASE_URL, api_key=DSV4_API_KEY)
with HTTP-identity encoding and an exponential-backoff retry; see
src/inference_dsv4.py for the exact client
configuration used by the paper runs.
If you do not have a node available to serve the open weights and just want to evaluate the skill on a small batch, the simplest path is to switch the default to Gemini:
results = edit_for_synthesizability(..., rescue_llm="gemini-2.5-pro")
Examples
examples/quickstart.py— 20-line minimum invocationexamples/01_mpro_targetdiff.ipynb— 47 TargetDiff candidates → Mpro (7L12)examples/02_kinase_p2m.ipynb— Pocket2Mol candidates → RIPK1 (7YDX)
Outputs
output_dir/
├── results.json full structured EditResult list
├── results.csv tabular view
├── summary.md human-readable top-10 summary
├── poses_originals/ original docked poses (PDB)
└── poses_edits/ edited docked poses (PDB)
Optional but recommended: RDKit SA_Score
Stage 4 of the pipeline ranks candidate edits using the RDKit-contrib
synthesizability score (sascorer.calculateScore). The conda-forge RDKit
package ships sascorer.py under <env>/share/RDKit/Contrib/SA_Score/; the
skill auto-discovers it there (and at the pip-wheel layout
site-packages/rdkit/Contrib/SA_Score/ for pip install rdkit-pypi). If
sascorer is found nowhere, the skill logs a warning and falls back to
ranking by Vina-gain alone — results are still returned and still usable, just
not SA-prioritised.
If your install does not include it and you want SA-first ranking, drop the file in place manually:
# conda-forge layout
curl -L https://raw.githubusercontent.com/rdkit/rdkit/master/Contrib/SA_Score/sascorer.py \
-o "$CONDA_PREFIX/share/RDKit/Contrib/SA_Score/sascorer.py"
curl -L https://raw.githubusercontent.com/rdkit/rdkit/master/Contrib/SA_Score/fpscores.pkl.gz \
-o "$CONDA_PREFIX/share/RDKit/Contrib/SA_Score/fpscores.pkl.gz"
Limitations (v0.1)
- Requires pre-prepared
<PDBID>_clean.pdbqt+<PDBID>_config.txt+<PDBID>_analysis.pdbin$SYNCRAFT_RECEPTOR_DIR(defaultSynCraft-Core/vina/). Auto-prep from raw PDB lands in v0.2. - Vina box must be defined in the receptor's
config.txt. Explicitpocket_residuesparameter not yet honored. - No retrosynthesis route output (use a dedicated retrosynthesis-planning tool downstream).
- Covalent ligands not supported.
- ADMET prediction left for downstream tools.
Citation
@article{li2026syncraft,
title = {SynCraft: Guiding Large Language Models to Predict Edit Sequences for Molecular Synthesizability Optimization},
author = {Li, Junren and ... and Lai, Luhua},
journal= {Nature Machine Intelligence},
year = {2026},
note = {under revision}
}
License
MIT. See LICENSE.