mgbam commited on
Commit
e421c45
·
verified ·
1 Parent(s): 146b143

Update mcp/mygene.py

Browse files
Files changed (1) hide show
  1. mcp/mygene.py +42 -13
mcp/mygene.py CHANGED
@@ -1,29 +1,58 @@
1
  #!/usr/bin/env python3
2
- """Async wrapper for MyGene.info API (https://mygene.info)
3
 
4
- Provides a single helper:
5
- • `fetch_gene_info(query)` → returns the first matching hit dict or `{}`.
6
-
7
- No API‑key needed; community tier allows 500 QPS.
 
 
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) -> dict: # noqa: D401
19
- """Return first hit for *query* or an empty dict."""
20
  params = {
21
  "q": query,
22
  "fields": "symbol,name,summary,alias,entrezgene,clinvar,location,go",
23
  "size": 1,
24
  }
25
- async with httpx.AsyncClient(timeout=10) as client:
26
- resp = await client.get(f"{_BASE}/query", params=params)
27
- resp.raise_for_status()
28
- hits = resp.json().get("hits", [])
29
- return hits[0] if hits else {}
 
 
 
 
 
 
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())