Update mcp/mygene.py
Browse files- mcp/mygene.py +42 -13
mcp/mygene.py
CHANGED
@@ -1,29 +1,58 @@
|
|
1 |
#!/usr/bin/env python3
|
2 |
-
"""
|
3 |
|
4 |
-
Provides
|
5 |
-
|
6 |
-
|
7 |
-
|
|
|
|
|
8 |
"""
|
9 |
from __future__ import annotations
|
10 |
|
11 |
-
import httpx
|
12 |
from functools import lru_cache
|
|
|
13 |
|
14 |
_BASE = "https://mygene.info/v3"
|
|
|
|
|
15 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
16 |
|
17 |
@lru_cache(maxsize=128)
|
18 |
-
async def fetch_gene_info(query: str) ->
|
19 |
-
"""Return
|
20 |
params = {
|
21 |
"q": query,
|
22 |
"fields": "symbol,name,summary,alias,entrezgene,clinvar,location,go",
|
23 |
"size": 1,
|
24 |
}
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
|
|
|
|
|
|
|
|
|
|
|
|
1 |
#!/usr/bin/env python3
|
2 |
+
"""MedGenesis – MyGene.info async helper (timeout‑safe).
|
3 |
|
4 |
+
Provides `fetch_gene_info(query)` → first hit dict or `{}`.
|
5 |
+
Adds:
|
6 |
+
* 3‑step back‑off retry (2 s → 4 s → 8 s) on time‑outs/429/5xx.
|
7 |
+
* 10 s client timeout.
|
8 |
+
* 24 h LRU cache (128 queries).
|
9 |
+
* Returns empty dict on any failure so orchestrator can’t crash.
|
10 |
"""
|
11 |
from __future__ import annotations
|
12 |
|
13 |
+
import asyncio, httpx
|
14 |
from functools import lru_cache
|
15 |
+
from typing import Dict
|
16 |
|
17 |
_BASE = "https://mygene.info/v3"
|
18 |
+
_TIMEOUT = 10
|
19 |
+
_MAX_RETRIES = 3
|
20 |
|
21 |
+
async def _query_mygene(params: Dict) -> Dict:
|
22 |
+
delay = 2
|
23 |
+
for attempt in range(_MAX_RETRIES):
|
24 |
+
try:
|
25 |
+
async with httpx.AsyncClient(timeout=_TIMEOUT) as client:
|
26 |
+
resp = await client.get(f"{_BASE}/query", params=params)
|
27 |
+
if resp.status_code == 200:
|
28 |
+
hits = resp.json().get("hits", [])
|
29 |
+
return hits[0] if hits else {}
|
30 |
+
if resp.status_code in {429, 500, 502, 503, 504}:
|
31 |
+
raise httpx.HTTPStatusError("retry", request=resp.request, response=resp)
|
32 |
+
except (httpx.HTTPStatusError, httpx.ReadTimeout):
|
33 |
+
if attempt == _MAX_RETRIES - 1:
|
34 |
+
return {}
|
35 |
+
await asyncio.sleep(delay)
|
36 |
+
delay *= 2
|
37 |
+
except Exception:
|
38 |
+
return {}
|
39 |
+
return {}
|
40 |
|
41 |
@lru_cache(maxsize=128)
|
42 |
+
async def fetch_gene_info(query: str) -> Dict: # noqa: D401
|
43 |
+
"""Return MyGene.info top hit or empty dict (never raises)."""
|
44 |
params = {
|
45 |
"q": query,
|
46 |
"fields": "symbol,name,summary,alias,entrezgene,clinvar,location,go",
|
47 |
"size": 1,
|
48 |
}
|
49 |
+
return await _query_mygene(params)
|
50 |
+
|
51 |
+
# ------------------------------------------------------------------
|
52 |
+
# CLI demo
|
53 |
+
# ------------------------------------------------------------------
|
54 |
+
if __name__ == "__main__":
|
55 |
+
import json, asyncio
|
56 |
+
async def _demo():
|
57 |
+
print(json.dumps(await fetch_gene_info("TP53"), indent=2))
|
58 |
+
asyncio.run(_demo())
|