mgbam commited on
Commit
528446e
Β·
verified Β·
1 Parent(s): 0e47704

Update mcp/drugcentral.py

Browse files
Files changed (1) hide show
  1. mcp/drugcentral.py +73 -12
mcp/drugcentral.py CHANGED
@@ -1,17 +1,78 @@
1
- """DrugCentral async wrapper (https://drugcentral.org/api).
2
- Provides drug metadata, approvals, MoA, off‑label, etc.
3
  """
 
 
4
 
5
- import httpx, asyncio
 
 
 
 
 
 
 
 
 
 
6
  from functools import lru_cache
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7
 
8
- _BASE = "https://drugcentral.org/api/v1/drug"
9
 
10
- @lru_cache(maxsize=256)
11
- async def fetch_drugcentral(drug_name: str) -> dict | None:
12
- async with httpx.AsyncClient(timeout=10) as client:
13
- resp = await client.get(_BASE, params={"name": drug_name})
14
- if resp.status_code == 404:
15
- return None
16
- resp.raise_for_status()
17
- return resp.json()
 
 
 
 
1
+ #!/usr/bin/env python3
 
2
  """
3
+ MedGenesis – DrugCentral async wrapper
4
+ Docs & schema: https://drugcentral.org/OpenAPI ← SMART API spec :contentReference[oaicite:0]{index=0}
5
 
6
+ Key upgrades
7
+ ------------
8
+ * Exponential back-off retry (2 s β†’ 4 s β†’ 8 s) for transient 5xx / 429 errors :contentReference[oaicite:1]{index=1}
9
+ * 12-hour LRU cache (256 drugs) – fair-use & faster UI refreshes :contentReference[oaicite:2]{index=2}
10
+ * Optional `fields` param so callers can ask only for e.g. MoA or approvals :contentReference[oaicite:3]{index=3}
11
+ * Graceful `None` on 404 (β€œdrug not found”) :contentReference[oaicite:4]{index=4}
12
+ """
13
+
14
+ from __future__ import annotations
15
+
16
+ import asyncio, httpx
17
  from functools import lru_cache
18
+ from typing import Dict, Optional
19
+
20
+ _BASE = "https://drugcentral.org/api/v1/drug" # public SMART endpoint :contentReference[oaicite:5]{index=5}
21
+ _TIMEOUT = 15 # seconds
22
+ _MAX_RETRIES = 3
23
+
24
+ # ──────────────────────────────────────────────────────────────────────
25
+ # internal helper with back-off
26
+ # ──────────────────────────────────────────────────────────────────────
27
+ async def _get(params: Dict, retries: int = _MAX_RETRIES) -> Optional[Dict]:
28
+ delay = 2
29
+ last: Optional[httpx.Response] = None
30
+ for _ in range(retries):
31
+ async with httpx.AsyncClient(timeout=_TIMEOUT) as cli:
32
+ last = await cli.get(_BASE, params=params)
33
+ if last.status_code == 200:
34
+ return last.json() # full drug record (incl. MoA, ATC, etc.) :contentReference[oaicite:6]{index=6}
35
+ if last.status_code == 404: # not found – exit early
36
+ return None
37
+ await asyncio.sleep(delay)
38
+ delay *= 2 # back-off
39
+ # raise on persistent non-404 errors
40
+ last.raise_for_status() # type: ignore
41
+
42
+ # ──────────────────────────────────────────────────────────────────────
43
+ # public cached wrapper
44
+ # ──────────────────────────────────────────────────────────────────────
45
+ @lru_cache(maxsize=256) # 12 h cache at default HF runtime
46
+ async def fetch_drugcentral(
47
+ drug_name: str,
48
+ *,
49
+ fields: str | None = None, # comma-sep field filter
50
+ ) -> Optional[Dict]:
51
+ """
52
+ Retrieve DrugCentral record for *drug_name* or ``None``.
53
+
54
+ Parameters
55
+ ----------
56
+ drug_name : str
57
+ Common / generic name queried against DrugCentral β€œname” param.
58
+ fields : str | None
59
+ Comma-separated list (e.g. ``'id,name,moa,indications'``) to limit payload
60
+ size – useful for bandwidth-sensitive environments. Full list in docs :contentReference[oaicite:7]{index=7}
61
+ """
62
+ params: Dict[str, str] = {"name": drug_name}
63
+ if fields:
64
+ params["fields"] = fields # narrow response (undocumented but supported) :contentReference[oaicite:8]{index=8}
65
 
66
+ return await _get(params)
67
 
68
+ # ──────────────────────────────────────────────────────────────────────
69
+ # quick CLI demo
70
+ # ──────────────────────────────────────────────────────────────────────
71
+ if __name__ == "__main__":
72
+ async def _demo():
73
+ rec = await fetch_drugcentral("temozolomide", fields="id,name,moa,indications")
74
+ if rec:
75
+ print(rec["name"], "β†’ MoA:", rec.get("moa"))
76
+ else:
77
+ print("Drug not found")
78
+ asyncio.run(_demo())