MCP_Res / mcp /targets.py
mgbam's picture
Update mcp/targets.py
b6b0f60 verified
#!/usr/bin/env python3
"""MedGenesis – **Open Targets** GraphQL helper (async, cached).
Updated version adds:
* Retry with exponential back‑off (2→4→8 s)
* 12‑h LRU cache (256 genes)
* Graceful empty‑list fallback & doc‑string examples.
"""
from __future__ import annotations
import asyncio, textwrap, httpx
from functools import lru_cache
from typing import List, Dict
_OT_URL = "https://api.platform.opentargets.org/api/v4/graphql"
_QUERY = textwrap.dedent(
"""
query Assoc($gene: String!, $size: Int!) {
associations(geneSymbol: $gene, size: $size) {
rows {
score
datatypeId
datasourceId
disease { id name }
target { id symbol }
}
}
}
"""
)
async def _post(payload: Dict, retries: int = 3) -> Dict:
"""POST with back‑off retry (2×, 4×); returns JSON."""
delay = 2
last_resp = None
for _ in range(retries):
async with httpx.AsyncClient(timeout=15) as cli:
last_resp = await cli.post(_OT_URL, json=payload)
if last_resp.status_code == 200:
return last_resp.json()
await asyncio.sleep(delay)
delay *= 2
# Raise from final attempt if still failing
last_resp.raise_for_status() # type: ignore
@lru_cache(maxsize=256)
async def fetch_ot_associations(gene_symbol: str, *, size: int = 30) -> List[Dict]:
"""Return association rows for *gene_symbol* (or []). Example::
rows = await fetch_ot_associations("TP53", size=20)
"""
payload = {"query": _QUERY, "variables": {"gene": gene_symbol, "size": size}}
data = await _post(payload)
return data.get("data", {}).get("associations", {}).get("rows", [])