aiagents4pharma 1.10.0__py3-none-any.whl → 1.12.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 (23) hide show
  1. aiagents4pharma/talk2biomodels/agents/t2b_agent.py +7 -10
  2. aiagents4pharma/talk2biomodels/models/basico_model.py +29 -32
  3. aiagents4pharma/talk2biomodels/models/sys_bio_model.py +9 -6
  4. aiagents4pharma/talk2biomodels/states/state_talk2biomodels.py +3 -3
  5. aiagents4pharma/talk2biomodels/tests/test_basico_model.py +7 -8
  6. aiagents4pharma/talk2biomodels/tests/test_langgraph.py +64 -2
  7. aiagents4pharma/talk2biomodels/tests/test_sys_bio_model.py +13 -7
  8. aiagents4pharma/talk2biomodels/tools/__init__.py +1 -0
  9. aiagents4pharma/talk2biomodels/tools/get_modelinfo.py +5 -3
  10. aiagents4pharma/talk2biomodels/tools/parameter_scan.py +292 -0
  11. aiagents4pharma/talk2biomodels/tools/simulate_model.py +9 -11
  12. aiagents4pharma/talk2knowledgegraphs/__init__.py +2 -1
  13. aiagents4pharma/talk2knowledgegraphs/tests/test_utils_enrichments_enrichments.py +39 -0
  14. aiagents4pharma/talk2knowledgegraphs/tests/test_utils_enrichments_ollama.py +117 -0
  15. aiagents4pharma/talk2knowledgegraphs/utils/__init__.py +5 -0
  16. aiagents4pharma/talk2knowledgegraphs/utils/enrichments/__init__.py +5 -0
  17. aiagents4pharma/talk2knowledgegraphs/utils/enrichments/enrichments.py +36 -0
  18. aiagents4pharma/talk2knowledgegraphs/utils/enrichments/ollama.py +123 -0
  19. {aiagents4pharma-1.10.0.dist-info → aiagents4pharma-1.12.0.dist-info}/METADATA +5 -6
  20. {aiagents4pharma-1.10.0.dist-info → aiagents4pharma-1.12.0.dist-info}/RECORD +23 -17
  21. {aiagents4pharma-1.10.0.dist-info → aiagents4pharma-1.12.0.dist-info}/LICENSE +0 -0
  22. {aiagents4pharma-1.10.0.dist-info → aiagents4pharma-1.12.0.dist-info}/WHEEL +0 -0
  23. {aiagents4pharma-1.10.0.dist-info → aiagents4pharma-1.12.0.dist-info}/top_level.txt +0 -0
@@ -16,6 +16,7 @@ from ..tools.get_modelinfo import GetModelInfoTool
16
16
  from ..tools.simulate_model import SimulateModelTool
17
17
  from ..tools.custom_plotter import CustomPlotterTool
18
18
  from ..tools.ask_question import AskQuestionTool
19
+ from ..tools.parameter_scan import ParameterScanTool
19
20
  from ..states.state_talk2biomodels import Talk2Biomodels
20
21
 
21
22
  # Initialize logger
@@ -35,17 +36,13 @@ def get_app(uniq_id, llm_model='gpt-4o-mini'):
35
36
  return response
36
37
 
37
38
  # Define the tools
38
- simulate_model = SimulateModelTool()
39
- custom_plotter = CustomPlotterTool()
40
- ask_question = AskQuestionTool()
41
- search_model = SearchModelsTool()
42
- get_modelinfo = GetModelInfoTool()
43
39
  tools = ToolNode([
44
- simulate_model,
45
- ask_question,
46
- custom_plotter,
47
- search_model,
48
- get_modelinfo
40
+ SimulateModelTool(),
41
+ AskQuestionTool(),
42
+ CustomPlotterTool(),
43
+ SearchModelsTool(),
44
+ GetModelInfoTool(),
45
+ ParameterScanTool()
49
46
  ])
50
47
 
51
48
  # Define the model
@@ -48,52 +48,49 @@ class BasicoModel(SysBioModel):
48
48
  self.name = basico.model_info.get_model_name(model=self.copasi_model)
49
49
  return self
50
50
 
51
- def simulate(self,
52
- parameters: Optional[Dict[str, Union[float, int]]] = None,
53
- duration: Union[int, float] = 10,
54
- interval: int = 10
55
- ) -> pd.DataFrame:
51
+ def update_parameters(self, parameters: Dict[str, Union[float, int]]) -> None:
52
+ """
53
+ Update model parameters with new values.
54
+ """
55
+ # Update parameters in the model
56
+ for param_name, param_value in parameters.items():
57
+ # check if the param_name is not None
58
+ if param_name is None:
59
+ continue
60
+ # if param is a kinetic parameter
61
+ df_all_params = basico.model_info.get_parameters(model=self.copasi_model)
62
+ if param_name in df_all_params.index.tolist():
63
+ basico.model_info.set_parameters(name=param_name,
64
+ exact=True,
65
+ initial_value=param_value,
66
+ model=self.copasi_model)
67
+ # if param is a species
68
+ else:
69
+ basico.model_info.set_species(name=param_name,
70
+ exact=True,
71
+ initial_concentration=param_value,
72
+ model=self.copasi_model)
73
+
74
+ def simulate(self, duration: Union[int, float] = 10, interval: int = 10) -> pd.DataFrame:
56
75
  """
57
76
  Simulate the COPASI model over a specified range of time points.
58
77
 
59
78
  Args:
60
- parameters: Dictionary of model parameters to update before simulation.
61
79
  duration: Duration of the simulation in time units.
62
80
  interval: Interval between time points in the simulation.
63
81
 
64
82
  Returns:
65
83
  Pandas DataFrame with time-course simulation results.
66
84
  """
67
-
68
- # Update parameters in the model
69
- if parameters:
70
- for param_name, param_value in parameters.items():
71
- # check if the param_name is not None
72
- if param_name is None:
73
- continue
74
- # if param is a kinectic parameter
75
- df_all_params = basico.model_info.get_parameters(model=self.copasi_model)
76
- if param_name in df_all_params.index.tolist():
77
- basico.model_info.set_parameters(name=param_name,
78
- exact=True,
79
- initial_value=param_value,
80
- model=self.copasi_model)
81
- # if param is a species
82
- else:
83
- basico.model_info.set_species(name=param_name,
84
- exact=True,
85
- initial_concentration=param_value,
86
- model=self.copasi_model)
87
-
88
85
  # Run the simulation and return results
89
86
  df_result = basico.run_time_course(model=self.copasi_model,
90
87
  intervals=interval,
91
88
  duration=duration)
92
- # Replace curly braces in column headers with square brackets
93
- # Because curly braces in the world of LLMS are used for
94
- # structured output
95
- df_result.columns = df_result.columns.str.replace('{', '[', regex=False).\
96
- str.replace('}', ']', regex=False)
89
+ # # Replace curly braces in column headers with square brackets
90
+ # # Because curly braces in the world of LLMS are used for
91
+ # # structured output
92
+ # df_result.columns = df_result.columns.str.replace('{', '[', regex=False).\
93
+ # str.replace('}', ']', regex=False)
97
94
  # Reset the index
98
95
  df_result.reset_index(inplace=True)
99
96
  # Store the simulation results
@@ -35,18 +35,21 @@ class SysBioModel(ABC, BaseModel):
35
35
  Returns:
36
36
  dict: Dictionary with model metadata
37
37
  """
38
+ @abstractmethod
39
+ def update_parameters(self, parameters: Dict[str, Union[float, int]]) -> None:
40
+ """
41
+ Abstract method to update model parameters.
42
+
43
+ Args:
44
+ parameters: Dictionary of parameter values.
45
+ """
38
46
 
39
47
  @abstractmethod
40
- def simulate(self,
41
- parameters: Dict[str, Union[float, int]],
42
- duration: Union[int, float]) -> List[float]:
48
+ def simulate(self, duration: Union[int, float]) -> List[float]:
43
49
  """
44
50
  Abstract method to run a simulation of the model.
45
- This method should be implemented to simulate model
46
- behavior based on the provided parameters.
47
51
 
48
52
  Args:
49
- parameters: Dictionary of parameter values.
50
53
  duration: Duration of the simulation.
51
54
 
52
55
  Returns:
@@ -12,13 +12,13 @@ class Talk2Biomodels(AgentState):
12
12
  """
13
13
  The state for the Talk2BioModels agent.
14
14
  """
15
- model_id: Annotated[list, operator.add]
16
- # sbml_file_path: str
15
+ llm_model: str
17
16
  # A StateGraph may receive a concurrent updates
18
17
  # which is not supported by the StateGraph.
19
18
  # Therefore, we need to use Annotated to specify
20
19
  # the operator for the sbml_file_path field.
21
20
  # https://langchain-ai.github.io/langgraph/troubleshooting/errors/INVALID_CONCURRENT_GRAPH_UPDATE/
21
+ model_id: Annotated[list, operator.add]
22
22
  sbml_file_path: Annotated[list, operator.add]
23
23
  dic_simulated_data: Annotated[list[dict], operator.add]
24
- llm_model: str
24
+ dic_scanned_data: Annotated[list[dict], operator.add]
@@ -19,13 +19,14 @@ def test_with_biomodel_id(model):
19
19
  Test initialization of BasicoModel with biomodel_id.
20
20
  """
21
21
  assert model.biomodel_id == 64
22
+ model.update_parameters(parameters={'Pyruvate': 0.5, 'KmPFKF6P': 1.5})
23
+ df_species = basico.model_info.get_species(model=model.copasi_model)
24
+ assert df_species.loc['Pyruvate', 'initial_concentration'] == 0.5
25
+ df_parameters = basico.model_info.get_parameters(model=model.copasi_model)
26
+ assert df_parameters.loc['KmPFKF6P', 'initial_value'] == 1.5
22
27
  # check if the simulation results are a pandas DataFrame object
23
- assert isinstance(model.simulate(parameters={'Pyruvate': 0.5, 'KmPFKF6P': 1.5},
24
- duration=2,
25
- interval=2),
26
- pd.DataFrame)
27
- assert isinstance(model.simulate(parameters={None: None}, duration=2, interval=2),
28
- pd.DataFrame)
28
+ assert isinstance(model.simulate(duration=2, interval=2), pd.DataFrame)
29
+ model.update_parameters(parameters={None: None})
29
30
  assert model.description == basico.biomodels.get_model_info(model.biomodel_id)["description"]
30
31
 
31
32
  def test_with_sbml_file():
@@ -35,8 +36,6 @@ def test_with_sbml_file():
35
36
  model_object = BasicoModel(sbml_file_path="./BIOMD0000000064_url.xml")
36
37
  assert model_object.sbml_file_path == "./BIOMD0000000064_url.xml"
37
38
  assert isinstance(model_object.simulate(duration=2, interval=2), pd.DataFrame)
38
- assert isinstance(model_object.simulate(parameters={'NADH': 0.5}, duration=2, interval=2),
39
- pd.DataFrame)
40
39
 
41
40
  def test_check_biomodel_id_or_sbml_file_path():
42
41
  '''
@@ -119,6 +119,68 @@ def test_simulate_model_tool():
119
119
  # Check if the data of the second model contains
120
120
  assert 'mTORC2' in dic_simulated_data[1]['data']
121
121
 
122
+ def test_param_scan_tool():
123
+ '''
124
+ In this test, we will test the parameter_scan tool.
125
+ We will prompt it to scan the parameter `kIL6RBind`
126
+ from 1 to 100 in steps of 10, record the changes
127
+ in the concentration of the species `Ab{serum}` in
128
+ model 537.
129
+
130
+ We will pass the inaccuarate parameter (`KIL6Rbind`)
131
+ and species names (just `Ab`) to the tool to test
132
+ if it can deal with it.
133
+
134
+ We expect the agent to first invoke the parameter_scan
135
+ tool and raise an error. It will then invoke another
136
+ tool get_modelinfo to get the correct parameter
137
+ and species names. Finally, the agent will reinvoke
138
+ the parameter_scan tool with the correct parameter
139
+ and species names.
140
+
141
+ '''
142
+ unique_id = 123
143
+ app = get_app(unique_id)
144
+ config = {"configurable": {"thread_id": unique_id}}
145
+ app.update_state(config, {"llm_model": "gpt-4o-mini"})
146
+ prompt = """How will the value of Ab in model 537 change
147
+ if the param kIL6Rbind is varied from 1 to 100 in steps of 10?
148
+ Set the initial `DoseQ2W` concentration to 300.
149
+ Reset the IL6{serum} concentration to 100 every 500 hours.
150
+ Assume that the model is simulated for 2016 hours with
151
+ an interval of 2016."""
152
+ # Invoke the agent
153
+ app.invoke(
154
+ {"messages": [HumanMessage(content=prompt)]},
155
+ config=config
156
+ )
157
+ current_state = app.get_state(config)
158
+ reversed_messages = current_state.values["messages"][::-1]
159
+ # Loop through the reversed messages until a
160
+ # ToolMessage is found.
161
+ df = pd.DataFrame(columns=['name', 'status', 'content'])
162
+ names = []
163
+ statuses = []
164
+ contents = []
165
+ for msg in reversed_messages:
166
+ # Assert that the message is a ToolMessage
167
+ # and its status is "error"
168
+ if not isinstance(msg, ToolMessage):
169
+ continue
170
+ names.append(msg.name)
171
+ statuses.append(msg.status)
172
+ contents.append(msg.content)
173
+ df = pd.DataFrame({'name': names, 'status': statuses, 'content': contents})
174
+ # print (df)
175
+ assert any((df["status"] == "error") &
176
+ (df["name"] == "parameter_scan") &
177
+ (df["content"].str.startswith("Error: ValueError('Invalid parameter name:")))
178
+ assert any((df["status"] == "success") &
179
+ (df["name"] == "parameter_scan") &
180
+ (df["content"].str.startswith("Parameter scan results of")))
181
+ assert any((df["status"] == "success") &
182
+ (df["name"] == "get_modelinfo"))
183
+
122
184
  def test_integration():
123
185
  '''
124
186
  Test the integration of the tools.
@@ -184,9 +246,9 @@ def test_integration():
184
246
  reversed_messages = current_state.values["messages"][::-1]
185
247
  # Loop through the reversed messages
186
248
  # until a ToolMessage is found.
187
- expected_header = ['Time', 'CRP[serum]', 'CRPExtracellular']
249
+ expected_header = ['Time', 'CRP{serum}', 'CRPExtracellular']
188
250
  expected_header += ['CRP Suppression (%)', 'CRP (% of baseline)']
189
- expected_header += ['CRP[liver]']
251
+ expected_header += ['CRP{liver}']
190
252
  predicted_artifact = []
191
253
  for msg in reversed_messages:
192
254
  if isinstance(msg, ToolMessage):
@@ -16,6 +16,8 @@ class TestBioModel(SysBioModel):
16
16
  sbml_file_path: Optional[str] = Field(None, description="Path to an SBML file")
17
17
  name: Optional[str] = Field(..., description="Name of the model")
18
18
  description: Optional[str] = Field("", description="Description of the model")
19
+ param1: Optional[float] = Field(0.0, description="Parameter 1")
20
+ param2: Optional[float] = Field(0.0, description="Parameter 2")
19
21
 
20
22
  def get_model_metadata(self) -> Dict[str, Union[str, int]]:
21
23
  '''
@@ -23,15 +25,18 @@ class TestBioModel(SysBioModel):
23
25
  '''
24
26
  return self.biomodel_id
25
27
 
26
- def simulate(self,
27
- parameters: Dict[str, Union[float, int]],
28
- duration: Union[int, float]) -> List[float]:
28
+ def update_parameters(self, parameters):
29
+ '''
30
+ Update the model parameters.
31
+ '''
32
+ self.param1 = parameters.get('param1', 0.0)
33
+ self.param2 = parameters.get('param2', 0.0)
34
+
35
+ def simulate(self, duration: Union[int, float]) -> List[float]:
29
36
  '''
30
37
  Simulate the model.
31
38
  '''
32
- param1 = parameters.get('param1', 0.0)
33
- param2 = parameters.get('param2', 0.0)
34
- return [param1 + param2 * t for t in range(int(duration))]
39
+ return [self.param1 + self.param2 * t for t in range(int(duration))]
35
40
 
36
41
  def test_get_model_metadata():
37
42
  '''
@@ -53,5 +58,6 @@ def test_simulate():
53
58
  Test the simulate method of the BioModel class.
54
59
  '''
55
60
  model = TestBioModel(biomodel_id=123, name="Test Model", description="A test model")
56
- results = model.simulate(parameters={'param1': 1.0, 'param2': 2.0}, duration=4.0)
61
+ model.update_parameters({'param1': 1.0, 'param2': 2.0})
62
+ results = model.simulate(duration=4.0)
57
63
  assert results == [1.0, 3.0, 5.0, 7.0]
@@ -6,4 +6,5 @@ from . import simulate_model
6
6
  from . import ask_question
7
7
  from . import custom_plotter
8
8
  from . import get_modelinfo
9
+ from . import parameter_scan
9
10
  from . import load_biomodel
@@ -47,8 +47,10 @@ class GetModelInfoTool(BaseTool):
47
47
  """
48
48
  This tool ise used extract model information.
49
49
  """
50
- name: str = "get_parameters"
51
- description: str = "A tool for extracting model information."
50
+ name: str = "get_modelinfo"
51
+ description: str = """A tool for extracting name,
52
+ description, species, parameters,
53
+ compartments, and units from a model."""
52
54
  args_schema: Type[BaseModel] = GetModelInfoInput
53
55
 
54
56
  def _run(self,
@@ -81,7 +83,7 @@ class GetModelInfoTool(BaseTool):
81
83
  # Extract species from the model
82
84
  if requested_model_info.species:
83
85
  df_species = basico.model_info.get_species(model=model_obj.copasi_model)
84
- dic_results['Species'] = df_species.index.tolist()
86
+ dic_results['Species'] = df_species['display_name'].tolist()
85
87
  dic_results['Species'] = ','.join(dic_results['Species'])
86
88
 
87
89
  # Extract parameters from the model
@@ -0,0 +1,292 @@
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
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
+
20
+ # Initialize logger
21
+ logging.basicConfig(level=logging.INFO)
22
+ logger = logging.getLogger(__name__)
23
+
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
+ @dataclass
63
+ class ParameterScanData(BaseModel):
64
+ """
65
+ Dataclass for storing the parameter scan data.
66
+ """
67
+ species_names: List[str] = Field(description="species names to scan",
68
+ default=[])
69
+ parameter_name: str = Field(description="Parameter name to scan",
70
+ default_factory=None)
71
+ parameter_values: List[Union[int, float]] = Field(
72
+ description="Parameter values to scan",
73
+ default_factory=None)
74
+
75
+ @dataclass
76
+ class ArgumentData:
77
+ """
78
+ Dataclass for storing the argument data.
79
+ """
80
+ time_data: TimeData = Field(description="time data", default=None)
81
+ species_data: SpeciesData = Field(
82
+ description="species name and initial concentration data",
83
+ default=None)
84
+ reocurring_data: ReocurringData = Field(
85
+ description="""Concentration and time data of species that reoccur
86
+ For example, a species whose concentration resets to a certain value
87
+ after a certain time interval""")
88
+ parameter_scan_data: ParameterScanData = Field(
89
+ description="parameter scan data",
90
+ default=None)
91
+ scan_name: str = Field(
92
+ description="""An AI assigned `_` separated name of
93
+ the parameter scan experiment based on human query""")
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)
105
+
106
+ def make_list_dic_scanned_data(dic_param_scan, arg_data, sys_bio_model, tool_call_id):
107
+ """
108
+ Prepare the list dictionary of scanned data
109
+ that will be passed to the state of the graph.
110
+
111
+ Args:
112
+ dic_param_scan: Dictionary of parameter scan results.
113
+ arg_data: The argument data.
114
+ sys_bio_model: The model data.
115
+ tool_call_id: The tool call ID.
116
+
117
+ Returns:
118
+ list: List of dictionary of scanned data.
119
+ """
120
+ list_dic_scanned_data = []
121
+ for species_name, df_param_scan in dic_param_scan.items():
122
+ logger.log(logging.INFO, "Parameter scan results for %s with shape %s",
123
+ species_name,
124
+ df_param_scan.shape)
125
+ # Prepare the list dictionary of scanned data
126
+ # that will be passed to the state of the graph
127
+ list_dic_scanned_data.append({
128
+ 'name': arg_data.scan_name+':'+species_name,
129
+ 'source': sys_bio_model.biomodel_id if sys_bio_model.biomodel_id else 'upload',
130
+ 'tool_call_id': tool_call_id,
131
+ 'data': df_param_scan.to_dict()
132
+ })
133
+ return list_dic_scanned_data
134
+ def run_parameter_scan(model_object, arg_data, dic_species_data, duration, interval) -> dict:
135
+ """
136
+ Run parameter scan on the model.
137
+
138
+ Args:
139
+ model_object: The model object.
140
+ arg_data: The argument data.
141
+ dic_species_data: Dictionary of species data.
142
+ duration: Duration of the simulation.
143
+ interval: Interval between time points in the simulation.
144
+
145
+ Returns:
146
+ dict: Dictionary of parameter scan results. Each key is a species name
147
+ and each value is a DataFrame containing the results of the parameter scan.
148
+ """
149
+ # Extract all parameter names from the model and verify if the given parameter name is valid
150
+ df_all_parameters = basico.model_info.get_parameters(model=model_object.copasi_model)
151
+ all_parameters = df_all_parameters.index.tolist()
152
+ if arg_data.parameter_scan_data.parameter_name not in all_parameters:
153
+ logger.error(
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
158
+ df_all_species = basico.model_info.get_species(model=model_object.copasi_model)
159
+ all_species = df_all_species['display_name'].tolist()
160
+ # Dictionary to store the parameter scan results
161
+ dic_param_scan_results = {}
162
+ for species_name in arg_data.parameter_scan_data.species_names:
163
+ if species_name not in all_species:
164
+ logger.error("Invalid species name: %s", species_name)
165
+ raise ValueError(f"Invalid species name: {species_name}")
166
+ # Update the fixed model species and parameters
167
+ # These are the initial conditions of the model
168
+ # set by the user
169
+ model_object.update_parameters(dic_species_data)
170
+ # Initialize empty DataFrame to store results
171
+ # of the parameter scan
172
+ df_param_scan = pd.DataFrame()
173
+ for param_value in arg_data.parameter_scan_data.parameter_values:
174
+ # Update the parameter value in the model
175
+ model_object.update_parameters(
176
+ {arg_data.parameter_scan_data.parameter_name: param_value})
177
+ # Simulate the model
178
+ model_object.simulate(duration=duration, interval=interval)
179
+ # If the column name 'Time' is not present in the results DataFrame
180
+ if 'Time' not in df_param_scan.columns:
181
+ df_param_scan['Time'] = model_object.simulation_results['Time']
182
+ # Add the simulation results to the results DataFrame
183
+ col_name = f"{arg_data.parameter_scan_data.parameter_name}_{param_value}"
184
+ df_param_scan[col_name] = model_object.simulation_results[species_name]
185
+
186
+ logger.log(logging.INFO, "Parameter scan results with shape %s", df_param_scan.shape)
187
+ # Add the results of the parameter scan to the dictionary
188
+ dic_param_scan_results[species_name] = df_param_scan
189
+ # return df_param_scan
190
+ return dic_param_scan_results
191
+
192
+ class ParameterScanInput(BaseModel):
193
+ """
194
+ Input schema for the ParameterScan tool.
195
+ """
196
+ sys_bio_model: ModelData = Field(description="model data",
197
+ default=None)
198
+ arg_data: ArgumentData = Field(description=
199
+ """time, species, and reocurring data
200
+ as well as the parameter scan name and
201
+ data""",
202
+ default=None)
203
+ tool_call_id: Annotated[str, InjectedToolCallId]
204
+ state: Annotated[dict, InjectedState]
205
+
206
+ # Note: It's important that every field has type hints. BaseTool is a
207
+ # Pydantic class and not having type hints can lead to unexpected behavior.
208
+ class ParameterScanTool(BaseTool):
209
+ """
210
+ Tool for parameter scan.
211
+ """
212
+ name: str = "parameter_scan"
213
+ description: str = """A tool to perform parameter scan
214
+ of a list of parameter values for a given species."""
215
+ args_schema: Type[BaseModel] = ParameterScanInput
216
+
217
+ def _run(self,
218
+ tool_call_id: Annotated[str, InjectedToolCallId],
219
+ state: Annotated[dict, InjectedState],
220
+ sys_bio_model: ModelData = None,
221
+ arg_data: ArgumentData = None
222
+ ) -> Command:
223
+ """
224
+ Run the tool.
225
+
226
+ Args:
227
+ tool_call_id (str): The tool call ID. This is injected by the system.
228
+ state (dict): The state of the tool.
229
+ sys_bio_model (ModelData): The model data.
230
+ arg_data (ArgumentData): The argument data.
231
+
232
+ Returns:
233
+ Command: The updated state of the tool.
234
+ """
235
+ logger.log(logging.INFO, "Calling parameter_scan tool %s, %s",
236
+ sys_bio_model, arg_data)
237
+ sbml_file_path = state['sbml_file_path'][-1] if len(state['sbml_file_path']) > 0 else None
238
+ model_object = load_biomodel(sys_bio_model,
239
+ sbml_file_path=sbml_file_path)
240
+ # Prepare the dictionary of species data
241
+ # that will be passed to the simulate method
242
+ # of the BasicoModel class
243
+ duration = 100.0
244
+ interval = 10
245
+ dic_species_data = {}
246
+ if arg_data:
247
+ # Prepare the dictionary of species data
248
+ if arg_data.species_data is not None:
249
+ dic_species_data = dict(zip(arg_data.species_data.species_name,
250
+ arg_data.species_data.species_concentration))
251
+ # Add reocurring events (if any) to the model
252
+ if arg_data.reocurring_data is not None:
253
+ add_rec_events(model_object, arg_data.reocurring_data)
254
+ # Set the duration and interval
255
+ if arg_data.time_data is not None:
256
+ duration = arg_data.time_data.duration
257
+ interval = arg_data.time_data.interval
258
+
259
+ # Run the parameter scan
260
+ dic_param_scan = run_parameter_scan(model_object,
261
+ arg_data,
262
+ dic_species_data,
263
+ duration,
264
+ interval)
265
+
266
+ logger.log(logging.INFO, "Parameter scan results ready")
267
+ # Prepare the list dictionary of scanned data
268
+ list_dic_scanned_data = make_list_dic_scanned_data(dic_param_scan,
269
+ arg_data,
270
+ sys_bio_model,
271
+ tool_call_id)
272
+ # Prepare the dictionary of updated state for the model
273
+ dic_updated_state_for_model = {}
274
+ for key, value in {
275
+ "model_id": [sys_bio_model.biomodel_id],
276
+ "sbml_file_path": [sbml_file_path],
277
+ "dic_scanned_data": list_dic_scanned_data,
278
+ }.items():
279
+ if value:
280
+ dic_updated_state_for_model[key] = value
281
+ # Return the updated state
282
+ return Command(
283
+ update=dic_updated_state_for_model|{
284
+ # update the message history
285
+ "messages": [
286
+ ToolMessage(
287
+ content=f"Parameter scan results of {arg_data.scan_name}",
288
+ tool_call_id=tool_call_id
289
+ )
290
+ ],
291
+ }
292
+ )
@@ -138,7 +138,7 @@ class SimulateModelTool(BaseTool):
138
138
  # of the BasicoModel class
139
139
  duration = 100.0
140
140
  interval = 10
141
- dic_species_data = None
141
+ dic_species_data = {}
142
142
  if arg_data:
143
143
  # Prepare the dictionary of species data
144
144
  if arg_data.species_data is not None:
@@ -151,22 +151,21 @@ class SimulateModelTool(BaseTool):
151
151
  if arg_data.time_data is not None:
152
152
  duration = arg_data.time_data.duration
153
153
  interval = arg_data.time_data.interval
154
-
154
+ # Update the model parameters
155
+ model_object.update_parameters(dic_species_data)
156
+ logger.log(logging.INFO,
157
+ "Following species/parameters updated in the model %s",
158
+ dic_species_data)
155
159
  # Simulate the model
156
- df = model_object.simulate(
157
- parameters=dic_species_data,
158
- duration=duration,
159
- interval=interval
160
- )
161
-
160
+ df = model_object.simulate(duration=duration, interval=interval)
161
+ logger.log(logging.INFO, "Simulation results ready with shape %s", df.shape)
162
162
  dic_simulated_data = {
163
163
  'name': arg_data.simulation_name,
164
164
  'source': sys_bio_model.biomodel_id if sys_bio_model.biomodel_id else 'upload',
165
165
  'tool_call_id': tool_call_id,
166
166
  'data': df.to_dict()
167
167
  }
168
-
169
- # Prepare the dictionary of updated state for the model
168
+ # Prepare the dictionary of updated state
170
169
  dic_updated_state_for_model = {}
171
170
  for key, value in {
172
171
  "model_id": [sys_bio_model.biomodel_id],
@@ -175,7 +174,6 @@ class SimulateModelTool(BaseTool):
175
174
  }.items():
176
175
  if value:
177
176
  dic_updated_state_for_model[key] = value
178
-
179
177
  # Return the updated state of the tool
180
178
  return Command(
181
179
  update=dic_updated_state_for_model|{
@@ -1,4 +1,5 @@
1
1
  '''
2
- This file is used to import the datasets, utils, and tools.
2
+ This file is used to import the datasets and utils.
3
3
  '''
4
4
  from . import datasets
5
+ from . import utils
@@ -0,0 +1,39 @@
1
+ """
2
+ Test cases for utils/enrichments/enrichments.py
3
+ """
4
+
5
+ from ..utils.enrichments.enrichments import Enrichments
6
+
7
+ class TestEnrichments(Enrichments):
8
+ """Test implementation of the Enrichments interface for testing purposes."""
9
+
10
+ def enrich_documents(self, texts: list[str]) -> list[list[float]]:
11
+ return [
12
+ f"Additional text description of {text} as the input." for text in texts
13
+ ]
14
+
15
+ def enrich_documents_with_rag(self, texts, docs):
16
+ # Currently we don't have a RAG model to test this method.
17
+ # Thus, we will just call the enrich_documents method instead.
18
+ return self.enrich_documents(texts)
19
+
20
+ def test_enrich_documents():
21
+ """Test enriching documents using the Enrichments interface."""
22
+ enrichments = TestEnrichments()
23
+ texts = ["text1", "text2"]
24
+ result = enrichments.enrich_documents(texts)
25
+ assert result == [
26
+ "Additional text description of text1 as the input.",
27
+ "Additional text description of text2 as the input.",
28
+ ]
29
+
30
+ def test_enrich_documents_with_rag():
31
+ """Test enriching documents with RAG using the Enrichments interface."""
32
+ enrichments = TestEnrichments()
33
+ texts = ["text1", "text2"]
34
+ docs = ["doc1", "doc2"]
35
+ result = enrichments.enrich_documents_with_rag(texts, docs)
36
+ assert result == [
37
+ "Additional text description of text1 as the input.",
38
+ "Additional text description of text2 as the input.",
39
+ ]
@@ -0,0 +1,117 @@
1
+ """
2
+ Test cases for utils/enrichments/ollama.py
3
+ """
4
+
5
+ import pytest
6
+ import ollama
7
+ from ..utils.enrichments.ollama import EnrichmentWithOllama
8
+
9
+ @pytest.fixture(name="ollama_config")
10
+ def fixture_ollama_config():
11
+ """Return a dictionary with Ollama configuration."""
12
+ return {
13
+ "model_name": "smollm2:360m",
14
+ "prompt_enrichment": """
15
+ Given the input as a list of strings, please return the list of addditional information of
16
+ each input terms using your prior knowledge.
17
+
18
+ Example:
19
+ Input: ['acetaminophen', 'aspirin']
20
+ Ouput: ['acetaminophen is a medication used to treat pain and fever',
21
+ 'aspirin is a medication used to treat pain, fever, and inflammation']
22
+
23
+ Do not include any pretext as the output, only the list of strings enriched.
24
+
25
+ Input: {input}
26
+ """,
27
+ "temperature": 0.0,
28
+ "streaming": False,
29
+ }
30
+
31
+ def test_no_model_ollama(ollama_config):
32
+ """Test the case when the Ollama model is not available."""
33
+ cfg = ollama_config
34
+ cfg_model = "smollm2:135m" # Choose a small model
35
+
36
+ # Delete the Ollama model
37
+ try:
38
+ ollama.delete(cfg_model)
39
+ except ollama.ResponseError:
40
+ pass
41
+
42
+ # Check if the model is available
43
+ with pytest.raises(
44
+ ValueError, match=f"Error: Pulled {cfg_model} model and restarted Ollama server."
45
+ ):
46
+ EnrichmentWithOllama(
47
+ model_name=cfg_model,
48
+ prompt_enrichment=cfg["prompt_enrichment"],
49
+ temperature=cfg["temperature"],
50
+ streaming=cfg["streaming"],
51
+ )
52
+ ollama.delete(cfg_model)
53
+
54
+ def test_enrich_nodes_ollama(ollama_config):
55
+ """Test the Ollama textual enrichment class for node enrichment."""
56
+ # Prepare enrichment model
57
+ cfg = ollama_config
58
+ enr_model = EnrichmentWithOllama(
59
+ model_name=cfg["model_name"],
60
+ prompt_enrichment=cfg["prompt_enrichment"],
61
+ temperature=cfg["temperature"],
62
+ streaming=cfg["streaming"],
63
+ )
64
+
65
+ # Perform enrichment for nodes
66
+ nodes = ["Adalimumab", "Infliximab"]
67
+ enriched_nodes = enr_model.enrich_documents(nodes)
68
+ # Check the enriched nodes
69
+ assert len(enriched_nodes) == 2
70
+ assert all(
71
+ enriched_nodes[i] != nodes[i] for i in range(len(nodes))
72
+ )
73
+
74
+
75
+ def test_enrich_relations_ollama(ollama_config):
76
+ """Test the Ollama textual enrichment class for relation enrichment."""
77
+ # Prepare enrichment model
78
+ cfg = ollama_config
79
+ enr_model = EnrichmentWithOllama(
80
+ model_name=cfg["model_name"],
81
+ prompt_enrichment=cfg["prompt_enrichment"],
82
+ temperature=cfg["temperature"],
83
+ streaming=cfg["streaming"],
84
+ )
85
+ # Perform enrichment for relations
86
+ relations = [
87
+ "IL23R-gene causation disease-inflammatory bowel diseases",
88
+ "NOD2-gene causation disease-inflammatory bowel diseases",
89
+ ]
90
+ enriched_relations = enr_model.enrich_documents(relations)
91
+ # Check the enriched relations
92
+ assert len(enriched_relations) == 2
93
+ assert all(
94
+ enriched_relations[i] != relations[i]
95
+ for i in range(len(relations))
96
+ )
97
+
98
+
99
+ def test_enrich_ollama_rag(ollama_config):
100
+ """Test the Ollama textual enrichment class for enrichment with RAG (not implemented)."""
101
+ # Prepare enrichment model
102
+ cfg = ollama_config
103
+ enr_model = EnrichmentWithOllama(
104
+ model_name=cfg["model_name"],
105
+ prompt_enrichment=cfg["prompt_enrichment"],
106
+ temperature=cfg["temperature"],
107
+ streaming=cfg["streaming"],
108
+ )
109
+ # Perform enrichment for nodes
110
+ nodes = ["Adalimumab", "Infliximab"]
111
+ docs = [r"\path\to\doc1", r"\path\to\doc2"]
112
+ enriched_nodes = enr_model.enrich_documents_with_rag(nodes, docs)
113
+ # Check the enriched nodes
114
+ assert len(enriched_nodes) == 2
115
+ assert all(
116
+ enriched_nodes[i] != nodes[i] for i in range(len(nodes))
117
+ )
@@ -0,0 +1,5 @@
1
+ '''
2
+ This file is used to import utlities.
3
+ '''
4
+ from . import enrichments
5
+ from . import embeddings
@@ -0,0 +1,5 @@
1
+ """
2
+ This package contains modules to use the enrichment model
3
+ """
4
+ from . import enrichments
5
+ from . import ollama
@@ -0,0 +1,36 @@
1
+ """
2
+ Enrichments interface
3
+ """
4
+
5
+ from abc import ABC, abstractmethod
6
+
7
+ class Enrichments(ABC):
8
+ """Interface for enrichment models.
9
+
10
+ This is an interface meant for implementing text enrichment models.
11
+
12
+ Enrichment models are used to enrich node or relation features in a given knowledge graph.
13
+ """
14
+
15
+ @abstractmethod
16
+ def enrich_documents(self, texts: list[str]) -> list[list[str]]:
17
+ """Enrich documents.
18
+
19
+ Args:
20
+ texts: List of documents to enrich.
21
+
22
+ Returns:
23
+ List of enriched documents.
24
+ """
25
+
26
+ @abstractmethod
27
+ def enrich_documents_with_rag(self, texts: list[str], docs: list[str]) -> list[str]:
28
+ """Enrich documents with RAG.
29
+
30
+ Args:
31
+ texts: List of documents to enrich.
32
+ docs: List of reference documents to enrich the input texts.
33
+
34
+ Returns:
35
+ List of enriched documents with RAG.
36
+ """
@@ -0,0 +1,123 @@
1
+ #!/usr/bin/env python3
2
+
3
+ """
4
+ Enrichment class using Ollama model based on LangChain Enrichment class.
5
+ """
6
+
7
+ import time
8
+ from typing import List
9
+ import subprocess
10
+ import ast
11
+ import ollama
12
+ from langchain_ollama import ChatOllama
13
+ from langchain_core.prompts import ChatPromptTemplate
14
+ from langchain_core.output_parsers import StrOutputParser
15
+ from .enrichments import Enrichments
16
+
17
+ class EnrichmentWithOllama(Enrichments):
18
+ """
19
+ Enrichment class using Ollama model based on the Enrichment abstract class.
20
+ """
21
+ def __init__(
22
+ self,
23
+ model_name: str,
24
+ prompt_enrichment: str,
25
+ temperature: float,
26
+ streaming: bool,
27
+ ):
28
+ """
29
+ Initialize the EnrichmentWithOllama class.
30
+
31
+ Args:
32
+ model_name: The name of the Ollama model to be used.
33
+ prompt_enrichment: The prompt enrichment template.
34
+ temperature: The temperature for the Ollama model.
35
+ streaming: The streaming flag for the Ollama model.
36
+ """
37
+ # Setup the Ollama server
38
+ self.__setup(model_name)
39
+
40
+ # Set parameters
41
+ self.model_name = model_name
42
+ self.prompt_enrichment = prompt_enrichment
43
+ self.temperature = temperature
44
+ self.streaming = streaming
45
+
46
+ # Prepare prompt template
47
+ self.prompt_template = ChatPromptTemplate.from_messages(
48
+ [
49
+ ("system", self.prompt_enrichment),
50
+ ("human", "{input}"),
51
+ ]
52
+ )
53
+
54
+ # Prepare model
55
+ self.model = ChatOllama(
56
+ model=self.model_name,
57
+ temperature=self.temperature,
58
+ streaming=self.streaming,
59
+ )
60
+
61
+ def __setup(self, model_name: str) -> None:
62
+ """
63
+ Check if the Ollama model is available and run the Ollama server if needed.
64
+
65
+ Args:
66
+ model_name: The name of the Ollama model to be used.
67
+ """
68
+ try:
69
+ models_list = ollama.list()["models"]
70
+ if model_name not in [m['model'].replace(":latest", "") for m in models_list]:
71
+ ollama.pull(model_name)
72
+ time.sleep(30)
73
+ raise ValueError(f"Pulled {model_name} model")
74
+ except Exception as e:
75
+ with subprocess.Popen(
76
+ "ollama serve", shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE
77
+ ):
78
+ time.sleep(10)
79
+ raise ValueError(f"Error: {e} and restarted Ollama server.") from e
80
+
81
+ def enrich_documents(self, texts: List[str]) -> List[str]:
82
+ """
83
+ Enrich a list of input texts with additional textual features using OLLAMA model.
84
+ Important: Make sure the input is a list of texts based on the defined prompt template
85
+ with 'input' as the variable name.
86
+
87
+ Args:
88
+ texts: The list of texts to be enriched.
89
+
90
+ Returns:
91
+ The list of enriched texts.
92
+ """
93
+
94
+ # Perform enrichment
95
+ chain = self.prompt_template | self.model | StrOutputParser()
96
+
97
+ # Generate the enriched node
98
+ # Important: Make sure the input is a list of texts based on the defined prompt template
99
+ # with 'input' as the variable name
100
+ enriched_texts = chain.invoke({"input": "[" + ", ".join(texts) + "]"})
101
+
102
+ # Convert the enriched nodes to a list of dictionary
103
+ enriched_texts = ast.literal_eval(enriched_texts.replace("```", ""))
104
+
105
+ # Final check for the enriched texts
106
+ assert len(enriched_texts) == len(texts)
107
+
108
+ return enriched_texts
109
+
110
+ def enrich_documents_with_rag(self, texts, docs):
111
+ """
112
+ Enrich a list of input texts with additional textual features using OLLAMA model with RAG.
113
+ As of now, we don't have a RAG model to test this method yet.
114
+ Thus, we will just call the enrich_documents method instead.
115
+
116
+ Args:
117
+ texts: The list of texts to be enriched.
118
+ docs: The list of reference documents to enrich the input texts.
119
+
120
+ Returns:
121
+ The list of enriched texts
122
+ """
123
+ return self.enrich_documents(texts)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: aiagents4pharma
3
- Version: 1.10.0
3
+ Version: 1.12.0
4
4
  Summary: AI Agents for drug discovery, drug development, and other pharmaceutical R&D
5
5
  Classifier: Programming Language :: Python :: 3
6
6
  Classifier: License :: OSI Approved :: MIT License
@@ -20,9 +20,11 @@ Requires-Dist: langchain-community==0.3.5
20
20
  Requires-Dist: langchain-core==0.3.31
21
21
  Requires-Dist: langchain-experimental==0.3.3
22
22
  Requires-Dist: langchain-openai==0.2.5
23
+ Requires-Dist: langchain_ollama==0.2.2
23
24
  Requires-Dist: langgraph==0.2.66
24
25
  Requires-Dist: matplotlib==3.9.2
25
26
  Requires-Dist: openai==1.59.4
27
+ Requires-Dist: ollama==0.4.6
26
28
  Requires-Dist: pandas==2.2.3
27
29
  Requires-Dist: plotly==5.24.1
28
30
  Requires-Dist: pydantic==2.9.2
@@ -74,6 +76,7 @@ Our toolkit currently consists of three intelligent agents, each designed to sim
74
76
  - Forward simulation of both internal and open-source models (BioModels).
75
77
  - Adjust parameters within the model to simulate different conditions.
76
78
  - Query simulation results.
79
+ - Extract model information such as species, parameters, units and description.
77
80
 
78
81
  ### 2. Talk2Cells _(Work in Progress)_
79
82
 
@@ -87,11 +90,7 @@ Our toolkit currently consists of three intelligent agents, each designed to sim
87
90
 
88
91
  ## Getting Started
89
92
 
90
- ### Prerequisites
91
-
92
- - **Python 3.10+**
93
- - **Git**
94
- - Required libraries specified in `requirements.txt`
93
+ ![Python Version from PEP 621 TOML](https://img.shields.io/python/required-version-toml?tomlFilePath=https%3A%2F%2Fraw.githubusercontent.com%2FVirtualPatientEngine%2FAIAgents4Pharma%2Frefs%2Fheads%2Fmain%2Fpyproject.toml)
95
94
 
96
95
  ### Installation
97
96
 
@@ -7,23 +7,24 @@ aiagents4pharma/configs/talk2biomodels/agents/t2b_agent/__init__.py,sha256=-fAOR
7
7
  aiagents4pharma/configs/talk2biomodels/agents/t2b_agent/default.yaml,sha256=yD7qZCneaM-JE5PdZjDmDoTRUdsFrzeCKZsBx1b-f20,293
8
8
  aiagents4pharma/talk2biomodels/__init__.py,sha256=qUw3qXrENqSCLIKSLy_qtNPwPDTb1wdZ8fZispcHb3g,141
9
9
  aiagents4pharma/talk2biomodels/agents/__init__.py,sha256=sn5-fREjMdEvb-OUan3iOqrgYGjplNx3J8hYOaW0Po8,128
10
- aiagents4pharma/talk2biomodels/agents/t2b_agent.py,sha256=nVWxHR-QMZDqDwxvDga_CvLo7LHP5cWCDl6lXCMcRO0,3264
10
+ aiagents4pharma/talk2biomodels/agents/t2b_agent.py,sha256=6Im4YFcdykN7wpEM8y9qi_x4lTg02WJpb0SEWh8TPLo,3188
11
11
  aiagents4pharma/talk2biomodels/models/__init__.py,sha256=5fTHHm3PVloYPNKXbgNlcPgv3-u28ZquxGydFYDfhJA,122
12
- aiagents4pharma/talk2biomodels/models/basico_model.py,sha256=js7ORLwbJPaIsko5oRToMMCh4l8LsN292OIvFzTfvRg,4946
13
- aiagents4pharma/talk2biomodels/models/sys_bio_model.py,sha256=ylpPba2SA8kl68q3k1kJbiUdRYplPHykyslTQLDZ19I,1995
12
+ aiagents4pharma/talk2biomodels/models/basico_model.py,sha256=PH25FTOuUjsmw_UUxoRb-4kptOYpicEn4GqS0phS3nk,4807
13
+ aiagents4pharma/talk2biomodels/models/sys_bio_model.py,sha256=JeoiGQAvQABHnG0wKR2XBmmxqQdtgO6kxaLDUTUmr1s,2001
14
14
  aiagents4pharma/talk2biomodels/states/__init__.py,sha256=YLg1-N0D9qyRRLRqwqfLCLAqZYDtMVZTfI8Y0b_4tbA,139
15
- aiagents4pharma/talk2biomodels/states/state_talk2biomodels.py,sha256=iob7q5Kpy6aWDLCiFsp4NNVYYXNdDU3vU50PmxyvBsU,792
15
+ aiagents4pharma/talk2biomodels/states/state_talk2biomodels.py,sha256=Dlsnh9dW1mCXTBXmlDAlOox7f4azFbLBG_2k3YPielM,824
16
16
  aiagents4pharma/talk2biomodels/tests/__init__.py,sha256=Jbw5tJxSrjGoaK5IX3pJWDCNzhrVQ10lkYq2oQ_KQD8,45
17
- aiagents4pharma/talk2biomodels/tests/test_basico_model.py,sha256=uqhbojcA4RRTDRUAF9B9DzKCo3OOIOWMDK8IViG0gsM,2038
18
- aiagents4pharma/talk2biomodels/tests/test_langgraph.py,sha256=GyqsUpcWgjuRb15DpGvLg-FZ8g3_cf0TwVcaCPp_vO0,9456
19
- aiagents4pharma/talk2biomodels/tests/test_sys_bio_model.py,sha256=nA6bRT16627mw8qzrv7cHM9AByHb9F0kxAuwOpE-avA,1961
20
- aiagents4pharma/talk2biomodels/tools/__init__.py,sha256=8hAT6z1OO8N9HRylh6fwoqyjYlGdpkngkElBNqH40Zo,237
17
+ aiagents4pharma/talk2biomodels/tests/test_basico_model.py,sha256=y82fpTJMPHwtXxlle1cGQ_2Bewwpxi0aJSVrVAYLhN0,2060
18
+ aiagents4pharma/talk2biomodels/tests/test_langgraph.py,sha256=_71UZS1zucn6Nus4oCH7tINQVRvJEFnL0UIZ6-sUd3I,11967
19
+ aiagents4pharma/talk2biomodels/tests/test_sys_bio_model.py,sha256=HSmBBViMi0jYf4gWX21IbppAfDzG0nr_S3KtKS9fZVQ,2165
20
+ aiagents4pharma/talk2biomodels/tools/__init__.py,sha256=SMTMlGHxTuJI7gjwGaTMv0XmilJ73-r5dp568hD3Fw0,266
21
21
  aiagents4pharma/talk2biomodels/tools/ask_question.py,sha256=uxCQ4ON8--D0ACPvT14t6x_aqm9LP6woBA4GM7bPXc4,3061
22
22
  aiagents4pharma/talk2biomodels/tools/custom_plotter.py,sha256=HWwKTX3o4dk0GcRVTO2hPrFSu98mtJ4TKC_hbHXOe1c,4018
23
- aiagents4pharma/talk2biomodels/tools/get_modelinfo.py,sha256=68KmeEpgvgaDQM9airOWVy4fGT33rG10RlXhps5W6C0,5279
23
+ aiagents4pharma/talk2biomodels/tools/get_modelinfo.py,sha256=qA-4FOI-O728Nmn7s8JJ8HKwxvA9MZbst7NkPKTAMV4,5391
24
24
  aiagents4pharma/talk2biomodels/tools/load_biomodel.py,sha256=pyVzLQoMnuJYEwsjeOlqcUrbU1F1Z-pNlgkhFaoKpy0,689
25
+ aiagents4pharma/talk2biomodels/tools/parameter_scan.py,sha256=aIyL_m46s3Q74ieJOZjZBM34VCjBKSMpEtckhdZofbE,12139
25
26
  aiagents4pharma/talk2biomodels/tools/search_models.py,sha256=Iq2ddofOOfZYtAurCISq3bAq5rbwB3l_rL1lgEFyFCI,2653
26
- aiagents4pharma/talk2biomodels/tools/simulate_model.py,sha256=1HVoI5SkktvpOmTnAG8hxrhpoxpg_he-bb5ZJ_UllI4,6833
27
+ aiagents4pharma/talk2biomodels/tools/simulate_model.py,sha256=sWmFVnVvJbdXXTqn_7gQl5UW0tv4FyU5yLXWLweLs_M,7059
27
28
  aiagents4pharma/talk2cells/__init__.py,sha256=zmOP5RAhabgKIQP-W4P4qKME2tG3fhAXM3MeO5_H8kE,120
28
29
  aiagents4pharma/talk2cells/agents/__init__.py,sha256=38nK2a_lEFRjO3qD6Fo9a3983ZCYat6hmJKWY61y2Mo,128
29
30
  aiagents4pharma/talk2cells/agents/scp_agent.py,sha256=gDMfhUNWHa_XWOqm1Ql6yLAdI_7bnIk5sRYn43H2sYk,3090
@@ -50,7 +51,7 @@ aiagents4pharma/talk2competitors/tools/s2/display_results.py,sha256=B8JJGohi1Eyx
50
51
  aiagents4pharma/talk2competitors/tools/s2/multi_paper_rec.py,sha256=FYLt47DAk6WOKfEk1Gj9zVvJGNyxA283PCp8IKW9U5M,4262
51
52
  aiagents4pharma/talk2competitors/tools/s2/search.py,sha256=pppjrQv5-8ep4fnqgTSBNgnbSnQsVIcNrRrH0p2TP1o,4025
52
53
  aiagents4pharma/talk2competitors/tools/s2/single_paper_rec.py,sha256=dAfUQxI7T5eu0eDxK8VAl7-JH0Wnw24CVkOQqwj-hXc,4810
53
- aiagents4pharma/talk2knowledgegraphs/__init__.py,sha256=SW7Ys2A4eXyFtizNPdSw91SHOPVUBGBsrCQ7TqwSUL0,91
54
+ aiagents4pharma/talk2knowledgegraphs/__init__.py,sha256=4smVQoSMM6rflVnNkABqlDAAlSn4bYsq7rMVWjRGvis,103
54
55
  aiagents4pharma/talk2knowledgegraphs/datasets/__init__.py,sha256=L3gPuHskSegmtXskVrLIYr7FXe_ibKgJ2GGr1_Wok6k,173
55
56
  aiagents4pharma/talk2knowledgegraphs/datasets/biobridge_primekg.py,sha256=QlzDXmXREoa9MA6-GwzqRjdzndQeGBAF11Td6NFk_9Y,23426
56
57
  aiagents4pharma/talk2knowledgegraphs/datasets/dataset.py,sha256=-LaPLse8BkALqwFetNK7wch2dt9Dz6QKGKZKBKM6bIk,409
@@ -64,14 +65,19 @@ aiagents4pharma/talk2knowledgegraphs/tests/test_datasets_starkqa_primekg.py,sha2
64
65
  aiagents4pharma/talk2knowledgegraphs/tests/test_utils_embeddings_embeddings.py,sha256=uYFoE_6zeU10_1mLLAHUr5c4S2XZMSc0Q_860o-KWEw,1517
65
66
  aiagents4pharma/talk2knowledgegraphs/tests/test_utils_embeddings_huggingface.py,sha256=EINWyXg_3AMHF3WzFLhIUiFDuaEhTVHBvVAJr8VtMDg,1624
66
67
  aiagents4pharma/talk2knowledgegraphs/tests/test_utils_embeddings_sentencetransformer.py,sha256=Qxo6WeIDRy8aLh1tNKw0kSlzmUj3MtTak63oW2YwB24,1327
67
- aiagents4pharma/talk2knowledgegraphs/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
68
+ aiagents4pharma/talk2knowledgegraphs/tests/test_utils_enrichments_enrichments.py,sha256=N6HRr4lWHXY7bTHe2uXJe4D_EG9WqZPibZne6qLl9_k,1447
69
+ aiagents4pharma/talk2knowledgegraphs/tests/test_utils_enrichments_ollama.py,sha256=kMuB_vci6hKr2qJgXBmcje7yxeJ2nY2ImXw-NSJpts0,3912
70
+ aiagents4pharma/talk2knowledgegraphs/utils/__init__.py,sha256=X8kSpmDTMkeE0fA5D0CWMsQ52YKoM5rRSXrjnali3IM,97
68
71
  aiagents4pharma/talk2knowledgegraphs/utils/kg_utils.py,sha256=6vQnPkeOWae_8jePjhma3sJuMTngy0I0tqzdFt6OqKg,2507
69
72
  aiagents4pharma/talk2knowledgegraphs/utils/embeddings/__init__.py,sha256=xRb0x7SoAb0nSVZYgjrqxWvENOMDuqIdL43NMjoOaCs,153
70
73
  aiagents4pharma/talk2knowledgegraphs/utils/embeddings/embeddings.py,sha256=1nGznrAj-xT0xuSMBGz2dOujJ7M_IwSR84njxtxsy9A,2523
71
74
  aiagents4pharma/talk2knowledgegraphs/utils/embeddings/huggingface.py,sha256=2vi_elf6EgzfagFAO5QnL3a_aXZyN7B1EBziu44MTfM,3806
72
75
  aiagents4pharma/talk2knowledgegraphs/utils/embeddings/sentence_transformer.py,sha256=36iKlisOpMtGR5xfTAlSHXWvPqVC_Jbezod8kbBBMVg,2136
73
- aiagents4pharma-1.10.0.dist-info/LICENSE,sha256=IcIbyB1Hyk5ZDah03VNQvJkbNk2hkBCDqQ8qtnCvB4Q,1077
74
- aiagents4pharma-1.10.0.dist-info/METADATA,sha256=a5XUji4VHk7HcE5GC7txe7v2sNUbgH4ijSHpxoNh74E,8340
75
- aiagents4pharma-1.10.0.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
76
- aiagents4pharma-1.10.0.dist-info/top_level.txt,sha256=-AH8rMmrSnJtq7HaAObS78UU-cTCwvX660dSxeM7a0A,16
77
- aiagents4pharma-1.10.0.dist-info/RECORD,,
76
+ aiagents4pharma/talk2knowledgegraphs/utils/enrichments/__init__.py,sha256=tW426knki2DBIHcWyF_K04iMMdbpIn_e_TpPmTgz2dI,113
77
+ aiagents4pharma/talk2knowledgegraphs/utils/enrichments/enrichments.py,sha256=Bx8x6zzk5614ApWB90N_iv4_Y_Uq0-KwUeBwYSdQMU4,924
78
+ aiagents4pharma/talk2knowledgegraphs/utils/enrichments/ollama.py,sha256=8eoxR-VHo0G7ReQIwje7xEhE-SJlHdef7_wJRpnvFIc,4116
79
+ aiagents4pharma-1.12.0.dist-info/LICENSE,sha256=IcIbyB1Hyk5ZDah03VNQvJkbNk2hkBCDqQ8qtnCvB4Q,1077
80
+ aiagents4pharma-1.12.0.dist-info/METADATA,sha256=Okx1RPc3qMmJ2a7ca2YvSFr7-gxqAR51LVLkOTh5O2k,8609
81
+ aiagents4pharma-1.12.0.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
82
+ aiagents4pharma-1.12.0.dist-info/top_level.txt,sha256=-AH8rMmrSnJtq7HaAObS78UU-cTCwvX660dSxeM7a0A,16
83
+ aiagents4pharma-1.12.0.dist-info/RECORD,,