aiagents4pharma 1.13.1__py3-none-any.whl → 1.14.1__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/configs/config.yaml +2 -1
- aiagents4pharma/configs/talk2biomodels/__init__.py +1 -0
- aiagents4pharma/configs/talk2biomodels/agents/t2b_agent/default.yaml +2 -3
- aiagents4pharma/configs/talk2biomodels/tools/__init__.py +4 -0
- aiagents4pharma/configs/talk2biomodels/tools/ask_question/__init__.py +3 -0
- aiagents4pharma/talk2biomodels/__init__.py +1 -0
- aiagents4pharma/talk2biomodels/agents/t2b_agent.py +4 -2
- 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/states/state_talk2biomodels.py +21 -6
- aiagents4pharma/talk2biomodels/tests/test_api.py +57 -0
- aiagents4pharma/talk2biomodels/tests/test_ask_question.py +44 -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_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/tools/__init__.py +1 -0
- aiagents4pharma/talk2biomodels/tools/ask_question.py +29 -8
- aiagents4pharma/talk2biomodels/tools/get_annotation.py +304 -0
- aiagents4pharma/talk2biomodels/tools/load_arguments.py +114 -0
- aiagents4pharma/talk2biomodels/tools/parameter_scan.py +91 -96
- aiagents4pharma/talk2biomodels/tools/simulate_model.py +14 -81
- aiagents4pharma/talk2biomodels/tools/steady_state.py +48 -89
- {aiagents4pharma-1.13.1.dist-info → aiagents4pharma-1.14.1.dist-info}/METADATA +1 -1
- {aiagents4pharma-1.13.1.dist-info → aiagents4pharma-1.14.1.dist-info}/RECORD +33 -17
- aiagents4pharma/talk2biomodels/tests/test_langgraph.py +0 -384
- {aiagents4pharma-1.13.1.dist-info → aiagents4pharma-1.14.1.dist-info}/LICENSE +0 -0
- {aiagents4pharma-1.13.1.dist-info → aiagents4pharma-1.14.1.dist-info}/WHEEL +0 -0
- {aiagents4pharma-1.13.1.dist-info → aiagents4pharma-1.14.1.dist-info}/top_level.txt +0 -0
@@ -6,7 +6,7 @@ Tool for parameter scan.
|
|
6
6
|
|
7
7
|
import logging
|
8
8
|
from dataclasses import dataclass
|
9
|
-
from typing import Type, Union, List, Annotated
|
9
|
+
from typing import Type, Union, List, Annotated, Optional
|
10
10
|
import pandas as pd
|
11
11
|
import basico
|
12
12
|
from pydantic import BaseModel, Field
|
@@ -16,61 +16,37 @@ from langchain_core.tools import BaseTool
|
|
16
16
|
from langchain_core.messages import ToolMessage
|
17
17
|
from langchain_core.tools.base import InjectedToolCallId
|
18
18
|
from .load_biomodel import ModelData, load_biomodel
|
19
|
+
from .load_arguments import TimeData, SpeciesInitialData
|
19
20
|
|
20
21
|
# Initialize logger
|
21
22
|
logging.basicConfig(level=logging.INFO)
|
22
23
|
logger = logging.getLogger(__name__)
|
23
24
|
|
24
|
-
@dataclass
|
25
|
-
class TimeData:
|
26
|
-
"""
|
27
|
-
Dataclass for storing the time data.
|
28
|
-
"""
|
29
|
-
duration: Union[int, float] = 100
|
30
|
-
interval: Union[int, float] = 10
|
31
|
-
|
32
|
-
@dataclass
|
33
|
-
class SpeciesData:
|
34
|
-
"""
|
35
|
-
Dataclass for storing the species data.
|
36
|
-
"""
|
37
|
-
species_name: List[str] = Field(description="species name", default=[])
|
38
|
-
species_concentration: List[Union[int, float]] = Field(
|
39
|
-
description="initial species concentration",
|
40
|
-
default=[])
|
41
|
-
|
42
|
-
@dataclass
|
43
|
-
class TimeSpeciesNameConcentration:
|
44
|
-
"""
|
45
|
-
Dataclass for storing the time, species name, and concentration data.
|
46
|
-
"""
|
47
|
-
time: Union[int, float] = Field(description="time point where the event occurs")
|
48
|
-
species_name: str = Field(description="species name")
|
49
|
-
species_concentration: Union[int, float] = Field(
|
50
|
-
description="species concentration at the time point")
|
51
|
-
|
52
|
-
@dataclass
|
53
|
-
class ReocurringData:
|
54
|
-
"""
|
55
|
-
Dataclass for species that reoccur. In other words, the concentration
|
56
|
-
of the species resets to a certain value after a certain time interval.
|
57
|
-
"""
|
58
|
-
data: List[TimeSpeciesNameConcentration] = Field(
|
59
|
-
description="time, name, and concentration data of species that reoccur",
|
60
|
-
default=[])
|
61
|
-
|
62
25
|
@dataclass
|
63
26
|
class ParameterScanData(BaseModel):
|
64
27
|
"""
|
65
28
|
Dataclass for storing the parameter scan data.
|
66
29
|
"""
|
67
|
-
species_names: List[str] = Field(
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
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)
|
74
50
|
|
75
51
|
@dataclass
|
76
52
|
class ArgumentData:
|
@@ -78,30 +54,20 @@ class ArgumentData:
|
|
78
54
|
Dataclass for storing the argument data.
|
79
55
|
"""
|
80
56
|
time_data: TimeData = Field(description="time data", default=None)
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
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)
|
88
64
|
parameter_scan_data: ParameterScanData = Field(
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
def add_rec_events(model_object, reocurring_data):
|
96
|
-
"""
|
97
|
-
Add reocurring events to the model.
|
98
|
-
"""
|
99
|
-
for row in reocurring_data.data:
|
100
|
-
tp, sn, sc = row.time, row.species_name, row.species_concentration
|
101
|
-
basico.add_event(f'{sn}_{tp}',
|
102
|
-
f'Time > {tp}',
|
103
|
-
[[sn, str(sc)]],
|
104
|
-
model=model_object.copasi_model)
|
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.")
|
105
71
|
|
106
72
|
def make_list_dic_scanned_data(dic_param_scan, arg_data, sys_bio_model, tool_call_id):
|
107
73
|
"""
|
@@ -125,13 +91,18 @@ def make_list_dic_scanned_data(dic_param_scan, arg_data, sys_bio_model, tool_cal
|
|
125
91
|
# Prepare the list dictionary of scanned data
|
126
92
|
# that will be passed to the state of the graph
|
127
93
|
list_dic_scanned_data.append({
|
128
|
-
'name': arg_data.
|
94
|
+
'name': arg_data.experiment_name+':'+species_name,
|
129
95
|
'source': sys_bio_model.biomodel_id if sys_bio_model.biomodel_id else 'upload',
|
130
96
|
'tool_call_id': tool_call_id,
|
131
97
|
'data': df_param_scan.to_dict()
|
132
98
|
})
|
133
99
|
return list_dic_scanned_data
|
134
|
-
|
100
|
+
|
101
|
+
def run_parameter_scan(model_object,
|
102
|
+
arg_data,
|
103
|
+
dic_species_data,
|
104
|
+
duration,
|
105
|
+
interval) -> dict:
|
135
106
|
"""
|
136
107
|
Run parameter scan on the model.
|
137
108
|
|
@@ -146,44 +117,61 @@ def run_parameter_scan(model_object, arg_data, dic_species_data, duration, inter
|
|
146
117
|
dict: Dictionary of parameter scan results. Each key is a species name
|
147
118
|
and each value is a DataFrame containing the results of the parameter scan.
|
148
119
|
"""
|
149
|
-
# Extract all parameter names from the model
|
120
|
+
# Extract all parameter names from the model
|
150
121
|
df_all_parameters = basico.model_info.get_parameters(model=model_object.copasi_model)
|
151
122
|
all_parameters = df_all_parameters.index.tolist()
|
152
|
-
|
153
|
-
|
154
|
-
"Invalid parameter name: %s", arg_data.parameter_scan_data.parameter_name)
|
155
|
-
raise ValueError(
|
156
|
-
f"Invalid parameter name: {arg_data.parameter_scan_data.parameter_name}")
|
157
|
-
# Extract all species name from the model and verify if the given species name is valid
|
123
|
+
|
124
|
+
# Extract all species name from the model
|
158
125
|
df_all_species = basico.model_info.get_species(model=model_object.copasi_model)
|
159
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
|
+
|
160
137
|
# Dictionary to store the parameter scan results
|
161
138
|
dic_param_scan_results = {}
|
139
|
+
|
140
|
+
# Loop through the species names that are to be observed
|
162
141
|
for species_name in arg_data.parameter_scan_data.species_names:
|
142
|
+
# Verify if the given species name to be observed is valid
|
163
143
|
if species_name not in all_species:
|
164
144
|
logger.error("Invalid species name: %s", species_name)
|
165
|
-
raise ValueError(f"Invalid species name: {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
|
+
|
166
150
|
# Update the fixed model species and parameters
|
167
151
|
# These are the initial conditions of the model
|
168
152
|
# set by the user
|
169
|
-
|
153
|
+
model_object_copy.update_parameters(dic_species_data)
|
154
|
+
|
170
155
|
# Initialize empty DataFrame to store results
|
171
156
|
# of the parameter scan
|
172
157
|
df_param_scan = pd.DataFrame()
|
173
|
-
|
158
|
+
|
159
|
+
# Loop through the parameter that are to be scanned
|
160
|
+
for param_value in arg_data.parameter_scan_data.species_parameter_values:
|
174
161
|
# Update the parameter value in the model
|
175
|
-
|
176
|
-
{arg_data.parameter_scan_data.
|
162
|
+
model_object_copy.update_parameters(
|
163
|
+
{arg_data.parameter_scan_data.species_parameter_name: param_value})
|
177
164
|
# Simulate the model
|
178
|
-
|
165
|
+
model_object_copy.simulate(duration=duration, interval=interval)
|
179
166
|
# If the column name 'Time' is not present in the results DataFrame
|
180
167
|
if 'Time' not in df_param_scan.columns:
|
181
|
-
df_param_scan['Time'] =
|
168
|
+
df_param_scan['Time'] = model_object_copy.simulation_results['Time']
|
182
169
|
# Add the simulation results to the results DataFrame
|
183
|
-
col_name = f"{arg_data.parameter_scan_data.
|
184
|
-
df_param_scan[col_name] =
|
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]
|
185
172
|
|
186
173
|
logger.log(logging.INFO, "Parameter scan results with shape %s", df_param_scan.shape)
|
174
|
+
|
187
175
|
# Add the results of the parameter scan to the dictionary
|
188
176
|
dic_param_scan_results[species_name] = df_param_scan
|
189
177
|
# return df_param_scan
|
@@ -210,8 +198,9 @@ class ParameterScanTool(BaseTool):
|
|
210
198
|
Tool for parameter scan.
|
211
199
|
"""
|
212
200
|
name: str = "parameter_scan"
|
213
|
-
description: str = """A tool to perform
|
214
|
-
|
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"""
|
215
204
|
args_schema: Type[BaseModel] = ParameterScanInput
|
216
205
|
|
217
206
|
def _run(self,
|
@@ -245,12 +234,18 @@ class ParameterScanTool(BaseTool):
|
|
245
234
|
dic_species_data = {}
|
246
235
|
if arg_data:
|
247
236
|
# Prepare the dictionary of species data
|
248
|
-
if arg_data.
|
249
|
-
dic_species_data = dict(
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
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
|
+
|
254
249
|
# Set the duration and interval
|
255
250
|
if arg_data.time_data is not None:
|
256
251
|
duration = arg_data.time_data.duration
|
@@ -284,7 +279,7 @@ class ParameterScanTool(BaseTool):
|
|
284
279
|
# update the message history
|
285
280
|
"messages": [
|
286
281
|
ToolMessage(
|
287
|
-
content=f"Parameter scan results of {arg_data.
|
282
|
+
content=f"Parameter scan results of {arg_data.experiment_name}",
|
288
283
|
tool_call_id=tool_call_id
|
289
284
|
)
|
290
285
|
],
|
@@ -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,76 +13,12 @@ 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 reocurring basis.
|
56
|
-
"""
|
57
|
-
data: List[TimeSpeciesNameConcentration] = Field(
|
58
|
-
description="species and time data on reocurring 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 reocurring basis",
|
72
|
-
default=None)
|
73
|
-
simulation_name: str = Field(
|
74
|
-
description="""An AI assigned `_` separated name of
|
75
|
-
the simulation based on human query""")
|
76
|
-
|
77
|
-
def add_rec_events(model_object, recurring_data):
|
78
|
-
"""
|
79
|
-
Add reocurring events to the model.
|
80
|
-
"""
|
81
|
-
for row in recurring_data.data:
|
82
|
-
tp, sn, sc = row.time, row.species_name, row.species_concentration
|
83
|
-
basico.add_event(f'{sn}_{tp}',
|
84
|
-
f'Time > {tp}',
|
85
|
-
[[sn, str(sc)]],
|
86
|
-
model=model_object.copasi_model)
|
87
|
-
|
88
22
|
class SimulateModelInput(BaseModel):
|
89
23
|
"""
|
90
24
|
Input schema for the SimulateModel tool.
|
@@ -138,29 +72,30 @@ class SimulateModelTool(BaseTool):
|
|
138
72
|
# of the BasicoModel class
|
139
73
|
duration = 100.0
|
140
74
|
interval = 10
|
141
|
-
|
75
|
+
dic_species_to_be_analyzed_before_experiment = {}
|
142
76
|
if arg_data:
|
143
77
|
# Prepare the dictionary of species data
|
144
|
-
if arg_data.
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
if
|
149
|
-
|
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)
|
150
85
|
# Set the duration and interval
|
151
86
|
if arg_data.time_data is not None:
|
152
87
|
duration = arg_data.time_data.duration
|
153
88
|
interval = arg_data.time_data.interval
|
154
89
|
# Update the model parameters
|
155
|
-
model_object.update_parameters(
|
90
|
+
model_object.update_parameters(dic_species_to_be_analyzed_before_experiment)
|
156
91
|
logger.log(logging.INFO,
|
157
92
|
"Following species/parameters updated in the model %s",
|
158
|
-
|
93
|
+
dic_species_to_be_analyzed_before_experiment)
|
159
94
|
# Simulate the model
|
160
95
|
df = model_object.simulate(duration=duration, interval=interval)
|
161
96
|
logger.log(logging.INFO, "Simulation results ready with shape %s", df.shape)
|
162
97
|
dic_simulated_data = {
|
163
|
-
'name': arg_data.
|
98
|
+
'name': arg_data.experiment_name,
|
164
99
|
'source': sys_bio_model.biomodel_id if sys_bio_model.biomodel_id else 'upload',
|
165
100
|
'tool_call_id': tool_call_id,
|
166
101
|
'data': df.to_dict()
|
@@ -177,12 +112,10 @@ class SimulateModelTool(BaseTool):
|
|
177
112
|
# Return the updated state of the tool
|
178
113
|
return Command(
|
179
114
|
update=dic_updated_state_for_model|{
|
180
|
-
# update the state keys
|
181
|
-
# "dic_simulated_data": df.to_dict(),
|
182
115
|
# update the message history
|
183
116
|
"messages": [
|
184
117
|
ToolMessage(
|
185
|
-
content=f"Simulation results of {arg_data.
|
118
|
+
content=f"Simulation results of {arg_data.experiment_name}",
|
186
119
|
tool_call_id=tool_call_id
|
187
120
|
)
|
188
121
|
],
|
@@ -5,8 +5,7 @@ Tool for parameter scan.
|
|
5
5
|
"""
|
6
6
|
|
7
7
|
import logging
|
8
|
-
from
|
9
|
-
from typing import Type, Union, List, Annotated
|
8
|
+
from typing import Type, Annotated
|
10
9
|
import basico
|
11
10
|
from pydantic import BaseModel, Field
|
12
11
|
from langgraph.types import Command
|
@@ -15,83 +14,20 @@ from langchain_core.tools import BaseTool
|
|
15
14
|
from langchain_core.messages import ToolMessage
|
16
15
|
from langchain_core.tools.base import InjectedToolCallId
|
17
16
|
from .load_biomodel import ModelData, load_biomodel
|
17
|
+
from .load_arguments import ArgumentData, add_rec_events
|
18
18
|
|
19
19
|
# Initialize logger
|
20
20
|
logging.basicConfig(level=logging.INFO)
|
21
21
|
logger = logging.getLogger(__name__)
|
22
22
|
|
23
|
-
|
24
|
-
|
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=[])
|
37
|
-
species_concentration: List[Union[int, float]] = Field(
|
38
|
-
description="initial species concentration",
|
39
|
-
default=[])
|
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 ReocurringData:
|
53
|
-
"""
|
54
|
-
Dataclass for species that reoccur. In other words, the concentration
|
55
|
-
of the species resets to a certain value after a certain time interval.
|
56
|
-
"""
|
57
|
-
data: List[TimeSpeciesNameConcentration] = Field(
|
58
|
-
description="time, name, and concentration data of species that reoccur",
|
59
|
-
default=[])
|
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
|
-
reocurring_data: ReocurringData = Field(
|
70
|
-
description="""Concentration and time data of species that reoccur
|
71
|
-
For example, a species whose concentration resets to a certain value
|
72
|
-
after a certain time interval""")
|
73
|
-
steadystate_name: str = Field(
|
74
|
-
description="""An AI assigned `_` separated name of
|
75
|
-
the steady state experiment based on human query""")
|
76
|
-
|
77
|
-
def add_rec_events(model_object, reocurring_data):
|
78
|
-
"""
|
79
|
-
Add reocurring events to the model.
|
80
|
-
"""
|
81
|
-
for row in reocurring_data.data:
|
82
|
-
tp, sn, sc = row.time, row.species_name, row.species_concentration
|
83
|
-
basico.add_event(f'{sn}_{tp}',
|
84
|
-
f'Time > {tp}',
|
85
|
-
[[sn, str(sc)]],
|
86
|
-
model=model_object.copasi_model)
|
87
|
-
|
88
|
-
def run_steady_state(model_object, dic_species_data):
|
23
|
+
def run_steady_state(model_object,
|
24
|
+
dic_species_to_be_analyzed_before_experiment):
|
89
25
|
"""
|
90
26
|
Run the steady state analysis.
|
91
27
|
|
92
28
|
Args:
|
93
29
|
model_object: The model object.
|
94
|
-
|
30
|
+
dic_species_to_be_analyzed_before_experiment: Dictionary of species data.
|
95
31
|
|
96
32
|
Returns:
|
97
33
|
DataFrame: The results of the steady state analysis.
|
@@ -99,7 +35,7 @@ def run_steady_state(model_object, dic_species_data):
|
|
99
35
|
# Update the fixed model species and parameters
|
100
36
|
# These are the initial conditions of the model
|
101
37
|
# set by the user
|
102
|
-
model_object.update_parameters(
|
38
|
+
model_object.update_parameters(dic_species_to_be_analyzed_before_experiment)
|
103
39
|
logger.log(logging.INFO, "Running steady state analysis")
|
104
40
|
# Run the steady state analysis
|
105
41
|
output = basico.task_steadystate.run_steadystate(model=model_object.copasi_model)
|
@@ -108,7 +44,30 @@ def run_steady_state(model_object, dic_species_data):
|
|
108
44
|
raise ValueError("A steady state was not found")
|
109
45
|
logger.log(logging.INFO, "Steady state analysis successful")
|
110
46
|
# Store the steady state results in a DataFrame
|
111
|
-
df_steady_state = basico.model_info.get_species(model=model_object.copasi_model)
|
47
|
+
df_steady_state = basico.model_info.get_species(model=model_object.copasi_model).reset_index()
|
48
|
+
# print (df_steady_state)
|
49
|
+
# Rename the column name to species_name
|
50
|
+
df_steady_state.rename(columns={'name': 'species_name'},
|
51
|
+
inplace=True)
|
52
|
+
# Rename the column concentration to steady_state_concentration
|
53
|
+
df_steady_state.rename(columns={'concentration': 'steady_state_concentration'},
|
54
|
+
inplace=True)
|
55
|
+
# Rename the column transition_time to steady_state_transition_time
|
56
|
+
df_steady_state.rename(columns={'transition_time': 'steady_state_transition_time'},
|
57
|
+
inplace=True)
|
58
|
+
# Drop some columns
|
59
|
+
df_steady_state.drop(columns=
|
60
|
+
[
|
61
|
+
'initial_particle_number',
|
62
|
+
'initial_expression',
|
63
|
+
'expression',
|
64
|
+
'particle_number',
|
65
|
+
'type',
|
66
|
+
'particle_number_rate',
|
67
|
+
'key',
|
68
|
+
'sbml_id',
|
69
|
+
'display_name'],
|
70
|
+
inplace=True)
|
112
71
|
logger.log(logging.INFO, "Steady state results with shape %s", df_steady_state.shape)
|
113
72
|
return df_steady_state
|
114
73
|
|
@@ -118,10 +77,10 @@ class SteadyStateInput(BaseModel):
|
|
118
77
|
"""
|
119
78
|
sys_bio_model: ModelData = Field(description="model data",
|
120
79
|
default=None)
|
121
|
-
arg_data: ArgumentData = Field(
|
122
|
-
|
123
|
-
|
124
|
-
|
80
|
+
arg_data: ArgumentData = Field(
|
81
|
+
description="time, species, and reocurring data"
|
82
|
+
" that must be set before the steady state analysis"
|
83
|
+
" as well as the experiment name", default=None)
|
125
84
|
tool_call_id: Annotated[str, InjectedToolCallId]
|
126
85
|
state: Annotated[dict, InjectedState]
|
127
86
|
|
@@ -129,12 +88,10 @@ class SteadyStateInput(BaseModel):
|
|
129
88
|
# Pydantic class and not having type hints can lead to unexpected behavior.
|
130
89
|
class SteadyStateTool(BaseTool):
|
131
90
|
"""
|
132
|
-
Tool
|
91
|
+
Tool to bring a model to steady state.
|
133
92
|
"""
|
134
93
|
name: str = "steady_state"
|
135
|
-
description: str = "
|
136
|
-
steady state analysisto answer questions
|
137
|
-
about the steady state of species."""
|
94
|
+
description: str = "A tool to bring a model to steady state."
|
138
95
|
args_schema: Type[BaseModel] = SteadyStateInput
|
139
96
|
|
140
97
|
def _run(self,
|
@@ -155,7 +112,7 @@ class SteadyStateTool(BaseTool):
|
|
155
112
|
Returns:
|
156
113
|
Command: The updated state of the tool.
|
157
114
|
"""
|
158
|
-
logger.log(logging.INFO, "Calling steady_state tool %s, %s",
|
115
|
+
logger.log(logging.INFO, "Calling the steady_state tool %s, %s",
|
159
116
|
sys_bio_model, arg_data)
|
160
117
|
# print (f'Calling steady_state tool {sys_bio_model}, {arg_data}, {tool_call_id}')
|
161
118
|
sbml_file_path = state['sbml_file_path'][-1] if len(state['sbml_file_path']) > 0 else None
|
@@ -164,21 +121,23 @@ class SteadyStateTool(BaseTool):
|
|
164
121
|
# Prepare the dictionary of species data
|
165
122
|
# that will be passed to the simulate method
|
166
123
|
# of the BasicoModel class
|
167
|
-
|
124
|
+
dic_species_to_be_analyzed_before_experiment = {}
|
168
125
|
if arg_data:
|
169
126
|
# Prepare the dictionary of species data
|
170
|
-
if arg_data.
|
171
|
-
|
172
|
-
|
127
|
+
if arg_data.species_to_be_analyzed_before_experiment is not None:
|
128
|
+
dic_species_to_be_analyzed_before_experiment = dict(
|
129
|
+
zip(arg_data.species_to_be_analyzed_before_experiment.species_name,
|
130
|
+
arg_data.species_to_be_analyzed_before_experiment.species_concentration))
|
173
131
|
# Add reocurring events (if any) to the model
|
174
132
|
if arg_data.reocurring_data is not None:
|
175
133
|
add_rec_events(model_object, arg_data.reocurring_data)
|
176
134
|
# Run the parameter scan
|
177
|
-
df_steady_state = run_steady_state(model_object,
|
135
|
+
df_steady_state = run_steady_state(model_object,
|
136
|
+
dic_species_to_be_analyzed_before_experiment)
|
178
137
|
# Prepare the dictionary of scanned data
|
179
138
|
# that will be passed to the state of the graph
|
180
139
|
dic_steady_state_data = {
|
181
|
-
'name': arg_data.
|
140
|
+
'name': arg_data.experiment_name,
|
182
141
|
'source': sys_bio_model.biomodel_id if sys_bio_model.biomodel_id else 'upload',
|
183
142
|
'tool_call_id': tool_call_id,
|
184
143
|
'data': df_steady_state.to_dict(orient='records')
|
@@ -198,9 +157,9 @@ class SteadyStateTool(BaseTool):
|
|
198
157
|
# Update the message history
|
199
158
|
"messages": [
|
200
159
|
ToolMessage(
|
201
|
-
content=f
|
202
|
-
{arg_data.
|
203
|
-
|
160
|
+
content=f"Steady state analysis of"
|
161
|
+
f" {arg_data.experiment_name}"
|
162
|
+
" was successful.",
|
204
163
|
tool_call_id=tool_call_id
|
205
164
|
)
|
206
165
|
],
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.2
|
2
2
|
Name: aiagents4pharma
|
3
|
-
Version: 1.
|
3
|
+
Version: 1.14.1
|
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
|