aiagents4pharma 1.8.0__py3-none-any.whl → 1.15.0__py3-none-any.whl
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/__init__.py +9 -5
- aiagents4pharma/configs/__init__.py +5 -0
- aiagents4pharma/configs/config.yaml +4 -0
- aiagents4pharma/configs/talk2biomodels/__init__.py +6 -0
- aiagents4pharma/configs/talk2biomodels/agents/__init__.py +5 -0
- aiagents4pharma/configs/talk2biomodels/agents/t2b_agent/__init__.py +3 -0
- aiagents4pharma/configs/talk2biomodels/agents/t2b_agent/default.yaml +14 -0
- aiagents4pharma/configs/talk2biomodels/tools/__init__.py +4 -0
- aiagents4pharma/configs/talk2biomodels/tools/ask_question/__init__.py +3 -0
- aiagents4pharma/talk2biomodels/__init__.py +3 -0
- aiagents4pharma/talk2biomodels/agents/__init__.py +5 -0
- aiagents4pharma/talk2biomodels/agents/t2b_agent.py +96 -0
- aiagents4pharma/talk2biomodels/api/__init__.py +6 -0
- aiagents4pharma/talk2biomodels/api/kegg.py +83 -0
- aiagents4pharma/talk2biomodels/api/ols.py +72 -0
- aiagents4pharma/talk2biomodels/api/uniprot.py +35 -0
- aiagents4pharma/talk2biomodels/models/basico_model.py +29 -32
- aiagents4pharma/talk2biomodels/models/sys_bio_model.py +9 -6
- aiagents4pharma/talk2biomodels/states/__init__.py +5 -0
- aiagents4pharma/talk2biomodels/states/state_talk2biomodels.py +41 -0
- aiagents4pharma/talk2biomodels/tests/__init__.py +3 -0
- aiagents4pharma/talk2biomodels/tests/test_api.py +57 -0
- aiagents4pharma/talk2biomodels/tests/test_ask_question.py +44 -0
- aiagents4pharma/talk2biomodels/tests/test_basico_model.py +54 -0
- aiagents4pharma/talk2biomodels/tests/test_get_annotation.py +171 -0
- aiagents4pharma/talk2biomodels/tests/test_getmodelinfo.py +26 -0
- aiagents4pharma/talk2biomodels/tests/test_integration.py +126 -0
- aiagents4pharma/talk2biomodels/tests/test_param_scan.py +68 -0
- aiagents4pharma/talk2biomodels/tests/test_query_article.py +76 -0
- aiagents4pharma/talk2biomodels/tests/test_search_models.py +28 -0
- aiagents4pharma/talk2biomodels/tests/test_simulate_model.py +39 -0
- aiagents4pharma/talk2biomodels/tests/test_steady_state.py +90 -0
- aiagents4pharma/talk2biomodels/tests/test_sys_bio_model.py +63 -0
- aiagents4pharma/talk2biomodels/tools/__init__.py +5 -0
- aiagents4pharma/talk2biomodels/tools/ask_question.py +61 -18
- aiagents4pharma/talk2biomodels/tools/custom_plotter.py +20 -14
- aiagents4pharma/talk2biomodels/tools/get_annotation.py +304 -0
- aiagents4pharma/talk2biomodels/tools/get_modelinfo.py +11 -9
- aiagents4pharma/talk2biomodels/tools/load_arguments.py +114 -0
- aiagents4pharma/talk2biomodels/tools/load_biomodel.py +0 -1
- aiagents4pharma/talk2biomodels/tools/parameter_scan.py +287 -0
- aiagents4pharma/talk2biomodels/tools/query_article.py +59 -0
- aiagents4pharma/talk2biomodels/tools/simulate_model.py +35 -90
- aiagents4pharma/talk2biomodels/tools/steady_state.py +167 -0
- aiagents4pharma/talk2cells/tests/scp_agent/test_scp_agent.py +23 -0
- aiagents4pharma/talk2cells/tools/scp_agent/__init__.py +6 -0
- aiagents4pharma/talk2cells/tools/scp_agent/display_studies.py +25 -0
- aiagents4pharma/talk2cells/tools/scp_agent/search_studies.py +79 -0
- aiagents4pharma/talk2competitors/__init__.py +5 -0
- aiagents4pharma/talk2competitors/agents/__init__.py +6 -0
- aiagents4pharma/talk2competitors/agents/main_agent.py +130 -0
- aiagents4pharma/talk2competitors/agents/s2_agent.py +75 -0
- aiagents4pharma/talk2competitors/config/__init__.py +5 -0
- aiagents4pharma/talk2competitors/config/config.py +110 -0
- aiagents4pharma/talk2competitors/state/__init__.py +5 -0
- aiagents4pharma/talk2competitors/state/state_talk2competitors.py +32 -0
- aiagents4pharma/talk2competitors/tests/__init__.py +3 -0
- aiagents4pharma/talk2competitors/tests/test_langgraph.py +274 -0
- aiagents4pharma/talk2competitors/tools/__init__.py +7 -0
- aiagents4pharma/talk2competitors/tools/s2/__init__.py +8 -0
- aiagents4pharma/talk2competitors/tools/s2/display_results.py +25 -0
- aiagents4pharma/talk2competitors/tools/s2/multi_paper_rec.py +132 -0
- aiagents4pharma/talk2competitors/tools/s2/search.py +119 -0
- aiagents4pharma/talk2competitors/tools/s2/single_paper_rec.py +141 -0
- aiagents4pharma/talk2knowledgegraphs/__init__.py +2 -1
- aiagents4pharma/talk2knowledgegraphs/tests/__init__.py +0 -0
- aiagents4pharma/talk2knowledgegraphs/tests/test_datasets_biobridge_primekg.py +242 -0
- aiagents4pharma/talk2knowledgegraphs/tests/test_datasets_dataset.py +29 -0
- aiagents4pharma/talk2knowledgegraphs/tests/test_datasets_primekg.py +73 -0
- aiagents4pharma/talk2knowledgegraphs/tests/test_datasets_starkqa_primekg.py +116 -0
- aiagents4pharma/talk2knowledgegraphs/tests/test_utils_embeddings_embeddings.py +47 -0
- aiagents4pharma/talk2knowledgegraphs/tests/test_utils_embeddings_huggingface.py +45 -0
- aiagents4pharma/talk2knowledgegraphs/tests/test_utils_embeddings_sentencetransformer.py +40 -0
- aiagents4pharma/talk2knowledgegraphs/tests/test_utils_enrichments_enrichments.py +39 -0
- aiagents4pharma/talk2knowledgegraphs/tests/test_utils_enrichments_ollama.py +117 -0
- aiagents4pharma/talk2knowledgegraphs/utils/__init__.py +5 -0
- aiagents4pharma/talk2knowledgegraphs/utils/enrichments/__init__.py +5 -0
- aiagents4pharma/talk2knowledgegraphs/utils/enrichments/enrichments.py +36 -0
- aiagents4pharma/talk2knowledgegraphs/utils/enrichments/ollama.py +123 -0
- {aiagents4pharma-1.8.0.dist-info → aiagents4pharma-1.15.0.dist-info}/METADATA +44 -25
- aiagents4pharma-1.15.0.dist-info/RECORD +102 -0
- aiagents4pharma-1.8.0.dist-info/RECORD +0 -35
- {aiagents4pharma-1.8.0.dist-info → aiagents4pharma-1.15.0.dist-info}/LICENSE +0 -0
- {aiagents4pharma-1.8.0.dist-info → aiagents4pharma-1.15.0.dist-info}/WHEEL +0 -0
- {aiagents4pharma-1.8.0.dist-info → aiagents4pharma-1.15.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,114 @@
|
|
1
|
+
#!/usr/bin/env python3
|
2
|
+
|
3
|
+
"""
|
4
|
+
A utility module for defining the dataclasses
|
5
|
+
for the arguments to set up initial settings
|
6
|
+
before the experiment is run.
|
7
|
+
"""
|
8
|
+
|
9
|
+
import logging
|
10
|
+
from dataclasses import dataclass
|
11
|
+
from typing import Union, List, Optional, Annotated
|
12
|
+
from pydantic import Field
|
13
|
+
import basico
|
14
|
+
|
15
|
+
# Initialize logger
|
16
|
+
logging.basicConfig(level=logging.INFO)
|
17
|
+
logger = logging.getLogger(__name__)
|
18
|
+
|
19
|
+
@dataclass
|
20
|
+
class TimeData:
|
21
|
+
"""
|
22
|
+
Dataclass for storing the time data.
|
23
|
+
"""
|
24
|
+
duration: Union[int, float] = Field(
|
25
|
+
description="Duration of the simulation",
|
26
|
+
default=100)
|
27
|
+
interval: Union[int, float] = Field(
|
28
|
+
description="The interval is the time step or"
|
29
|
+
" the step size of the simulation. It is unrelated"
|
30
|
+
" to the step size of species concentration and parameter values.",
|
31
|
+
default=100)
|
32
|
+
|
33
|
+
@dataclass
|
34
|
+
class SpeciesInitialData:
|
35
|
+
"""
|
36
|
+
Dataclass for storing the species initial data.
|
37
|
+
"""
|
38
|
+
species_name: List[str] = Field(
|
39
|
+
description="List of species whose initial concentration is to be set."
|
40
|
+
" This does not include species that reoccur or the species whose"
|
41
|
+
" concentration is to be determined/observed at the end of the experiment."
|
42
|
+
" Do not hallucinate the species name.",
|
43
|
+
default=[])
|
44
|
+
species_concentration: List[Union[int, float]] = Field(
|
45
|
+
description="List of initial concentrations of species."
|
46
|
+
" This does not include species that reoccur or the species whose"
|
47
|
+
" concentration is to be determined/observed at the end of the experiment."
|
48
|
+
" Do not hallucinate the species concentration.",
|
49
|
+
default=[])
|
50
|
+
|
51
|
+
@dataclass
|
52
|
+
class TimeSpeciesNameConcentration:
|
53
|
+
"""
|
54
|
+
Dataclass for storing the time,
|
55
|
+
species name, and concentration data.
|
56
|
+
"""
|
57
|
+
time: Union[int, float] = Field(description="time point where the event occurs")
|
58
|
+
species_name: str = Field(description="species name")
|
59
|
+
species_concentration: Union[int, float] = Field(
|
60
|
+
description="species concentration at the time point")
|
61
|
+
|
62
|
+
@dataclass
|
63
|
+
class ReocurringData:
|
64
|
+
"""
|
65
|
+
Dataclass for species that reoccur. In other words,
|
66
|
+
the concentration of the species resets to a certain
|
67
|
+
value after a certain time interval.
|
68
|
+
"""
|
69
|
+
data: List[TimeSpeciesNameConcentration] = Field(
|
70
|
+
description="List of time, name, and concentration data"
|
71
|
+
" of species or parameters that reoccur",
|
72
|
+
default=[])
|
73
|
+
|
74
|
+
@dataclass
|
75
|
+
class ArgumentData:
|
76
|
+
"""
|
77
|
+
Dataclass for storing the argument data.
|
78
|
+
"""
|
79
|
+
experiment_name: Annotated[str, "An AI assigned _ separated name of"
|
80
|
+
" the experiment based on human query"
|
81
|
+
" and the context of the experiment."
|
82
|
+
" This must be set before the experiment is run."]
|
83
|
+
time_data: Optional[TimeData] = Field(
|
84
|
+
description="time data",
|
85
|
+
default=None)
|
86
|
+
species_to_be_analyzed_before_experiment: Optional[SpeciesInitialData] = Field(
|
87
|
+
description="Data of species whose initial concentration"
|
88
|
+
" is to be set before the experiment. This does not include"
|
89
|
+
" species that reoccur or the species whose concentration"
|
90
|
+
" is to be determined at the end of the experiment.",
|
91
|
+
default=None)
|
92
|
+
reocurring_data: Optional[ReocurringData] = Field(
|
93
|
+
description="List of concentration and time data of species that"
|
94
|
+
" reoccur. For example, a species whose concentration resets"
|
95
|
+
" to a certain value after a certain time interval.",
|
96
|
+
default=None)
|
97
|
+
|
98
|
+
def add_rec_events(model_object, reocurring_data):
|
99
|
+
"""
|
100
|
+
Add reocurring events to the model.
|
101
|
+
|
102
|
+
Args:
|
103
|
+
model_object: The model object.
|
104
|
+
reocurring_data: The reocurring data.
|
105
|
+
|
106
|
+
Returns:
|
107
|
+
None
|
108
|
+
"""
|
109
|
+
for row in reocurring_data.data:
|
110
|
+
tp, sn, sc = row.time, row.species_name, row.species_concentration
|
111
|
+
basico.add_event(f'{sn}_{tp}',
|
112
|
+
f'Time > {tp}',
|
113
|
+
[[sn, str(sc)]],
|
114
|
+
model=model_object.copasi_model)
|
@@ -0,0 +1,287 @@
|
|
1
|
+
#!/usr/bin/env python3
|
2
|
+
|
3
|
+
"""
|
4
|
+
Tool for parameter scan.
|
5
|
+
"""
|
6
|
+
|
7
|
+
import logging
|
8
|
+
from dataclasses import dataclass
|
9
|
+
from typing import Type, Union, List, Annotated, Optional
|
10
|
+
import pandas as pd
|
11
|
+
import basico
|
12
|
+
from pydantic import BaseModel, Field
|
13
|
+
from langgraph.types import Command
|
14
|
+
from langgraph.prebuilt import InjectedState
|
15
|
+
from langchain_core.tools import BaseTool
|
16
|
+
from langchain_core.messages import ToolMessage
|
17
|
+
from langchain_core.tools.base import InjectedToolCallId
|
18
|
+
from .load_biomodel import ModelData, load_biomodel
|
19
|
+
from .load_arguments import TimeData, SpeciesInitialData
|
20
|
+
|
21
|
+
# Initialize logger
|
22
|
+
logging.basicConfig(level=logging.INFO)
|
23
|
+
logger = logging.getLogger(__name__)
|
24
|
+
|
25
|
+
@dataclass
|
26
|
+
class ParameterScanData(BaseModel):
|
27
|
+
"""
|
28
|
+
Dataclass for storing the parameter scan data.
|
29
|
+
"""
|
30
|
+
species_names: List[str] = Field(
|
31
|
+
description="species to be observed after each scan."
|
32
|
+
" These are the species whose concentration"
|
33
|
+
" will be observed after the parameter scan."
|
34
|
+
" Do not make up this data.",
|
35
|
+
default=[])
|
36
|
+
species_parameter_name: str = Field(
|
37
|
+
description="Species or parameter name to be scanned."
|
38
|
+
" This is the species or parameter whose value will be scanned"
|
39
|
+
" over a range of values. This does not include the species"
|
40
|
+
" that are to be observed after the scan."
|
41
|
+
"Do not make up this data.",
|
42
|
+
default=None)
|
43
|
+
species_parameter_values: List[Union[int, float]] = Field(
|
44
|
+
description="Species or parameter values to be scanned."
|
45
|
+
" These are the values of the species or parameters that will be"
|
46
|
+
" scanned over a range of values. This does not include the "
|
47
|
+
"species that are to be observed after the scan."
|
48
|
+
"Do not make up this data.",
|
49
|
+
default=None)
|
50
|
+
|
51
|
+
@dataclass
|
52
|
+
class ArgumentData:
|
53
|
+
"""
|
54
|
+
Dataclass for storing the argument data.
|
55
|
+
"""
|
56
|
+
time_data: TimeData = Field(description="time data", default=None)
|
57
|
+
species_to_be_analyzed_before_experiment: Optional[SpeciesInitialData] = Field(
|
58
|
+
description=" This is the initial condition of the model."
|
59
|
+
" This does not include species that reoccur or the species"
|
60
|
+
" whose concentration is to be determined/observed at the end"
|
61
|
+
" of the experiment. This also does not include the species"
|
62
|
+
" or the parameter that is to be scanned. Do not make up this data.",
|
63
|
+
default=None)
|
64
|
+
parameter_scan_data: ParameterScanData = Field(
|
65
|
+
description="parameter scan data",
|
66
|
+
default=None)
|
67
|
+
experiment_name: str = Field(
|
68
|
+
description="An AI assigned `_` separated unique name of"
|
69
|
+
" the parameter scan experiment based on human query."
|
70
|
+
" This must be unique for each experiment.")
|
71
|
+
|
72
|
+
def make_list_dic_scanned_data(dic_param_scan, arg_data, sys_bio_model, tool_call_id):
|
73
|
+
"""
|
74
|
+
Prepare the list dictionary of scanned data
|
75
|
+
that will be passed to the state of the graph.
|
76
|
+
|
77
|
+
Args:
|
78
|
+
dic_param_scan: Dictionary of parameter scan results.
|
79
|
+
arg_data: The argument data.
|
80
|
+
sys_bio_model: The model data.
|
81
|
+
tool_call_id: The tool call ID.
|
82
|
+
|
83
|
+
Returns:
|
84
|
+
list: List of dictionary of scanned data.
|
85
|
+
"""
|
86
|
+
list_dic_scanned_data = []
|
87
|
+
for species_name, df_param_scan in dic_param_scan.items():
|
88
|
+
logger.log(logging.INFO, "Parameter scan results for %s with shape %s",
|
89
|
+
species_name,
|
90
|
+
df_param_scan.shape)
|
91
|
+
# Prepare the list dictionary of scanned data
|
92
|
+
# that will be passed to the state of the graph
|
93
|
+
list_dic_scanned_data.append({
|
94
|
+
'name': arg_data.experiment_name+':'+species_name,
|
95
|
+
'source': sys_bio_model.biomodel_id if sys_bio_model.biomodel_id else 'upload',
|
96
|
+
'tool_call_id': tool_call_id,
|
97
|
+
'data': df_param_scan.to_dict()
|
98
|
+
})
|
99
|
+
return list_dic_scanned_data
|
100
|
+
|
101
|
+
def run_parameter_scan(model_object,
|
102
|
+
arg_data,
|
103
|
+
dic_species_data,
|
104
|
+
duration,
|
105
|
+
interval) -> dict:
|
106
|
+
"""
|
107
|
+
Run parameter scan on the model.
|
108
|
+
|
109
|
+
Args:
|
110
|
+
model_object: The model object.
|
111
|
+
arg_data: The argument data.
|
112
|
+
dic_species_data: Dictionary of species data.
|
113
|
+
duration: Duration of the simulation.
|
114
|
+
interval: Interval between time points in the simulation.
|
115
|
+
|
116
|
+
Returns:
|
117
|
+
dict: Dictionary of parameter scan results. Each key is a species name
|
118
|
+
and each value is a DataFrame containing the results of the parameter scan.
|
119
|
+
"""
|
120
|
+
# Extract all parameter names from the model
|
121
|
+
df_all_parameters = basico.model_info.get_parameters(model=model_object.copasi_model)
|
122
|
+
all_parameters = df_all_parameters.index.tolist()
|
123
|
+
|
124
|
+
# Extract all species name from the model
|
125
|
+
df_all_species = basico.model_info.get_species(model=model_object.copasi_model)
|
126
|
+
all_species = df_all_species['display_name'].tolist()
|
127
|
+
|
128
|
+
# Verify if the given species or parameter names to be scanned are valid
|
129
|
+
if arg_data.parameter_scan_data.species_parameter_name not in all_parameters + all_species:
|
130
|
+
logger.error(
|
131
|
+
"Invalid species or parameter name: %s",
|
132
|
+
arg_data.parameter_scan_data.species_parameter_name)
|
133
|
+
raise ValueError(
|
134
|
+
"Invalid species or parameter name: "
|
135
|
+
f"{arg_data.parameter_scan_data.species_parameter_name}.")
|
136
|
+
|
137
|
+
# Dictionary to store the parameter scan results
|
138
|
+
dic_param_scan_results = {}
|
139
|
+
|
140
|
+
# Loop through the species names that are to be observed
|
141
|
+
for species_name in arg_data.parameter_scan_data.species_names:
|
142
|
+
# Verify if the given species name to be observed is valid
|
143
|
+
if species_name not in all_species:
|
144
|
+
logger.error("Invalid species name: %s", species_name)
|
145
|
+
raise ValueError(f"Invalid species name: {species_name}.")
|
146
|
+
|
147
|
+
# Copy the model object to avoid modifying the original model
|
148
|
+
model_object_copy = model_object.model_copy()
|
149
|
+
|
150
|
+
# Update the fixed model species and parameters
|
151
|
+
# These are the initial conditions of the model
|
152
|
+
# set by the user
|
153
|
+
model_object_copy.update_parameters(dic_species_data)
|
154
|
+
|
155
|
+
# Initialize empty DataFrame to store results
|
156
|
+
# of the parameter scan
|
157
|
+
df_param_scan = pd.DataFrame()
|
158
|
+
|
159
|
+
# Loop through the parameter that are to be scanned
|
160
|
+
for param_value in arg_data.parameter_scan_data.species_parameter_values:
|
161
|
+
# Update the parameter value in the model
|
162
|
+
model_object_copy.update_parameters(
|
163
|
+
{arg_data.parameter_scan_data.species_parameter_name: param_value})
|
164
|
+
# Simulate the model
|
165
|
+
model_object_copy.simulate(duration=duration, interval=interval)
|
166
|
+
# If the column name 'Time' is not present in the results DataFrame
|
167
|
+
if 'Time' not in df_param_scan.columns:
|
168
|
+
df_param_scan['Time'] = model_object_copy.simulation_results['Time']
|
169
|
+
# Add the simulation results to the results DataFrame
|
170
|
+
col_name = f"{arg_data.parameter_scan_data.species_parameter_name}_{param_value}"
|
171
|
+
df_param_scan[col_name] = model_object_copy.simulation_results[species_name]
|
172
|
+
|
173
|
+
logger.log(logging.INFO, "Parameter scan results with shape %s", df_param_scan.shape)
|
174
|
+
|
175
|
+
# Add the results of the parameter scan to the dictionary
|
176
|
+
dic_param_scan_results[species_name] = df_param_scan
|
177
|
+
# return df_param_scan
|
178
|
+
return dic_param_scan_results
|
179
|
+
|
180
|
+
class ParameterScanInput(BaseModel):
|
181
|
+
"""
|
182
|
+
Input schema for the ParameterScan tool.
|
183
|
+
"""
|
184
|
+
sys_bio_model: ModelData = Field(description="model data",
|
185
|
+
default=None)
|
186
|
+
arg_data: ArgumentData = Field(description=
|
187
|
+
"""time, species, and reocurring data
|
188
|
+
as well as the parameter scan name and
|
189
|
+
data""",
|
190
|
+
default=None)
|
191
|
+
tool_call_id: Annotated[str, InjectedToolCallId]
|
192
|
+
state: Annotated[dict, InjectedState]
|
193
|
+
|
194
|
+
# Note: It's important that every field has type hints. BaseTool is a
|
195
|
+
# Pydantic class and not having type hints can lead to unexpected behavior.
|
196
|
+
class ParameterScanTool(BaseTool):
|
197
|
+
"""
|
198
|
+
Tool for parameter scan.
|
199
|
+
"""
|
200
|
+
name: str = "parameter_scan"
|
201
|
+
description: str = """A tool to perform scanning of a given
|
202
|
+
parameter over a range of values and observe the effect on
|
203
|
+
the concentration of a given species"""
|
204
|
+
args_schema: Type[BaseModel] = ParameterScanInput
|
205
|
+
|
206
|
+
def _run(self,
|
207
|
+
tool_call_id: Annotated[str, InjectedToolCallId],
|
208
|
+
state: Annotated[dict, InjectedState],
|
209
|
+
sys_bio_model: ModelData = None,
|
210
|
+
arg_data: ArgumentData = None
|
211
|
+
) -> Command:
|
212
|
+
"""
|
213
|
+
Run the tool.
|
214
|
+
|
215
|
+
Args:
|
216
|
+
tool_call_id (str): The tool call ID. This is injected by the system.
|
217
|
+
state (dict): The state of the tool.
|
218
|
+
sys_bio_model (ModelData): The model data.
|
219
|
+
arg_data (ArgumentData): The argument data.
|
220
|
+
|
221
|
+
Returns:
|
222
|
+
Command: The updated state of the tool.
|
223
|
+
"""
|
224
|
+
logger.log(logging.INFO, "Calling parameter_scan tool %s, %s",
|
225
|
+
sys_bio_model, arg_data)
|
226
|
+
sbml_file_path = state['sbml_file_path'][-1] if len(state['sbml_file_path']) > 0 else None
|
227
|
+
model_object = load_biomodel(sys_bio_model,
|
228
|
+
sbml_file_path=sbml_file_path)
|
229
|
+
# Prepare the dictionary of species data
|
230
|
+
# that will be passed to the simulate method
|
231
|
+
# of the BasicoModel class
|
232
|
+
duration = 100.0
|
233
|
+
interval = 10
|
234
|
+
dic_species_data = {}
|
235
|
+
if arg_data:
|
236
|
+
# Prepare the dictionary of species data
|
237
|
+
if arg_data.species_to_be_analyzed_before_experiment is not None:
|
238
|
+
dic_species_data = dict(
|
239
|
+
zip(
|
240
|
+
arg_data.species_to_be_analyzed_before_experiment.species_name,
|
241
|
+
arg_data.species_to_be_analyzed_before_experiment.species_concentration
|
242
|
+
)
|
243
|
+
)
|
244
|
+
|
245
|
+
# # Add reocurring events (if any) to the model
|
246
|
+
# if arg_data.reocurring_data is not None:
|
247
|
+
# add_rec_events(model_object, arg_data.reocurring_data)
|
248
|
+
|
249
|
+
# Set the duration and interval
|
250
|
+
if arg_data.time_data is not None:
|
251
|
+
duration = arg_data.time_data.duration
|
252
|
+
interval = arg_data.time_data.interval
|
253
|
+
|
254
|
+
# Run the parameter scan
|
255
|
+
dic_param_scan = run_parameter_scan(model_object,
|
256
|
+
arg_data,
|
257
|
+
dic_species_data,
|
258
|
+
duration,
|
259
|
+
interval)
|
260
|
+
|
261
|
+
logger.log(logging.INFO, "Parameter scan results ready")
|
262
|
+
# Prepare the list dictionary of scanned data
|
263
|
+
list_dic_scanned_data = make_list_dic_scanned_data(dic_param_scan,
|
264
|
+
arg_data,
|
265
|
+
sys_bio_model,
|
266
|
+
tool_call_id)
|
267
|
+
# Prepare the dictionary of updated state for the model
|
268
|
+
dic_updated_state_for_model = {}
|
269
|
+
for key, value in {
|
270
|
+
"model_id": [sys_bio_model.biomodel_id],
|
271
|
+
"sbml_file_path": [sbml_file_path],
|
272
|
+
"dic_scanned_data": list_dic_scanned_data,
|
273
|
+
}.items():
|
274
|
+
if value:
|
275
|
+
dic_updated_state_for_model[key] = value
|
276
|
+
# Return the updated state
|
277
|
+
return Command(
|
278
|
+
update=dic_updated_state_for_model|{
|
279
|
+
# update the message history
|
280
|
+
"messages": [
|
281
|
+
ToolMessage(
|
282
|
+
content=f"Parameter scan results of {arg_data.experiment_name}",
|
283
|
+
tool_call_id=tool_call_id
|
284
|
+
)
|
285
|
+
],
|
286
|
+
}
|
287
|
+
)
|
@@ -0,0 +1,59 @@
|
|
1
|
+
#!/usr/bin/env python3
|
2
|
+
|
3
|
+
"""
|
4
|
+
Tool for asking questions to the article.
|
5
|
+
"""
|
6
|
+
|
7
|
+
import logging
|
8
|
+
from typing import Type, Annotated
|
9
|
+
from pydantic import BaseModel, Field
|
10
|
+
from langchain_core.tools import BaseTool
|
11
|
+
from langchain_core.vectorstores import InMemoryVectorStore
|
12
|
+
from langchain_openai.embeddings import OpenAIEmbeddings
|
13
|
+
from langchain_community.document_loaders import PyPDFLoader
|
14
|
+
from langgraph.prebuilt import InjectedState
|
15
|
+
|
16
|
+
# Initialize logger
|
17
|
+
logging.basicConfig(level=logging.INFO)
|
18
|
+
logger = logging.getLogger(__name__)
|
19
|
+
|
20
|
+
class QueryArticleInput(BaseModel):
|
21
|
+
"""
|
22
|
+
Input schema for the query_articles tool.
|
23
|
+
"""
|
24
|
+
question: Annotated[str, Field(description="User question to search articles.")]
|
25
|
+
state: Annotated[dict, InjectedState]
|
26
|
+
|
27
|
+
# Note: It's important that every field has type hints. BaseTool is a
|
28
|
+
# Pydantic class and not having type hints can lead to unexpected behavior.
|
29
|
+
class QueryArticle(BaseTool):
|
30
|
+
"""
|
31
|
+
Tool to ask questions to the article.
|
32
|
+
"""
|
33
|
+
name: str = "query_article"
|
34
|
+
description: str = "Ask questions to the article."
|
35
|
+
args_schema: Type[BaseModel] = QueryArticleInput
|
36
|
+
|
37
|
+
def _run(self,
|
38
|
+
question: str,
|
39
|
+
state: Annotated[dict, InjectedState]):
|
40
|
+
"""
|
41
|
+
Run the tool.
|
42
|
+
|
43
|
+
Args:
|
44
|
+
query (str): The search query.
|
45
|
+
"""
|
46
|
+
logger.log(logging.INFO, "loading the article from %s", state['pdf_file_name'])
|
47
|
+
logger.log(logging.INFO, "searching the article with the question: %s", question)
|
48
|
+
# Load the article
|
49
|
+
loader = PyPDFLoader(state['pdf_file_name'])
|
50
|
+
# Load the pages of the article
|
51
|
+
pages = []
|
52
|
+
for page in loader.lazy_load():
|
53
|
+
pages.append(page)
|
54
|
+
# Create a vector store from the pages
|
55
|
+
vector_store = InMemoryVectorStore.from_documents(pages, OpenAIEmbeddings())
|
56
|
+
# Search the article with the question
|
57
|
+
docs = vector_store.similarity_search(question)
|
58
|
+
# Return the content of the pages
|
59
|
+
return "\n".join([doc.page_content for doc in docs])
|
@@ -5,9 +5,7 @@ Tool for simulating a model.
|
|
5
5
|
"""
|
6
6
|
|
7
7
|
import logging
|
8
|
-
from
|
9
|
-
from typing import Type, Union, List, Annotated
|
10
|
-
import basico
|
8
|
+
from typing import Type, Annotated
|
11
9
|
from pydantic import BaseModel, Field
|
12
10
|
from langgraph.types import Command
|
13
11
|
from langgraph.prebuilt import InjectedState
|
@@ -15,80 +13,22 @@ from langchain_core.tools import BaseTool
|
|
15
13
|
from langchain_core.messages import ToolMessage
|
16
14
|
from langchain_core.tools.base import InjectedToolCallId
|
17
15
|
from .load_biomodel import ModelData, load_biomodel
|
16
|
+
from .load_arguments import ArgumentData, add_rec_events
|
18
17
|
|
19
18
|
# Initialize logger
|
20
19
|
logging.basicConfig(level=logging.INFO)
|
21
20
|
logger = logging.getLogger(__name__)
|
22
21
|
|
23
|
-
@dataclass
|
24
|
-
class TimeData:
|
25
|
-
"""
|
26
|
-
Dataclass for storing the time data.
|
27
|
-
"""
|
28
|
-
duration: Union[int, float] = 100
|
29
|
-
interval: Union[int, float] = 10
|
30
|
-
|
31
|
-
@dataclass
|
32
|
-
class SpeciesData:
|
33
|
-
"""
|
34
|
-
Dataclass for storing the species data.
|
35
|
-
"""
|
36
|
-
species_name: List[str] = Field(description="species name", default=None)
|
37
|
-
species_concentration: List[Union[int, float]] = Field(
|
38
|
-
description="initial species concentration",
|
39
|
-
default=None)
|
40
|
-
|
41
|
-
@dataclass
|
42
|
-
class TimeSpeciesNameConcentration:
|
43
|
-
"""
|
44
|
-
Dataclass for storing the time, species name, and concentration data.
|
45
|
-
"""
|
46
|
-
time: Union[int, float] = Field(description="time point where the event occurs")
|
47
|
-
species_name: str = Field(description="species name")
|
48
|
-
species_concentration: Union[int, float] = Field(
|
49
|
-
description="species concentration at the time point")
|
50
|
-
|
51
|
-
@dataclass
|
52
|
-
class RecurringData:
|
53
|
-
"""
|
54
|
-
Dataclass for storing the species and time data
|
55
|
-
on recurring basis.
|
56
|
-
"""
|
57
|
-
data: List[TimeSpeciesNameConcentration] = Field(
|
58
|
-
description="species and time data on recurring basis",
|
59
|
-
default=None)
|
60
|
-
|
61
|
-
@dataclass
|
62
|
-
class ArgumentData:
|
63
|
-
"""
|
64
|
-
Dataclass for storing the argument data.
|
65
|
-
"""
|
66
|
-
time_data: TimeData = Field(description="time data", default=None)
|
67
|
-
species_data: SpeciesData = Field(
|
68
|
-
description="species name and initial concentration data",
|
69
|
-
default=None)
|
70
|
-
recurring_data: RecurringData = Field(
|
71
|
-
description="species and time data on recurring basis",
|
72
|
-
default=None)
|
73
|
-
|
74
|
-
def add_rec_events(model_object, recurring_data):
|
75
|
-
"""
|
76
|
-
Add recurring events to the model.
|
77
|
-
"""
|
78
|
-
for row in recurring_data.data:
|
79
|
-
tp, sn, sc = row.time, row.species_name, row.species_concentration
|
80
|
-
basico.add_event(f'{sn}_{tp}',
|
81
|
-
f'Time > {tp}',
|
82
|
-
[[sn, str(sc)]],
|
83
|
-
model=model_object.copasi_model)
|
84
|
-
|
85
22
|
class SimulateModelInput(BaseModel):
|
86
23
|
"""
|
87
24
|
Input schema for the SimulateModel tool.
|
88
25
|
"""
|
89
|
-
sys_bio_model: ModelData = Field(description="model data",
|
90
|
-
|
91
|
-
|
26
|
+
sys_bio_model: ModelData = Field(description="model data",
|
27
|
+
default=None)
|
28
|
+
arg_data: ArgumentData = Field(description=
|
29
|
+
"""time, species, and reocurring data
|
30
|
+
as well as the simulation name""",
|
31
|
+
default=None)
|
92
32
|
tool_call_id: Annotated[str, InjectedToolCallId]
|
93
33
|
state: Annotated[dict, InjectedState]
|
94
34
|
|
@@ -132,45 +72,50 @@ class SimulateModelTool(BaseTool):
|
|
132
72
|
# of the BasicoModel class
|
133
73
|
duration = 100.0
|
134
74
|
interval = 10
|
135
|
-
|
75
|
+
dic_species_to_be_analyzed_before_experiment = {}
|
136
76
|
if arg_data:
|
137
77
|
# Prepare the dictionary of species data
|
138
|
-
if arg_data.
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
if
|
143
|
-
|
78
|
+
if arg_data.species_to_be_analyzed_before_experiment is not None:
|
79
|
+
dic_species_to_be_analyzed_before_experiment = dict(
|
80
|
+
zip(arg_data.species_to_be_analyzed_before_experiment.species_name,
|
81
|
+
arg_data.species_to_be_analyzed_before_experiment.species_concentration))
|
82
|
+
# Add reocurring events (if any) to the model
|
83
|
+
if arg_data.reocurring_data is not None:
|
84
|
+
add_rec_events(model_object, arg_data.reocurring_data)
|
144
85
|
# Set the duration and interval
|
145
86
|
if arg_data.time_data is not None:
|
146
87
|
duration = arg_data.time_data.duration
|
147
88
|
interval = arg_data.time_data.interval
|
148
|
-
|
89
|
+
# Update the model parameters
|
90
|
+
model_object.update_parameters(dic_species_to_be_analyzed_before_experiment)
|
91
|
+
logger.log(logging.INFO,
|
92
|
+
"Following species/parameters updated in the model %s",
|
93
|
+
dic_species_to_be_analyzed_before_experiment)
|
149
94
|
# Simulate the model
|
150
|
-
df = model_object.simulate(
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
95
|
+
df = model_object.simulate(duration=duration, interval=interval)
|
96
|
+
logger.log(logging.INFO, "Simulation results ready with shape %s", df.shape)
|
97
|
+
dic_simulated_data = {
|
98
|
+
'name': arg_data.experiment_name,
|
99
|
+
'source': sys_bio_model.biomodel_id if sys_bio_model.biomodel_id else 'upload',
|
100
|
+
'tool_call_id': tool_call_id,
|
101
|
+
'data': df.to_dict()
|
102
|
+
}
|
103
|
+
# Prepare the dictionary of updated state
|
157
104
|
dic_updated_state_for_model = {}
|
158
105
|
for key, value in {
|
159
|
-
|
160
|
-
|
161
|
-
|
106
|
+
"model_id": [sys_bio_model.biomodel_id],
|
107
|
+
"sbml_file_path": [sbml_file_path],
|
108
|
+
"dic_simulated_data": [dic_simulated_data],
|
109
|
+
}.items():
|
162
110
|
if value:
|
163
111
|
dic_updated_state_for_model[key] = value
|
164
|
-
|
165
112
|
# Return the updated state of the tool
|
166
113
|
return Command(
|
167
114
|
update=dic_updated_state_for_model|{
|
168
|
-
# update the state keys
|
169
|
-
"dic_simulated_data": df.to_dict(),
|
170
115
|
# update the message history
|
171
116
|
"messages": [
|
172
117
|
ToolMessage(
|
173
|
-
content="Simulation results
|
118
|
+
content=f"Simulation results of {arg_data.experiment_name}",
|
174
119
|
tool_call_id=tool_call_id
|
175
120
|
)
|
176
121
|
],
|