tooluniverse.xenbase_tool 源代码

"""
Xenbase Tool - Xenopus (Frog) Model Organism Database

Provides access to Xenopus gene data through the Alliance of Genome Resources
API. Xenbase is the primary database for Xenopus tropicalis and Xenopus laevis
genetics, genomics, and developmental biology.

Note: Xenbase does not have a public REST/JSON API. Data is accessed through
the Alliance of Genome Resources (alliancegenome.org) which aggregates Xenbase
data alongside other model organism databases.

Supported species:
- Xenopus tropicalis (NCBITaxon:8364) - Western clawed frog
- Xenopus laevis (NCBITaxon:8355) - African clawed frog

API: https://www.alliancegenome.org/api
No authentication required.

Reference: Fisher et al., Nucl. Acids Res. 2023 (Xenbase)
"""

import requests
from typing import Dict, Any
from .base_tool import BaseTool
from .tool_registry import register_tool


ALLIANCE_BASE = "https://www.alliancegenome.org/api"
XENOPUS_TAXON_IDS = ["NCBITaxon:8364", "NCBITaxon:8355"]


[文档] @register_tool("XenbaseTool") class XenbaseTool(BaseTool): """ Tool for querying Xenopus gene data via the Alliance of Genome Resources. Supported operations: - search_genes: Search Xenopus genes by name/symbol - get_gene: Get detailed gene information by Xenbase gene ID """
[文档] def __init__(self, tool_config: Dict[str, Any]): super().__init__(tool_config) self.timeout = 30 self.endpoint_type = tool_config.get("fields", {}).get( "endpoint_type", "search_genes" )
[文档] def run(self, arguments: Dict[str, Any]) -> Dict[str, Any]: """Execute the Xenbase/Alliance API call.""" try: if self.endpoint_type == "search_genes": return self._search_genes(arguments) elif self.endpoint_type == "get_gene": return self._get_gene(arguments) else: return { "status": "error", "error": f"Unknown endpoint type: {self.endpoint_type}", } except requests.exceptions.Timeout: return {"status": "error", "error": "Xenbase API request timed out"} except requests.exceptions.ConnectionError: return { "status": "error", "error": "Failed to connect to Alliance API for Xenbase data", } except Exception as e: return {"status": "error", "error": f"Xenbase API error: {str(e)}"}
[文档] def _search_genes(self, arguments: Dict[str, Any]) -> Dict[str, Any]: """Search for Xenopus genes by name or symbol.""" query = arguments.get("query") or arguments.get("gene_name", "") if not query: return {"status": "error", "error": "Missing required parameter: query"} limit = arguments.get("limit", 10) species = arguments.get("species", "") # Map species names to taxon IDs taxon_filter = None if species: species_lower = species.lower() if "tropicalis" in species_lower or "8364" in species_lower: taxon_filter = "NCBITaxon:8364" elif "laevis" in species_lower or "8355" in species_lower: taxon_filter = "NCBITaxon:8355" url = f"{ALLIANCE_BASE}/search" params = {"q": query, "category": "gene", "limit": limit} resp = requests.get(url, params=params, timeout=self.timeout) resp.raise_for_status() data = resp.json() results = [] for r in data.get("results", []): pk = r.get("primaryKey", "") sp = r.get("species", "") if not pk.startswith("Xenbase:"): continue if taxon_filter and taxon_filter not in str(r.get("taxonId", "")): # Filter by species name in text if taxonId not present if taxon_filter == "NCBITaxon:8364" and "tropicalis" not in sp.lower(): continue if taxon_filter == "NCBITaxon:8355" and "laevis" not in sp.lower(): continue results.append( { "gene_id": pk, "symbol": r.get("symbol"), "name": r.get("name"), "species": sp, "synonyms": r.get("synonyms", [])[:5], } ) return { "status": "success", "data": results, "metadata": { "query": query, "total_alliance_results": data.get("total", 0), "xenbase_results": len(results), }, }
[文档] def _get_gene(self, arguments: Dict[str, Any]) -> Dict[str, Any]: """Get detailed gene information by Xenbase gene ID.""" gene_id = arguments.get("gene_id", "") if not gene_id: return {"status": "error", "error": "Missing required parameter: gene_id"} # Ensure Xenbase: prefix if not gene_id.startswith("Xenbase:"): gene_id = f"Xenbase:{gene_id}" url = f"{ALLIANCE_BASE}/gene/{gene_id}" resp = requests.get(url, timeout=self.timeout) if resp.status_code == 404: return {"status": "error", "error": f"Gene not found: {gene_id}"} resp.raise_for_status() data = resp.json() species_info = data.get("species", {}) genome_locations = data.get("genomeLocations", []) location = None if genome_locations: loc = genome_locations[0] location = { "chromosome": loc.get("chromosome"), "start": loc.get("start"), "end": loc.get("end"), "strand": loc.get("strand"), "assembly": loc.get("assembly"), } result = { "gene_id": data.get("id"), "symbol": data.get("symbol"), "name": data.get("name"), "species": { "name": species_info.get("name"), "short_name": species_info.get("shortName"), "taxon_id": species_info.get("taxonId"), "data_provider": species_info.get("dataProviderShortName"), }, "synonyms": data.get("synonyms", []), "gene_synopsis": data.get("geneSynopsis"), "automated_gene_synopsis": data.get("automatedGeneSynopsis"), "so_term": data.get("soTerm", {}).get("name") if data.get("soTerm") else None, "genomic_location": location, "xenbase_url": data.get("modCrossRefCompleteUrl"), "cross_references": { k: v for k, v in (data.get("crossReferenceMap") or {}).items() if v }, } return {"status": "success", "data": result}