tooluniverse.opencravat_tool 源代码

# opencravat_tool.py
"""
OpenCRAVAT (Open Custom Ranked Analysis of Variants Toolkit) API tool.

OpenCRAVAT aggregates 182+ variant annotation sources into a single query,
including ClinVar, gnomAD, SIFT, PolyPhen-2, CADD, REVEL, AlphaMissense,
SpliceAI, GERP, PhastCons, DANN, FATHMM, and many more.

API: https://run.opencravat.org/submit/annotate (single variant, no auth)
API: https://run.opencravat.org/submit/annotators (list available annotators)
"""

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

OPENCRAVAT_BASE_URL = "https://run.opencravat.org/submit"


[文档] @register_tool("OpenCRAVATTool") class OpenCRAVATTool(BaseTool): """ Tool for querying OpenCRAVAT variant annotation API. Supports single-variant annotation with configurable annotator selection. No authentication required for the public annotation endpoint. """
[文档] def __init__(self, tool_config: Dict[str, Any]): super().__init__(tool_config) self.timeout = tool_config.get("timeout", 30) self.operation = tool_config.get("fields", {}).get( "operation", "annotate_variant" )
[文档] def run(self, arguments: Dict[str, Any]) -> Dict[str, Any]: """Execute the OpenCRAVAT API call.""" op = self.operation if op == "annotate_variant": return self._annotate_variant(arguments) if op == "list_annotators": return self._list_annotators(arguments) return {"status": "error", "error": f"Unknown operation: {op}"}
[文档] def _annotate_variant(self, arguments: Dict[str, Any]) -> Dict[str, Any]: """Annotate a single variant with multiple annotation sources.""" chrom = arguments.get("chrom") or arguments.get("chromosome", "") pos = arguments.get("pos") or arguments.get("position") ref_base = arguments.get("ref_base") or arguments.get("ref", "") alt_base = arguments.get("alt_base") or arguments.get("alt", "") annotators = arguments.get("annotators") if not all([chrom, pos, ref_base, alt_base]): return { "status": "error", "error": "chrom, pos, ref_base, and alt_base are all required", } # Ensure chr prefix if not str(chrom).startswith("chr"): chrom = f"chr{chrom}" params = { "chrom": chrom, "pos": int(pos), "ref_base": ref_base.upper(), "alt_base": alt_base.upper(), } if annotators: params["annotators"] = annotators try: resp = requests.get( f"{OPENCRAVAT_BASE_URL}/annotate", params=params, timeout=self.timeout, ) resp.raise_for_status() data = resp.json() except requests.exceptions.Timeout: return {"status": "error", "error": "OpenCRAVAT API request timed out"} except requests.exceptions.RequestException as e: return {"status": "error", "error": f"OpenCRAVAT API error: {e}"} except ValueError: return {"status": "error", "error": "Invalid JSON response from OpenCRAVAT"} crx = data.get("crx", {}) result = { "gene": crx.get("hugo"), "amino_acid_change": crx.get("achange"), "coding_change": crx.get("cchange"), "consequence": crx.get("so"), "transcript": crx.get("transcript"), "chrom": crx.get("chrom", chrom), "pos": crx.get("pos", pos), "ref_base": crx.get("ref_base", ref_base), "alt_base": crx.get("alt_base", alt_base), "annotations": {}, "module_versions": data.get("module_versions", {}), } # Collect all annotation results skip_keys = {"crx", "alternateAlleles", "module_versions", "originalInput"} for key, val in data.items(): if key not in skip_keys and val is not None: result["annotations"][key] = val return {"status": "success", "data": result}
[文档] def _list_annotators(self, arguments: Dict[str, Any]) -> Dict[str, Any]: """List all available annotators on the OpenCRAVAT server.""" category = arguments.get("category") try: resp = requests.get( f"{OPENCRAVAT_BASE_URL}/annotators", timeout=self.timeout, ) resp.raise_for_status() data = resp.json() except requests.exceptions.RequestException as e: return {"status": "error", "error": f"OpenCRAVAT API error: {e}"} except ValueError: return {"status": "error", "error": "Invalid JSON response from OpenCRAVAT"} annotators = [] for name, info in data.items(): entry = { "name": name, "title": info.get("title", ""), "description": info.get("description", ""), "type": info.get("type", ""), } if category and category.lower() not in entry["type"].lower(): continue annotators.append(entry) annotators.sort(key=lambda x: x["name"]) return {"status": "success", "data": annotators}