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 .pdb file)

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

ModelLicenseLocalDefault?
gemini-2.5-proAPI onlynopaper headline
gemini-3.1-proAPI onlyno
deepseek-v4-proMIT (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

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)

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.pdb in $SYNCRAFT_RECEPTOR_DIR (default SynCraft-Core/vina/). Auto-prep from raw PDB lands in v0.2.
  • Vina box must be defined in the receptor's config.txt. Explicit pocket_residues parameter 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.