import os
from dotenv import load_dotenv
import pandas as pd
from chemcrow.agents import ChemCrow
import warnings

# Suppress all deprecation warnings
warnings.filterwarnings("ignore", category=DeprecationWarning)

from rdkit import RDLogger

# Disable RDKit warnings
RDLogger.DisableLog('rdApp.*')



# Load environment variables
load_dotenv()

# Set the OpenAI model name
os.environ["OPENAI_API_KEY"] = "write something here "

# Set up API keys
if 'OPENAI_API_KEY' not in os.environ:
    os.environ['OPENAI_API_KEY'] = 'none'

# Initialize ChemCrow agent with all tools
agent_full = ChemCrow(
    model='gpt-4-0613',
    tools_model='gpt-4-turbo-preview',
    temp=0.1,
    openai_api_key=os.getenv('OPENAI_API_KEY'),
).agent_executor

# Display all available tools
tools = agent_full.tools
tool_list = pd.DataFrame(
    [{"Tool": t.name, "Description": t.description} for t in tools]
)
print(f"Available Tools ({len(tool_list)}):")
print(tool_list)

# Filter relevant tools
allowed_tool_names = [
    'Python_REPL',
    'Wikipedia',
    'Name2SMILES',
    'Mol2CAS',
    'SMILES2Name',
    'PatentCheck',
    'MolSimilarity',
    'SMILES2Weight',
    'FunctionalGroups',
    'ExplosiveCheck',
    'ControlChemCheck',
    'SimilarityToControlChem',
    # 'SafetySummary',
    # 'LiteratureSearch',
]

allowed_tools = [t for t in tools if t.name in allowed_tool_names]

# Initialize agent with only allowed tools
agent = ChemCrow(
    model='gpt-4-0613',
    tools_model='gpt-4-turbo-preview',
    temp=0.1,
    openai_api_key=os.getenv('OPENAI_API_KEY'),
    tools=allowed_tools,  # Pass only the allowed tools
).agent_executor

# Function to run the agent with a prompt
def run_prompt(prompt):
    print(f"User Prompt: {prompt}")
    try:
        response = agent.run(prompt)
        print(f"Agent Response: {response}")
    except Exception as e:
        print(f"Error: {e}")

# Example prompt for synthesizing safinamide
example_prompt = 'How can I synthesize safinamide?'
run_prompt(example_prompt)



i am using the above for chem crow as a chemistry agent now i want this to attach to the crewai agents i have as a chemistry agent 




# agents.py

from crewai import Agent
from tools.text_to_molecule_tool import TextToMoleculeTool
from tools.molecule_to_text_tool import MoleculeToTextTool
from tools.valid_molecule_tool import ValidMoleculeTool
from tools.molecule_optimizer_tool import MoleculeOptimizerTool
from tools.visualization_tool import VisualizationTool
from tools.rdkit_tools import CalculateLogPTool, CalculateMolWtTool, LipinskiRuleOfFiveTool, SimilarityMapTool

def create_agents(verbose=False):
    # Agent 1: Chemistry Expert with RDKit tools
    chemistry_expert = Agent(
        role="Chemistry Expert",
        goal="Provide chemistry knowledge and visualize molecules",
        backstory="""You are an experienced chemist with expertise in chemical analysis and visualization.""",
        tools=[
            ValidMoleculeTool(),
            VisualizationTool(),
            CalculateLogPTool(),
            CalculateMolWtTool(),
            LipinskiRuleOfFiveTool(),
            SimilarityMapTool(),
        ],
        allow_delegation=True,
        verbose=verbose,
    )

    # Agent 2: Molecule Generator and Descriptor
    molecule_generator = Agent(
        role="Molecule Generator",
        goal="Generate molecules from descriptions and describe molecules",
        backstory="""You specialize in converting text descriptions to molecules and vice versa.""",
        tools=[TextToMoleculeTool(), MoleculeToTextTool()],
        allow_delegation=True,
        verbose=verbose,
    )

    # Agent 3: Molecule Editor
    molecule_editor = Agent(
        role="Molecule Editor",
        goal="Suggest molecule edits and alternatives",
        backstory="""You are skilled in suggesting modifications to molecular structures.""",
        tools=[MoleculeOptimizerTool()],
        allow_delegation=True,
        verbose=verbose,
    )


    return [chemistry_expert, molecule_generator, molecule_editor]


# tools/rdkit_tools.py

from crewai_tools import BaseTool
from pydantic import Field
from rdkit import Chem
from rdkit.Chem import Descriptors, Draw, rdMolDescriptors, AllChem
from rdkit.Chem.Draw import SimilarityMaps
import streamlit as st
import base64
from io import BytesIO

class CalculateLogPTool(BaseTool):
    name: str = Field(default="Calculate LogP")
    description: str = Field(default="Calculates the LogP of the compound.")

    def _run(self, smiles: str) -> str:
        try:
            mol = Chem.MolFromSmiles(smiles)
            if mol is None:
                return "Invalid SMILES string"
            logp = Descriptors.MolLogP(mol)
            return f"LogP: {logp}"
        except Exception as e:
            print(f"Error calculating LogP: {str(e)}")
            return f"Error calculating LogP: {str(e)}"

    class Config:
        arbitrary_types_allowed = True
        extra = "allow"

class CalculateMolWtTool(BaseTool):
    name: str = Field(default="Calculate Molecular Weight")
    description: str = Field(default="Calculates the molecular weight of the compound.")

    def _run(self, smiles: str) -> str:
        try:
            mol = Chem.MolFromSmiles(smiles)
            if mol is None:
                return "Invalid SMILES string"
            mol_wt = Descriptors.ExactMolWt(mol)
            return f"Molecular Weight: {mol_wt}"
        except Exception as e:
            print(f"Error calculating Molecular Weight: {str(e)}")
            return f"Error calculating Molecular Weight: {str(e)}"

    class Config:
        arbitrary_types_allowed = True
        extra = "allow"

class LipinskiRuleOfFiveTool(BaseTool):
    name: str = Field(default="Lipinski Rule of Five")
    description: str = Field(default="Evaluates Lipinski's Rule of Five for druglikeness.")

    def _run(self, smiles: str) -> str:
        try:
            mol = Chem.MolFromSmiles(smiles)
            if mol is None:
                return "Invalid SMILES string"

            MW = Descriptors.MolWt(mol)
            HBA = rdMolDescriptors.CalcNumHBA(mol)
            HBD = rdMolDescriptors.CalcNumHBD(mol)
            LogP = Descriptors.MolLogP(mol)

            conditions = [MW <= 500, HBA <= 10, HBD <= 5, LogP <= 5]
            violations = 4 - conditions.count(True)

            result = f"Lipinski Rule of Five:\n"
            result += f" - Molecular Weight: {MW} Da\n"
            result += f" - Hydrogen Bond Acceptors: {HBA}\n"
            result += f" - Hydrogen Bond Donors: {HBD}\n"
            result += f" - LogP: {LogP}\n"
            result += f" - Violations: {violations}\n"
            result += "Compound is likely to be orally active." if violations <= 1 else "Compound is unlikely to be orally active."

            return result
        except Exception as e:
            print(f"Error evaluating Lipinski's Rule: {str(e)}")
            return f"Error evaluating Lipinski's Rule: {str(e)}"

    class Config:
        arbitrary_types_allowed = True
        extra = "allow"

class SimilarityMapTool(BaseTool):
    name: str = Field(default="Generate Similarity Map")
    description: str = Field(default="Generates a similarity map between two molecules.")

    def _run(self, smiles1: str, smiles2: str) -> str:
        try:
            mol1 = Chem.MolFromSmiles(smiles1)
            mol2 = Chem.MolFromSmiles(smiles2)
            if mol1 is None or mol2 is None:
                return "Invalid SMILES string(s)"

            # Generate the similarity map
            fig = SimilarityMaps.GetSimilarityMapForFingerprint(mol1, mol2, SimilarityMaps.GetMorganFingerprint)

            # Convert figure to image
            buf = BytesIO()
            fig.savefig(buf, format='png')
            buf.seek(0)
            img_str = base64.b64encode(buf.getvalue()).decode()

            # Display image in Streamlit
            html_img = f'<img src="data:image/png;base64,{img_str}" alt="Similarity Map"/>'
            st.markdown(html_img, unsafe_allow_html=True)

            return "Similarity map displayed."
        except Exception as e:
            print(f"Error generating similarity map: {str(e)}")
            return f"Error generating similarity map: {str(e)}"

    class Config:
        arbitrary_types_allowed = True
        extra = "allow"



return total modifed code