tooluniverse 1.0.11.2__py3-none-any.whl → 1.0.12__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.

Potentially problematic release.


This version of tooluniverse might be problematic. Click here for more details.

Files changed (58) hide show
  1. tooluniverse/build_optimizer.py +115 -22
  2. tooluniverse/data/encode_tools.json +139 -0
  3. tooluniverse/data/gbif_tools.json +152 -0
  4. tooluniverse/data/gdc_tools.json +116 -0
  5. tooluniverse/data/gtex_tools.json +116 -0
  6. tooluniverse/data/icgc_tools.json +0 -0
  7. tooluniverse/data/mgnify_tools.json +121 -0
  8. tooluniverse/data/obis_tools.json +122 -0
  9. tooluniverse/data/optimizer_tools.json +275 -0
  10. tooluniverse/data/rnacentral_tools.json +99 -0
  11. tooluniverse/data/smolagent_tools.json +206 -0
  12. tooluniverse/data/wikipathways_tools.json +106 -0
  13. tooluniverse/default_config.py +12 -0
  14. tooluniverse/encode_tool.py +245 -0
  15. tooluniverse/execute_function.py +46 -8
  16. tooluniverse/gbif_tool.py +166 -0
  17. tooluniverse/gdc_tool.py +175 -0
  18. tooluniverse/generate_tools.py +121 -9
  19. tooluniverse/gtex_tool.py +168 -0
  20. tooluniverse/mgnify_tool.py +181 -0
  21. tooluniverse/obis_tool.py +185 -0
  22. tooluniverse/pypi_package_inspector_tool.py +3 -2
  23. tooluniverse/rnacentral_tool.py +124 -0
  24. tooluniverse/smcp_server.py +1 -1
  25. tooluniverse/smolagent_tool.py +555 -0
  26. tooluniverse/tools/ArgumentDescriptionOptimizer.py +55 -0
  27. tooluniverse/tools/ENCODE_list_files.py +59 -0
  28. tooluniverse/tools/ENCODE_search_experiments.py +67 -0
  29. tooluniverse/tools/GBIF_search_occurrences.py +67 -0
  30. tooluniverse/tools/GBIF_search_species.py +55 -0
  31. tooluniverse/tools/GDC_list_files.py +55 -0
  32. tooluniverse/tools/GDC_search_cases.py +55 -0
  33. tooluniverse/tools/GTEx_get_expression_summary.py +49 -0
  34. tooluniverse/tools/GTEx_query_eqtl.py +59 -0
  35. tooluniverse/tools/MGnify_list_analyses.py +52 -0
  36. tooluniverse/tools/MGnify_search_studies.py +55 -0
  37. tooluniverse/tools/OBIS_search_occurrences.py +59 -0
  38. tooluniverse/tools/OBIS_search_taxa.py +52 -0
  39. tooluniverse/tools/RNAcentral_get_by_accession.py +46 -0
  40. tooluniverse/tools/RNAcentral_search.py +52 -0
  41. tooluniverse/tools/TestCaseGenerator.py +46 -0
  42. tooluniverse/tools/ToolDescriptionOptimizer.py +67 -0
  43. tooluniverse/tools/ToolDiscover.py +4 -0
  44. tooluniverse/tools/UniProt_search.py +17 -44
  45. tooluniverse/tools/WikiPathways_get_pathway.py +52 -0
  46. tooluniverse/tools/WikiPathways_search.py +52 -0
  47. tooluniverse/tools/__init__.py +43 -1
  48. tooluniverse/tools/advanced_literature_search_agent.py +46 -0
  49. tooluniverse/tools/alphafold_get_annotations.py +4 -10
  50. tooluniverse/tools/download_binary_file.py +3 -6
  51. tooluniverse/tools/open_deep_research_agent.py +46 -0
  52. tooluniverse/wikipathways_tool.py +122 -0
  53. {tooluniverse-1.0.11.2.dist-info → tooluniverse-1.0.12.dist-info}/METADATA +3 -1
  54. {tooluniverse-1.0.11.2.dist-info → tooluniverse-1.0.12.dist-info}/RECORD +58 -17
  55. {tooluniverse-1.0.11.2.dist-info → tooluniverse-1.0.12.dist-info}/WHEEL +0 -0
  56. {tooluniverse-1.0.11.2.dist-info → tooluniverse-1.0.12.dist-info}/entry_points.txt +0 -0
  57. {tooluniverse-1.0.11.2.dist-info → tooluniverse-1.0.12.dist-info}/licenses/LICENSE +0 -0
  58. {tooluniverse-1.0.11.2.dist-info → tooluniverse-1.0.12.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,168 @@
1
+ import json
2
+ from typing import Any, Dict
3
+ from urllib.parse import urlencode
4
+ from urllib.request import Request, urlopen
5
+
6
+ from tooluniverse.tool_registry import register_tool
7
+
8
+
9
+ def _http_get(
10
+ url: str,
11
+ headers: Dict[str, str] | None = None,
12
+ timeout: int = 30,
13
+ ) -> Dict[str, Any]:
14
+ req = Request(url, headers=headers or {})
15
+ with urlopen(req, timeout=timeout) as resp:
16
+ data = resp.read()
17
+ try:
18
+ return json.loads(data.decode("utf-8", errors="ignore"))
19
+ except Exception:
20
+ return {"raw": data.decode("utf-8", errors="ignore")}
21
+
22
+
23
+ @register_tool(
24
+ "GTExExpressionTool",
25
+ config={
26
+ "name": "GTEx_get_expression_summary",
27
+ "type": "GTExExpressionTool",
28
+ "description": "Get GTEx expression summary for a gene via /expression/geneExpression",
29
+ "parameter": {
30
+ "type": "object",
31
+ "properties": {
32
+ "ensembl_gene_id": {
33
+ "type": "string",
34
+ "description": "Ensembl gene ID, e.g., ENSG00000141510",
35
+ }
36
+ },
37
+ "required": ["ensembl_gene_id"],
38
+ },
39
+ "settings": {"base_url": "https://gtexportal.org/api/v2", "timeout": 30},
40
+ },
41
+ )
42
+ class GTExExpressionTool:
43
+ def __init__(self, tool_config=None):
44
+ self.tool_config = tool_config or {}
45
+
46
+ def run(self, arguments: Dict[str, Any]):
47
+ base = self.tool_config.get("settings", {}).get(
48
+ "base_url", "https://gtexportal.org/api/v2"
49
+ )
50
+ timeout = int(self.tool_config.get("settings", {}).get("timeout", 30))
51
+
52
+ query = {"gencodeId": arguments.get("ensembl_gene_id")}
53
+ url = f"{base}/expression/geneExpression?{urlencode(query)}"
54
+ try:
55
+ api_response = _http_get(
56
+ url, headers={"Accept": "application/json"}, timeout=timeout
57
+ )
58
+ # Wrap API response to match schema: data.geneExpression should be array
59
+ # API returns {"data": [...], "paging_info": {...}}
60
+ # Schema expects {"data": {"geneExpression": [...]}}
61
+ if isinstance(api_response, dict) and "data" in api_response:
62
+ wrapped_data = {"geneExpression": api_response.get("data", [])}
63
+ else:
64
+ # Fallback if response format is unexpected
65
+ wrapped_data = {
66
+ "geneExpression": (
67
+ api_response if isinstance(api_response, list) else []
68
+ )
69
+ }
70
+
71
+ return {
72
+ "source": "GTEx",
73
+ "endpoint": "expression/geneExpression",
74
+ "query": query,
75
+ "data": wrapped_data,
76
+ "success": True,
77
+ }
78
+ except Exception as e:
79
+ return {
80
+ "error": str(e),
81
+ "source": "GTEx",
82
+ "endpoint": "expression/geneExpression",
83
+ "success": False,
84
+ }
85
+
86
+
87
+ @register_tool(
88
+ "GTExEQTLTool",
89
+ config={
90
+ "name": "GTEx_query_eqtl",
91
+ "type": "GTExEQTLTool",
92
+ "description": "Query GTEx single-tissue eQTL via /association/singleTissueEqtl",
93
+ "parameter": {
94
+ "type": "object",
95
+ "properties": {
96
+ "ensembl_gene_id": {
97
+ "type": "string",
98
+ "description": "Ensembl gene ID, e.g., ENSG00000141510",
99
+ },
100
+ "page": {
101
+ "type": "integer",
102
+ "default": 1,
103
+ "minimum": 1,
104
+ "description": "Page number (1-based)",
105
+ },
106
+ "size": {
107
+ "type": "integer",
108
+ "default": 10,
109
+ "minimum": 1,
110
+ "maximum": 100,
111
+ "description": "Page size (1–100)",
112
+ },
113
+ },
114
+ "required": ["ensembl_gene_id"],
115
+ },
116
+ "settings": {"base_url": "https://gtexportal.org/api/v2", "timeout": 30},
117
+ },
118
+ )
119
+ class GTExEQTLTool:
120
+ def __init__(self, tool_config=None):
121
+ self.tool_config = tool_config or {}
122
+
123
+ def run(self, arguments: Dict[str, Any]):
124
+ base = self.tool_config.get("settings", {}).get(
125
+ "base_url", "https://gtexportal.org/api/v2"
126
+ )
127
+ timeout = int(self.tool_config.get("settings", {}).get("timeout", 30))
128
+
129
+ query: Dict[str, Any] = {
130
+ "gencodeId": arguments.get("ensembl_gene_id"),
131
+ }
132
+ if "page" in arguments:
133
+ query["page"] = int(arguments["page"])
134
+ if "size" in arguments:
135
+ query["pageSize"] = int(arguments["size"])
136
+
137
+ url = f"{base}/association/singleTissueEqtl?{urlencode(query)}"
138
+ try:
139
+ api_response = _http_get(
140
+ url, headers={"Accept": "application/json"}, timeout=timeout
141
+ )
142
+ # Wrap API response to match schema: data.singleTissueEqtl should be array
143
+ # API returns {"data": [...], "paging_info": {...}}
144
+ # Schema expects {"data": {"singleTissueEqtl": [...]}}
145
+ if isinstance(api_response, dict) and "data" in api_response:
146
+ wrapped_data = {"singleTissueEqtl": api_response.get("data", [])}
147
+ else:
148
+ # Fallback if response format is unexpected
149
+ wrapped_data = {
150
+ "singleTissueEqtl": (
151
+ api_response if isinstance(api_response, list) else []
152
+ )
153
+ }
154
+
155
+ return {
156
+ "source": "GTEx",
157
+ "endpoint": "association/singleTissueEqtl",
158
+ "query": query,
159
+ "data": wrapped_data,
160
+ "success": True,
161
+ }
162
+ except Exception as e:
163
+ return {
164
+ "error": str(e),
165
+ "source": "GTEx",
166
+ "endpoint": "association/singleTissueEqtl",
167
+ "success": False,
168
+ }
@@ -0,0 +1,181 @@
1
+ import json
2
+ from typing import Any, Dict
3
+ from urllib.parse import urlencode
4
+ from urllib.request import Request, urlopen
5
+
6
+ from tooluniverse.tool_registry import register_tool
7
+
8
+
9
+ def _http_get(
10
+ url: str,
11
+ headers: Dict[str, str] | None = None,
12
+ timeout: int = 30,
13
+ ) -> Dict[str, Any]:
14
+ req = Request(url, headers=headers or {})
15
+ with urlopen(req, timeout=timeout) as resp:
16
+ data = resp.read()
17
+ try:
18
+ return json.loads(data.decode("utf-8", errors="ignore"))
19
+ except Exception:
20
+ return {"raw": data.decode("utf-8", errors="ignore")}
21
+
22
+
23
+ @register_tool(
24
+ "MGnifyStudiesTool",
25
+ config={
26
+ "name": "MGnify_search_studies",
27
+ "type": "MGnifyStudiesTool",
28
+ "description": "Search MGnify studies via /studies with optional biome/search filters",
29
+ "parameter": {
30
+ "type": "object",
31
+ "properties": {
32
+ "biome": {
33
+ "type": "string",
34
+ "description": "Biome identifier, e.g., 'root:Host-associated'",
35
+ },
36
+ "search": {
37
+ "type": "string",
38
+ "description": "Keyword to search in study title/description",
39
+ },
40
+ "size": {
41
+ "type": "integer",
42
+ "default": 10,
43
+ "minimum": 1,
44
+ "maximum": 100,
45
+ },
46
+ },
47
+ },
48
+ "settings": {
49
+ "base_url": "https://www.ebi.ac.uk/metagenomics/api/latest",
50
+ "timeout": 30,
51
+ },
52
+ },
53
+ )
54
+ class MGnifyStudiesTool:
55
+ def __init__(self, tool_config=None):
56
+ self.tool_config = tool_config or {}
57
+
58
+ def run(self, arguments: Dict[str, Any]):
59
+ base = self.tool_config.get("settings", {}).get(
60
+ "base_url", "https://www.ebi.ac.uk/metagenomics/api/latest"
61
+ )
62
+ timeout = int(self.tool_config.get("settings", {}).get("timeout", 30))
63
+
64
+ query: Dict[str, Any] = {}
65
+ if arguments.get("biome"):
66
+ query["biome"] = arguments["biome"]
67
+ if arguments.get("search"):
68
+ query["search"] = arguments["search"]
69
+ if arguments.get("size") is not None:
70
+ query["size"] = int(arguments["size"])
71
+ else:
72
+ query["size"] = 10
73
+
74
+ url = f"{base}/studies?{urlencode(query)}"
75
+ try:
76
+ api_response = _http_get(
77
+ url, headers={"Accept": "application/json"}, timeout=timeout
78
+ )
79
+ # Wrap API response to match schema: data.data should be array
80
+ # API returns {"data": [...], "links": {...}, "meta": {...}}
81
+ # Schema expects {"data": {"data": [...]}}
82
+ if isinstance(api_response, dict) and "data" in api_response:
83
+ wrapped_data = {"data": api_response.get("data", [])}
84
+ else:
85
+ # Fallback if response format is unexpected
86
+ wrapped_data = {
87
+ "data": api_response if isinstance(api_response, list) else []
88
+ }
89
+
90
+ return {
91
+ "source": "MGnify",
92
+ "endpoint": "studies",
93
+ "query": query,
94
+ "data": wrapped_data,
95
+ "success": True,
96
+ }
97
+ except Exception as e:
98
+ return {
99
+ "error": str(e),
100
+ "source": "MGnify",
101
+ "endpoint": "studies",
102
+ "success": False,
103
+ }
104
+
105
+
106
+ @register_tool(
107
+ "MGnifyAnalysesTool",
108
+ config={
109
+ "name": "MGnify_list_analyses",
110
+ "type": "MGnifyAnalysesTool",
111
+ "description": "List MGnify analyses via /analyses for a given study_accession",
112
+ "parameter": {
113
+ "type": "object",
114
+ "properties": {
115
+ "study_accession": {
116
+ "type": "string",
117
+ "description": "MGnify study accession, e.g., 'MGYS00000001'",
118
+ },
119
+ "size": {
120
+ "type": "integer",
121
+ "default": 10,
122
+ "minimum": 1,
123
+ "maximum": 100,
124
+ },
125
+ },
126
+ "required": ["study_accession"],
127
+ },
128
+ "settings": {
129
+ "base_url": "https://www.ebi.ac.uk/metagenomics/api/latest",
130
+ "timeout": 30,
131
+ },
132
+ },
133
+ )
134
+ class MGnifyAnalysesTool:
135
+ def __init__(self, tool_config=None):
136
+ self.tool_config = tool_config or {}
137
+
138
+ def run(self, arguments: Dict[str, Any]):
139
+ base = self.tool_config.get("settings", {}).get(
140
+ "base_url", "https://www.ebi.ac.uk/metagenomics/api/latest"
141
+ )
142
+ timeout = int(self.tool_config.get("settings", {}).get("timeout", 30))
143
+
144
+ query: Dict[str, Any] = {
145
+ "study_accession": arguments.get("study_accession"),
146
+ }
147
+ if arguments.get("size") is not None:
148
+ query["size"] = int(arguments["size"])
149
+ else:
150
+ query["size"] = 10
151
+
152
+ url = f"{base}/analyses?{urlencode(query)}"
153
+ try:
154
+ api_response = _http_get(
155
+ url, headers={"Accept": "application/json"}, timeout=timeout
156
+ )
157
+ # Wrap API response to match schema: data.data should be array
158
+ # API returns {"data": [...], "links": {...}, "meta": {...}}
159
+ # Schema expects {"data": {"data": [...]}}
160
+ if isinstance(api_response, dict) and "data" in api_response:
161
+ wrapped_data = {"data": api_response.get("data", [])}
162
+ else:
163
+ # Fallback if response format is unexpected
164
+ wrapped_data = {
165
+ "data": api_response if isinstance(api_response, list) else []
166
+ }
167
+
168
+ return {
169
+ "source": "MGnify",
170
+ "endpoint": "analyses",
171
+ "query": query,
172
+ "data": wrapped_data,
173
+ "success": True,
174
+ }
175
+ except Exception as e:
176
+ return {
177
+ "error": str(e),
178
+ "source": "MGnify",
179
+ "endpoint": "analyses",
180
+ "success": False,
181
+ }
@@ -0,0 +1,185 @@
1
+ import json
2
+ from typing import Any, Dict
3
+ from urllib.parse import urlencode
4
+ from urllib.request import Request, urlopen
5
+
6
+ from tooluniverse.tool_registry import register_tool
7
+
8
+
9
+ def _http_get(
10
+ url: str,
11
+ headers: Dict[str, str] | None = None,
12
+ timeout: int = 30,
13
+ ) -> Dict[str, Any]:
14
+ req = Request(url, headers=headers or {})
15
+ with urlopen(req, timeout=timeout) as resp:
16
+ data = resp.read()
17
+ try:
18
+ return json.loads(data.decode("utf-8", errors="ignore"))
19
+ except Exception:
20
+ return {"raw": data.decode("utf-8", errors="ignore")}
21
+
22
+
23
+ @register_tool(
24
+ "OBISTaxaTool",
25
+ config={
26
+ "name": "OBIS_search_taxa",
27
+ "type": "OBISTaxaTool",
28
+ "description": "Resolve marine taxa by scientific name via OBIS /v3/taxon",
29
+ "parameter": {
30
+ "type": "object",
31
+ "properties": {
32
+ "scientificname": {
33
+ "type": "string",
34
+ "description": "Scientific name to search, e.g., 'Gadus'",
35
+ },
36
+ "size": {
37
+ "type": "integer",
38
+ "default": 10,
39
+ "minimum": 1,
40
+ "maximum": 100,
41
+ },
42
+ },
43
+ "required": ["scientificname"],
44
+ },
45
+ "settings": {"base_url": "https://api.obis.org/v3", "timeout": 30},
46
+ },
47
+ )
48
+ class OBISTaxaTool:
49
+ def __init__(self, tool_config=None):
50
+ self.tool_config = tool_config or {}
51
+
52
+ def run(self, arguments: Dict[str, Any]):
53
+ base = self.tool_config.get("settings", {}).get(
54
+ "base_url", "https://api.obis.org/v3"
55
+ )
56
+ timeout = int(self.tool_config.get("settings", {}).get("timeout", 30))
57
+
58
+ scientificname = arguments.get("scientificname")
59
+ size = int(arguments.get("size", 10))
60
+
61
+ # Note: OBIS v3 API does not have /taxon endpoint
62
+ # Use occurrence search with scientificname filter instead
63
+ # This returns occurrences which can be used to identify taxa
64
+ query = {
65
+ "scientificname": scientificname,
66
+ "size": size,
67
+ }
68
+ url = f"{base}/occurrence?{urlencode(query)}"
69
+ try:
70
+ data = _http_get(
71
+ url, headers={"Accept": "application/json"}, timeout=timeout
72
+ )
73
+ # Extract unique taxa from occurrences
74
+ if isinstance(data, dict) and "results" in data:
75
+ results = data.get("results", [])
76
+ # Extract unique scientific names and taxonomic info
77
+ taxa_list = []
78
+ seen_names = set()
79
+ for occ in results:
80
+ sci_name = occ.get("scientificName")
81
+ if sci_name and sci_name not in seen_names:
82
+ seen_names.add(sci_name)
83
+ taxa_list.append(
84
+ {
85
+ "scientificName": sci_name,
86
+ "aphiaID": occ.get("aphiaID"),
87
+ "rank": occ.get("taxonRank"),
88
+ "kingdom": occ.get("kingdom"),
89
+ "phylum": occ.get("phylum"),
90
+ "class": occ.get("class_"),
91
+ "order": occ.get("order"),
92
+ "family": occ.get("family"),
93
+ "genus": occ.get("genus"),
94
+ }
95
+ )
96
+ if len(taxa_list) >= size:
97
+ break
98
+ # Return in expected schema format
99
+ wrapped_data = {
100
+ "results": taxa_list,
101
+ "total": len(taxa_list),
102
+ }
103
+ else:
104
+ wrapped_data = {"results": [], "total": 0}
105
+
106
+ return {
107
+ "source": "OBIS",
108
+ "endpoint": "occurrence", # Note: taxon endpoint not available, using occurrence
109
+ "query": query,
110
+ "data": wrapped_data,
111
+ "success": True,
112
+ }
113
+ except Exception as e:
114
+ return {
115
+ "error": str(e),
116
+ "source": "OBIS",
117
+ "endpoint": "occurrence",
118
+ "success": False,
119
+ }
120
+
121
+
122
+ @register_tool(
123
+ "OBISOccurrenceTool",
124
+ config={
125
+ "name": "OBIS_search_occurrences",
126
+ "type": "OBISOccurrenceTool",
127
+ "description": "Search OBIS occurrences via /v3/occurrence",
128
+ "parameter": {
129
+ "type": "object",
130
+ "properties": {
131
+ "scientificname": {
132
+ "type": "string",
133
+ "description": "Scientific name filter (optional)",
134
+ },
135
+ "areaid": {
136
+ "type": "string",
137
+ "description": "Area identifier filter (optional)",
138
+ },
139
+ "size": {
140
+ "type": "integer",
141
+ "default": 10,
142
+ "minimum": 1,
143
+ "maximum": 100,
144
+ },
145
+ },
146
+ },
147
+ "settings": {"base_url": "https://api.obis.org/v3", "timeout": 30},
148
+ },
149
+ )
150
+ class OBISOccurrenceTool:
151
+ def __init__(self, tool_config=None):
152
+ self.tool_config = tool_config or {}
153
+
154
+ def run(self, arguments: Dict[str, Any]):
155
+ base = self.tool_config.get("settings", {}).get(
156
+ "base_url", "https://api.obis.org/v3"
157
+ )
158
+ timeout = int(self.tool_config.get("settings", {}).get("timeout", 30))
159
+
160
+ query: Dict[str, Any] = {}
161
+ for key in ("scientificname", "areaid", "size"):
162
+ if key in arguments and arguments[key] is not None:
163
+ query[key] = arguments[key]
164
+ if "size" not in query:
165
+ query["size"] = 10
166
+
167
+ url = f"{base}/occurrence?{urlencode(query)}"
168
+ try:
169
+ data = _http_get(
170
+ url, headers={"Accept": "application/json"}, timeout=timeout
171
+ )
172
+ return {
173
+ "source": "OBIS",
174
+ "endpoint": "occurrence",
175
+ "query": query,
176
+ "data": data,
177
+ "success": True,
178
+ }
179
+ except Exception as e:
180
+ return {
181
+ "error": str(e),
182
+ "source": "OBIS",
183
+ "endpoint": "occurrence",
184
+ "success": False,
185
+ }
@@ -5,6 +5,7 @@ import time
5
5
  from datetime import datetime, timedelta
6
6
  from typing import Dict, Any
7
7
  from .tool_registry import register_tool
8
+ from .base_tool import BaseTool
8
9
 
9
10
 
10
11
  @register_tool(
@@ -39,7 +40,7 @@ from .tool_registry import register_tool
39
40
  },
40
41
  },
41
42
  )
42
- class PyPIPackageInspector:
43
+ class PyPIPackageInspector(BaseTool):
43
44
  """
44
45
  Extracts comprehensive package information from PyPI and GitHub.
45
46
  Provides detailed metrics on popularity, maintenance, security,
@@ -47,7 +48,7 @@ class PyPIPackageInspector:
47
48
  """
48
49
 
49
50
  def __init__(self, tool_config: Dict[str, Any] = None):
50
- self.tool_config = tool_config or {}
51
+ BaseTool.__init__(self, tool_config or {})
51
52
  self.pypi_api_url = "https://pypi.org/pypi/{package}/json"
52
53
  self.pypistats_api_url = "https://pypistats.org/api/packages/{package}/recent"
53
54
  self.github_api_url = "https://api.github.com/repos/{owner}/{repo}"
@@ -0,0 +1,124 @@
1
+ import json
2
+ from typing import Any, Dict
3
+ from urllib.parse import urlencode
4
+ from urllib.request import Request, urlopen
5
+
6
+ from tooluniverse.tool_registry import register_tool
7
+
8
+
9
+ def _http_get(
10
+ url: str, headers: Dict[str, str] | None = None, timeout: int = 30
11
+ ) -> Dict[str, Any]:
12
+ req = Request(url, headers=headers or {})
13
+ with urlopen(req, timeout=timeout) as resp:
14
+ data = resp.read()
15
+ try:
16
+ return json.loads(data.decode("utf-8", errors="ignore"))
17
+ except Exception:
18
+ return {"raw": data.decode("utf-8", errors="ignore")}
19
+
20
+
21
+ @register_tool(
22
+ "RNAcentralSearchTool",
23
+ config={
24
+ "name": "RNAcentral_search",
25
+ "type": "RNAcentralSearchTool",
26
+ "description": "Search RNA records via RNAcentral API",
27
+ "parameter": {
28
+ "type": "object",
29
+ "properties": {
30
+ "query": {"type": "string", "description": "Keyword or accession"},
31
+ "page_size": {
32
+ "type": "integer",
33
+ "default": 10,
34
+ "minimum": 1,
35
+ "maximum": 100,
36
+ },
37
+ },
38
+ "required": ["query"],
39
+ },
40
+ "settings": {"base_url": "https://rnacentral.org/api/v1", "timeout": 30},
41
+ },
42
+ )
43
+ class RNAcentralSearchTool:
44
+ def __init__(self, tool_config=None):
45
+ self.tool_config = tool_config or {}
46
+
47
+ def run(self, arguments: Dict[str, Any]):
48
+ base = self.tool_config.get("settings", {}).get(
49
+ "base_url", "https://rnacentral.org/api/v1"
50
+ )
51
+ timeout = int(self.tool_config.get("settings", {}).get("timeout", 30))
52
+
53
+ query = {
54
+ "query": arguments.get("query"),
55
+ "page_size": int(arguments.get("page_size", 10)),
56
+ }
57
+ url = f"{base}/rna/?{urlencode(query)}"
58
+ try:
59
+ data = _http_get(
60
+ url, headers={"Accept": "application/json"}, timeout=timeout
61
+ )
62
+ return {
63
+ "source": "RNAcentral",
64
+ "endpoint": "rna",
65
+ "query": query,
66
+ "data": data,
67
+ "success": True,
68
+ }
69
+ except Exception as e:
70
+ return {
71
+ "error": str(e),
72
+ "source": "RNAcentral",
73
+ "endpoint": "rna",
74
+ "success": False,
75
+ }
76
+
77
+
78
+ @register_tool(
79
+ "RNAcentralGetTool",
80
+ config={
81
+ "name": "RNAcentral_get_by_accession",
82
+ "type": "RNAcentralGetTool",
83
+ "description": "Get RNAcentral entry by accession",
84
+ "parameter": {
85
+ "type": "object",
86
+ "properties": {
87
+ "accession": {"type": "string", "description": "RNAcentral accession"}
88
+ },
89
+ "required": ["accession"],
90
+ },
91
+ "settings": {"base_url": "https://rnacentral.org/api/v1", "timeout": 30},
92
+ },
93
+ )
94
+ class RNAcentralGetTool:
95
+ def __init__(self, tool_config=None):
96
+ self.tool_config = tool_config or {}
97
+
98
+ def run(self, arguments: Dict[str, Any]):
99
+ base = self.tool_config.get("settings", {}).get(
100
+ "base_url", "https://rnacentral.org/api/v1"
101
+ )
102
+ timeout = int(self.tool_config.get("settings", {}).get("timeout", 30))
103
+
104
+ acc = arguments.get("accession")
105
+ url = f"{base}/rna/{acc}"
106
+ try:
107
+ data = _http_get(
108
+ url, headers={"Accept": "application/json"}, timeout=timeout
109
+ )
110
+ return {
111
+ "source": "RNAcentral",
112
+ "endpoint": "rna/{accession}",
113
+ "accession": acc,
114
+ "data": data,
115
+ "success": True,
116
+ }
117
+ except Exception as e:
118
+ return {
119
+ "error": str(e),
120
+ "source": "RNAcentral",
121
+ "endpoint": "rna/{accession}",
122
+ "accession": acc,
123
+ "success": False,
124
+ }