Source code for tooluniverse.oxo_tool

# oxo_tool.py
"""
EBI OxO (Ontology Xref Service) tool for ToolUniverse.

Provides cross-reference mappings between ontology terms across biomedical
databases including Disease Ontology, HPO, MeSH, SNOMED-CT, UMLS, OMIM,
ICD-10, NCIt, Gene Ontology, ChEBI, and many more.

API: https://www.ebi.ac.uk/spot/oxo/
No authentication required. Free public access.
"""

import requests
from typing import Dict, Any, List
from .base_tool import BaseTool


OXO_BASE_URL = "https://www.ebi.ac.uk/spot/oxo/api"


[docs] class OxOTool(BaseTool): """ Tool for EBI OxO (Ontology Xref Service) API providing cross-reference mappings between ontology terms across biomedical databases. No authentication required. """
[docs] def __init__(self, tool_config: Dict[str, Any]): super().__init__(tool_config) self.timeout = tool_config.get("timeout", 30) fields = tool_config.get("fields", {}) self.endpoint = fields.get("endpoint", "get_mappings")
[docs] def run(self, arguments: Dict[str, Any]) -> Dict[str, Any]: """Execute the OxO API call.""" try: return self._query(arguments) except requests.exceptions.Timeout: return {"error": f"OxO API timed out after {self.timeout}s"} except requests.exceptions.ConnectionError: return {"error": "Failed to connect to EBI OxO API"} except requests.exceptions.HTTPError as e: code = e.response.status_code if e.response is not None else "unknown" return {"error": f"OxO API HTTP error: {code}"} except Exception as e: return {"error": f"Unexpected error querying OxO API: {str(e)}"}
[docs] def _query(self, arguments: Dict[str, Any]) -> Dict[str, Any]: """Route to appropriate endpoint.""" if self.endpoint == "get_mappings": return self._get_mappings(arguments) elif self.endpoint == "search_mappings": return self._search_mappings(arguments) else: return {"error": f"Unknown endpoint: {self.endpoint}"}
[docs] def _get_mappings(self, arguments: Dict[str, Any]) -> Dict[str, Any]: """Get cross-reference mappings for a single ontology term.""" term_id = arguments.get("term_id", "") if not term_id: return {"error": "term_id parameter is required (e.g., 'HP:0001250')"} distance = min(arguments.get("distance", 1), 3) size = min(arguments.get("size", 20), 100) url = f"{OXO_BASE_URL}/search" params = { "ids": term_id, "distance": distance, "size": size, } response = requests.get(url, params=params, timeout=self.timeout) response.raise_for_status() data = response.json() embedded = data.get("_embedded", {}) search_results = embedded.get("searchResults", []) if not search_results: return { "data": { "query_id": term_id, "query_label": None, "total_mappings": 0, "mappings": [], }, "metadata": { "source": "EBI OxO (Ontology Xref Service)", "term_id": term_id, "distance": distance, }, } result = search_results[0] query_source = result.get("querySource", {}) mapping_list = result.get("mappingResponseList", []) mappings = [] for m in mapping_list: mappings.append( { "curie": m.get("curie", ""), "label": m.get("label"), "prefix": m.get("targetPrefix", ""), "distance": m.get("distance", 1), "source_type": m.get("sourceType"), } ) return { "data": { "query_id": result.get("queryId", term_id), "query_label": query_source.get("label") if isinstance(query_source, dict) else None, "total_mappings": len(mappings), "mappings": mappings, }, "metadata": { "source": "EBI OxO (Ontology Xref Service)", "term_id": term_id, "distance": distance, }, }
[docs] def _search_mappings(self, arguments: Dict[str, Any]) -> Dict[str, Any]: """Search mappings for multiple terms at once.""" term_ids_str = arguments.get("term_ids", "") if not term_ids_str: return {"error": "term_ids parameter is required (comma-separated list)"} term_ids = [t.strip() for t in term_ids_str.split(",") if t.strip()] distance = min(arguments.get("distance", 1), 3) target_prefix = arguments.get("target_prefix") all_results = [] for term_id in term_ids: url = f"{OXO_BASE_URL}/search" params = { "ids": term_id, "distance": distance, "size": 50, } response = requests.get(url, params=params, timeout=self.timeout) response.raise_for_status() data = response.json() embedded = data.get("_embedded", {}) search_results = embedded.get("searchResults", []) mappings = [] if search_results: mapping_list = search_results[0].get("mappingResponseList", []) for m in mapping_list: # Apply target prefix filter if specified if target_prefix: if ( target_prefix.lower() not in m.get("targetPrefix", "").lower() ): continue mappings.append( { "curie": m.get("curie", ""), "label": m.get("label"), "prefix": m.get("targetPrefix", ""), "distance": m.get("distance", 1), } ) all_results.append( { "query_id": term_id, "mappings_count": len(mappings), "mappings": mappings, } ) return { "data": { "queries": len(term_ids), "results": all_results, }, "metadata": { "source": "EBI OxO (Ontology Xref Service)", "term_ids": term_ids, "distance": distance, "target_prefix": target_prefix, }, }