mgbam commited on
Commit
f6de761
Β·
verified Β·
1 Parent(s): 8ab7297

Update mcp/umls.py

Browse files
Files changed (1) hide show
  1. mcp/umls.py +46 -28
mcp/umls.py CHANGED
@@ -1,48 +1,66 @@
1
- import os, httpx, asyncio
 
 
 
 
 
 
 
2
  from functools import lru_cache
3
 
4
- UMLS_KEY = os.getenv("UMLS_KEY") # MUST be set in HF Secrets
5
- _AUTH_URL = "https://utslogin.nlm.nih.gov/cas/v1/api-key"
6
- _SEARCH_URL= "https://uts-ws.nlm.nih.gov/rest/search/current"
7
 
8
- # ── internal helpers ────────────────────────────────────────────────
9
  async def _get_ticket() -> str | None:
10
- """Return a single-use service ticket or None on auth failure."""
 
 
11
  if not UMLS_KEY:
12
  return None
13
  try:
14
- async with httpx.AsyncClient(timeout=10) as c:
15
- tgt = await c.post(_AUTH_URL, data={"apikey": UMLS_KEY})
16
- tgt.raise_for_status()
17
- action = tgt.text.split('action="')[1].split('"')[0]
18
- st = await c.post(action, data={"service": "http://umlsks.nlm.nih.gov"})
19
- return st.text
 
 
20
  except Exception:
21
  return None
22
 
23
- # ── public API (cached) ─────────────────────────────────────────────
24
  @lru_cache(maxsize=512)
25
  async def lookup_umls(term: str) -> dict:
26
  """
27
- Return {term,cui,name,definition}. Never raises.
28
- If auth/quota fails β†’ returns everything as None (safe for UI).
 
29
  """
30
  ticket = await _get_ticket()
31
  if not ticket:
32
  return {"term": term, "cui": None, "name": None, "definition": None}
33
-
34
- params = {"string": term, "ticket": ticket, "pageSize": 1}
 
 
 
35
  try:
36
- async with httpx.AsyncClient(timeout=8) as c:
37
- r = await c.get(_SEARCH_URL, params=params)
38
- r.raise_for_status()
39
- items = r.json()["result"]["results"]
40
- hit = items[0] if items else {}
41
- return {
42
- "term": term,
43
- "cui": hit.get("ui"),
44
- "name": hit.get("name"),
45
- "definition": hit.get("rootSource"),
46
- }
 
 
 
47
  except Exception:
48
  return {"term": term, "cui": None, "name": None, "definition": None}
 
1
+ # mcp/umls.py
2
+ """
3
+ Async UMLS lookup via NLM REST API.
4
+ Graceful fallback: always returns a dict with all keys even on failure.
5
+ """
6
+
7
+ import os
8
+ import httpx
9
  from functools import lru_cache
10
 
11
+ UMLS_KEY = os.getenv("UMLS_KEY") # Should be set as a secret (Hugging Face/colab/etc)
12
+ _AUTH_URL = "https://utslogin.nlm.nih.gov/cas/v1/api-key"
13
+ _SEARCH_URL = "https://uts-ws.nlm.nih.gov/rest/search/current"
14
 
15
+ # ── Internal helper: get a single-use service ticket ──────────────
16
  async def _get_ticket() -> str | None:
17
+ """
18
+ Return a single-use UMLS service ticket, or None if auth fails.
19
+ """
20
  if not UMLS_KEY:
21
  return None
22
  try:
23
+ async with httpx.AsyncClient(timeout=10) as client:
24
+ # 1. Get ticket-granting ticket (TGT)
25
+ tgt_resp = await client.post(_AUTH_URL, data={"apikey": UMLS_KEY})
26
+ tgt_resp.raise_for_status()
27
+ tgt_url = tgt_resp.text.split('action="')[1].split('"')[0]
28
+ # 2. Exchange for single-use service ticket
29
+ st_resp = await client.post(tgt_url, data={"service": "http://umlsks.nlm.nih.gov"})
30
+ return st_resp.text.strip()
31
  except Exception:
32
  return None
33
 
34
+ # ── Public API: async, returns safe dict ──────────────────────────
35
  @lru_cache(maxsize=512)
36
  async def lookup_umls(term: str) -> dict:
37
  """
38
+ Lookup a UMLS concept by text term.
39
+ Returns dict with {term, cui, name, definition}, always.
40
+ If quota/auth fails: all values None except term.
41
  """
42
  ticket = await _get_ticket()
43
  if not ticket:
44
  return {"term": term, "cui": None, "name": None, "definition": None}
45
+ params = {
46
+ "string": term,
47
+ "ticket": ticket,
48
+ "pageSize": 1,
49
+ }
50
  try:
51
+ async with httpx.AsyncClient(timeout=8) as client:
52
+ resp = await client.get(_SEARCH_URL, params=params)
53
+ resp.raise_for_status()
54
+ items = resp.json().get("result", {}).get("results", [])
55
+ if items:
56
+ hit = items[0]
57
+ return {
58
+ "term": term,
59
+ "cui": hit.get("ui"),
60
+ "name": hit.get("name"),
61
+ "definition": hit.get("rootSource"),
62
+ }
63
+ else:
64
+ return {"term": term, "cui": None, "name": None, "definition": None}
65
  except Exception:
66
  return {"term": term, "cui": None, "name": None, "definition": None}