Unverified Commit 5f7c6d9f authored by jinyuan sun's avatar jinyuan sun Committed by GitHub
Browse files

Merge pull request #30 from JinyuanSun/sjy_dev

Sjy dev
parents c2d615e8 f47ea28f
Loading
Loading
Loading
Loading
+29 −1
Original line number Diff line number Diff line
@@ -64,7 +64,35 @@ You are more than welcome to contribute any function to ChatMol copilot.
4. Create a pull request
5. We will review your code and merge it to the main branch  

**If you still don't know what to do, just paste this and the content in `copilot_public/new_function_template.py` to the input box of ChatGPT and ask it to do all the coding for you.** *Remeber to add the magic prompt: "I don't have fingers, can you write the complete code for me."*
**If you still don't know what to do, just paste this and the content in `copilot_public/new_function_template.py` to the input box of ChatGPT and ask it to do all the coding for you.**  
*Remeber to add the magic prompt: "I don't have fingers, can you write the complete code for me."*


## TODO
### Analysis tools
- [ ] **Protein Docking Simulation**: Develop a simulation tool for docking small molecule ligands to protein targets, exploring potential binding modes.
  - AutoDock Vina (High priority)
  - DiffDock (Low priority)
  
- [ ] **Protein Structure and Sequence Comparison**: Build a tool for comparing the structures of multiple proteins, identifying similarities, differences, and motifs.
  - TM-align (High priority)
  - Kalign (Medium priority)
  - MMseqs2 (Low priority)

- [ ] **Ligand Binding Affinity Prediction**: Create a tool to predict the binding affinity of ligands with protein targets, aiding in understanding ligand-protein interaction strength.
  - RF-Score (High priority)
  - 

- [ ] **Protein Design and Engineering**: Design a tool for engineering proteins with specific functions or properties, like enzyme activity or substrate binding.

- [ ] **Protein-Protein Interaction Prediction**: Develop a tool to predict protein-protein interaction partners and binding sites based on their 3D structures.

- [ ] **Protein Function Prediction**: Build a tool for predicting the biological function of proteins based on their structure and sequence.


### visualization tools
- [ ] **Protein-Ligand Interaction Visualization**: Create a tool for visualizing and analyzing protein-ligand interactions, focusing on key binding residues.


## Online Version
We provided an online version for you. [Click here](https://chatmol.org/copilot/) to try it.  
 No newline at end of file
+138 −43
Original line number Diff line number Diff line
@@ -4,7 +4,7 @@ import chatmol_fn as cfn
from stmol import showmol
from streamlit_float import *
from viewer_utils import show_pdb, update_view
from utils import test_openai_api
from utils import test_openai_api, function_args_to_streamlit_ui
from streamlit_molstar import st_molstar, st_molstar_rcsb, st_molstar_remote
import new_function_template
import shutil
@@ -16,16 +16,17 @@ import pickle
import re
import streamlit_analytics
import requests
import inspect

st.set_page_config(layout="wide")
st.session_state.new_added_functions = []
try:
    print(st.session_state["messages"])
except:
    pass


pattern = r"<chatmol_sys>.*?</chatmol_sys>"
# wide
if "function_queue" not in st.session_state:
    st.session_state["function_queue"] = []

if "cfn" in st.session_state:
    cfn = st.session_state["cfn"]
else:
@@ -33,7 +34,7 @@ else:
    st.session_state["cfn"] = cfn

st.title("ChatMol copilot", anchor="center")
st.sidebar.write("2023 Dec 16 public version")
st.sidebar.write("2024 Jan 05 public version")
st.sidebar.write(
    "ChatMol copilot is a copilot for protein engineering. Also chekcout our [GitHub](https://github.com/JinyuanSun/ChatMol)."
)
@@ -41,6 +42,7 @@ st.write("Enjoy modeling proteins with ChatMol copilot! 🤖️ 🧬")
float_init()

st.sidebar.title("Settings")
mode = st.sidebar.selectbox("Mode", ["automatic", "manual"], index=0)
with streamlit_analytics.track():
    project_id = st.sidebar.text_input("Project Name", "Project-X")
openai_api_key = st.sidebar.text_input("OpenAI API key", type="password")
@@ -83,6 +85,9 @@ if st.sidebar.button("Clear Project History"):
    if os.path.exists(f"./{hash_string}"):
        shutil.rmtree(f"./{hash_string}")
        st.session_state.messages = []
        st.session_state.function_queue = []
        st.session_state.new_added_functions = []
        st.session_state.cfn = cfn.ChatmolFN()
# try to bring back the previous session
work_dir = f"./{hash_string}"
cfn.WORK_DIR = work_dir
@@ -157,7 +162,6 @@ if "messages" not in st.session_state:
chatcol, displaycol = st.columns([1, 1])
with chatcol:
    for message in st.session_state.messages:
        # print(type(message))
        try:
            if message["role"] != "system":
                cleaned_string = re.sub(
@@ -172,6 +176,52 @@ with chatcol:
                if cleaned_string != "":
                    with st.chat_message(message.role):
                        st.markdown(cleaned_string)
    function_called = False

    for tool_call in st.session_state.function_queue:
        if tool_call["status"] == "pending":
            function_called = True
            # with st.chat_message("assistant"):
                # st.write(f"Please provide arguments for {tool_call['name']}")
            try:
                function_name = tool_call["name"]
                function_to_call = tool_call["func"]
                function_args = json.loads(tool_call["args"])
                function_response = function_to_call(**function_args)
                if function_response:
                    st.session_state.messages.append(
                        {
                            "tool_call_id": tool_call["tool_call_id"],
                            "role": "tool",
                            "name": function_name,
                            "content": function_response,
                        }
                    )
                    tool_call["status"] = "done"
                    
            except Exception as e:
                print(f"The error is:\n{e}")
                st.session_state.messages.append(
                    {
                        "tool_call_id": tool_call["tool_call_id"],
                        "role": "tool",
                        "name": function_name,
                        "content": f"error: {e}",
                    }
                )
                tool_call["status"] = "done"
    if function_called:
        with st.chat_message("assistant"):
            message_placeholder = st.empty()
            full_response = ""
            for response in client.chat.completions.create(
                model=st.session_state["openai_model"],
                messages=st.session_state.messages,
                stream=True,
            ):
                full_response += response.choices[0].delta.content or ""
                message_placeholder.markdown(full_response)
    

if prompt := st.chat_input("What is up?"):
    with chatcol:
@@ -233,12 +283,15 @@ if prompt := st.chat_input("What is up?"):
                    tool_call_dict_list=tool_calls,
                )
                st.session_state.messages.append(response_message)
                if mode == "automatic":
                    for tool_call in tool_calls:
                        function_name = tool_call["function"]["name"]
                        function_to_call = available_functions[function_name]
                        try:
                            function_args = json.loads(tool_call["function"]["arguments"])

                            function_response = function_to_call(**function_args)
                            if function_response:
                                st.session_state.messages.append(
                                    {
                                        "tool_call_id": tool_call["id"],
@@ -257,8 +310,51 @@ if prompt := st.chat_input("What is up?"):
                                    "content": f"error: {e}",
                                }
                            )
            # if full_response:
            # message_placeholder.markdown(full_response)

                if mode == "manual":
                    print("manual mode")
                    for tool_call in tool_calls:
                        function_name = tool_call["function"]["name"]
                        function_to_call = available_functions[function_name]
                        function_arg_string = tool_call["function"]["arguments"]
                        st.session_state.function_queue.append(
                            {
                                "tool_call_id": tool_call["id"],
                                "role": "tool",
                                "name": function_name,
                                "func": function_to_call,
                                "args": function_arg_string,
                                "status": "pending",
                                "content": "",
                            }
                        )
                    for tool_call in st.session_state.function_queue:
                        if tool_call["status"] == "pending":
                            try:
                                function_args = json.loads(function_arg_string)
                                function_response = function_args_to_streamlit_ui(function_to_call, function_args, tool_call["tool_call_id"])
                                print(function_response)
                                if function_response:
                                    st.session_state.messages.append(
                                        {
                                            "tool_call_id": tool_call["tool_call_id"],
                                            "role": "tool",
                                            "name": function_name,
                                            "content": function_response,
                                        }
                                    )
                                    tool_call["status"] = "done"
                            except Exception as e:
                                print(f"The error is:\n{e}")
                                st.session_state.messages.append(
                                    {
                                        "tool_call_id": tool_call["tool_call_id"],
                                        "role": "tool",
                                        "name": function_name,
                                        "content": f"error: {e}",
                                    }
                                )
                                tool_call["status"] = "done"
                if function_response:
                    for response in client.chat.completions.create(
                        model=st.session_state["openai_model"],
@@ -275,7 +371,6 @@ if prompt := st.chat_input("What is up?"):
                    )

                message_placeholder.markdown(full_response)
            # print(st.session_state.messages)
uploaded_file = st.sidebar.file_uploader("Upload PDB file", type=["pdb"])

if uploaded_file:
+78 −0
Original line number Diff line number Diff line
@@ -221,6 +221,84 @@ test_data = {
    }
}


def predict_rna_secondary_structure(self, rna_seq: str):
    from seqfold import fold, dg, dot_bracket
    """
    Predict the secondary structure of an RNA sequence using the seqfold library.

    Parameters:
    - rna_seq (str): The RNA sequence to be analyzed.

    Returns:
    - A dictionary with the minimum free energy and the dot-bracket representation of the structure.
    """
    mfe = dg(rna_seq)
    structs = fold(rna_seq)
    dot_bracket_structure = dot_bracket(rna_seq, structs)

    return f"Minimum Free Energy (MFE): `{mfe}`\nDot-Bracket Structure: `{dot_bracket_structure}`"

# Update the function description in the function_descriptions list
function_descriptions.append({
    "type": "function",
    "function": {
        "name": "predict_rna_secondary_structure",
        "description": "Predict the secondary structure of an RNA sequence using the seqfold library",
        "parameters": {
            "type": "object",
            "properties": {
                "rna_seq": {"type": "string", "description": "The RNA sequence"},
            },
        },
        "required": ["rna_seq"],
    },
})

# Update test data for the new function
test_data["predict_rna_secondary_structure"] = {
    "input": {
        "self": None,
        "rna_seq": "GGGAGGTCGTTACATCTGGGTAACACCGGTACTGATCCGGTGACCTCCC",
    },
    "output": {"Minimum Free Energy (MFE): `-13.4`\nDot-Bracket Structure: `((((((((.((((......))))..((((.......)))).))))))))`"
    },
}



def predict_logp_from_smiles(self, smiles: str):
    from rdkit import Chem
    from rdkit.Chem import Crippen
    mol = Chem.MolFromSmiles(smiles)
    if mol is None:
        return "Invalid SMILES string"
    logp = Crippen.MolLogP(mol)
    return f"The predicted logP value for the molecule {smiles} is {logp}"

function_descriptions.append({
    "type": "function",
    "function": {
        "name": "predict_logp_from_smiles",
        "description": "Predicts the logP value of a molecule from its SMILES notation",
        "parameters": {
            "type": "object",
            "properties": {
                "smiles": {"type": "string", "description": "The SMILES notation of the molecule"},
            },
        },
        "required": ["smiles"],
    },
})

test_data["predict_logp_from_smiles"] = {
    "input": {
        "self": None,
        "smiles": "CCO",
    },
    "output": "The predicted logP value for the molecule CCO is 0.4605",  # Example output
}

### DO NOT MODIFY BELOW THIS LINE ###
def get_all_functions():
    all_functions = []
+30 −0
Original line number Diff line number Diff line
import requests
import inspect
import functools
import streamlit as st
from openai import OpenAI

def handle_file_not_found_error(func):
@@ -11,6 +13,34 @@ def handle_file_not_found_error(func):
            return f"Current working directory is: {args[0].get_work_dir()}"
    return wrapper

def function_args_to_streamlit_ui(func, args=None, tool_call_id=None):
    # clicked = False
    signature = inspect.signature(func)
    docstring = inspect.getdoc(func)
    if docstring:
        st.write(docstring)
    args_values = {}
    for name, param in signature.parameters.items():
        if param.annotation is str:
            if name == "seq":
                args_values[name] = st.text_area(name, key=f"{tool_call_id}_{name}", value=args[name] if name in args else None)
            else:
                args_values[name] = st.text_input(name, key=f"{tool_call_id}_{name}", value=args[name] if name in args else None)
        elif param.annotation is int:
            args_values[name] = st.number_input(name, key=f"{tool_call_id}_{name}", value=args[name] if name in args else None)
        else:
            args_values[name] = st.text_input(name, key=f"{tool_call_id}_{name}", value=args[name] if name in args else None)
    # while not clicked:
    if st.button('Submit'):
            # clicked = True
        print(args_values)
        result = func(**args_values)
        st.write(result)
        return result
        # else:
        #     st.write("Waiting for submission...")
            

def test_openai_api(api_key):
    client = OpenAI(api_key=api_key)
    try: