""" MyAbs — v0.2 product spine + developability flags. Paste a heavy (VH) + light (VL) chain -> fold with ABodyBuilder2 -> view the 3D structure with CDR loops highlighted -> scan for common developability liabilities (PTM hotspots, glycosylation sequons, free cysteines, long CDR-H3) and paint the offending residues onto the structure. Runs locally (WSL 'base'/'myabs' env) and drops onto a free HF CPU Space unchanged. Run: pip install gradio # once, into the same env as ImmuneBuilder python app.py # opens http://127.0.0.1:7900 The liability flags are HEURISTIC screens, not disqualifiers. MyAbs yields in-silico CANDIDATES, not patent-ready antibodies — wet-lab validation still required. """ import os import time import gradio as gr import py3Dmol from ImmuneBuilder import ABodyBuilder2 try: from anarci import number as anarci_number HAVE_ANARCI = True except Exception: HAVE_ANARCI = False print("Loading ABodyBuilder2 ensemble...") PREDICTOR = ABodyBuilder2() print("Ready.") # IMGT CDR position ranges (ABodyBuilder2 writes IMGT-numbered PDBs). CDR_RANGES = {"1": (27, 38), "2": (56, 65), "3": (105, 117)} # CDR cartoon colors: heavy = warm, light = cool. Framework stays grey. CDR_COLORS = { "H": {"1": "#ffca28", "2": "#ff7043", "3": "#e53935"}, "L": {"1": "#4dd0e1", "2": "#29b6f6", "3": "#1e88e5"}, } # Illustrative demo VH / VL (verify before any real use). DEMO_H = ("EVQLVESGGGLVQPGGSLRLSCAASGFTFSSYAMSWVRQAPGKGLEWVSAISGSGGST" "YYADSVKGRFTISRDNSKNTLYLQMNSLRAEDTAVYYCAKDRGYYYGMDVWGQGTTVTVSS") DEMO_L = ("DIQMTQSPSSLSASVGDRVTITCRASQSISSYLNWYQQKPGKAPKLLIYAASSLQSGVP" "SRFSGSGSGTDFTLTISSLQPEDFATYYCQQSYSTPLTFGGGTKVEIK") # Therapeutic library — variable domains only, self-curated from PUBLIC sources. # Every sequence cross-checked against two independent authoritative sources. PASTE = "— paste your own —" LIBRARY = { PASTE: None, "Trastuzumab (Herceptin) · anti-HER2": { "H": "EVQLVESGGGLVQPGGSLRLSCAASGFNIKDTYIHWVRQAPGKGLEWVARIYPTNGYTRYADSVKGRFTISADTSKNTAYLQMNSLRAEDTAVYYCSRWGGDGFYAMDYWGQGTLVTVSS", "L": "DIQMTQSPSSLSASVGDRVTITCRASQDVNTAVAWYQQKPGKAPKLLIYSASFLYSGVPSRFSGSRSGTDFTLTISSLQPEDFATYYCQQHYTTPPTFGQGTKVEIK", "target": "HER2 (ERBB2)", "src": "PDB 1N8Z SEQRES + KEGG DRUG D03257 (identical)", "url": "https://www.rcsb.org/structure/1N8Z", }, "Adalimumab (Humira) · anti-TNF-α": { "H": "EVQLVESGGGLVQPGRSLRLSCAASGFTFDDYAMHWVRQAPGKGLEWVSAITWNSGHIDYADSVEGRFTISRDNAKNSLYLQMNSLRAEDTAVYYCAKVSYLSTASSLDYWGQGTLVTVSS", "L": "DIQMTQSPSSLSASVGDRVTITCRASQGIRNYLAWYQQKPGKAPKLLIYAASTLQSGVPSRFSGSGSGTDFTLTISSLQPEDVATYYCQRYNRAPYTFGQGTKVEIK", "target": "TNF-α", "src": "PDB 6CR1 + DrugBank DB00051 (consensus; lone 3WD5 outlier residue rejected)", "url": "https://www.rcsb.org/structure/6CR1", }, "Pembrolizumab (Keytruda) · anti-PD-1": { "H": "QVQLVQSGVEVKKPGASVKVSCKASGYTFTNYYMYWVRQAPGQGLEWMGGINPSNGGTNFNEKFKNRVTLTTDSSTTTAYMELKSLQFDDTAVYYCARRDYRFDMGFDYWGQGTTVTVSS", "L": "EIVLTQSPATLSLSPGERATLSCRASKGVSTSGYSYLHWYQQKPGQAPRLLIYLASYLESGVPARFSGSGSGTDFTLTISSLEPEDFAVYYCQHSRDLPLTFGGGTKLEIK", "target": "PD-1 (PDCD1)", "src": "PDB 5DK3 + 5GGS (identical)", "url": "https://www.rcsb.org/structure/5DK3", }, "Rituximab (Rituxan) · anti-CD20": { "H": "QVQLQQPGAELVKPGASVKMSCKASGYTFTSYNMHWVKQTPGRGLEWIGAIYPGNGDTSYNQKFKGKATLTADKSSSTAYMQLSSLTSEDSAVYYCARSTYYGGDWYFNVWGAGTTVTVSA", "L": "QIVLSQSPAILSASPGEKVTMTCRASSSVSYIHWFQQKPGSSPKPWIYATSNLASGVPVRFSGSGSGTSYSLTISRVEAEDAATYYCQQWTSNPPTFGGGTKLEIK", "target": "CD20 (MS4A1)", "src": "PDB 2OSL + 6VJA (identical)", "url": "https://www.rcsb.org/structure/2OSL", }, "Bevacizumab (Avastin) · anti-VEGF-A": { "H": "EVQLVESGGGLVQPGGSLRLSCAASGYTFTNYGMNWVRQAPGKGLEWVGWINTYTGEPTYAADFKRRFTFSLDTSKSTAYLQMNSLRAEDTAVYYCAKYPHYYGSSHWYFDVWGQGTLVTVSS", "L": "DIQMTQSPSSLSASVGDRVTITCSASQDISNYLNWYQQKPGKAPKVLIYFTSSLHSGVPSRFSGSGSGTDFTLTISSLQPEDFATYYCQQYSTVPWTFGQGTKVEIK", "target": "VEGF-A", "src": "PDB 1BJ1 + NIH GSRS (confirmed)", "url": "https://www.rcsb.org/structure/1BJ1", }, } # On-screen CDR + liability legend (static HTML). def _swatch(label, color): return (f"" f"{label}") LEGEND_HTML = ( "
Enter both a heavy and a light chain.
", "Need both chains.", "", None, "", {}, False, reset_btn) try: t0 = time.time() antibody = PREDICTOR.predict({"H": heavy, "L": light}) dt = time.time() - t0 antibody.save("myabs_fold.pdb") except Exception as e: # OpenMM refinement / numbering can occasionally fail return (f"Fold failed: {e}
", f"Error: {e}", "", None, "", {}, False, reset_btn) pdb = open("myabs_fold.pdb").read() flags, highlights, h3_len, ok = developability(heavy, light) viewer = render_structure(pdb, spin=False, highlights=highlights) status = (f"Folded in {dt:.1f} s · VH {len(heavy)} aa / VL {len(light)} aa · " f"CDRs highlighted (H: yellow/orange/red, L: cyan/blue).") flags_md = format_flags(flags, h3_len, ok) return viewer, status, flags_md, "myabs_fold.pdb", pdb, highlights, False, reset_btn def load_library(name: str): """Populate VH/VL from the therapeutic library + show provenance.""" entry = LIBRARY.get(name) if not entry: # "paste your own" return (DEMO_H, DEMO_L, "_Built-in demo Fv (illustrative, not a real drug). " "Pick a therapeutic above, or paste your own sequences._") prov = (f"**{name.split(' · ')[0]}** · Target: **{entry['target']}** · " f"variable domains from a public source: {entry['src']} " f"([reference]({entry['url']})).") return entry["H"], entry["L"], prov def toggle_spin(spin: bool, pdb: str, highlights): """Flip spin on/off and re-render the stored structure (no re-fold).""" spin = not spin label = "⏸ Stop" if spin else "▶ Spin" if not pdb: return spin, gr.update(value=label), gr.update() return spin, gr.update(value=label), render_structure(pdb, spin, highlights) with gr.Blocks(title="MyAbs") as demo: # theme moved to launch() in Gradio 6 gr.Markdown( "# MyAbs\n" "**Look at an antibody candidate, fold it, see its CDR loops, and flag its " "developability liabilities — in your browser.**\n\n" "_In-silico candidates only. Not patent-ready antibodies; wet-lab validation required._" ) with gr.Row(): with gr.Column(scale=2): lib_dd = gr.Dropdown( choices=list(LIBRARY.keys()), value=PASTE, label="Load a known therapeutic (public sequences) — or paste your own", ) provenance = gr.Markdown() h_in = gr.Textbox(label="Heavy chain (VH)", value=DEMO_H, lines=4) l_in = gr.Textbox(label="Light chain (VL)", value=DEMO_L, lines=4) with gr.Row(): go = gr.Button("Fold", variant="primary") spin_btn = gr.Button("▶ Spin") status = gr.Markdown() pdb_file = gr.File(label="Download structure (.pdb)") with gr.Column(scale=3): viewer = gr.HTML() gr.HTML(LEGEND_HTML) gr.Markdown( "**Rotate it yourself** — _Touchscreen:_ one-finger drag = rotate · " "pinch = zoom · two-finger drag = pan. _Mouse:_ drag = rotate · " "scroll = zoom · right-drag = pan." ) gr.Markdown("---") flags_md = gr.Markdown() pdb_state = gr.State("") # last folded PDB, for re-render without re-folding hi_state = gr.State({}) # liability highlight positions by chain spin_state = gr.State(False) # is the structure currently spinning? fold_outputs = [viewer, status, flags_md, pdb_file, pdb_state, hi_state, spin_state, spin_btn] go.click(fold, inputs=[h_in, l_in], outputs=fold_outputs) # Pick a therapeutic -> load its sequences + provenance -> auto-fold. lib_dd.change(load_library, inputs=lib_dd, outputs=[h_in, l_in, provenance]).then( fold, inputs=[h_in, l_in], outputs=fold_outputs ) spin_btn.click( toggle_spin, inputs=[spin_state, pdb_state, hi_state], outputs=[spin_state, spin_btn, viewer], ) if __name__ == "__main__": # Local dev defaults to 127.0.0.1:7900. On an HF Docker Space the Dockerfile # sets GRADIO_SERVER_NAME=0.0.0.0 and GRADIO_SERVER_PORT=7860 (app_port). host = os.environ.get("GRADIO_SERVER_NAME", "127.0.0.1") port = int(os.environ.get("GRADIO_SERVER_PORT", "7900")) demo.launch(server_name=host, server_port=port, theme=gr.themes.Soft())