Spaces:
Running
Running
Upload MoDrAg_Chat_HFS.py
Browse files- MoDrAg_Chat_HFS.py +246 -0
MoDrAg_Chat_HFS.py
ADDED
|
@@ -0,0 +1,246 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import openai
|
| 2 |
+
import os
|
| 3 |
+
from openai import OpenAI
|
| 4 |
+
import gradio as gr
|
| 5 |
+
from elevenlabs.client import ElevenLabs
|
| 6 |
+
from elevenlabs import stream
|
| 7 |
+
import base64
|
| 8 |
+
|
| 9 |
+
api_key = os.getenv("OPENAI_API_KEY")
|
| 10 |
+
eleven_key = os.getenv("eleven_key")
|
| 11 |
+
|
| 12 |
+
elevenlabs = ElevenLabs(api_key=eleven_key)
|
| 13 |
+
client = OpenAI(api_key=api_key)
|
| 14 |
+
|
| 15 |
+
cafchem_tools = [
|
| 16 |
+
{
|
| 17 |
+
"type" : "mcp",
|
| 18 |
+
"server_label":"cafiero-proteinagent",
|
| 19 |
+
"server_url":"https://cafierom-proteinagent.hf.space/gradio_api/mcp/",
|
| 20 |
+
"require_approval": "never",
|
| 21 |
+
"allowed_tools": ["ProteinAgent_ProteinAgent"],
|
| 22 |
+
},
|
| 23 |
+
{
|
| 24 |
+
"type" : "mcp",
|
| 25 |
+
"server_label":"cafiero-propagent",
|
| 26 |
+
"server_url":"https://cafierom-propagent.hf.space/gradio_api/mcp/",
|
| 27 |
+
"require_approval": "never",
|
| 28 |
+
"allowed_tools": ["PropAgent_PropAgent"],
|
| 29 |
+
},
|
| 30 |
+
{
|
| 31 |
+
"type" : "mcp",
|
| 32 |
+
"server_label":"cafiero-moleculeagent",
|
| 33 |
+
"server_url":"https://cafierom-moleculeagent.hf.space/gradio_api/mcp/",
|
| 34 |
+
"require_approval": "never",
|
| 35 |
+
"allowed_tools": ["MoleculeAgent_MoleculeAgent"],
|
| 36 |
+
},
|
| 37 |
+
{
|
| 38 |
+
"type" : "mcp",
|
| 39 |
+
"server_label":"cafiero-dockagent",
|
| 40 |
+
"server_url":"https://cafierom-dockagent.hf.space/gradio_api/mcp/",
|
| 41 |
+
"require_approval": "never",
|
| 42 |
+
"allowed_tools": ["DockAgent_DockAgent"],
|
| 43 |
+
},
|
| 44 |
+
]
|
| 45 |
+
|
| 46 |
+
chat_history = []
|
| 47 |
+
global last_id
|
| 48 |
+
last_id = None
|
| 49 |
+
|
| 50 |
+
def chat(prompt, tools, voice_choice):
|
| 51 |
+
chat_history.append(
|
| 52 |
+
{"role": "user", "content": prompt}
|
| 53 |
+
)
|
| 54 |
+
global last_id
|
| 55 |
+
|
| 56 |
+
if tools == "Yes":
|
| 57 |
+
if (last_id != None):
|
| 58 |
+
response = client.responses.create(
|
| 59 |
+
model = "o4-mini",
|
| 60 |
+
tools = cafchem_tools,
|
| 61 |
+
input=prompt,
|
| 62 |
+
previous_response_id = last_id
|
| 63 |
+
)
|
| 64 |
+
else:
|
| 65 |
+
response = client.responses.create(
|
| 66 |
+
model = "o4-mini",
|
| 67 |
+
tools = cafchem_tools,
|
| 68 |
+
input=prompt
|
| 69 |
+
)
|
| 70 |
+
else:
|
| 71 |
+
if (last_id != None):
|
| 72 |
+
response = client.responses.create(
|
| 73 |
+
model = "o4-mini",
|
| 74 |
+
input=prompt,
|
| 75 |
+
previous_response_id = last_id
|
| 76 |
+
)
|
| 77 |
+
else:
|
| 78 |
+
response = client.responses.create(
|
| 79 |
+
model = "o4-mini",
|
| 80 |
+
input=prompt
|
| 81 |
+
)
|
| 82 |
+
|
| 83 |
+
chat_history.append(
|
| 84 |
+
{"role": "assistant", "content": response.output_text}
|
| 85 |
+
)
|
| 86 |
+
last_id = response.id
|
| 87 |
+
|
| 88 |
+
if voice_choice == "On":
|
| 89 |
+
elita_text = response.output_text
|
| 90 |
+
|
| 91 |
+
voice_settings = {
|
| 92 |
+
"stability": 0.37,
|
| 93 |
+
"similarity_boost": 0.90,
|
| 94 |
+
"style": 0.0,
|
| 95 |
+
"speed": 1.05
|
| 96 |
+
}
|
| 97 |
+
|
| 98 |
+
audio_stream = elevenlabs.text_to_speech.convert(
|
| 99 |
+
text = elita_text,
|
| 100 |
+
voice_id = 'vxO9F6g9yqYJ4RsWvMbc',
|
| 101 |
+
model_id = 'eleven_multilingual_v2',
|
| 102 |
+
output_format='mp3_44100_128',
|
| 103 |
+
voice_settings=voice_settings
|
| 104 |
+
)
|
| 105 |
+
|
| 106 |
+
audio_converted = b"".join(audio_stream)
|
| 107 |
+
audio = base64.b64encode(audio_converted).decode("utf-8")
|
| 108 |
+
audio_player = f'<audio src="data:audio/mpeg;base64,{audio}" controls autoplay></audio>'
|
| 109 |
+
else:
|
| 110 |
+
audio_player = ''
|
| 111 |
+
|
| 112 |
+
return "", chat_history, audio_player
|
| 113 |
+
|
| 114 |
+
def clear_history():
|
| 115 |
+
global chat_history
|
| 116 |
+
chat_history = []
|
| 117 |
+
global last_id
|
| 118 |
+
last_id = None
|
| 119 |
+
|
| 120 |
+
def voice_from_file(file_name):
|
| 121 |
+
audio_file = file_name
|
| 122 |
+
with open(audio_file, 'rb') as audio_bytes:
|
| 123 |
+
audio = base64.b64encode(audio_bytes.read()).decode("utf-8")
|
| 124 |
+
audio_player = f'<audio src="data:audio/mpeg;base64,{audio}" controls autoplay></audio>'
|
| 125 |
+
return audio_player
|
| 126 |
+
|
| 127 |
+
def prot_workflow():
|
| 128 |
+
elita_text = "Starting with a protein, try searching for Uniprot IDs, followed by Chembl IDs. \
|
| 129 |
+
Then you can look for bioactive molecules for each Chembl ID. You can also search for crystal structures \
|
| 130 |
+
in the PDB and get titles of those structures, sequences, numbers of chains, and small molecules in the structure. \
|
| 131 |
+
Generate novel bioactive molecules based on a protein Chembl ID using a GPT, or predict an IC50 for a molecule \
|
| 132 |
+
based on a protein Chembl ID using a gradient-boosting model."
|
| 133 |
+
messages = [{'role': 'assistant', 'content': elita_text}]
|
| 134 |
+
audio_player = voice_from_file('protein_chat.mp3')
|
| 135 |
+
return audio_player, messages
|
| 136 |
+
|
| 137 |
+
def mol_workflow():
|
| 138 |
+
elita_text = "Starting with a molecule, try finding it's Lipinski properties, or its pharmacophore similarity to a known active. \
|
| 139 |
+
Find similar molecules or, if it has substituted rings, find analogues."
|
| 140 |
+
messages = [{'role': 'assistant', 'content': elita_text}]
|
| 141 |
+
audio_player = voice_from_file('mol_wf.mp3')
|
| 142 |
+
return audio_player, messages
|
| 143 |
+
|
| 144 |
+
def combo_workflow():
|
| 145 |
+
elita_text ="Starting with a protein and a molecule, try docking the molecule in the protein. If you have a Chembl ID, predict the IC50 \
|
| 146 |
+
value of the molecule in the protein."
|
| 147 |
+
messages = [{'role': 'assistant', 'content': elita_text}]
|
| 148 |
+
audio_player = voice_from_file('combo_wf.mp3')
|
| 149 |
+
return audio_player, messages
|
| 150 |
+
|
| 151 |
+
def prot_accordions():
|
| 152 |
+
elita_text = 'Try queries like: find UNIPROT IDs for the protein MAOB; find PDB IDs for MAOB; how many chains \
|
| 153 |
+
are in the PDB structure 4A7G; find PDB IDs matching the protein MAOB; list the bioactive molecules for the CHEMBL \
|
| 154 |
+
ID CHEMBL2039; dock the molecule CCCC(F) in the protein DRD2; predict the IC50 value for CCCC(F) based on the CHEMBL \
|
| 155 |
+
ID CHEMBL2039; or generate novel molecules based on the CHEMBL ID CHEMBL2039.'
|
| 156 |
+
messages = [{'role': 'assistant', 'content': elita_text}]
|
| 157 |
+
audio_player = voice_from_file('protein.mp3')
|
| 158 |
+
return audio_player, messages
|
| 159 |
+
|
| 160 |
+
def mol_accordions():
|
| 161 |
+
elita_text = 'Try queries like: Find the name of CCCF, find the smiles for paracetamol, or find molecules similar to paracetamol.'
|
| 162 |
+
messages = [{'role': 'assistant', 'content': elita_text}]
|
| 163 |
+
audio_player = voice_from_file('mol.mp3')
|
| 164 |
+
return audio_player, messages
|
| 165 |
+
|
| 166 |
+
def prop_accordions():
|
| 167 |
+
elita_text = 'Try queries like: Find Lipinski properties for CCCF, find pharmacophore-similarity between \
|
| 168 |
+
CCCF and CCCBr, or generate analogues of c1ccc(O)cc1.'
|
| 169 |
+
messages = [{'role': 'assistant', 'content': elita_text}]
|
| 170 |
+
audio_player = voice_from_file('Props.mp3')
|
| 171 |
+
return audio_player, messages
|
| 172 |
+
|
| 173 |
+
def dock_accordions():
|
| 174 |
+
elita_text = 'Try queries like: dock CCC(F) in the protein MAOB'
|
| 175 |
+
messages = [{'role': 'assistant', 'content': elita_text}]
|
| 176 |
+
audio_player = voice_from_file('Dock.mp3')
|
| 177 |
+
return audio_player, messages
|
| 178 |
+
|
| 179 |
+
with gr.Blocks() as forest:
|
| 180 |
+
gr.Markdown(
|
| 181 |
+
"""
|
| 182 |
+
# Chat with MoDrAg! OpenAI 04-mini can tap into Modrag through an MCP and use all of your favourite drug design tools.
|
| 183 |
+
- Currently using the tools below:
|
| 184 |
+
""")
|
| 185 |
+
with gr.Row():
|
| 186 |
+
with gr.Accordion("Protein Agent - Click to open/close.", open=False)as prot:
|
| 187 |
+
gr.Markdown('''
|
| 188 |
+
- Find Uniprot IDs for a protein/gene name.
|
| 189 |
+
- report the number of bioactive molecules for a protein, organized by Chembl ID.
|
| 190 |
+
- report the SMILES and IC50 values of bioactive molecules for a particular Chembl ID.
|
| 191 |
+
- find protein sequences, report number fo chains.
|
| 192 |
+
- find small molecules present in a PDB structure.
|
| 193 |
+
- find PDB IDs that match a protein.
|
| 194 |
+
- predict the IC50 value of a small molecule based on a Chembl ID.
|
| 195 |
+
- generate novel molecules based on a Chembl ID.
|
| 196 |
+
''')
|
| 197 |
+
with gr.Accordion("Molecule Agent - Click to open/close.", open=False) as mol:
|
| 198 |
+
gr.Markdown('''
|
| 199 |
+
- find the name of a molecule from the SMILES string.
|
| 200 |
+
- find the SMILES string of a molecule from the name
|
| 201 |
+
- find similar or related molecules with some basic properties from a name or SMILES.
|
| 202 |
+
''')
|
| 203 |
+
with gr.Accordion("Property Agent - Click to open/close.", open=False) as prop:
|
| 204 |
+
gr.Markdown('''
|
| 205 |
+
- calculate Lipinski properties from a SMILES string.
|
| 206 |
+
- find the pharmacophore-similarity between two molecules (a molecule and a reference).
|
| 207 |
+
- generate analogues of ring molecules and report their QED values.
|
| 208 |
+
''')
|
| 209 |
+
with gr.Accordion("Docking Agent - Click to open/close.", open=False) as dock:
|
| 210 |
+
gr.Markdown('''
|
| 211 |
+
- Find the docking score and pose coordinates for a molecules defined by a SMILES string in on of the proteins below:
|
| 212 |
+
- IGF1R,JAK2,KIT,LCK,MAPK14,MAPKAPK2,MET,PTK2,PTPN1,SRC,ABL1,AKT1,AKT2,CDK2,CSF1R,EGFR,KDR,MAPK1,FGFR1,ROCK1,MAP2K1,
|
| 213 |
+
PLK1,HSD11B1,PARP1,PDE5A,PTGS2,ACHE,MAOB,CA2,GBA,HMGCR,NOS1,REN,DHFR,ESR1,ESR2,NR3C1,PGR,PPARA,PPARD,PPARG,AR,THRB,
|
| 214 |
+
ADAM17,F10,F2,BACE1,CASP3,MMP13,DPP4,ADRB1,ADRB2,DRD2,DRD3,ADORA2A,CYP2C9,CYP3A4,HSP90AA1
|
| 215 |
+
''')
|
| 216 |
+
with gr.Row():
|
| 217 |
+
molecule_workflow = gr.Button(value = "Sample Molecule Workflow")
|
| 218 |
+
protein_workflow = gr.Button(value = "Sample Protein Workflow")
|
| 219 |
+
combined_workflow = gr.Button(value = "Sample Combined Workflow")
|
| 220 |
+
|
| 221 |
+
with gr.Row():
|
| 222 |
+
tools = gr.Radio(choices = ["Yes", "No"],label="Use CafChem tools?",interactive=True, value = "Yes", scale = 2)
|
| 223 |
+
voice_choice = gr.Radio(choices = ['On', 'Off'],label="Audio Voice Response?", interactive=True, value='Off', scale = 2)
|
| 224 |
+
|
| 225 |
+
chatbot = gr.Chatbot()
|
| 226 |
+
|
| 227 |
+
with gr.Row():
|
| 228 |
+
msg = gr.Textbox(label="Type your messages here and hit enter.", scale = 2)
|
| 229 |
+
chat_btn = gr.Button(value = "Send", scale = 0)
|
| 230 |
+
elitas_voice = gr.HTML()
|
| 231 |
+
|
| 232 |
+
clear = gr.ClearButton([msg, chatbot])
|
| 233 |
+
|
| 234 |
+
chat_btn.click(chat, [msg, tools, voice_choice], [msg, chatbot, elitas_voice])
|
| 235 |
+
msg.submit(chat, [msg, tools, voice_choice], [msg, chatbot, elitas_voice])
|
| 236 |
+
mol.expand(mol_accordions, outputs = [talk_ele, chatbot])
|
| 237 |
+
prop.expand(prop_accordions, outputs = [talk_ele, chatbot])
|
| 238 |
+
prot.expand(prot_accordions, outputs = [talk_ele, chatbot])
|
| 239 |
+
dock.expand(dock_accordions, outputs = [talk_ele, chatbot])
|
| 240 |
+
molecule_workflow.click(mol_workflow, outputs = [talk_ele, chatbot])
|
| 241 |
+
protein_workflow.click(prot_workflow, outputs = [talk_ele, chatbot])
|
| 242 |
+
combined_workflow.click(combo_workflow, outputs = [talk_ele, chatbot])
|
| 243 |
+
clear.click(clear_history)
|
| 244 |
+
|
| 245 |
+
if __name__ == "__main__":
|
| 246 |
+
forest.launch(debug=False, share=True)
|