Source code for tooluniverse.gnomad_tool
"""
gnomAD GraphQL API Tool
This tool provides access to the gnomAD (Genome Aggregation Database) for
population genetics data, variant frequencies, and gene constraint metrics
using GraphQL.
"""
import requests
from typing import Dict, Any
from .base_tool import BaseTool
from .tool_registry import register_tool
[docs]
class gnomADGraphQLTool(BaseTool):
"""Base class for gnomAD GraphQL API tools."""
[docs]
def __init__(self, tool_config):
super().__init__(tool_config)
self.endpoint_url = "https://gnomad.broadinstitute.org/api"
# Prefer JSON-driven query definitions. Support both legacy top-level
# `query_schema` and `fields.query_schema`.
fields_cfg = tool_config.get("fields", {}) or {}
self.query_schema = tool_config.get("query_schema") or fields_cfg.get(
"query_schema", ""
)
self.session = requests.Session()
self.session.headers.update(
{
"Accept": "application/json",
"Content-Type": "application/json",
"User-Agent": "ToolUniverse/1.0",
}
)
self.timeout = 30
[docs]
def run(self, arguments: Dict[str, Any]) -> Dict[str, Any]:
"""Execute GraphQL query with given arguments."""
try:
response = self.session.post(
self.endpoint_url,
json={"query": self.query_schema, "variables": arguments},
timeout=self.timeout,
)
status_code = getattr(response, "status_code", None)
response.raise_for_status()
result = response.json()
# GraphQL errors are returned with HTTP 200; surface them to users.
errors = result.get("errors")
if errors:
first = errors[0] if isinstance(errors, list) and errors else None
msg = first.get("message") if isinstance(first, dict) else None
msg = msg or "gnomAD GraphQL query returned errors"
return {
"status": "error",
"error": msg,
"url": getattr(response, "url", self.endpoint_url),
"status_code": status_code,
"detail": errors[:3],
"data": None,
}
data = result.get("data")
if not data or all(not v for v in data.values()):
return {
"status": "error",
"error": "No data returned from gnomAD API",
"url": getattr(response, "url", self.endpoint_url),
"status_code": status_code,
"data": None,
}
return {
"status": "success",
"data": result,
"url": getattr(response, "url", self.endpoint_url),
}
except requests.exceptions.HTTPError as e:
resp = getattr(e, "response", None)
return {
"status": "error",
"error": (
f"gnomAD API returned HTTP {getattr(resp, 'status_code', None)}"
),
"url": getattr(resp, "url", self.endpoint_url),
"status_code": getattr(resp, "status_code", None),
"detail": (getattr(resp, "text", "") or "")[:500] or None,
"data": None,
}
except (requests.exceptions.RequestException, ValueError) as e:
return {
"status": "error",
"error": f"gnomAD GraphQL request failed: {str(e)}",
"url": self.endpoint_url,
"status_code": None,
"detail": None,
"data": None,
}
except Exception as e:
return {
"status": "error",
"error": f"gnomAD GraphQL request failed: {str(e)}",
"url": self.endpoint_url,
"status_code": None,
"detail": None,
"data": None,
}
[docs]
@register_tool("gnomADGraphQLQueryTool")
class gnomADGraphQLQueryTool(gnomADGraphQLTool):
"""
Generic gnomAD GraphQL tool driven by JSON config.
Config fields supported:
- fields.query_schema: GraphQL query string
- fields.variable_map: map tool argument names -> GraphQL variable names
- fields.default_variables: default GraphQL variable values
"""
[docs]
def __init__(self, tool_config):
super().__init__(tool_config)
fields_cfg = tool_config.get("fields", {}) or {}
self.variable_map = fields_cfg.get("variable_map", {}) or {}
self.default_variables = fields_cfg.get("default_variables", {}) or {}
[docs]
def run(self, arguments: Dict[str, Any]) -> Dict[str, Any]:
# Merge defaults + map argument names to GraphQL variables
variables: Dict[str, Any] = dict(self.default_variables)
for k, v in (arguments or {}).items():
if v is None:
continue
variables[self.variable_map.get(k, k)] = v
return super().run(variables)
[docs]
@register_tool("gnomADGetGeneConstraints")
class gnomADGetGeneConstraints(gnomADGraphQLTool):
"""Get gene constraint metrics from gnomAD."""
[docs]
def __init__(self, tool_config):
super().__init__(tool_config)
# Set default query schema if not provided in config
if not self.query_schema:
self.query_schema = """
query GeneConstraints(
$geneSymbol: String!,
$referenceGenome: ReferenceGenomeId!
) {
gene(gene_symbol: $geneSymbol, reference_genome: $referenceGenome) {
symbol
gene_id
exac_constraint {
exp_lof
obs_lof
pLI
exp_mis
obs_mis
exp_syn
obs_syn
}
gnomad_constraint {
exp_lof
obs_lof
oe_lof
pLI
exp_mis
obs_mis
oe_mis
exp_syn
obs_syn
oe_syn
}
}
}
"""
[docs]
def run(self, arguments: Dict[str, Any]) -> Dict[str, Any]:
"""Get gene constraints."""
gene_symbol = arguments.get("gene_symbol", "")
if not gene_symbol:
return {"status": "error", "error": "gene_symbol is required"}
reference_genome = arguments.get("reference_genome") or "GRCh38"
# Convert tool args to GraphQL variables
graphql_args = {
"geneSymbol": gene_symbol,
"referenceGenome": reference_genome,
}
result = super().run(graphql_args)
# Add gene_symbol to result for reference
if result.get("status") == "success":
result["gene_symbol"] = gene_symbol
return result