aiagents4pharma 1.9.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.
Files changed (66) hide show
  1. aiagents4pharma/__init__.py +9 -6
  2. aiagents4pharma/configs/config.yaml +2 -1
  3. aiagents4pharma/configs/talk2biomodels/__init__.py +1 -0
  4. aiagents4pharma/configs/talk2biomodels/agents/t2b_agent/default.yaml +9 -3
  5. aiagents4pharma/configs/talk2biomodels/tools/__init__.py +4 -0
  6. aiagents4pharma/configs/talk2biomodels/tools/ask_question/__init__.py +3 -0
  7. aiagents4pharma/talk2biomodels/__init__.py +1 -0
  8. aiagents4pharma/talk2biomodels/agents/t2b_agent.py +14 -11
  9. aiagents4pharma/talk2biomodels/api/__init__.py +6 -0
  10. aiagents4pharma/talk2biomodels/api/kegg.py +83 -0
  11. aiagents4pharma/talk2biomodels/api/ols.py +72 -0
  12. aiagents4pharma/talk2biomodels/api/uniprot.py +35 -0
  13. aiagents4pharma/talk2biomodels/models/basico_model.py +29 -32
  14. aiagents4pharma/talk2biomodels/models/sys_bio_model.py +9 -6
  15. aiagents4pharma/talk2biomodels/states/state_talk2biomodels.py +24 -7
  16. aiagents4pharma/talk2biomodels/tests/test_api.py +57 -0
  17. aiagents4pharma/talk2biomodels/tests/test_ask_question.py +44 -0
  18. aiagents4pharma/talk2biomodels/tests/test_basico_model.py +7 -8
  19. aiagents4pharma/talk2biomodels/tests/test_get_annotation.py +171 -0
  20. aiagents4pharma/talk2biomodels/tests/test_getmodelinfo.py +26 -0
  21. aiagents4pharma/talk2biomodels/tests/test_integration.py +126 -0
  22. aiagents4pharma/talk2biomodels/tests/test_param_scan.py +68 -0
  23. aiagents4pharma/talk2biomodels/tests/test_query_article.py +76 -0
  24. aiagents4pharma/talk2biomodels/tests/test_search_models.py +28 -0
  25. aiagents4pharma/talk2biomodels/tests/test_simulate_model.py +39 -0
  26. aiagents4pharma/talk2biomodels/tests/test_steady_state.py +90 -0
  27. aiagents4pharma/talk2biomodels/tests/test_sys_bio_model.py +13 -7
  28. aiagents4pharma/talk2biomodels/tools/__init__.py +4 -0
  29. aiagents4pharma/talk2biomodels/tools/ask_question.py +59 -25
  30. aiagents4pharma/talk2biomodels/tools/get_annotation.py +304 -0
  31. aiagents4pharma/talk2biomodels/tools/get_modelinfo.py +5 -3
  32. aiagents4pharma/talk2biomodels/tools/load_arguments.py +114 -0
  33. aiagents4pharma/talk2biomodels/tools/parameter_scan.py +287 -0
  34. aiagents4pharma/talk2biomodels/tools/query_article.py +59 -0
  35. aiagents4pharma/talk2biomodels/tools/simulate_model.py +20 -89
  36. aiagents4pharma/talk2biomodels/tools/steady_state.py +167 -0
  37. aiagents4pharma/talk2competitors/__init__.py +5 -0
  38. aiagents4pharma/talk2competitors/agents/__init__.py +6 -0
  39. aiagents4pharma/talk2competitors/agents/main_agent.py +130 -0
  40. aiagents4pharma/talk2competitors/agents/s2_agent.py +75 -0
  41. aiagents4pharma/talk2competitors/config/__init__.py +5 -0
  42. aiagents4pharma/talk2competitors/config/config.py +110 -0
  43. aiagents4pharma/talk2competitors/state/__init__.py +5 -0
  44. aiagents4pharma/talk2competitors/state/state_talk2competitors.py +32 -0
  45. aiagents4pharma/talk2competitors/tests/__init__.py +3 -0
  46. aiagents4pharma/talk2competitors/tests/test_langgraph.py +274 -0
  47. aiagents4pharma/talk2competitors/tools/__init__.py +7 -0
  48. aiagents4pharma/talk2competitors/tools/s2/__init__.py +8 -0
  49. aiagents4pharma/talk2competitors/tools/s2/display_results.py +25 -0
  50. aiagents4pharma/talk2competitors/tools/s2/multi_paper_rec.py +132 -0
  51. aiagents4pharma/talk2competitors/tools/s2/search.py +119 -0
  52. aiagents4pharma/talk2competitors/tools/s2/single_paper_rec.py +141 -0
  53. aiagents4pharma/talk2knowledgegraphs/__init__.py +2 -1
  54. aiagents4pharma/talk2knowledgegraphs/tests/test_utils_enrichments_enrichments.py +39 -0
  55. aiagents4pharma/talk2knowledgegraphs/tests/test_utils_enrichments_ollama.py +117 -0
  56. aiagents4pharma/talk2knowledgegraphs/utils/__init__.py +5 -0
  57. aiagents4pharma/talk2knowledgegraphs/utils/enrichments/__init__.py +5 -0
  58. aiagents4pharma/talk2knowledgegraphs/utils/enrichments/enrichments.py +36 -0
  59. aiagents4pharma/talk2knowledgegraphs/utils/enrichments/ollama.py +123 -0
  60. {aiagents4pharma-1.9.0.dist-info → aiagents4pharma-1.15.0.dist-info}/METADATA +42 -23
  61. aiagents4pharma-1.15.0.dist-info/RECORD +102 -0
  62. aiagents4pharma/talk2biomodels/tests/test_langgraph.py +0 -240
  63. aiagents4pharma-1.9.0.dist-info/RECORD +0 -62
  64. {aiagents4pharma-1.9.0.dist-info → aiagents4pharma-1.15.0.dist-info}/LICENSE +0 -0
  65. {aiagents4pharma-1.9.0.dist-info → aiagents4pharma-1.15.0.dist-info}/WHEEL +0 -0
  66. {aiagents4pharma-1.9.0.dist-info → aiagents4pharma-1.15.0.dist-info}/top_level.txt +0 -0
@@ -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 dataclasses import dataclass
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,35 +72,35 @@ class SimulateModelTool(BaseTool):
138
72
  # of the BasicoModel class
139
73
  duration = 100.0
140
74
  interval = 10
141
- dic_species_data = None
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.species_data is not None:
145
- dic_species_data = dict(zip(arg_data.species_data.species_name,
146
- arg_data.species_data.species_concentration))
147
- # Add recurring events (if any) to the model
148
- if arg_data.recurring_data is not None:
149
- add_rec_events(model_object, arg_data.recurring_data)
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
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)
155
94
  # Simulate the model
156
- df = model_object.simulate(
157
- parameters=dic_species_data,
158
- duration=duration,
159
- interval=interval
160
- )
161
-
95
+ df = model_object.simulate(duration=duration, interval=interval)
96
+ logger.log(logging.INFO, "Simulation results ready with shape %s", df.shape)
162
97
  dic_simulated_data = {
163
- 'name': arg_data.simulation_name,
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()
167
102
  }
168
-
169
- # Prepare the dictionary of updated state for the model
103
+ # Prepare the dictionary of updated state
170
104
  dic_updated_state_for_model = {}
171
105
  for key, value in {
172
106
  "model_id": [sys_bio_model.biomodel_id],
@@ -175,16 +109,13 @@ class SimulateModelTool(BaseTool):
175
109
  }.items():
176
110
  if value:
177
111
  dic_updated_state_for_model[key] = value
178
-
179
112
  # Return the updated state of the tool
180
113
  return Command(
181
114
  update=dic_updated_state_for_model|{
182
- # update the state keys
183
- # "dic_simulated_data": df.to_dict(),
184
115
  # update the message history
185
116
  "messages": [
186
117
  ToolMessage(
187
- content=f"Simulation results of {arg_data.simulation_name}",
118
+ content=f"Simulation results of {arg_data.experiment_name}",
188
119
  tool_call_id=tool_call_id
189
120
  )
190
121
  ],
@@ -0,0 +1,167 @@
1
+ #!/usr/bin/env python3
2
+
3
+ """
4
+ Tool for parameter scan.
5
+ """
6
+
7
+ import logging
8
+ from typing import Type, Annotated
9
+ import basico
10
+ from pydantic import BaseModel, Field
11
+ from langgraph.types import Command
12
+ from langgraph.prebuilt import InjectedState
13
+ from langchain_core.tools import BaseTool
14
+ from langchain_core.messages import ToolMessage
15
+ from langchain_core.tools.base import InjectedToolCallId
16
+ from .load_biomodel import ModelData, load_biomodel
17
+ from .load_arguments import ArgumentData, add_rec_events
18
+
19
+ # Initialize logger
20
+ logging.basicConfig(level=logging.INFO)
21
+ logger = logging.getLogger(__name__)
22
+
23
+ def run_steady_state(model_object,
24
+ dic_species_to_be_analyzed_before_experiment):
25
+ """
26
+ Run the steady state analysis.
27
+
28
+ Args:
29
+ model_object: The model object.
30
+ dic_species_to_be_analyzed_before_experiment: Dictionary of species data.
31
+
32
+ Returns:
33
+ DataFrame: The results of the steady state analysis.
34
+ """
35
+ # Update the fixed model species and parameters
36
+ # These are the initial conditions of the model
37
+ # set by the user
38
+ model_object.update_parameters(dic_species_to_be_analyzed_before_experiment)
39
+ logger.log(logging.INFO, "Running steady state analysis")
40
+ # Run the steady state analysis
41
+ output = basico.task_steadystate.run_steadystate(model=model_object.copasi_model)
42
+ if output == 0:
43
+ logger.error("Steady state analysis failed")
44
+ raise ValueError("A steady state was not found")
45
+ logger.log(logging.INFO, "Steady state analysis successful")
46
+ # Store the steady state results in a DataFrame
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)
71
+ logger.log(logging.INFO, "Steady state results with shape %s", df_steady_state.shape)
72
+ return df_steady_state
73
+
74
+ class SteadyStateInput(BaseModel):
75
+ """
76
+ Input schema for the steady state tool.
77
+ """
78
+ sys_bio_model: ModelData = Field(description="model data",
79
+ default=None)
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)
84
+ tool_call_id: Annotated[str, InjectedToolCallId]
85
+ state: Annotated[dict, InjectedState]
86
+
87
+ # Note: It's important that every field has type hints. BaseTool is a
88
+ # Pydantic class and not having type hints can lead to unexpected behavior.
89
+ class SteadyStateTool(BaseTool):
90
+ """
91
+ Tool to bring a model to steady state.
92
+ """
93
+ name: str = "steady_state"
94
+ description: str = "A tool to bring a model to steady state."
95
+ args_schema: Type[BaseModel] = SteadyStateInput
96
+
97
+ def _run(self,
98
+ tool_call_id: Annotated[str, InjectedToolCallId],
99
+ state: Annotated[dict, InjectedState],
100
+ sys_bio_model: ModelData = None,
101
+ arg_data: ArgumentData = None
102
+ ) -> Command:
103
+ """
104
+ Run the tool.
105
+
106
+ Args:
107
+ tool_call_id (str): The tool call ID. This is injected by the system.
108
+ state (dict): The state of the tool.
109
+ sys_bio_model (ModelData): The model data.
110
+ arg_data (ArgumentData): The argument data.
111
+
112
+ Returns:
113
+ Command: The updated state of the tool.
114
+ """
115
+ logger.log(logging.INFO, "Calling the steady_state tool %s, %s",
116
+ sys_bio_model, arg_data)
117
+ # print (f'Calling steady_state tool {sys_bio_model}, {arg_data}, {tool_call_id}')
118
+ sbml_file_path = state['sbml_file_path'][-1] if len(state['sbml_file_path']) > 0 else None
119
+ model_object = load_biomodel(sys_bio_model,
120
+ sbml_file_path=sbml_file_path)
121
+ # Prepare the dictionary of species data
122
+ # that will be passed to the simulate method
123
+ # of the BasicoModel class
124
+ dic_species_to_be_analyzed_before_experiment = {}
125
+ if arg_data:
126
+ # Prepare the dictionary of species data
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))
131
+ # Add reocurring events (if any) to the model
132
+ if arg_data.reocurring_data is not None:
133
+ add_rec_events(model_object, arg_data.reocurring_data)
134
+ # Run the parameter scan
135
+ df_steady_state = run_steady_state(model_object,
136
+ dic_species_to_be_analyzed_before_experiment)
137
+ # Prepare the dictionary of scanned data
138
+ # that will be passed to the state of the graph
139
+ dic_steady_state_data = {
140
+ 'name': arg_data.experiment_name,
141
+ 'source': sys_bio_model.biomodel_id if sys_bio_model.biomodel_id else 'upload',
142
+ 'tool_call_id': tool_call_id,
143
+ 'data': df_steady_state.to_dict(orient='records')
144
+ }
145
+ # Prepare the dictionary of updated state for the model
146
+ dic_updated_state_for_model = {}
147
+ for key, value in {
148
+ "model_id": [sys_bio_model.biomodel_id],
149
+ "sbml_file_path": [sbml_file_path],
150
+ "dic_steady_state_data": [dic_steady_state_data]
151
+ }.items():
152
+ if value:
153
+ dic_updated_state_for_model[key] = value
154
+ # Return the updated state
155
+ return Command(
156
+ update=dic_updated_state_for_model|{
157
+ # Update the message history
158
+ "messages": [
159
+ ToolMessage(
160
+ content=f"Steady state analysis of"
161
+ f" {arg_data.experiment_name}"
162
+ " was successful.",
163
+ tool_call_id=tool_call_id
164
+ )
165
+ ],
166
+ }
167
+ )
@@ -0,0 +1,5 @@
1
+ """
2
+ This file is used to import all the modules in the package.
3
+ """
4
+
5
+ from . import agents, config, state, tests, tools
@@ -0,0 +1,6 @@
1
+ '''
2
+ This file is used to import all the modules in the package.
3
+ '''
4
+
5
+ from . import main_agent
6
+ from . import s2_agent