Source code for tooluniverse.clingen_dosage_tool

"""
ClinGen Dosage Sensitivity JSON API Tool

Provides access to ClinGen dosage sensitivity curations via the JSON API
at search.clinicalgenome.org. Returns haploinsufficiency and triplosensitivity
scores with genomic coordinates, pLI scores, and disease associations.

This uses the structured JSON API rather than CSV parsing, providing richer
data including genomic coordinates in both GRCh37 and GRCh38.
"""

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

CLINGEN_API_URL = "https://search.clinicalgenome.org/api/dosage"


[docs] @register_tool("ClinGenDosageTool") class ClinGenDosageTool(BaseTool): """ ClinGen Dosage Sensitivity JSON API tool. Uses the search.clinicalgenome.org/api/dosage endpoint which returns structured JSON with detailed dosage sensitivity curations. """
[docs] def __init__(self, tool_config): super().__init__(tool_config) self.parameter = tool_config.get("parameter", {}) self.required = self.parameter.get("required", []) fields = tool_config.get("fields", {}) self.operation = fields.get("operation", "") self.timeout = fields.get("timeout", 30)
[docs] def run(self, arguments: Dict[str, Any]) -> Dict[str, Any]: """Route to operation handler based on config.""" operation = self.operation or arguments.get("operation") if not operation: return {"status": "error", "error": "Missing: operation"} operation_map = { "dosage_by_gene": self._dosage_by_gene, "dosage_region_search": self._dosage_region_search, } handler = operation_map.get(operation) if not handler: return {"status": "error", "error": f"Unknown operation: {operation}"} return handler(arguments)
[docs] def _dosage_by_gene(self, arguments: Dict[str, Any]) -> Dict[str, Any]: """Search ClinGen dosage sensitivity curations by gene symbol.""" gene = arguments.get("gene") if not gene: return {"status": "error", "error": "Missing required parameter: gene"} try: response = requests.get( CLINGEN_API_URL, params={"search": gene}, timeout=self.timeout, ) response.raise_for_status() data = response.json() rows = data.get("rows", []) # Filter for exact or close matches gene_upper = gene.upper() matches = [ row for row in rows if row.get("symbol", "").upper() == gene_upper ] # If no exact match, try partial if not matches: matches = [ row for row in rows if gene_upper in row.get("symbol", "").upper() ] return { "status": "success", "data": matches, "total": len(matches), "gene_searched": gene, "source": "ClinGen Dosage Sensitivity (JSON API)", } except requests.exceptions.Timeout: return {"status": "error", "error": f"Timeout after {self.timeout}s"} except requests.exceptions.HTTPError as e: return { "status": "error", "error": f"HTTP {e.response.status_code}: {e.response.text[:200]}", } except Exception as e: return {"status": "error", "error": str(e)}