RFdiffusion3 / app.py
gabboud's picture
fix tab selection logic
efab625
import gradio as gr
import warnings
import os
import subprocess
from pathlib import Path
import shutil
import spaces
from atomworks.io.utils.visualize import view
from lightning.fabric import seed_everything
from rfd3.engine import RFD3InferenceConfig, RFD3InferenceEngine
from utils.download_weights import download_weights
from utils.pipelines import test_rfd3_from_notebook, unconditional_generation
#from gradio_molecule3d import Molecule3D
from utils.handle_events import *
download_weights()
# Gradio UI
with gr.Blocks(title="RFD3 Test") as demo:
gr.Markdown("# RFdiffusion3 for Backbone generation 🧬 ")
with gr.Row():
gr.Markdown("""When the Baker lab released the first version of [RFdiffusion](https://www.nature.com/articles/s41586-023-06415-8), they opened up new avenues for protein design.
The model was based on the previous structure prediction architectures, yet employing the diffusion framework typical of image generation models.
It showed impressive results in generating protein backbones for motif scaffolding and binder design.
Now in its third version, [RFD3](https://pubmed.ncbi.nlm.nih.gov/41000976/) can create binders for an extended set of targets, from DNA/RNA to small molecules, and allows advanced conditiong.
This space allows you to run backbone generation jobs using Hugging Face's hardware and download the results!
<u>Image and Model Source</u>: Butcher J, Krishna R, Mitra R, Brent RI, Li Y, Corley N, Kim PT, Funk J, Mathis S, Salike S, Muraishi A, Eisenach H, Thompson TR, Chen J, Politanska Y, Sehgal E, Coventry B, Zhang O, Qiang B, Didi K, Kazman M, DiMaio F, Baker D. De novo Design of All-atom Biomolecular Interactions with RFdiffusion3. bioRxiv [Preprint]. 2025 Nov 19:2025.09.18.676967. doi: 10.1101/2025.09.18.676967. PMID: 41000976; PMCID: PMC12458353.""")
gr.Image("assets/overview_rfd3_baker.png", width=600)
tab_selected = gr.State("upload")
config_ready = gr.State(None)
scaffold_ready = gr.State(None)
with gr.Row():
with gr.Column(scale=1): # Left half
gr.Markdown("Set up the configuration for your run through a valid yaml file or by manually setting minimal parameters for an unconditional run.")
with gr.Tabs() as config_tabs:
with gr.TabItem("Upload Config") as upload_tab:
config_upload = gr.File(label="PDB + Config", file_types=[".pdb", ".yaml", ".json"])
with gr.TabItem("Manual Config") as manual_tab:
num_designs_per_batch = gr.Number(
value=2,
label="Number of Designs per Batch",
precision=0,
minimum=1,
maximum=8
)
num_batches = gr.Number(
value=5,
label="Number of Batches",
precision=0,
minimum=1,
maximum=10
)
length = gr.Number(
value=40,
label="Length of Protein (number of residues)",
precision=0,
minimum=10,
maximum=200
)
config_validation_btn = gr.Button("Validate Config")
config_textbox = gr.Textbox(value ="Waiting for config validation...")
with gr.Column(scale=1): # Right half
gr.Markdown("Upload your target/scaffold structure as a PDB file to condition the generation. Press no input if you want to run an unconditional generation.")
with gr.Tabs():
with gr.TabItem("Scaffold PDB"):
scaffold_upload = gr.File(label="Target/Scaffold PDB", file_types=[".pdb"])
with gr.Row():
scaffold_validation_btn = gr.Button("Validate Scaffold")
no_input_btn = gr.Button("No Scaffold/Target")
scaffold_textbox = gr.Textbox(value ="Waiting for scaffold validation...")
run_btn = gr.Button("Run Generation", variant="primary")
runtextbox = gr.Textbox(value="Waiting for generation run...")
#config_tabs.select(lambda tab_selected: "upload" if tab_selected == "manual" else "manual", inputs=tab_selected, outputs=tab_selected)
upload_tab.select(lambda: "upload", outputs=tab_selected)
manual_tab.select(lambda: "manual", outputs=tab_selected)
def validate_config_ready(tab_selected, config_upload):
print("Tab selected:", tab_selected)
print("Config upload:", config_upload)
print("Current config_ready state:", config_ready.value)
if tab_selected == "manual":
return "Manual parameters selected and valid!", "manual"
elif tab_selected == "upload" and config_upload is None:
return "Please upload a config file or switch to manual config for uncondition generation.", None
elif tab_selected == "upload" and config_upload is not None:
return "Config file uploaded and valid!", "upload"
config_validation_btn.click(validate_config_ready, inputs=[tab_selected, config_upload], outputs=[config_textbox, config_ready])
def validate_scaffold_ready_with_file(scaffold_upload):
if scaffold_upload is not None:
return "Scaffold file uploaded and valid!", "upload"
else:
return "Please upload a scaffold file or press 'No Scaffold/Target' for unconditioned generation.", None
scaffold_validation_btn.click(validate_scaffold_ready_with_file, inputs=scaffold_upload, outputs=[scaffold_textbox, scaffold_ready])
no_input_btn.click(lambda: ("No scaffold/target will be used. Ready for unconditioned generation!", "no_input"), outputs=[scaffold_textbox, scaffold_ready])
#run_btn.click(give_run_status, inputs=[config_ready, scaffold_ready], outputs=runtextbox)
#gr.Markdown("Unconditional generation of backbones")
#with gr.Row():
# num_designs_per_batch = gr.Number(
# value=2,
# label="Number of Designs per Batch",
# precision=0,
# minimum=1,
# maximum=8
# )
# num_batches = gr.Number(
# value=5,
# label="Number of Batches",
# precision=0,
# minimum=1,
# maximum=10
# )
# length = gr.Number(
# value=40,
# label="Length of Protein (number of residues)",
# precision=0,
# minimum=10,
# maximum=200
# )
gen_directory = gr.State(None)
gen_results = gr.State(None)
#gen_btn = gr.Button("Run Unconditional Generation")
output_file = gr.File(label="Download RFD3 results as zip", visible=True)
#
#
# Section to inspect PDB of generated structures
with gr.Row():
batch_dropdown = gr.Dropdown(
choices=[],
label="Select Batch",
visible=True
)
design_dropdown = gr.Dropdown(
choices=[],
label="Select Design",
visible=True
)
show_pdb_btn = gr.Button("Show PDB content", visible=True)
display_state = gr.Textbox(label="Selected Batch and Design", visible=True)
display_state.value = "Please Select a Batch and Design number to show sequence"
def download_results_as_zip(directory):
if directory is None:
return gr.update()
zip_path = f"{directory}.zip"
shutil.make_archive(directory, 'zip', directory)
return gr.update(value=zip_path, visible=True)
def give_run_status(config_ready, scaffold_ready, num_batches, num_designs_per_batch, length):
if config_ready is None or scaffold_ready is None:
return gr.update(value="Please ensure both config and scaffold are validated before running the generation.")
elif config_ready=="manual" and scaffold_ready=="no_input":
return gr.update(value=f"Running unconditional generation with minimal configuration: {num_batches} batches of {num_designs_per_batch} designs each, length = {length} aa")
else:
return gr.update(value=f"Not implemented yet")
def generate(config_ready, scaffold_ready, num_batches, num_designs_per_batch, length):
if config_ready is None or scaffold_ready is None:
return None, None
if config_ready=="manual" and scaffold_ready=="no_input":
gen_directory, gen_results = unconditional_generation(num_batches, num_designs_per_batch, length)
return gen_directory, gen_results
run_btn.click(give_run_status, inputs=[config_ready, scaffold_ready, num_batches, num_designs_per_batch, length], outputs=runtextbox).then(
generate, inputs=[config_ready, scaffold_ready, num_batches, num_designs_per_batch, length], outputs=[gen_directory, gen_results]
).then(
update_batch_choices,
inputs=gen_results,
outputs=batch_dropdown
).then(
download_results_as_zip,
inputs=gen_directory,
outputs=output_file
)
#run_btn.click(unconditional_generation, inputs=[num_batches, num_designs_per_batch, length], outputs=[gen_directory, gen_results]).then(
# update_batch_choices,
# inputs=gen_results,
# outputs=batch_dropdown).then(
# download_results_as_zip,
# inputs=gen_directory,
# outputs=output_file
# )
batch_dropdown.change(update_designs, inputs=[batch_dropdown, gen_results], outputs=[design_dropdown])
design_dropdown.change()
show_pdb_btn.click(show_pdb, inputs=[batch_dropdown, design_dropdown, gen_results], outputs=display_state)
#def load_viewer(batch, design, result):
# if batch is None or design is None:
# return gr.update()
# pdb_data = next(d["pdb"] for d in result if d["batch"] == int(batch) and d["design"] == int(design))
# return gr.update(value=pdb_data, visible=True, reps=[{"style": "cartoon"}]) # Customize style
#
#visualize_btn.click(load_viewer, inputs=[batch_dropdown, design_dropdown, gen_results], outputs=viewer)
if __name__ == "__main__":
demo.launch()