aiagents4pharma 1.6.2__tar.gz → 1.7.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {aiagents4pharma-1.6.2 → aiagents4pharma-1.7.0}/PKG-INFO +6 -2
- {aiagents4pharma-1.6.2 → aiagents4pharma-1.7.0}/README.md +4 -1
- {aiagents4pharma-1.6.2 → aiagents4pharma-1.7.0}/aiagents4pharma/talk2biomodels/models/basico_model.py +24 -21
- {aiagents4pharma-1.6.2 → aiagents4pharma-1.7.0}/aiagents4pharma/talk2biomodels/models/sys_bio_model.py +5 -5
- {aiagents4pharma-1.6.2 → aiagents4pharma-1.7.0}/aiagents4pharma/talk2biomodels/tools/__init__.py +3 -5
- aiagents4pharma-1.7.0/aiagents4pharma/talk2biomodels/tools/ask_question.py +72 -0
- aiagents4pharma-1.7.0/aiagents4pharma/talk2biomodels/tools/custom_plotter.py +84 -0
- aiagents4pharma-1.7.0/aiagents4pharma/talk2biomodels/tools/get_modelinfo.py +130 -0
- aiagents4pharma-1.7.0/aiagents4pharma/talk2biomodels/tools/load_biomodel.py +29 -0
- {aiagents4pharma-1.6.2 → aiagents4pharma-1.7.0}/aiagents4pharma/talk2biomodels/tools/search_models.py +9 -29
- aiagents4pharma-1.7.0/aiagents4pharma/talk2biomodels/tools/simulate_model.py +178 -0
- {aiagents4pharma-1.6.2 → aiagents4pharma-1.7.0}/aiagents4pharma.egg-info/PKG-INFO +6 -2
- {aiagents4pharma-1.6.2 → aiagents4pharma-1.7.0}/aiagents4pharma.egg-info/SOURCES.txt +2 -2
- {aiagents4pharma-1.6.2 → aiagents4pharma-1.7.0}/aiagents4pharma.egg-info/requires.txt +1 -0
- {aiagents4pharma-1.6.2 → aiagents4pharma-1.7.0}/pyproject.toml +1 -0
- aiagents4pharma-1.7.0/release_version.txt +1 -0
- aiagents4pharma-1.6.2/aiagents4pharma/talk2biomodels/tools/ask_question.py +0 -113
- aiagents4pharma-1.6.2/aiagents4pharma/talk2biomodels/tools/custom_plotter.py +0 -79
- aiagents4pharma-1.6.2/aiagents4pharma/talk2biomodels/tools/fetch_parameters.py +0 -84
- aiagents4pharma-1.6.2/aiagents4pharma/talk2biomodels/tools/model_description.py +0 -135
- aiagents4pharma-1.6.2/aiagents4pharma/talk2biomodels/tools/simulate_model.py +0 -190
- aiagents4pharma-1.6.2/release_version.txt +0 -1
- {aiagents4pharma-1.6.2 → aiagents4pharma-1.7.0}/LICENSE +0 -0
- {aiagents4pharma-1.6.2 → aiagents4pharma-1.7.0}/aiagents4pharma/__init__.py +0 -0
- {aiagents4pharma-1.6.2 → aiagents4pharma-1.7.0}/aiagents4pharma/talk2biomodels/__init__.py +0 -0
- {aiagents4pharma-1.6.2 → aiagents4pharma-1.7.0}/aiagents4pharma/talk2biomodels/models/__init__.py +0 -0
- {aiagents4pharma-1.6.2 → aiagents4pharma-1.7.0}/aiagents4pharma/talk2cells/__init__.py +0 -0
- {aiagents4pharma-1.6.2 → aiagents4pharma-1.7.0}/aiagents4pharma/talk2cells/agents/__init__.py +0 -0
- {aiagents4pharma-1.6.2 → aiagents4pharma-1.7.0}/aiagents4pharma/talk2cells/agents/scp_agent.py +0 -0
- {aiagents4pharma-1.6.2 → aiagents4pharma-1.7.0}/aiagents4pharma/talk2cells/states/__init__.py +0 -0
- {aiagents4pharma-1.6.2 → aiagents4pharma-1.7.0}/aiagents4pharma/talk2cells/states/state_talk2cells.py +0 -0
- {aiagents4pharma-1.6.2 → aiagents4pharma-1.7.0}/aiagents4pharma/talk2cells/tools/__init__.py +0 -0
- {aiagents4pharma-1.6.2 → aiagents4pharma-1.7.0}/aiagents4pharma/talk2knowledgegraphs/__init__.py +0 -0
- {aiagents4pharma-1.6.2 → aiagents4pharma-1.7.0}/aiagents4pharma/talk2knowledgegraphs/datasets/__init__.py +0 -0
- {aiagents4pharma-1.6.2 → aiagents4pharma-1.7.0}/aiagents4pharma/talk2knowledgegraphs/datasets/biobridge_primekg.py +0 -0
- {aiagents4pharma-1.6.2 → aiagents4pharma-1.7.0}/aiagents4pharma/talk2knowledgegraphs/datasets/dataset.py +0 -0
- {aiagents4pharma-1.6.2 → aiagents4pharma-1.7.0}/aiagents4pharma/talk2knowledgegraphs/datasets/primekg.py +0 -0
- {aiagents4pharma-1.6.2 → aiagents4pharma-1.7.0}/aiagents4pharma/talk2knowledgegraphs/datasets/starkqa_primekg.py +0 -0
- {aiagents4pharma-1.6.2 → aiagents4pharma-1.7.0}/aiagents4pharma/talk2knowledgegraphs/utils/__init__.py +0 -0
- {aiagents4pharma-1.6.2 → aiagents4pharma-1.7.0}/aiagents4pharma/talk2knowledgegraphs/utils/embeddings/__init__.py +0 -0
- {aiagents4pharma-1.6.2 → aiagents4pharma-1.7.0}/aiagents4pharma/talk2knowledgegraphs/utils/embeddings/embeddings.py +0 -0
- {aiagents4pharma-1.6.2 → aiagents4pharma-1.7.0}/aiagents4pharma/talk2knowledgegraphs/utils/embeddings/huggingface.py +0 -0
- {aiagents4pharma-1.6.2 → aiagents4pharma-1.7.0}/aiagents4pharma/talk2knowledgegraphs/utils/embeddings/sentence_transformer.py +0 -0
- {aiagents4pharma-1.6.2 → aiagents4pharma-1.7.0}/aiagents4pharma/talk2knowledgegraphs/utils/kg_utils.py +0 -0
- {aiagents4pharma-1.6.2 → aiagents4pharma-1.7.0}/aiagents4pharma.egg-info/dependency_links.txt +0 -0
- {aiagents4pharma-1.6.2 → aiagents4pharma-1.7.0}/aiagents4pharma.egg-info/top_level.txt +0 -0
- {aiagents4pharma-1.6.2 → aiagents4pharma-1.7.0}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.2
|
2
2
|
Name: aiagents4pharma
|
3
|
-
Version: 1.
|
3
|
+
Version: 1.7.0
|
4
4
|
Summary: AI Agents for drug discovery, drug development, and other pharmaceutical R&D
|
5
5
|
Classifier: Programming Language :: Python :: 3
|
6
6
|
Classifier: License :: OSI Approved :: MIT License
|
@@ -13,6 +13,7 @@ Requires-Dist: coverage==7.6.4
|
|
13
13
|
Requires-Dist: einops==0.8.0
|
14
14
|
Requires-Dist: gdown==5.2.0
|
15
15
|
Requires-Dist: huggingface_hub==0.26.5
|
16
|
+
Requires-Dist: hydra-core==1.3.2
|
16
17
|
Requires-Dist: joblib==1.4.2
|
17
18
|
Requires-Dist: langchain==0.3.7
|
18
19
|
Requires-Dist: langchain-community==0.3.5
|
@@ -43,7 +44,10 @@ Requires-Dist: mkdocs-include-markdown-plugin==7.1.2
|
|
43
44
|
Requires-Dist: mkdocstrings==0.27.0
|
44
45
|
Requires-Dist: streamlit-feedback
|
45
46
|
|
46
|
-
[](https://github.com/VirtualPatientEngine/AIAgents4Pharma/actions/workflows/tests_talk2biomodels.yml)
|
48
|
+
[](https://github.com/VirtualPatientEngine/AIAgents4Pharma/actions/workflows/tests_talk2cells.yml)
|
49
|
+
[](https://github.com/VirtualPatientEngine/AIAgents4Pharma/actions/workflows/tests_talk2knowledgegraphs.yml)
|
50
|
+
[](https://github.com/VirtualPatientEngine/AIAgents4Pharma/actions/workflows/tests_talk2competitors.yml)
|
47
51
|
|
48
52
|
<h1 align="center" style="border-bottom: none;">🤖 AIAgents4Pharma</h1>
|
49
53
|
|
@@ -1,4 +1,7 @@
|
|
1
|
-
[](https://github.com/VirtualPatientEngine/AIAgents4Pharma/actions/workflows/tests_talk2biomodels.yml)
|
2
|
+
[](https://github.com/VirtualPatientEngine/AIAgents4Pharma/actions/workflows/tests_talk2cells.yml)
|
3
|
+
[](https://github.com/VirtualPatientEngine/AIAgents4Pharma/actions/workflows/tests_talk2knowledgegraphs.yml)
|
4
|
+
[](https://github.com/VirtualPatientEngine/AIAgents4Pharma/actions/workflows/tests_talk2competitors.yml)
|
2
5
|
|
3
6
|
<h1 align="center" style="border-bottom: none;">🤖 AIAgents4Pharma</h1>
|
4
7
|
|
@@ -5,20 +5,23 @@ BasicoModel class for loading and simulating SBML models
|
|
5
5
|
using the basico package.
|
6
6
|
"""
|
7
7
|
|
8
|
+
import logging
|
8
9
|
from typing import Optional, Dict, Union
|
9
|
-
from time import sleep
|
10
|
-
from urllib.error import URLError
|
11
10
|
from pydantic import Field, model_validator
|
12
11
|
import pandas as pd
|
13
12
|
import basico
|
14
13
|
from .sys_bio_model import SysBioModel
|
15
14
|
|
15
|
+
# Initialize logger
|
16
|
+
logging.basicConfig(level=logging.INFO)
|
17
|
+
logger = logging.getLogger(__name__)
|
18
|
+
|
16
19
|
class BasicoModel(SysBioModel):
|
17
20
|
"""
|
18
21
|
Model that loads and simulates SBML models using the basico package.
|
19
|
-
Can load models from an SBML file or download them using a BioModels
|
22
|
+
Can load models from an SBML file or download them using a BioModels biomodel_id.
|
20
23
|
"""
|
21
|
-
|
24
|
+
biomodel_id: Optional[int] = Field(None, description="BioModels model ID to download and load")
|
22
25
|
sbml_file_path: Optional[str] = Field(None, description="Path to an SBML file to load")
|
23
26
|
simulation_results: Optional[str] = None
|
24
27
|
name: Optional[str] = Field("", description="Name of the model")
|
@@ -28,27 +31,21 @@ class BasicoModel(SysBioModel):
|
|
28
31
|
copasi_model: Optional[object] = None # Holds the loaded Copasi model
|
29
32
|
|
30
33
|
@model_validator(mode="after")
|
31
|
-
def
|
34
|
+
def check_biomodel_id_or_sbml_file_path(self):
|
32
35
|
"""
|
33
|
-
Validate that either
|
36
|
+
Validate that either biomodel_id or sbml_file_path is provided.
|
34
37
|
"""
|
35
|
-
if not self.
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
self.copasi_model = basico.load_biomodel(self.model_id)
|
43
|
-
break
|
44
|
-
except URLError as e:
|
45
|
-
attempts += 1
|
46
|
-
sleep(10*attempts)
|
47
|
-
if attempts >= max_retries:
|
48
|
-
raise e
|
49
|
-
self.description = basico.biomodels.get_model_info(self.model_id)["description"]
|
38
|
+
if not self.biomodel_id and not self.sbml_file_path:
|
39
|
+
logger.error("Either biomodel_id or sbml_file_path must be provided.")
|
40
|
+
raise ValueError("Either biomodel_id or sbml_file_path must be provided.")
|
41
|
+
if self.biomodel_id:
|
42
|
+
self.copasi_model = basico.load_biomodel(self.biomodel_id)
|
43
|
+
self.description = basico.biomodels.get_model_info(self.biomodel_id)["description"]
|
44
|
+
self.name = basico.model_info.get_model_name(model=self.copasi_model)
|
50
45
|
elif self.sbml_file_path:
|
51
46
|
self.copasi_model = basico.load_model(self.sbml_file_path)
|
47
|
+
self.description = basico.model_info.get_notes(model=self.copasi_model)
|
48
|
+
self.name = basico.model_info.get_model_name(model=self.copasi_model)
|
52
49
|
return self
|
53
50
|
|
54
51
|
def simulate(self,
|
@@ -92,10 +89,16 @@ class BasicoModel(SysBioModel):
|
|
92
89
|
df_result = basico.run_time_course(model=self.copasi_model,
|
93
90
|
intervals=interval,
|
94
91
|
duration=duration)
|
92
|
+
# Replace curly braces in column headers with square brackets
|
93
|
+
# Because curly braces in the world of LLMS are used for
|
94
|
+
# structured output
|
95
95
|
df_result.columns = df_result.columns.str.replace('{', '[', regex=False).\
|
96
96
|
str.replace('}', ']', regex=False)
|
97
|
+
# Reset the index
|
97
98
|
df_result.reset_index(inplace=True)
|
99
|
+
# Store the simulation results
|
98
100
|
self.simulation_results = df_result
|
101
|
+
# Return copy of the simulation results
|
99
102
|
return df_result.copy()
|
100
103
|
|
101
104
|
def get_model_metadata(self) -> Dict[str, Union[str, int]]:
|
@@ -12,18 +12,18 @@ class SysBioModel(ABC, BaseModel):
|
|
12
12
|
This class serves as a general structure for models, allowing
|
13
13
|
different mathematical approaches to be implemented in subclasses.
|
14
14
|
"""
|
15
|
-
|
15
|
+
biomodel_id: Optional[int] = Field(None, description="BioModel ID of the model")
|
16
16
|
sbml_file_path: Optional[str] = Field(None, description="Path to an SBML file")
|
17
17
|
name: Optional[str] = Field(..., description="Name of the model")
|
18
18
|
description: Optional[str] = Field("", description="Description of the model")
|
19
19
|
|
20
20
|
@model_validator(mode="after")
|
21
|
-
def
|
21
|
+
def check_biomodel_id_or_sbml_file_path(self):
|
22
22
|
"""
|
23
|
-
Validate that either
|
23
|
+
Validate that either biomodel_id or sbml_file_path is provided.
|
24
24
|
"""
|
25
|
-
if not self.
|
26
|
-
raise ValueError("Either
|
25
|
+
if not self.biomodel_id and not self.sbml_file_path:
|
26
|
+
raise ValueError("Either biomodel_id or sbml_file_path must be provided.")
|
27
27
|
return self
|
28
28
|
|
29
29
|
@abstractmethod
|
{aiagents4pharma-1.6.2 → aiagents4pharma-1.7.0}/aiagents4pharma/talk2biomodels/tools/__init__.py
RENAMED
@@ -1,10 +1,8 @@
|
|
1
1
|
'''
|
2
2
|
This file is used to import all the modules in the package.
|
3
3
|
'''
|
4
|
-
|
5
|
-
from . import ask_question
|
4
|
+
from . import search_models
|
6
5
|
from . import simulate_model
|
6
|
+
from . import ask_question
|
7
7
|
from . import custom_plotter
|
8
|
-
from . import
|
9
|
-
from . import model_description
|
10
|
-
from . import search_models
|
8
|
+
from . import get_modelinfo
|
@@ -0,0 +1,72 @@
|
|
1
|
+
#!/usr/bin/env python3
|
2
|
+
|
3
|
+
"""
|
4
|
+
Tool for asking a question about the simulation results.
|
5
|
+
"""
|
6
|
+
|
7
|
+
import logging
|
8
|
+
from typing import Type, Annotated
|
9
|
+
import pandas as pd
|
10
|
+
from pydantic import BaseModel, Field
|
11
|
+
from langchain_core.tools.base import BaseTool
|
12
|
+
from langchain.agents.agent_types import AgentType
|
13
|
+
from langchain_experimental.agents import create_pandas_dataframe_agent
|
14
|
+
from langchain_openai import ChatOpenAI
|
15
|
+
from langgraph.prebuilt import InjectedState
|
16
|
+
|
17
|
+
# Initialize logger
|
18
|
+
logging.basicConfig(level=logging.INFO)
|
19
|
+
logger = logging.getLogger(__name__)
|
20
|
+
|
21
|
+
class AskQuestionInput(BaseModel):
|
22
|
+
"""
|
23
|
+
Input schema for the AskQuestion tool.
|
24
|
+
"""
|
25
|
+
question: str = Field(description="question about the simulation results")
|
26
|
+
state: Annotated[dict, InjectedState]
|
27
|
+
|
28
|
+
# Note: It's important that every field has type hints.
|
29
|
+
# BaseTool is a Pydantic class and not having type hints
|
30
|
+
# can lead to unexpected behavior.
|
31
|
+
class AskQuestionTool(BaseTool):
|
32
|
+
"""
|
33
|
+
Tool for calculating the product of two numbers.
|
34
|
+
"""
|
35
|
+
name: str = "ask_question"
|
36
|
+
description: str = "A tool to ask question about the simulation results."
|
37
|
+
args_schema: Type[BaseModel] = AskQuestionInput
|
38
|
+
return_direct: bool = False
|
39
|
+
|
40
|
+
def _run(self,
|
41
|
+
question: str,
|
42
|
+
state: Annotated[dict, InjectedState]) -> str:
|
43
|
+
"""
|
44
|
+
Run the tool.
|
45
|
+
|
46
|
+
Args:
|
47
|
+
question (str): The question to ask about the simulation results.
|
48
|
+
state (dict): The state of the graph.
|
49
|
+
run_manager (Optional[CallbackManagerForToolRun]): The CallbackManagerForToolRun object.
|
50
|
+
|
51
|
+
Returns:
|
52
|
+
str: The answer to the question.
|
53
|
+
"""
|
54
|
+
logger.log(logging.INFO,
|
55
|
+
"Calling ask_question tool %s", question)
|
56
|
+
# Check if the simulation results are available
|
57
|
+
if 'dic_simulated_data' not in state:
|
58
|
+
return "Please run the simulation first before \
|
59
|
+
asking a question about the simulation results."
|
60
|
+
df = pd.DataFrame.from_dict(state['dic_simulated_data'])
|
61
|
+
prompt_content = None
|
62
|
+
# if run_manager and 'prompt' in run_manager.metadata:
|
63
|
+
# prompt_content = run_manager.metadata['prompt']
|
64
|
+
# Create a pandas dataframe agent with OpenAI
|
65
|
+
df_agent = create_pandas_dataframe_agent(
|
66
|
+
ChatOpenAI(model=state['llm_model']),
|
67
|
+
allow_dangerous_code=True,
|
68
|
+
agent_type=AgentType.OPENAI_FUNCTIONS,
|
69
|
+
df=df,
|
70
|
+
prefix=prompt_content)
|
71
|
+
llm_result = df_agent.invoke(question)
|
72
|
+
return llm_result["output"]
|
@@ -0,0 +1,84 @@
|
|
1
|
+
#!/usr/bin/env python3
|
2
|
+
|
3
|
+
"""
|
4
|
+
Tool for plotting a custom figure.
|
5
|
+
"""
|
6
|
+
|
7
|
+
import logging
|
8
|
+
from typing import Type, List, TypedDict, Annotated, Tuple, Union, Literal
|
9
|
+
from pydantic import BaseModel, Field
|
10
|
+
import pandas as pd
|
11
|
+
from langchain_openai import ChatOpenAI
|
12
|
+
from langchain_core.tools import BaseTool
|
13
|
+
from langgraph.prebuilt import InjectedState
|
14
|
+
|
15
|
+
# Initialize logger
|
16
|
+
logging.basicConfig(level=logging.INFO)
|
17
|
+
logger = logging.getLogger(__name__)
|
18
|
+
|
19
|
+
class CustomPlotterInput(BaseModel):
|
20
|
+
"""
|
21
|
+
Input schema for the PlotImage tool.
|
22
|
+
"""
|
23
|
+
question: str = Field(description="Description of the plot")
|
24
|
+
state: Annotated[dict, InjectedState]
|
25
|
+
|
26
|
+
# Note: It's important that every field has type hints.
|
27
|
+
# BaseTool is a Pydantic class and not having type hints
|
28
|
+
# can lead to unexpected behavior.
|
29
|
+
class CustomPlotterTool(BaseTool):
|
30
|
+
"""
|
31
|
+
Tool for plotting a custom plot.
|
32
|
+
"""
|
33
|
+
name: str = "custom_plotter"
|
34
|
+
description: str = "A tool to plot a custom figure."
|
35
|
+
args_schema: Type[BaseModel] = CustomPlotterInput
|
36
|
+
response_format: str = "content_and_artifact"
|
37
|
+
|
38
|
+
def _run(self,
|
39
|
+
question: str,
|
40
|
+
state: Annotated[dict, InjectedState]
|
41
|
+
) -> Tuple[str, Union[None, List[str]]]:
|
42
|
+
"""
|
43
|
+
Run the tool.
|
44
|
+
|
45
|
+
Args:
|
46
|
+
question (str): The question about the custom plot.
|
47
|
+
state (dict): The state of the graph.
|
48
|
+
|
49
|
+
Returns:
|
50
|
+
str: The answer to the question
|
51
|
+
"""
|
52
|
+
logger.log(logging.INFO, "Calling custom_plotter tool %s", question)
|
53
|
+
# Check if the simulation results are available
|
54
|
+
# if 'dic_simulated_data' not in state:
|
55
|
+
# return "Please run the simulation first before plotting the figure.", None
|
56
|
+
df = pd.DataFrame.from_dict(state['dic_simulated_data'])
|
57
|
+
species_names = df.columns.tolist()
|
58
|
+
# Exclude the time column
|
59
|
+
species_names.remove('Time')
|
60
|
+
# In the following code, we extract the species
|
61
|
+
# from the user question. We use Literal to restrict
|
62
|
+
# the species names to the ones available in the
|
63
|
+
# simulation results.
|
64
|
+
class CustomHeader(TypedDict):
|
65
|
+
"""
|
66
|
+
A list of species based on user question.
|
67
|
+
"""
|
68
|
+
relevant_species: Union[None, List[Literal[*species_names]]] = Field(
|
69
|
+
description="List of species based on user question.")
|
70
|
+
# Create an instance of the LLM model
|
71
|
+
llm = ChatOpenAI(model=state['llm_model'], temperature=0)
|
72
|
+
llm_with_structured_output = llm.with_structured_output(CustomHeader)
|
73
|
+
results = llm_with_structured_output.invoke(question)
|
74
|
+
extracted_species = []
|
75
|
+
# Extract the species from the results
|
76
|
+
# that are available in the simulation results
|
77
|
+
for species in results['relevant_species']:
|
78
|
+
if species in species_names:
|
79
|
+
extracted_species.append(species)
|
80
|
+
logger.info("Extracted species: %s", extracted_species)
|
81
|
+
if len(extracted_species) == 0:
|
82
|
+
return "No species found in the simulation results that matches the user prompt.", None
|
83
|
+
content = f"Plotted custom figure with species: {', '.join(extracted_species)}"
|
84
|
+
return content, extracted_species
|
@@ -0,0 +1,130 @@
|
|
1
|
+
#!/usr/bin/env python3
|
2
|
+
|
3
|
+
"""
|
4
|
+
Tool for get model information.
|
5
|
+
"""
|
6
|
+
|
7
|
+
import logging
|
8
|
+
from typing import Type, Optional, Annotated
|
9
|
+
from dataclasses import dataclass
|
10
|
+
import basico
|
11
|
+
from pydantic import BaseModel, Field
|
12
|
+
from langchain_core.tools import BaseTool
|
13
|
+
from langchain_core.messages import ToolMessage
|
14
|
+
from langchain_core.tools.base import InjectedToolCallId
|
15
|
+
from langgraph.prebuilt import InjectedState
|
16
|
+
from langgraph.types import Command
|
17
|
+
from .load_biomodel import ModelData, load_biomodel
|
18
|
+
|
19
|
+
# Initialize logger
|
20
|
+
logging.basicConfig(level=logging.INFO)
|
21
|
+
logger = logging.getLogger(__name__)
|
22
|
+
|
23
|
+
@dataclass
|
24
|
+
class RequestedModelInfo:
|
25
|
+
"""
|
26
|
+
Dataclass for storing the requested model information.
|
27
|
+
"""
|
28
|
+
species: bool = Field(description="Get species from the model.")
|
29
|
+
parameters: bool = Field(description="Get parameters from the model.")
|
30
|
+
compartments: bool = Field(description="Get compartments from the model.")
|
31
|
+
units: bool = Field(description="Get units from the model.")
|
32
|
+
description: bool = Field(description="Get description from the model.")
|
33
|
+
name: bool = Field(description="Get name from the model.")
|
34
|
+
|
35
|
+
class GetModelInfoInput(BaseModel):
|
36
|
+
"""
|
37
|
+
Input schema for the GetModelInfo tool.
|
38
|
+
"""
|
39
|
+
requested_model_info: RequestedModelInfo = Field(description="requested model information")
|
40
|
+
sys_bio_model: ModelData = Field(description="model data")
|
41
|
+
tool_call_id: Annotated[str, InjectedToolCallId]
|
42
|
+
state: Annotated[dict, InjectedState]
|
43
|
+
|
44
|
+
# Note: It's important that every field has type hints. BaseTool is a
|
45
|
+
# Pydantic class and not having type hints can lead to unexpected behavior.
|
46
|
+
class GetModelInfoTool(BaseTool):
|
47
|
+
"""
|
48
|
+
This tool ise used extract model information.
|
49
|
+
"""
|
50
|
+
name: str = "get_parameters"
|
51
|
+
description: str = "A tool for extracting model information."
|
52
|
+
args_schema: Type[BaseModel] = GetModelInfoInput
|
53
|
+
|
54
|
+
def _run(self,
|
55
|
+
requested_model_info: RequestedModelInfo,
|
56
|
+
tool_call_id: Annotated[str, InjectedToolCallId],
|
57
|
+
state: Annotated[dict, InjectedState],
|
58
|
+
sys_bio_model: Optional[ModelData] = None,
|
59
|
+
) -> Command:
|
60
|
+
"""
|
61
|
+
Run the tool.
|
62
|
+
|
63
|
+
Args:
|
64
|
+
requested_model_info (RequestedModelInfo): The requested model information.
|
65
|
+
tool_call_id (str): The tool call ID. This is injected by the system.
|
66
|
+
state (dict): The state of the tool.
|
67
|
+
sys_bio_model (ModelData): The model data.
|
68
|
+
|
69
|
+
Returns:
|
70
|
+
Command: The updated state of the tool.
|
71
|
+
"""
|
72
|
+
logger.log(logging.INFO,
|
73
|
+
"Calling get_modelinfo tool %s, %s",
|
74
|
+
sys_bio_model,
|
75
|
+
requested_model_info)
|
76
|
+
# print (state, 'state')
|
77
|
+
sbml_file_path = state['sbml_file_path'][-1] if len(state['sbml_file_path']) > 0 else None
|
78
|
+
model_obj = load_biomodel(sys_bio_model,
|
79
|
+
sbml_file_path=sbml_file_path)
|
80
|
+
dic_results = {}
|
81
|
+
# Extract species from the model
|
82
|
+
if requested_model_info.species:
|
83
|
+
df_species = basico.model_info.get_species(model=model_obj.copasi_model)
|
84
|
+
dic_results['Species'] = df_species.index.tolist()
|
85
|
+
dic_results['Species'] = ','.join(dic_results['Species'])
|
86
|
+
|
87
|
+
# Extract parameters from the model
|
88
|
+
if requested_model_info.parameters:
|
89
|
+
df_parameters = basico.model_info.get_parameters(model=model_obj.copasi_model)
|
90
|
+
dic_results['Parameters'] = df_parameters.index.tolist()
|
91
|
+
dic_results['Parameters'] = ','.join(dic_results['Parameters'])
|
92
|
+
|
93
|
+
# Extract compartments from the model
|
94
|
+
if requested_model_info.compartments:
|
95
|
+
df_compartments = basico.model_info.get_compartments(model=model_obj.copasi_model)
|
96
|
+
dic_results['Compartments'] = df_compartments.index.tolist()
|
97
|
+
dic_results['Compartments'] = ','.join(dic_results['Compartments'])
|
98
|
+
|
99
|
+
# Extract description from the model
|
100
|
+
if requested_model_info.description:
|
101
|
+
dic_results['Description'] = model_obj.description
|
102
|
+
|
103
|
+
# Extract description from the model
|
104
|
+
if requested_model_info.name:
|
105
|
+
dic_results['Name'] = model_obj.name
|
106
|
+
|
107
|
+
# Extract time unit from the model
|
108
|
+
if requested_model_info.units:
|
109
|
+
dic_results['Units'] = basico.model_info.get_model_units(model=model_obj.copasi_model)
|
110
|
+
|
111
|
+
# Prepare the dictionary of updated state for the model
|
112
|
+
dic_updated_state_for_model = {}
|
113
|
+
for key, value in {
|
114
|
+
"model_id": [sys_bio_model.biomodel_id],
|
115
|
+
"sbml_file_path": [sbml_file_path],
|
116
|
+
}.items():
|
117
|
+
if value:
|
118
|
+
dic_updated_state_for_model[key] = value
|
119
|
+
|
120
|
+
return Command(
|
121
|
+
update=dic_updated_state_for_model|{
|
122
|
+
# update the message history
|
123
|
+
"messages": [
|
124
|
+
ToolMessage(
|
125
|
+
content=dic_results,
|
126
|
+
tool_call_id=tool_call_id
|
127
|
+
)
|
128
|
+
],
|
129
|
+
}
|
130
|
+
)
|
@@ -0,0 +1,29 @@
|
|
1
|
+
#!/usr/bin/env python3
|
2
|
+
|
3
|
+
"""
|
4
|
+
Function for loading the BioModel.
|
5
|
+
"""
|
6
|
+
|
7
|
+
from dataclasses import dataclass
|
8
|
+
from ..models.basico_model import BasicoModel
|
9
|
+
|
10
|
+
@dataclass
|
11
|
+
class ModelData:
|
12
|
+
"""
|
13
|
+
Dataclass for storing the model data.
|
14
|
+
"""
|
15
|
+
biomodel_id: int = None
|
16
|
+
# sbml_file_path: Optional[str] = None
|
17
|
+
use_uploaded_sbml_file: bool = False
|
18
|
+
|
19
|
+
def load_biomodel(sys_bio_model, sbml_file_path=None):
|
20
|
+
"""
|
21
|
+
Load the BioModel.
|
22
|
+
"""
|
23
|
+
model_object = None
|
24
|
+
if sys_bio_model.biomodel_id:
|
25
|
+
model_object = BasicoModel(biomodel_id=sys_bio_model.biomodel_id)
|
26
|
+
elif sbml_file_path:
|
27
|
+
model_object = BasicoModel(sbml_file_path=sbml_file_path)
|
28
|
+
return model_object
|
29
|
+
# return None
|
@@ -4,21 +4,21 @@
|
|
4
4
|
Tool for searching models based on search query.
|
5
5
|
"""
|
6
6
|
|
7
|
-
from
|
8
|
-
from time import sleep
|
9
|
-
from typing import Type
|
7
|
+
from typing import Type, Annotated
|
10
8
|
from pydantic import BaseModel, Field
|
11
9
|
from basico import biomodels
|
12
10
|
from langchain_core.tools import BaseTool
|
13
11
|
from langchain_core.output_parsers import StrOutputParser
|
14
12
|
from langchain_core.prompts import ChatPromptTemplate
|
15
13
|
from langchain_openai import ChatOpenAI
|
14
|
+
from langgraph.prebuilt import InjectedState
|
16
15
|
|
17
16
|
class SearchModelsInput(BaseModel):
|
18
17
|
"""
|
19
18
|
Input schema for the search models tool.
|
20
19
|
"""
|
21
20
|
query: str = Field(description="Search models query", default=None)
|
21
|
+
state: Annotated[dict, InjectedState]
|
22
22
|
|
23
23
|
# Note: It's important that every field has type hints. BaseTool is a
|
24
24
|
# Pydantic class and not having type hints can lead to unexpected behavior.
|
@@ -31,7 +31,9 @@ class SearchModelsTool(BaseTool):
|
|
31
31
|
args_schema: Type[BaseModel] = SearchModelsInput
|
32
32
|
return_direct: bool = True
|
33
33
|
|
34
|
-
def _run(self,
|
34
|
+
def _run(self,
|
35
|
+
query: str,
|
36
|
+
state: Annotated[dict, InjectedState]) -> dict:
|
35
37
|
"""
|
36
38
|
Run the tool.
|
37
39
|
|
@@ -39,20 +41,10 @@ class SearchModelsTool(BaseTool):
|
|
39
41
|
query (str): The search query.
|
40
42
|
|
41
43
|
Returns:
|
42
|
-
|
44
|
+
dict: The answer to the question in the form of a dictionary.
|
43
45
|
"""
|
44
|
-
|
45
|
-
|
46
|
-
while attempts < max_retries:
|
47
|
-
try:
|
48
|
-
search_results = biomodels.search_for_model(query)
|
49
|
-
break
|
50
|
-
except URLError as e:
|
51
|
-
attempts += 1
|
52
|
-
sleep(10)
|
53
|
-
if attempts >= max_retries:
|
54
|
-
raise e
|
55
|
-
llm = ChatOpenAI(model="gpt-4o-mini")
|
46
|
+
search_results = biomodels.search_for_model(query)
|
47
|
+
llm = ChatOpenAI(model=state['llm_model'])
|
56
48
|
# Check if run_manager's metadata has the key 'prompt_content'
|
57
49
|
prompt_content = f'''
|
58
50
|
Convert the input into a table.
|
@@ -80,15 +72,3 @@ class SearchModelsTool(BaseTool):
|
|
80
72
|
parser = StrOutputParser()
|
81
73
|
chain = prompt_template | llm | parser
|
82
74
|
return chain.invoke({"input": search_results})
|
83
|
-
|
84
|
-
def get_metadata(self):
|
85
|
-
"""
|
86
|
-
Get metadata for the tool.
|
87
|
-
|
88
|
-
Returns:
|
89
|
-
dict: The metadata for the tool.
|
90
|
-
"""
|
91
|
-
return {
|
92
|
-
"name": self.name,
|
93
|
-
"description": self.description
|
94
|
-
}
|