aiagents4pharma 1.6.2__tar.gz → 1.8.0__tar.gz

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