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
@@ -1,8 +1,11 @@
1
- '''
1
+ """
2
2
  This file is used to import aiagents4pharma modules.
3
- '''
3
+ """
4
4
 
5
- from . import talk2biomodels
6
- from . import talk2cells
7
- from . import talk2knowledgegraphs
8
- from . import configs
5
+ from . import (
6
+ configs,
7
+ talk2biomodels,
8
+ talk2cells,
9
+ talk2competitors,
10
+ talk2knowledgegraphs,
11
+ )
@@ -1,3 +1,4 @@
1
1
  defaults:
2
2
  - _self_
3
- - talk2biomodels/agents/t2b_agent: default
3
+ - talk2biomodels/agents/t2b_agent: default
4
+ - talk2biomodels/tools/ask_question: default
@@ -3,3 +3,4 @@ Import all the modules in the package
3
3
  '''
4
4
 
5
5
  from . import agents
6
+ from . import tools
@@ -3,6 +3,12 @@ state_modifier: >
3
3
  You are Talk2BioModels agent.
4
4
  If the user asks for the uploaded model,
5
5
  then pass the use_uploaded_model argument
6
- as True. If the user asks for simulation,
7
- then suggest a value for the `simulation_name`
8
- argument.
6
+ as True. If the user asks for simulation
7
+ or param_scan or steady state, suggest a
8
+ value for the `experiment_name` argument.
9
+
10
+ If the user asks question related to the
11
+ uploaded document/pdf/article/document,
12
+ use the tool `query_article` to answer the
13
+ question. Please note that the `experiment_name`
14
+ argument may be unrelated to the question asked.
@@ -0,0 +1,4 @@
1
+ '''
2
+ Import all the modules in the package
3
+ '''
4
+ from . import ask_question
@@ -0,0 +1,3 @@
1
+ '''
2
+ Import all the modules in the package
3
+ '''
@@ -5,3 +5,4 @@ from . import models
5
5
  from . import tools
6
6
  from . import agents
7
7
  from . import states
8
+ from . import api
@@ -15,7 +15,11 @@ from ..tools.search_models import SearchModelsTool
15
15
  from ..tools.get_modelinfo import GetModelInfoTool
16
16
  from ..tools.simulate_model import SimulateModelTool
17
17
  from ..tools.custom_plotter import CustomPlotterTool
18
+ from ..tools.get_annotation import GetAnnotationTool
18
19
  from ..tools.ask_question import AskQuestionTool
20
+ from ..tools.parameter_scan import ParameterScanTool
21
+ from ..tools.steady_state import SteadyStateTool
22
+ from ..tools.query_article import QueryArticle
19
23
  from ..states.state_talk2biomodels import Talk2Biomodels
20
24
 
21
25
  # Initialize logger
@@ -35,18 +39,17 @@ def get_app(uniq_id, llm_model='gpt-4o-mini'):
35
39
  return response
36
40
 
37
41
  # Define the tools
38
- simulate_model = SimulateModelTool()
39
- custom_plotter = CustomPlotterTool()
40
- ask_question = AskQuestionTool()
41
- search_model = SearchModelsTool()
42
- get_modelinfo = GetModelInfoTool()
43
42
  tools = ToolNode([
44
- simulate_model,
45
- ask_question,
46
- custom_plotter,
47
- search_model,
48
- get_modelinfo
49
- ])
43
+ SimulateModelTool(),
44
+ AskQuestionTool(),
45
+ CustomPlotterTool(),
46
+ SearchModelsTool(),
47
+ GetModelInfoTool(),
48
+ SteadyStateTool(),
49
+ ParameterScanTool(),
50
+ GetAnnotationTool(),
51
+ QueryArticle()
52
+ ])
50
53
 
51
54
  # Define the model
52
55
  llm = ChatOpenAI(model=llm_model, temperature=0)
@@ -0,0 +1,6 @@
1
+ '''
2
+ This file is used to import the modules in the package.
3
+ '''
4
+ from . import uniprot
5
+ from . import ols
6
+ from . import kegg
@@ -0,0 +1,83 @@
1
+ """
2
+ This module contains the API for fetching Kegg database
3
+ """
4
+ import re
5
+ from typing import List, Dict
6
+ import requests
7
+
8
+ def fetch_from_api(base_url: str, query: str) -> str:
9
+ """Fetch data from the given API endpoint."""
10
+ try:
11
+ response = requests.get(base_url + query, timeout=10)
12
+ response.raise_for_status()
13
+ return response.text
14
+ except requests.exceptions.RequestException as e:
15
+ print(f"Error fetching data for query {query}: {e}")
16
+ return ""
17
+
18
+ def fetch_kegg_names(ids: List[str], batch_size: int = 10) -> Dict[str, str]:
19
+ """
20
+ Fetch the names of multiple KEGG entries using the KEGG REST API in batches.
21
+
22
+ Args:
23
+ ids (List[str]): List of KEGG IDs.
24
+ batch_size (int): Maximum number of IDs to include in a single request.
25
+
26
+ Returns:
27
+ Dict[str, str]: A mapping of KEGG IDs to their names.
28
+ """
29
+ if not ids:
30
+ return {}
31
+
32
+ base_url = "https://rest.kegg.jp/get/"
33
+ entry_name_map = {}
34
+
35
+ # Process IDs in batches
36
+ for i in range(0, len(ids), batch_size):
37
+ batch = ids[i:i + batch_size]
38
+ query = "+".join(batch)
39
+ entry_data = fetch_from_api(base_url, query)
40
+
41
+ # if not entry_data:
42
+ # continue
43
+ entries = entry_data.split("///")
44
+ for entry in entries:
45
+ if not entry.strip():
46
+ continue
47
+ lines = entry.strip().split("\n")
48
+ entry_line = next((line for line in lines
49
+ if line.startswith("ENTRY")), None)
50
+ name_line = next((line for line in lines
51
+ if line.startswith("NAME")), None)
52
+
53
+ # if not entry_line and not name_line:
54
+ # continue
55
+ entry_id = entry_line.split()[1]
56
+ # Split multiple names in the NAME field and clean them
57
+ names = [
58
+ re.sub(r'[^a-zA-Z0-9\s]', '', name).strip()
59
+ for name in name_line.replace("NAME", "").strip().split(";")
60
+ ]
61
+ # Join cleaned names into a single string
62
+ entry_name_map[entry_id] = " ".join(names).strip()
63
+
64
+ return entry_name_map
65
+
66
+ def fetch_kegg_annotations(data: List[Dict[str, str]],
67
+ batch_size: int = 10) -> Dict[str, Dict[str, str]]:
68
+ """Fetch KEGG entry descriptions grouped by database type."""
69
+ grouped_data = {}
70
+ for entry in data:
71
+ db_type = entry["Database"].lower()
72
+ grouped_data.setdefault(db_type, []).append(entry["Id"])
73
+
74
+ results = {}
75
+ for db_type, ids in grouped_data.items():
76
+ results[db_type] = fetch_kegg_names(ids, batch_size=batch_size)
77
+
78
+ return results
79
+
80
+ # def get_protein_name_or_label(data: List[Dict[str, str]],
81
+ # batch_size: int = 10) -> Dict[str, Dict[str, str]]:
82
+ # """Fetch descriptions for KEGG-related identifiers."""
83
+ # return fetch_kegg_annotations(data, batch_size=batch_size)
@@ -0,0 +1,72 @@
1
+ """
2
+ This module contains the API for fetching ols database
3
+ """
4
+ from typing import List, Dict
5
+ import requests
6
+
7
+ def fetch_from_ols(term: str) -> str:
8
+ """
9
+ Fetch the label for a single term from OLS.
10
+
11
+ Args:
12
+ term (str): The term in the format "ONTOLOGY:TERM_ID".
13
+
14
+ Returns:
15
+ str: The label for the term or an error message.
16
+ """
17
+ try:
18
+ ontology, _ = term.split(":")
19
+ base_url = f"https://www.ebi.ac.uk/ols4/api/ontologies/{ontology.lower()}/terms"
20
+ params = {"obo_id": term}
21
+ response = requests.get(
22
+ base_url,
23
+ params=params,
24
+ headers={"Accept": "application/json"},
25
+ timeout=10
26
+ )
27
+ response.raise_for_status()
28
+ data = response.json()
29
+ label = '-'
30
+ # Extract and return the label
31
+ if "_embedded" in data and "terms" in data["_embedded"] \
32
+ and len(data["_embedded"]["terms"]) > 0:
33
+ label = data["_embedded"]["terms"][0].get("label", "Label not found")
34
+ return label
35
+ except (requests.exceptions.RequestException, KeyError, IndexError) as e:
36
+ return f"Error: {str(e)}"
37
+
38
+ def fetch_ols_labels(terms: List[str]) -> Dict[str, str]:
39
+ """
40
+ Fetch labels for multiple terms from OLS.
41
+
42
+ Args:
43
+ terms (List[str]): A list of terms in the format "ONTOLOGY:TERM_ID".
44
+
45
+ Returns:
46
+ Dict[str, str]: A mapping of term IDs to their labels or error messages.
47
+ """
48
+ results = {}
49
+ for term in terms:
50
+ results[term] = fetch_from_ols(term)
51
+ return results
52
+
53
+ def search_ols_labels(data: List[Dict[str, str]]) -> Dict[str, Dict[str, str]]:
54
+ """
55
+ Fetch OLS annotations grouped by ontology type.
56
+
57
+ Args:
58
+ data (List[Dict[str, str]]): A list of dictionaries containing 'Id' and 'Database'.
59
+
60
+ Returns:
61
+ Dict[str, Dict[str, str]]: A mapping of ontology type to term labels.
62
+ """
63
+ grouped_data = {}
64
+ for entry in data:
65
+ ontology = entry["Database"].lower()
66
+ grouped_data.setdefault(ontology, []).append(entry["Id"])
67
+
68
+ results = {}
69
+ for ontology, terms in grouped_data.items():
70
+ results[ontology] = fetch_ols_labels(terms)
71
+
72
+ return results
@@ -0,0 +1,35 @@
1
+ """
2
+ This module contains the API for fetching uniprot database
3
+ """
4
+ from typing import List, Dict
5
+ import requests
6
+
7
+ def search_uniprot_labels(identifiers: List[str]) -> Dict[str, str]:
8
+ """
9
+ Fetch protein names or labels for a list of UniProt identifiers by making sequential requests.
10
+
11
+ Args:
12
+ identifiers (List[str]): A list of UniProt identifiers.
13
+
14
+ Returns:
15
+ Dict[str, str]: A mapping of UniProt identifiers to their protein names or error messages.
16
+ """
17
+ results = {}
18
+ base_url = "https://www.uniprot.org/uniprot/"
19
+
20
+ for identifier in identifiers:
21
+ url = f"{base_url}{identifier}.json"
22
+ try:
23
+ response = requests.get(url, timeout=10)
24
+ response.raise_for_status()
25
+ data = response.json()
26
+ protein_name = (
27
+ data.get('proteinDescription', {})
28
+ .get('recommendedName', {})
29
+ .get('fullName', {})
30
+ .get('value', 'Name not found')
31
+ )
32
+ results[identifier] = protein_name
33
+ except requests.exceptions.RequestException as e:
34
+ results[identifier] = f"Error: {str(e)}"
35
+ return results
@@ -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:
@@ -8,17 +8,34 @@ from typing import Annotated
8
8
  import operator
9
9
  from langgraph.prebuilt.chat_agent_executor import AgentState
10
10
 
11
+ def add_data(data1: dict, data2: dict) -> dict:
12
+ """
13
+ A reducer function to merge two dictionaries.
14
+ """
15
+ left_idx_by_name = {data['name']: idx for idx, data in enumerate(data1)}
16
+ merged = data1.copy()
17
+ for data in data2:
18
+ idx = left_idx_by_name.get(data['name'])
19
+ if idx is not None:
20
+ merged[idx] = data
21
+ else:
22
+ merged.append(data)
23
+ return merged
24
+
11
25
  class Talk2Biomodels(AgentState):
12
26
  """
13
27
  The state for the Talk2BioModels agent.
14
28
  """
15
- model_id: Annotated[list, operator.add]
16
- # sbml_file_path: str
29
+ llm_model: str
30
+ pdf_file_name: str
17
31
  # A StateGraph may receive a concurrent updates
18
- # which is not supported by the StateGraph.
19
- # Therefore, we need to use Annotated to specify
20
- # the operator for the sbml_file_path field.
32
+ # which is not supported by the StateGraph. Hence,
33
+ # we need to add a reducer function to handle the
34
+ # concurrent updates.
21
35
  # https://langchain-ai.github.io/langgraph/troubleshooting/errors/INVALID_CONCURRENT_GRAPH_UPDATE/
36
+ model_id: Annotated[list, operator.add]
22
37
  sbml_file_path: Annotated[list, operator.add]
23
- dic_simulated_data: Annotated[list[dict], operator.add]
24
- llm_model: str
38
+ dic_simulated_data: Annotated[list[dict], add_data]
39
+ dic_scanned_data: Annotated[list[dict], add_data]
40
+ dic_steady_state_data: Annotated[list[dict], add_data]
41
+ dic_annotations_data : Annotated[list[dict], add_data]
@@ -0,0 +1,57 @@
1
+ '''
2
+ Test cases for Talk2Biomodels.
3
+ '''
4
+
5
+ from ..api.uniprot import search_uniprot_labels
6
+ from ..api.ols import fetch_from_ols
7
+ from ..api.kegg import fetch_kegg_names, fetch_from_api
8
+
9
+ def test_search_uniprot_labels():
10
+ '''
11
+ Test the search_uniprot_labels function.
12
+ '''
13
+ # "P61764" = Positive result, "P0000Q" = negative result
14
+ identifiers = ["P61764", "P0000Q"]
15
+ results = search_uniprot_labels(identifiers)
16
+ assert results["P61764"] == "Syntaxin-binding protein 1"
17
+ assert results["P0000Q"].startswith("Error: 400")
18
+
19
+ def test_fetch_from_ols():
20
+ '''
21
+ Test the fetch_from_ols function.
22
+ '''
23
+ term_1 = "GO:0005886" #Positive result
24
+ term_2 = "GO:ABC123" #Negative result
25
+ label_1 = fetch_from_ols(term_1)
26
+ label_2 = fetch_from_ols(term_2)
27
+ assert isinstance(label_1, str), f"Expected string, got {type(label_1)}"
28
+ assert isinstance(label_2, str), f"Expected string, got {type(label_2)}"
29
+ assert label_1 == "plasma membrane"
30
+ assert label_2.startswith("Error: 404")
31
+
32
+ def test_fetch_kegg_names():
33
+ '''
34
+ Test the fetch_kegg_names function.
35
+ '''
36
+ ids = ["C00001", "C00002"]
37
+ results = fetch_kegg_names(ids)
38
+ assert results["C00001"] == "H2O"
39
+ assert results["C00002"] == "ATP"
40
+
41
+ # Try with an empty list
42
+ results = fetch_kegg_names([])
43
+ assert not results
44
+
45
+ def test_fetch_from_api():
46
+ '''
47
+ Test the fetch_from_api function.
48
+ '''
49
+ base_url = "https://rest.kegg.jp/get/"
50
+ query = "C00001"
51
+ entry_data = fetch_from_api(base_url, query)
52
+ assert entry_data.startswith("ENTRY C00001")
53
+
54
+ # Try with an invalid query
55
+ query = "C0000Q"
56
+ entry_data = fetch_from_api(base_url, query)
57
+ assert not entry_data
@@ -0,0 +1,44 @@
1
+ '''
2
+ Test cases for Talk2Biomodels.
3
+ '''
4
+
5
+ from langchain_core.messages import HumanMessage, ToolMessage
6
+ from ..agents.t2b_agent import get_app
7
+
8
+ def test_ask_question_tool():
9
+ '''
10
+ Test the ask_question tool without the simulation results.
11
+ '''
12
+ unique_id = 12345
13
+ app = get_app(unique_id, llm_model='gpt-4o-mini')
14
+ config = {"configurable": {"thread_id": unique_id}}
15
+
16
+ ##########################################
17
+ # Test ask_question tool when simulation
18
+ # results are not available i.e. the
19
+ # simulation has not been run. In this
20
+ # case, the tool should return an error
21
+ ##########################################
22
+ # Update state
23
+ app.update_state(config, {"llm_model": "gpt-4o-mini"})
24
+ # Define the prompt
25
+ prompt = "Call the ask_question tool to answer the "
26
+ prompt += "question: What is the concentration of CRP "
27
+ prompt += "in serum at 1000 hours? The simulation name "
28
+ prompt += "is `simulation_name`."
29
+ # Invoke the tool
30
+ app.invoke(
31
+ {"messages": [HumanMessage(content=prompt)]},
32
+ config=config
33
+ )
34
+ # Get the messages from the current state
35
+ # and reverse the order
36
+ current_state = app.get_state(config)
37
+ reversed_messages = current_state.values["messages"][::-1]
38
+ # Loop through the reversed messages until a
39
+ # ToolMessage is found.
40
+ for msg in reversed_messages:
41
+ # Assert that the message is a ToolMessage
42
+ # and its status is "error"
43
+ if isinstance(msg, ToolMessage):
44
+ assert msg.status == "error"
@@ -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
  '''