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())
|