Update mcp/drugcentral.py
Browse files- mcp/drugcentral.py +73 -12
mcp/drugcentral.py
CHANGED
@@ -1,17 +1,78 @@
|
|
1 |
-
|
2 |
-
Provides drug metadata, approvals, MoA, offβlabel, etc.
|
3 |
"""
|
|
|
|
|
4 |
|
5 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
6 |
from functools import lru_cache
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
7 |
|
8 |
-
|
9 |
|
10 |
-
|
11 |
-
|
12 |
-
|
13 |
-
|
14 |
-
|
15 |
-
|
16 |
-
|
17 |
-
|
|
|
|
|
|
|
|
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())
|