Skip to content

Commit 6efcee5

Browse files
committed
Bump version to 1.0.15.2: Update src and tests only
1 parent 332ea72 commit 6efcee5

31 files changed

Lines changed: 2221 additions & 2 deletions

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
44

55
[project]
66
name = "tooluniverse"
7-
version = "1.0.15.1"
7+
version = "1.0.15.2"
88
description = "A comprehensive collection of scientific tools for Agentic AI, offering integration with the ToolUniverse SDK and MCP Server to support advanced scientific workflows."
99
authors = [
1010
{ name = "Shanghua Gao", email = "shanghuagao@gmail.com" }

src/tooluniverse/biomodels_tool.py

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
from typing import Dict, Any, List, Optional
2+
import requests
3+
from .base_tool import BaseTool
4+
from .tool_registry import register_tool
5+
6+
7+
@register_tool("BioModelsTool")
8+
class BioModelsTool(BaseTool):
9+
"""
10+
Tool for searching and retrieving models from EBI BioModels.
11+
"""
12+
13+
BASE_URL = "https://www.ebi.ac.uk/biomodels"
14+
15+
def run(self, arguments: Dict[str, Any]) -> Dict[str, Any]:
16+
"""
17+
Executes the BioModels tool action.
18+
"""
19+
action = arguments.get("action")
20+
21+
if action == "search_models":
22+
return self.search_models(
23+
query=arguments.get("query"), limit=arguments.get("limit", 10)
24+
)
25+
elif action == "get_model_files":
26+
model_id = arguments.get("model_id")
27+
if not model_id:
28+
raise ValueError("model_id is required for get_model_files")
29+
return self.get_model_files(model_id)
30+
else:
31+
raise ValueError(f"Unknown action: {action}")
32+
33+
def search_models(self, query: str, limit: int = 10) -> Dict[str, Any]:
34+
"""
35+
Search for biological models.
36+
"""
37+
url = f"{self.BASE_URL}/search"
38+
params = {"query": query, "format": "json", "numResults": limit}
39+
40+
try:
41+
response = requests.get(url, params=params, timeout=30)
42+
response.raise_for_status()
43+
data = response.json()
44+
45+
# The API returns 'models' list directly? Or wrapped?
46+
# Test output showed `{"models": [...]}`
47+
48+
models = []
49+
for m in data.get("models", []):
50+
models.append(
51+
{
52+
"id": m.get("id"),
53+
"name": m.get("name"),
54+
"format": m.get("format"),
55+
"url": m.get("url"),
56+
}
57+
)
58+
59+
return {"count": len(models), "models": models}
60+
except Exception as e:
61+
return {"error": str(e)}
62+
63+
def get_model_files(self, model_id: str) -> Dict[str, Any]:
64+
"""
65+
Get file download links for a model.
66+
Usually returning the main SBML file link is sufficient.
67+
The search result usually gives a URL.
68+
Constructing download URL: https://www.ebi.ac.uk/biomodels/model/download/MODEL_ID?filename=MODEL_ID_url.xml ??
69+
Actually, API might have an endpoint for files.
70+
Documentation says `get_model_files(model_id)`.
71+
Let's try to find a specific endpoint or just return the model page/download link.
72+
Common pattern: https://www.ebi.ac.uk/biomodels/{model_id}#Files
73+
Or https://www.ebi.ac.uk/biomodels/model/download/{model_id}
74+
75+
I'll return the download URL.
76+
"""
77+
# If I want to list files, I might need another endpoint.
78+
# But simply returning the download link for the main model file is often what's needed.
79+
# Download URL: https://www.ebi.ac.uk/biomodels/{model_id}?format=sbml (maybe?)
80+
81+
# Let's check `search` result 'url'.
82+
# Sample: "url": "https://www.ebi.ac.uk/biomodels/BIOMD0000000469"
83+
84+
# I'll rely on a known pattern or just return the main URL.
85+
# A more specific API call `GET /model/files/{modelId}`?
86+
# I'll assume just returning the main page URL and formulated download URL is enough for now.
87+
download_url = (
88+
f"{self.BASE_URL}/model/download/{model_id}?filename={model_id}_url.xml"
89+
)
90+
91+
return {
92+
"model_id": model_id,
93+
"main_page": f"{self.BASE_URL}/{model_id}",
94+
"download_url": download_url, # This is a best guess, user might need to verify
95+
}
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
from typing import Dict, Any, List, Optional
2+
import requests
3+
from .base_tool import BaseTool
4+
from .tool_registry import register_tool
5+
6+
7+
@register_tool("ClinicalTrialsGovTool")
8+
class ClinicalTrialsGovTool(BaseTool):
9+
"""
10+
Tool for searching clinical trials using ClinicalTrials.gov API v2.
11+
"""
12+
13+
BASE_URL = "https://clinicaltrials.gov/api/v2/studies"
14+
15+
def run(self, arguments: Dict[str, Any]) -> Dict[str, Any]:
16+
"""
17+
Executes the ClinicalTrials tool action.
18+
19+
Args:
20+
arguments (Dict[str, Any]): Dictionary containing the action and its parameters.
21+
Expected keys:
22+
- action (str): "search_studies" or "get_study_details"
23+
- condition (str, optional): Condition to search for.
24+
- intervention (str, optional): Intervention/Drug to search for.
25+
- nct_id (str, optional): NCT ID for details.
26+
- limit (int, optional): Max results (default 10).
27+
28+
Returns:
29+
Dict[str, Any]: The results.
30+
"""
31+
action = arguments.get("action")
32+
33+
if action == "search_studies":
34+
return self.search_studies(
35+
condition=arguments.get("condition"),
36+
intervention=arguments.get("intervention"),
37+
limit=arguments.get("limit", 10),
38+
)
39+
elif action == "get_study_details":
40+
nct_id = arguments.get("nct_id")
41+
if not nct_id:
42+
raise ValueError("nct_id is required for get_study_details")
43+
return self.get_study_details(nct_id)
44+
else:
45+
raise ValueError(f"Unknown action: {action}")
46+
47+
def search_studies(
48+
self,
49+
condition: Optional[str] = None,
50+
intervention: Optional[str] = None,
51+
limit: int = 10,
52+
) -> Dict[str, Any]:
53+
"""
54+
Search for clinical trials.
55+
"""
56+
params = {"pageSize": limit, "format": "json"}
57+
58+
if condition:
59+
params["query.cond"] = condition
60+
if intervention:
61+
params["query.intr"] = intervention
62+
63+
try:
64+
response = requests.get(self.BASE_URL, params=params, timeout=30)
65+
response.raise_for_status()
66+
data = response.json()
67+
68+
studies = []
69+
for study in data.get("studies", []):
70+
proto = study.get("protocolSection", {})
71+
ident = proto.get("identificationModule", {})
72+
status = proto.get("statusModule", {})
73+
74+
studies.append(
75+
{
76+
"nctId": ident.get("nctId"),
77+
"title": ident.get("officialTitle") or ident.get("briefTitle"),
78+
"status": status.get("overallStatus"),
79+
"conditions": proto.get("conditionsModule", {}).get(
80+
"conditions", []
81+
),
82+
}
83+
)
84+
85+
return {"total_count": data.get("totalCount"), "studies": studies}
86+
except Exception as e:
87+
return {"error": str(e)}
88+
89+
def get_study_details(self, nct_id: str) -> Dict[str, Any]:
90+
"""
91+
Get full details for a study.
92+
"""
93+
url = f"{self.BASE_URL}/{nct_id}"
94+
try:
95+
response = requests.get(url, timeout=30)
96+
response.raise_for_status()
97+
data = response.json()
98+
99+
proto = data.get("protocolSection", {})
100+
101+
return {
102+
"nctId": nct_id,
103+
"title": proto.get("identificationModule", {}).get("officialTitle"),
104+
"summary": proto.get("descriptionModule", {}).get("briefSummary"),
105+
"eligibility": proto.get("eligibilityModule", {}),
106+
"contacts": proto.get("contactsLocationsModule", {}),
107+
"full_data_link": url,
108+
}
109+
except Exception as e:
110+
return {"error": str(e)}
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
[
2+
{
3+
"name": "biomodels_search",
4+
"type": "BioModelsTool",
5+
"description": "Searches for computational biological models in the EBI BioModels database. Use this tool to find models related to specific biological processes, such as 'glycolysis' or 'apoptosis'.",
6+
"parameter": {
7+
"type": "object",
8+
"properties": {
9+
"action": {
10+
"type": "string",
11+
"enum": [
12+
"search_models"
13+
],
14+
"default": "search_models",
15+
"description": "The specific action to perform. Must be set to 'search_models'."
16+
},
17+
"query": {
18+
"type": "string",
19+
"description": "The search term to query the BioModels database. Example queries include pathway names ('glycolysis'), disease names, or other biological keywords."
20+
},
21+
"limit": {
22+
"type": "integer",
23+
"description": "The maximum number of model results to return. The default is 10.",
24+
"default": 10
25+
}
26+
},
27+
"required": [
28+
"action",
29+
"query"
30+
]
31+
},
32+
"return_schema": {
33+
"type": "object",
34+
"description": "A dictionary containing the search results, including the count and a list of matching models.",
35+
"properties": {
36+
"count": {
37+
"type": "integer",
38+
"description": "The number of models matching the search query."
39+
},
40+
"models": {
41+
"type": "array",
42+
"description": "A list of model objects containing summary information.",
43+
"items": {
44+
"type": "object",
45+
"properties": {
46+
"id": {
47+
"type": "string",
48+
"description": "The unique BioModels identifier (e.g., 'BIOMD0000000469')."
49+
},
50+
"name": {
51+
"type": "string",
52+
"description": "The name or title of the biological model."
53+
},
54+
"format": {
55+
"type": "string",
56+
"description": "The file format of the model, typically 'SBML'."
57+
},
58+
"url": {
59+
"type": "string",
60+
"description": "The URL to the model's page on the BioModels website."
61+
}
62+
}
63+
}
64+
}
65+
}
66+
},
67+
"test_examples": [
68+
{
69+
"action": "search_models",
70+
"query": "glycolysis",
71+
"limit": 2
72+
}
73+
]
74+
},
75+
{
76+
"name": "biomodels_get_files",
77+
"type": "BioModelsTool",
78+
"description": "Retrieves the download link for a specific biological model from the BioModels database using its Model ID.",
79+
"parameter": {
80+
"type": "object",
81+
"properties": {
82+
"action": {
83+
"type": "string",
84+
"enum": [
85+
"get_model_files"
86+
],
87+
"default": "get_model_files",
88+
"description": "The specific action to perform. Must be set to 'get_model_files'."
89+
},
90+
"model_id": {
91+
"type": "string",
92+
"description": "The unique Model ID (e.g., 'BIOMD0000000469') for which to retrieve the download link."
93+
}
94+
},
95+
"required": [
96+
"action",
97+
"model_id"
98+
]
99+
},
100+
"return_schema": {
101+
"type": "object",
102+
"description": "A dictionary containing the model ID and its download URL.",
103+
"properties": {
104+
"model_id": {
105+
"type": "string",
106+
"description": "The BioModels ID of the requested model."
107+
},
108+
"main_page": {
109+
"type": "string",
110+
"description": "The URL to the main page of the model on the BioModels website."
111+
},
112+
"download_url": {
113+
"type": "string",
114+
"description": "The direct URL to download the model file (usually in SBML format)."
115+
}
116+
}
117+
},
118+
"test_examples": [
119+
{
120+
"action": "get_model_files",
121+
"model_id": "BIOMD0000000469"
122+
}
123+
]
124+
}
125+
]

0 commit comments

Comments
 (0)