tooluniverse.rxnorm_extended_tool 源代码

"""
RxNorm Extended Tool

Extends the basic RxNorm drug name tool with additional endpoints from the
U.S. National Library of Medicine (NLM) RxNorm API:

  - rxnorm_get_drug_info    : comprehensive properties for a drug by name or RXCUI
  - rxnorm_get_related_drugs: branded and generic products for an ingredient RXCUI
  - rxnorm_find_rxcui       : resolve a drug name to its RXCUI(s)

API base: https://rxnav.nlm.nih.gov/REST
No authentication required.
"""

import requests
from typing import Dict, Any
from .base_tool import BaseTool
from .tool_registry import register_tool

RXNORM_BASE = "https://rxnav.nlm.nih.gov/REST"

# Term-type (tty) labels used in RxNorm
TTY_LABELS = {
    "IN": "Ingredient (generic)",
    "PIN": "Precise Ingredient",
    "BN": "Brand Name",
    "SCD": "Semantic Clinical Drug (generic product)",
    "SBD": "Semantic Branded Drug",
    "GPCK": "Generic Pack",
    "BPCK": "Branded Pack",
    "SCDF": "Semantic Clinical Drug Form",
    "SBDF": "Semantic Branded Drug Form",
    "SCDC": "Semantic Drug Component",
    "MIN": "Multiple Ingredients",
    "DF": "Dose Form",
}


[文档] @register_tool("RxNormExtendedTool") class RxNormExtendedTool(BaseTool): """ Extended RxNorm tools for drug information retrieval. Supports three operations: - find_rxcui: Resolve a drug name to its RXCUI identifier(s) - get_drug_info: Fetch full properties (name, TTY, synonym) by RXCUI or name - get_related_drugs: List all branded and generic clinical drug products that share an active ingredient """
[文档] def __init__(self, tool_config: Dict[str, Any]): super().__init__(tool_config) self.timeout = tool_config.get("timeout", 30) self.operation = tool_config.get("fields", {}).get("operation", "find_rxcui")
[文档] def run(self, arguments: Dict[str, Any]) -> Dict[str, Any]: op = self.operation if op == "find_rxcui": return self._find_rxcui(arguments) elif op == "get_drug_info": return self._get_drug_info(arguments) elif op == "get_related_drugs": return self._get_related_drugs(arguments) return {"status": "error", "error": f"Unknown operation: {op}"}
# ------------------------------------------------------------------ # helpers # ------------------------------------------------------------------
[文档] def _resolve_rxcui(self, arguments: Dict[str, Any]): """Return (rxcui_str, error_dict_or_None).""" rxcui = arguments.get("rxcui") drug_name = arguments.get("drug_name") or arguments.get("name") if rxcui: return str(rxcui).strip(), None if drug_name: url = f"{RXNORM_BASE}/rxcui.json" try: resp = requests.get( url, params={"name": drug_name.strip()}, timeout=self.timeout ) resp.raise_for_status() ids = resp.json().get("idGroup", {}).get("rxnormId", []) if not ids: return None, { "status": "error", "error": f"No RXCUI found for drug name: {drug_name!r}", } return str(ids[0]), None except requests.exceptions.RequestException as e: return None, { "status": "error", "error": f"RxNorm API request failed: {e}", } return None, { "status": "error", "error": "Provide either 'rxcui' or 'drug_name'.", }
# ------------------------------------------------------------------ # operations # ------------------------------------------------------------------
[文档] def _find_rxcui(self, arguments: Dict[str, Any]) -> Dict[str, Any]: """Resolve a drug name to one or more RXCUIs.""" drug_name = arguments.get("drug_name") or arguments.get("name") if not drug_name or not str(drug_name).strip(): return {"status": "error", "error": "drug_name is required"} url = f"{RXNORM_BASE}/rxcui.json" try: resp = requests.get( url, params={"name": drug_name.strip(), "search": 2}, timeout=self.timeout, ) resp.raise_for_status() data = resp.json() except requests.exceptions.RequestException as e: return {"status": "error", "error": f"RxNorm API request failed: {e}"} except Exception as e: return {"status": "error", "error": f"Failed to parse response: {e}"} rxcuis = data.get("idGroup", {}).get("rxnormId", []) if not rxcuis: return { "status": "success", "data": {"drug_name": drug_name, "rxcuis": [], "found": False}, "metadata": { "note": "No RXCUI found. Try a simpler drug name (generic, no dosage)." }, } return { "status": "success", "data": { "drug_name": drug_name, "rxcuis": rxcuis, "primary_rxcui": rxcuis[0], "found": True, }, "metadata": {"source": "NLM RxNorm API"}, }
[文档] def _get_drug_info(self, arguments: Dict[str, Any]) -> Dict[str, Any]: """Fetch comprehensive drug properties by RXCUI or drug name.""" rxcui, err = self._resolve_rxcui(arguments) if err: return err url = f"{RXNORM_BASE}/rxcui/{rxcui}/properties.json" try: resp = requests.get(url, timeout=self.timeout) resp.raise_for_status() props = resp.json().get("properties", {}) except requests.exceptions.RequestException as e: return {"status": "error", "error": f"RxNorm API request failed: {e}"} except Exception as e: return {"status": "error", "error": f"Failed to parse response: {e}"} if not props: return { "status": "error", "error": f"No properties found for RXCUI {rxcui}", } tty = props.get("tty", "") tty_label = TTY_LABELS.get(tty, tty) return { "status": "success", "data": { "rxcui": props.get("rxcui", rxcui), "name": props.get("name"), "synonym": props.get("synonym") or None, "term_type": tty, "term_type_label": tty_label, "language": props.get("language"), }, "metadata": {"source": "NLM RxNorm API"}, }