File size: 7,511 Bytes
e8373ce
b327bba
 
 
 
 
 
e8373ce
 
4cc69fa
e8373ce
 
 
71b6cd0
0a38dda
 
71b6cd0
 
0a38dda
71b6cd0
 
 
0a38dda
71b6cd0
0a38dda
 
71b6cd0
0a38dda
 
 
 
 
 
71b6cd0
0a38dda
 
71b6cd0
0a38dda
 
 
 
71b6cd0
 
0a38dda
71b6cd0
 
0a38dda
71b6cd0
4b4da04
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
e8373ce
b327bba
60c5fba
 
b327bba
 
1f14419
 
 
 
 
 
 
81c8a55
b327bba
f944ef3
 
b327bba
 
 
60c5fba
 
d08ff62
b327bba
0a38dda
60c5fba
b327bba
 
60c5fba
 
b327bba
 
 
 
60c5fba
 
b327bba
 
 
 
60c5fba
 
 
b327bba
 
 
 
 
124827b
4dcb0b1
243f678
124827b
 
 
 
 
 
 
 
d239c49
d136fb1
124827b
ca622bc
 
 
 
 
 
 
9aeca93
0a38dda
 
 
 
9aeca93
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
0a38dda
 
9aeca93
 
 
 
 
 
 
 
81c8a55
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
# Import librairies
from pathlib import Path
from typing import Optional
from uuid import uuid4
import hashlib
import json
import gradio as gr
import gemmi
from gradio_molecule3d import Molecule3D
from modal_app import app, chai1_inference, download_inference_dependencies, here

# Definition of the tools for the MCP server 

# Function to return a fasta file
def create_fasta_file(sequence: str, name: Optional[str] = None) -> str:
    """Create a FASTA file from a protein sequence string with a unique name.
    
    Args:
        sequence (str): The protein sequence string with optional line breaks
        name (str, optional): The name/identifier for the sequence. Defaults to "PROTEIN"
    
    Returns:
        str: Name of the created FASTA file
    """
    # Remove any trailing/leading whitespace but preserve line breaks
    lines = sequence.strip().split('\n')
    
    # Check if the first line is a FASTA header
    if not lines[0].startswith('>'):
        # If no header provided, add one
        if name is None:
            name = "PROTEIN"
        sequence = f">{name}\n{sequence}"
    
    # Create FASTA content (preserving line breaks)
    fasta_content = sequence
    
    # Generate a unique file name
    unique_id = hashlib.sha256(uuid4().bytes).hexdigest()[:8]
    file_name = f"chai1_{unique_id}_input.fasta"
    file_path = here / "inputs" / file_name
    
    # Write the FASTA file
    with open(file_path, "w") as f:
        f.write(fasta_content)
    
    return file_name

# Function to create a custom JSON config file
def create_custom_config(
    num_trunk_recycles: int = 3,
    num_diffn_timesteps: int = 200,
    seed: int = 42,
    use_esm_embeddings: bool = True,
    use_msa_server: bool = True,
    output_file: Optional[str] = None
) -> str:
    """Create a custom JSON configuration file for Chai1 inference.
    
    Args:
        num_trunk_recycles (int, optional): Number of trunk recycles. Defaults to 3.
        num_diffn_timesteps (int, optional): Number of diffusion timesteps. Defaults to 200.
        seed (int, optional): Random seed. Defaults to 42.
        use_esm_embeddings (bool, optional): Whether to use ESM embeddings. Defaults to True.
        use_msa_server (bool, optional): Whether to use MSA server. Defaults to True.
        output_file (str, optional): Path to save the config file. If None, saves to default location.
    
    Returns:
        str: Path to the created config file
    """
    config = {
        "num_trunk_recycles": num_trunk_recycles,
        "num_diffn_timesteps": num_diffn_timesteps,
        "seed": seed,
        "use_esm_embeddings": use_esm_embeddings,
        "use_msa_server": use_msa_server
    }
    
    if output_file is None:
        output_file = here / "inputs" / "chai1_custom_inference.json"
        
    with open(output_file, "w") as f:
        json.dump(config, f, indent=4)
        
    return str(output_file)

# Function to compute Chai1 inference
def compute_Chai1(
    fasta_file: Optional[str] = "",
    inference_config_file: Optional[str] = "",
):
    """Compute a Chai1 simulation.

    Args:
        x (float | int): The number to square.

    Returns:
        float: The square of the input number.
    """
    with app.run():
        
        force_redownload = False
        
        print("🧬 checking inference dependencies")
        download_inference_dependencies.remote(force=force_redownload)

        # Define fasta file
        if not fasta_file:
            fasta_file = here / "inputs" / "chai1_default_input.fasta"   
        print(f"🧬 running Chai inference on {fasta_file}")
        fasta_file = here / "inputs" / fasta_file
        print(fasta_file)
        fasta_content = Path(fasta_file).read_text()

        # Define inference config file
        if not inference_config_file:
            inference_config_file = here / "inputs" / "chai1_quick_inference.json"
        print(f"🧬 loading Chai inference config from {inference_config_file}")
        inference_config = json.loads(Path(inference_config_file).read_text())

        # Generate a unique run ID
        run_id = hashlib.sha256(uuid4().bytes).hexdigest()[:8]  # short id
        print(f"🧬 running inference with {run_id=}")

        results = chai1_inference.remote(fasta_content, inference_config, run_id)

        # Define output directory
        output_dir = Path("./results")
        output_dir.mkdir(parents=True, exist_ok=True)

        print(f"🧬 saving results to disk locally in {output_dir}")
        for ii, (scores, cif) in enumerate(results):
            (Path(output_dir) / f"{run_id}-scores.model_idx_{ii}.npz").write_bytes(scores)
            (Path(output_dir) / f"{run_id}-preds.model_idx_{ii}.cif").write_text(cif)
        
        # Take the last cif file and convert it to pdb
        cif_name = str(output_dir)+"/"+str(run_id)+"-preds.model_idx_"+str(ii)+".cif"
        pdb_name = cif_name.split('.cif')[0] + '.pdb'
        st = gemmi.read_structure(cif_name)
        st.write_minimal_pdb(pdb_name)
        
        return pdb_name


# Create the Gradio interface
reps = [{"model": 0,"style": "cartoon","color": "hydrophobicity"}]

with gr.Blocks() as demo:
    
    gr.Markdown(
    """
    # Chai1 Simulation Interface
    This interface allows you to run Chai1 simulations on a given Fasta sequence file.     
    """) 
    
    with gr.Tab("Configuration 📦"):
        text_input = gr.Textbox(placeholder="Fasta format sequences", label="Fasta content", lines=10)
        text_output = gr.Textbox(placeholder="Fasta file name", label="Fasta file name")
        text_button = gr.Button("Create Fasta file")
        text_button.click(fn=create_fasta_file, inputs=[text_input], outputs=[text_output])
        
        gr.Markdown(
        """
        You can input a Fasta file containing the sequence of the molecule you want to simulate.
        The output will be a 3D representation of the molecule based on the Chai1 model.
        ## Instructions
        1. Upload a Fasta sequence file containing the molecule sequence.
        2. Click the "Run" button to start the simulation.
        3. The output will be a 3D visualization of the molecule.
        ## Example Input
        You can use the default Fasta file provided in the inputs directory, or upload your own.
        ## Output
        The output will be a 3D representation of the molecule, which you can interact with.
        ## Note
        Make sure to have the necessary dependencies installed and the Chai1 model available in the specified directory.
        ## Disclaimer
        This interface is for educational and research purposes only. The results may vary based on the input sequence and the Chai1 model's capabilities.
        ## Contact
        For any issues or questions, please contact the developer or refer to the documentation.
        ## Example Fasta File
        ```
        >protein|name=example-protein
        AGSHSMRYFSTSVSRPGRGEPRFIAVGYVDDTQFVRFD      
        """) 
       
    with gr.Tab("Run folding simulation 🚀"): 
        inp1 = gr.Textbox(placeholder="Fasta Sequence file", label="Input Fasta file")
        inp2 = gr.Textbox(placeholder="Config file", label="JSON Config file")
        btn = gr.Button("Run")
        out = Molecule3D(label="Molecule3D", reps=reps)
        btn.click(fn=compute_Chai1, inputs=[inp1 , inp2], outputs=[out])

# Launch both the Gradio web interface and the MCP server
if __name__ == "__main__":
    demo.launch(mcp_server=True)