From 94589e35bb3618ba48dea450afa45ad1a6587660 Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 1 May 2026 17:26:04 +0100 Subject: [PATCH 1/2] Add AI Drug safety project - complete drug safety agent implementation --- AI Drug safety/.gitignore | 14 + AI Drug safety/README.md | 76 ++++ AI Drug safety/ai_drug_safety/__init__.py | 7 + AI Drug safety/ai_drug_safety/adjudicate.py | 217 +++++++++++ AI Drug safety/ai_drug_safety/agent.py | 27 ++ AI Drug safety/ai_drug_safety/agent_chain.py | 94 +++++ AI Drug safety/ai_drug_safety/api_clients.py | 212 +++++++++++ AI Drug safety/ai_drug_safety/cache.py | 71 ++++ AI Drug safety/ai_drug_safety/cli.py | 26 ++ AI Drug safety/ai_drug_safety/data/beers.json | 26 ++ AI Drug safety/ai_drug_safety/evaluation.py | 251 +++++++++++++ .../ai_drug_safety/llm_integration.py | 78 ++++ AI Drug safety/ai_drug_safety/llm_reasoner.py | 55 +++ AI Drug safety/ai_drug_safety/naranjo.py | 62 ++++ AI Drug safety/ai_drug_safety/risk_scoring.py | 346 ++++++++++++++++++ AI Drug safety/ai_drug_safety/rxnorm.py | 59 +++ AI Drug safety/ai_drug_safety/web.py | 58 +++ AI Drug safety/docs/annotation_protocol.md | 71 ++++ AI Drug safety/docs/evaluation_spec.md | 61 +++ .../adjudicated_gold_20260430T235016Z.csv | 16 + .../adjudicated_gold_converted.csv | 16 + .../adjudication_report_20260430T235016Z.json | 32 ++ .../evaluations/annotations_annotatorA.csv | 16 + .../evaluations/annotations_annotatorB.csv | 16 + .../evaluation_metrics_20260501T151852Z.json | 13 + .../evaluation_real_20260501T151845Z.json | 312 ++++++++++++++++ .../evaluations/gold_standard_sample.csv | 6 + AI Drug safety/requirements-streamlit.txt | 1 + AI Drug safety/requirements.txt | 7 + AI Drug safety/run_real_evaluation.py | 86 +++++ AI Drug safety/run_web.py | 11 + .../scripts/convert_adjudicated_to_gold.py | 47 +++ AI Drug safety/streamlit_app.py | 240 ++++++++++++ 33 files changed, 2630 insertions(+) create mode 100644 AI Drug safety/.gitignore create mode 100644 AI Drug safety/README.md create mode 100644 AI Drug safety/ai_drug_safety/__init__.py create mode 100644 AI Drug safety/ai_drug_safety/adjudicate.py create mode 100644 AI Drug safety/ai_drug_safety/agent.py create mode 100644 AI Drug safety/ai_drug_safety/agent_chain.py create mode 100644 AI Drug safety/ai_drug_safety/api_clients.py create mode 100644 AI Drug safety/ai_drug_safety/cache.py create mode 100644 AI Drug safety/ai_drug_safety/cli.py create mode 100644 AI Drug safety/ai_drug_safety/data/beers.json create mode 100644 AI Drug safety/ai_drug_safety/evaluation.py create mode 100644 AI Drug safety/ai_drug_safety/llm_integration.py create mode 100644 AI Drug safety/ai_drug_safety/llm_reasoner.py create mode 100644 AI Drug safety/ai_drug_safety/naranjo.py create mode 100644 AI Drug safety/ai_drug_safety/risk_scoring.py create mode 100644 AI Drug safety/ai_drug_safety/rxnorm.py create mode 100644 AI Drug safety/ai_drug_safety/web.py create mode 100644 AI Drug safety/docs/annotation_protocol.md create mode 100644 AI Drug safety/docs/evaluation_spec.md create mode 100644 AI Drug safety/evaluations/adjudicated_gold_20260430T235016Z.csv create mode 100644 AI Drug safety/evaluations/adjudicated_gold_converted.csv create mode 100644 AI Drug safety/evaluations/adjudication_report_20260430T235016Z.json create mode 100644 AI Drug safety/evaluations/annotations_annotatorA.csv create mode 100644 AI Drug safety/evaluations/annotations_annotatorB.csv create mode 100644 AI Drug safety/evaluations/evaluation_metrics_20260501T151852Z.json create mode 100644 AI Drug safety/evaluations/evaluation_real_20260501T151845Z.json create mode 100644 AI Drug safety/evaluations/gold_standard_sample.csv create mode 100644 AI Drug safety/requirements-streamlit.txt create mode 100644 AI Drug safety/requirements.txt create mode 100644 AI Drug safety/run_real_evaluation.py create mode 100644 AI Drug safety/run_web.py create mode 100644 AI Drug safety/scripts/convert_adjudicated_to_gold.py create mode 100644 AI Drug safety/streamlit_app.py diff --git a/AI Drug safety/.gitignore b/AI Drug safety/.gitignore new file mode 100644 index 0000000..e8deb68 --- /dev/null +++ b/AI Drug safety/.gitignore @@ -0,0 +1,14 @@ +# Virtual environment +venv/ +env/ + +# Python cache +__pycache__/ +*.pyc + +# Environment variables file +.env + +# Local virtualenv and caches +.venv/ +.cache/ diff --git a/AI Drug safety/README.md b/AI Drug safety/README.md new file mode 100644 index 0000000..2ba202c --- /dev/null +++ b/AI Drug safety/README.md @@ -0,0 +1,76 @@ +# AI Drug Safety Agent + +This repository contains a Python scaffold for an AI Drug Safety Agent that +fetches real drug data (openFDA / optional DrugBank), computes clinical risk +scores (HAS-BLED, Tisdale), and synthesizes a concise clinical recommendation +using an LLM when available. + +Requirements +------------ + +Install dependencies into a virtual environment (recommended): + +```bash +python -m venv .venv +source .venv/bin/activate # or .venv\Scripts\activate on Windows +pip install -r requirements.txt +``` + +Quick start +----------- + +Run the Streamlit tester UI: + +```bash +streamlit run streamlit_app.py +``` + +Run the FastAPI web shim (optional): + +```bash +``` + +CLI example: + +```bash +python -m ai_drug_safety.cli aspirin --age 70 --conditions "hypertension" +``` + +Environment / secrets +--------------------- + +Set `OPENAI_API_KEY` and `DRUGBANK_API_KEY` in your local environment or a +`.env` file at the project root. IMPORTANT: do not commit secrets. The +repository `.gitignore` contains `.env` and `.venv` entries — ensure you do not +push actual API keys to GitHub. + +What I changed for you +---------------------- + +- Added `requirements.txt` with core dependencies. +- Updated the Streamlit UI to accept `sex` and relevant clinical flags. +- Improved the LLM prompt to request `confidence` and `assumptions` fields + and explicitly consider `patient.sex` when relevant. + +What to keep (recommended) +-------------------------- + +- `ai_drug_safety/` — core package and clinical logic (keep). +- `evaluations/` — adjudicated gold and annotation CSVs (keep for evaluation). +- `streamlit_app.py` — interactive tester UI (keep if you use it). + +Optional files you can remove +---------------------------- + +- `run_example.py` — tiny demo script (non-essential). +- `.cache/` — cache files from previous runs (safe to remove if you want a clean repo). +- `__pycache__/` directories — Python bytecode caches (safe to remove). + +Pushing to GitHub +------------------ + +Before you push, ensure: +- `.env` does not contain secrets (remove or redact keys). +- `.venv/` is not committed (use `.gitignore`). + +If you want, I can remove non-essential files now (e.g., `run_example.py`, `.cache/`). diff --git a/AI Drug safety/ai_drug_safety/__init__.py b/AI Drug safety/ai_drug_safety/__init__.py new file mode 100644 index 0000000..fd709f6 --- /dev/null +++ b/AI Drug safety/ai_drug_safety/__init__.py @@ -0,0 +1,7 @@ +__all__ = [ + "api_clients", + "risk_scoring", + "llm_reasoner", + "agent", + "cli", +] diff --git a/AI Drug safety/ai_drug_safety/adjudicate.py b/AI Drug safety/ai_drug_safety/adjudicate.py new file mode 100644 index 0000000..f249ec2 --- /dev/null +++ b/AI Drug safety/ai_drug_safety/adjudicate.py @@ -0,0 +1,217 @@ +"""Adjudication tooling: pairwise annotation comparison + Cohen's kappa report. + +Reads two annotator CSV files and optionally an adjudicator CSV. Produces +an adjudication report JSON and an adjudicated CSV with consensus/adjudicated +labels where available. + +Usage: + python -m ai_drug_safety.adjudicate --a evaluations/annotations_annotatorA.csv \ + --b evaluations/annotations_annotatorB.csv --out-dir evaluations +""" +from __future__ import annotations + +import argparse +import csv +import json +import os +import random +from datetime import datetime +from typing import Dict, List, Tuple, Optional + + +def read_csv(path: str) -> List[Dict[str, str]]: + out = [] + with open(path, newline='', encoding='utf-8') as f: + reader = csv.DictReader(f) + for r in reader: + out.append(r) + return out + + +def norm_key(row: Dict[str, str]) -> str: + # Prefer numeric id if present, else normalized drug name + idv = (row.get('id') or '').strip() + if idv: + return idv + return (row.get('drug') or '').strip().lower() + + +def parse_bool(x: Optional[str]) -> Optional[bool]: + if x is None: + return None + s = str(x).strip().lower() + if s == '': + return None + if s in ('1', 'true', 'yes', 'y'): + return True + if s in ('0', 'false', 'no', 'n'): + return False + return None + + +def compute_confusion(pairs: List[Tuple[bool, bool]]) -> Dict[str, int]: + n00 = n01 = n10 = n11 = 0 + for a, b in pairs: + if a and b: + n11 += 1 + elif a and not b: + n10 += 1 + elif not a and b: + n01 += 1 + else: + n00 += 1 + return {'n00': n00, 'n01': n01, 'n10': n10, 'n11': n11} + + +def kappa_from_conf(conf: Dict[str, int]) -> Optional[float]: + n00 = conf['n00']; n01 = conf['n01']; n10 = conf['n10']; n11 = conf['n11'] + N = n00 + n01 + n10 + n11 + if N == 0: + return None + Po = (n00 + n11) / N + pA_true = (n10 + n11) / N + pB_true = (n01 + n11) / N + Pe = pA_true * pB_true + (1 - pA_true) * (1 - pB_true) + denom = 1 - Pe + if denom == 0: + return None + return (Po - Pe) / denom + + +def bootstrap_kappa(pairs: List[Tuple[bool, bool]], n_boot: int = 1000, seed: int = 42) -> Tuple[float, float]: + random.seed(seed) + N = len(pairs) + if N == 0: + return (0.0, 0.0) + vals = [] + for _ in range(n_boot): + sample = [pairs[random.randrange(N)] for _ in range(N)] + conf = compute_confusion(sample) + k = kappa_from_conf(conf) + vals.append(k if k is not None else 0.0) + vals.sort() + lo = vals[int(0.025 * len(vals))] + hi = vals[int(0.975 * len(vals)) - 1] + return (lo, hi) + + +def main(): + parser = argparse.ArgumentParser(description='Adjudicate pairwise annotations and compute Cohen\'s kappa') + parser.add_argument('--a', required=True, help='Annotator A CSV') + parser.add_argument('--b', required=True, help='Annotator B CSV') + parser.add_argument('--adjudicator', help='Optional adjudicator CSV') + parser.add_argument('--out-dir', default='evaluations', help='Output directory') + parser.add_argument('--bootstrap', type=int, default=1000, help='Bootstrap iterations for CI') + args = parser.parse_args() + + os.makedirs(args.out_dir, exist_ok=True) + a_rows = read_csv(args.a) + b_rows = read_csv(args.b) + adj_rows = read_csv(args.adjudicator) if args.adjudicator else [] + adj_map = {norm_key(r): r for r in adj_rows} if adj_rows else {} + + a_map = {norm_key(r): r for r in a_rows} + b_map = {norm_key(r): r for r in b_rows} + + keys = sorted(set(a_map.keys()) & set(b_map.keys())) + pairs_major: List[Tuple[bool, bool]] = [] + pairs_adverse: List[Tuple[bool, bool]] = [] + merged_output = [] + + for k in keys: + ra = a_map[k] + rb = b_map[k] + drug = ra.get('drug') or rb.get('drug') or k + ida = ra.get('id') or '' + idb = rb.get('id') or '' + a_major = parse_bool(ra.get('major_interaction')) + b_major = parse_bool(rb.get('major_interaction')) + a_adv = parse_bool(ra.get('adverse_outcome')) + b_adv = parse_bool(rb.get('adverse_outcome')) + + if a_major is not None and b_major is not None: + pairs_major.append((a_major, b_major)) + if a_adv is not None and b_adv is not None: + pairs_adverse.append((a_adv, b_adv)) + + # adjudication logic + adj = adj_map.get(k) + final_major = '' + major_agree = False + if a_major is not None and b_major is not None and a_major == b_major: + final_major = str(a_major) + major_agree = True + elif adj and adj.get('major_interaction') is not None: + final_major = str(parse_bool(adj.get('major_interaction'))) + else: + final_major = '' + + final_adv = '' + adv_agree = False + if a_adv is not None and b_adv is not None and a_adv == b_adv: + final_adv = str(a_adv) + adv_agree = True + elif adj and adj.get('adverse_outcome') is not None: + final_adv = str(parse_bool(adj.get('adverse_outcome'))) + else: + final_adv = '' + + merged_output.append({ + 'id': ida or idb or '', + 'drug': drug, + 'major_interaction_A': '' if a_major is None else str(a_major), + 'major_interaction_B': '' if b_major is None else str(b_major), + 'major_interaction_final': final_major, + 'major_interaction_agree': major_agree, + 'adverse_outcome_A': '' if a_adv is None else str(a_adv), + 'adverse_outcome_B': '' if b_adv is None else str(b_adv), + 'adverse_outcome_final': final_adv, + 'adverse_outcome_agree': adv_agree, + 'notes_A': ra.get('notes',''), + 'notes_B': rb.get('notes',''), + }) + + conf_major = compute_confusion(pairs_major) + conf_adv = compute_confusion(pairs_adverse) + k_major = kappa_from_conf(conf_major) + k_adv = kappa_from_conf(conf_adv) + ci_major = bootstrap_kappa(pairs_major, n_boot=args.bootstrap) if pairs_major else (0.0, 0.0) + ci_adv = bootstrap_kappa(pairs_adverse, n_boot=args.bootstrap) if pairs_adverse else (0.0, 0.0) + + ts = datetime.utcnow().strftime('%Y%m%dT%H%M%SZ') + report = { + 'n_pairs': len(keys), + 'major': { + 'confusion': conf_major, + 'kappa': k_major, + 'bootstrap_ci': {'2.5%': ci_major[0], '97.5%': ci_major[1]}, + }, + 'adverse': { + 'confusion': conf_adv, + 'kappa': k_adv, + 'bootstrap_ci': {'2.5%': ci_adv[0], '97.5%': ci_adv[1]}, + }, + 'annotator_a_file': args.a, + 'annotator_b_file': args.b, + 'adjudicator_file': args.adjudicator or None, + } + + report_path = os.path.join(args.out_dir, f'adjudication_report_{ts}.json') + with open(report_path, 'w', encoding='utf-8') as f: + json.dump(report, f, indent=2, ensure_ascii=False) + + out_csv = os.path.join(args.out_dir, f'adjudicated_gold_{ts}.csv') + with open(out_csv, 'w', newline='', encoding='utf-8') as f: + keys = ['id','drug','major_interaction_A','major_interaction_B','major_interaction_final','major_interaction_agree', + 'adverse_outcome_A','adverse_outcome_B','adverse_outcome_final','adverse_outcome_agree','notes_A','notes_B'] + writer = csv.DictWriter(f, fieldnames=keys) + writer.writeheader() + for r in merged_output: + writer.writerow(r) + + print(f'Wrote adjudication report: {report_path}') + print(f'Wrote adjudicated gold CSV: {out_csv}') + + +if __name__ == '__main__': + main() diff --git a/AI Drug safety/ai_drug_safety/agent.py b/AI Drug safety/ai_drug_safety/agent.py new file mode 100644 index 0000000..d43e74d --- /dev/null +++ b/AI Drug safety/ai_drug_safety/agent.py @@ -0,0 +1,27 @@ +from typing import Dict, Any +from . import api_clients, risk_scoring, llm_reasoner + +def run_agent(drug_input: str, patient_info: Dict[str, Any] = None, use_mock: bool = False) -> Dict[str, Any]: + """ + Orchestrate data lookup, risk scoring and reasoning. + All drug inputs are normalized to RXCUI for robust rule matching. + Returns a structured result suitable for CLI or programmatic use. + """ + if patient_info is None: + patient_info = {} + + # Normalize input to RXCUI if possible + from .rxnorm import get_rxcui + rxcui = get_rxcui(drug_input) + findings = api_clients.get_drug_data(drug_input, use_mock=use_mock) + if rxcui: + findings["rxcui"] = rxcui + risk = risk_scoring.score_risk(findings, patient_info) + analysis = llm_reasoner.analyze(findings, patient_info, risk) + return { + "drug": drug_input, + "rxcui": rxcui, + "findings": findings, + "risk": risk, + "analysis": analysis, + } diff --git a/AI Drug safety/ai_drug_safety/agent_chain.py b/AI Drug safety/ai_drug_safety/agent_chain.py new file mode 100644 index 0000000..234d75c --- /dev/null +++ b/AI Drug safety/ai_drug_safety/agent_chain.py @@ -0,0 +1,94 @@ +"""Simple orchestrator that chains tools and caches intermediate results. + +This module performs tool chaining in a deterministic sequence: + 1. `get_drug_data(drug_name)` + 2. `score_risk(findings, patient_info)` + 3. `analyze(findings, patient_info, risk)` (LLM via LangChain when available) + +All intermediate results are cached (file-based) to reduce repeated API +calls and LLM usage. If `OPENAI_API_KEY` is not set or the LangChain LLM +call fails, a deterministic fallback analyzer is used. +""" +import os +import json +from typing import Dict, Any, Optional + +from .cache import get as cache_get, set as cache_set + + +def run_chain_agent( + drug_input: str, + patient_info: Optional[Dict[str, Any]] = None, + use_mock: bool = False, + model_name: str = "gpt-3.5-turbo", + temperature: float = 0.0, + cache_ttl: float = 60 * 60 * 24, +) -> Dict[str, Any]: + """ + Run the orchestrator: fetch drug data, score risk, synthesize analysis. + + - `cache_ttl` controls how long (in seconds) cached entries remain valid. + - Falls back to `ai_drug_safety.agent.run_agent` if an error occurs. + """ + if patient_info is None: + patient_info = {} + + try: + # Normalize input to RXCUI if possible + from .rxnorm import get_rxcui + rxcui = get_rxcui(drug_input) + except Exception: + rxcui = None + + try: + # 1) Findings (cached by drug name or RXCUI) + cache_key = rxcui if rxcui else drug_input + findings = cache_get("get_drug_data", cache_key) + if findings is None: + from .api_clients import get_drug_data + findings = get_drug_data(drug_input, use_mock=use_mock) + if rxcui: + findings["rxcui"] = rxcui + cache_set("get_drug_data", cache_key, findings, ttl=cache_ttl) + else: + if rxcui: + findings["rxcui"] = rxcui + + # 2) Risk scoring (cached by findings + patient) + risk_key_obj = {"findings": findings, "patient": patient_info} + risk = cache_get("score_risk", risk_key_obj) + if risk is None: + from .risk_scoring import score_risk + risk = score_risk(findings, patient_info) + cache_set("score_risk", risk_key_obj, risk, ttl=cache_ttl) + + # 3) Analysis (prefer LLM via LangChain, cached) + analysis_key_obj = {"findings": findings, "patient": patient_info, "risk": risk} + analysis = cache_get("analysis", analysis_key_obj) + if analysis is None: + # try LangChain-based LLM analysis when API key is present + if os.getenv("OPENAI_API_KEY"): + try: + from .llm_integration import analyze_with_langchain + analysis = analyze_with_langchain(findings, patient_info, risk, model_name=model_name, temperature=temperature) + except Exception: + from .llm_reasoner import analyze as _deterministic_analyze + analysis = _deterministic_analyze(findings, patient_info, risk) + else: + from .llm_reasoner import analyze as _deterministic_analyze + analysis = _deterministic_analyze(findings, patient_info, risk) + + # normalize analysis to dict (in case LLM returned string) + if isinstance(analysis, str): + try: + analysis = json.loads(analysis) + except Exception: + analysis = {"text": analysis} + + cache_set("analysis", analysis_key_obj, analysis, ttl=cache_ttl) + + return {"drug": drug_input, "rxcui": rxcui, "findings": findings, "risk": risk, "analysis": analysis} + except Exception: + # On any failure, fall back to the simple synchronous orchestrator + from .agent import run_agent as _local_run + return _local_run(drug_input, patient_info=patient_info, use_mock=use_mock) diff --git a/AI Drug safety/ai_drug_safety/api_clients.py b/AI Drug safety/ai_drug_safety/api_clients.py new file mode 100644 index 0000000..c06fe8e --- /dev/null +++ b/AI Drug safety/ai_drug_safety/api_clients.py @@ -0,0 +1,212 @@ +import os +import json +import urllib.request +import urllib.parse +from typing import Dict, Any, Optional +from functools import lru_cache + +OPENFDA_BASE = "https://api.fda.gov" + + +def get_drug_data(drug_name: str, use_mock: bool = False, prefer_drugbank: bool = False) -> Dict[str, Any]: + """ + Retrieve drug information (side effects, interactions, warnings) from real sources. + `use_mock` flag is accepted for backward compatibility but mocked responses are + no longer provided — the function always queries real data (openFDA and + optional DrugBank) and returns a normalized structure. + """ + + drugbank_key = os.getenv("DRUGBANK_API_KEY") + if drugbank_key and prefer_drugbank: + db = fetch_drugbank(drug_name, api_key=drugbank_key) + if db: + return db + + label = fetch_openfda_label(drug_name) + events = fetch_openfda_events(drug_name) + merged = merge_openfda_results(drug_name, label, events) + + if drugbank_key: + db = fetch_drugbank(drug_name, api_key=drugbank_key) + if db: + merged = _merge_sources(merged, db) + + return merged + + +def _to_list(x): + if not x: + return [] + if isinstance(x, list): + return x + return [x] + + +@lru_cache(maxsize=128) +def fetch_openfda_label(drug_name: str, limit: int = 1) -> Optional[Dict[str, Any]]: + """ + Query openFDA label endpoint for a drug. Tries several openFDA name fields. + Returns the raw label dict or None if not found. + """ + if not drug_name: + return None + queries = [ + f'openfda.brand_name:"{drug_name}"', + f'openfda.generic_name:"{drug_name}"', + f'openfda.substance_name:"{drug_name}"', + f'active_ingredient:"{drug_name}"', + ] + for q in queries: + try: + path = "/drug/label.json" + qs = urllib.parse.quote(q, safe='') + url = f"{OPENFDA_BASE}{path}?search={qs}&limit={limit}" + with urllib.request.urlopen(url, timeout=10) as resp: + text = resp.read().decode("utf-8") + parsed = json.loads(text) + results = parsed.get("results") or [] + if results: + return results[0] + except Exception: + continue + return None + + +@lru_cache(maxsize=128) +def fetch_openfda_events(drug_name: str, limit: int = 10) -> Dict[str, Any]: + """ + Query openFDA adverse event reports and summarize reactions. + Returns {"event_count": int, "common_reactions": [(term,count), ...]}. + """ + out = {"event_count": 0, "common_reactions": []} + if not drug_name: + return out + queries = [ + f'patient.drug.openfda.substance_name:"{drug_name}"', + f'patient.drug.medicinalproduct:"{drug_name}"', + ] + reactions_count = {} + total = 0 + for q in queries: + try: + path = "/drug/event.json" + qs = urllib.parse.quote(q, safe='') + url = f"{OPENFDA_BASE}{path}?search={qs}&limit={limit}" + with urllib.request.urlopen(url, timeout=10) as resp: + parsed = json.loads(resp.read().decode("utf-8")) + results = parsed.get("results") or [] + for r in results: + total += 1 + patient = r.get("patient", {}) + for react in patient.get("reaction", []) or []: + term = react.get("reactionmeddrapt") or react.get("reactionmeddra") + if term: + reactions_count[term] = reactions_count.get(term, 0) + 1 + except Exception: + continue + if total == 0: + return out + sorted_reacts = sorted(reactions_count.items(), key=lambda x: x[1], reverse=True) + out["event_count"] = total + out["common_reactions"] = sorted_reacts[:10] + return out + + +def merge_openfda_results(drug_name: str, label: Optional[Dict[str, Any]], events: Dict[str, Any]) -> Dict[str, Any]: + """ + Convert openFDA label + events into normalized structure matching the mock. + """ + side_effects = [] + interactions = [] + warnings = [] + sources = [] + name = drug_name + if label: + name = _extract_label_name(label) or drug_name + side_effects = _to_list(label.get("adverse_reactions") or label.get("adverse_reactions", [])) + inter_raw = label.get("drug_interactions") or label.get("precautions") or [] + if inter_raw: + interactions = _to_list(inter_raw) + warnings = _to_list(label.get("warnings") or label.get("boxed_warning") or []) + sources.append({"name": "openFDA label", "url": f"{OPENFDA_BASE}/drug/label"}) + if events and events.get("common_reactions"): + se_terms = [r for r, _ in events["common_reactions"]] + side_effects = list(dict.fromkeys(side_effects + se_terms)) + sources.append({"name": "openFDA events", "url": f"{OPENFDA_BASE}/drug/event"}) + return { + "name": name, + "side_effects": side_effects, + "interactions": [{"drug": i.get("name") if isinstance(i, dict) else str(i), "severity": "unknown", "description": str(i)} for i in interactions], + "warnings": warnings, + "sources": sources, + } + + +def _extract_label_name(label: Dict[str, Any]) -> Optional[str]: + od = label.get("openfda", {}) + for key in ("brand_name", "generic_name", "substance_name"): + val = od.get(key) + if isinstance(val, list): + return val[0] + if val: + return val + return label.get("setid") or None + + +def fetch_drugbank(drug_name: str, api_key: Optional[str] = None) -> Optional[Dict[str, Any]]: + """ + Minimal DrugBank wrapper. Requires `DRUGBANK_API_KEY` environment variable. + The exact host/endpoint may vary by license; set `DRUGBANK_API_HOST` if needed. + Returns normalized structure similar to openFDA output or None on failure. + """ + api_key = api_key or os.getenv("DRUGBANK_API_KEY") + if not api_key: + return None + host = os.getenv("DRUGBANK_API_HOST", "https://api.drugbank.com") + try: + path = f"/v1/us/drugs?name={urllib.parse.quote(drug_name)}" + url = host.rstrip("/") + path + req = urllib.request.Request(url, headers={ + "Authorization": f"Bearer {api_key}", + "Accept": "application/json", + }) + with urllib.request.urlopen(req, timeout=15) as resp: + parsed = json.loads(resp.read().decode("utf-8")) + name = parsed.get("name") or drug_name + side_effects = parsed.get("adverse_effects") or parsed.get("adverseReactions") or [] + interactions = parsed.get("drug_interactions") or parsed.get("interactions") or [] + warnings = parsed.get("warnings") or parsed.get("boxedWarnings") or [] + return { + "name": name, + "side_effects": side_effects, + "interactions": interactions, + "warnings": warnings, + "sources": [{"name": "DrugBank API", "url": url}], + } + except Exception: + return None + + +def _merge_sources(a: Dict[str, Any], b: Dict[str, Any]) -> Dict[str, Any]: + out = { + "name": a.get("name") or b.get("name"), + "side_effects": list(dict.fromkeys((a.get("side_effects") or []) + (b.get("side_effects") or []))), + "warnings": list(dict.fromkeys((a.get("warnings") or []) + (b.get("warnings") or []))), + "sources": (a.get("sources") or []) + (b.get("sources") or []), + } + ai = a.get("interactions") or [] + bi = b.get("interactions") or [] + merged_inter = { (i.get("drug") if isinstance(i, dict) else str(i)): i for i in ai } + for i in bi: + key = i.get("drug") if isinstance(i, dict) else str(i) + if key in merged_inter: + existing = merged_inter[key] + if isinstance(existing, dict) and isinstance(i, dict): + merged_inter[key] = {**existing, **i} + else: + merged_inter[key] = i + out["interactions"] = list(merged_inter.values()) + return out + + +# Note: mock data generator removed to ensure only real data sources are used. diff --git a/AI Drug safety/ai_drug_safety/cache.py b/AI Drug safety/ai_drug_safety/cache.py new file mode 100644 index 0000000..980a3ad --- /dev/null +++ b/AI Drug safety/ai_drug_safety/cache.py @@ -0,0 +1,71 @@ +"""Simple file-based cache used by the agent orchestration. + +Cache entries are stored in the project `.cache/` directory as JSON files +named by SHA256(prefix + canonical_json(payload)). Each entry stores the +creation timestamp, optional TTL (seconds), and the stored value as JSON. +""" +import os +import json +import time +import hashlib +from typing import Any, Optional + + +PROJECT_ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), "..")) +CACHE_DIR = os.path.join(PROJECT_ROOT, ".cache") + + +def _ensure_cache_dir() -> None: + os.makedirs(CACHE_DIR, exist_ok=True) + + +def _canonical_json(obj: Any) -> str: + return json.dumps(obj, sort_keys=True, default=str, separators=(",", ":")) + + +def make_cache_key(prefix: str, payload: Any) -> str: + s = f"{prefix}:{_canonical_json(payload)}" + return hashlib.sha256(s.encode("utf-8")).hexdigest() + + +def _cache_path(key: str) -> str: + return os.path.join(CACHE_DIR, f"{key}.json") + + +def get(prefix: str, payload: Any) -> Optional[Any]: + _ensure_cache_dir() + key = make_cache_key(prefix, payload) + path = _cache_path(key) + if not os.path.exists(path): + return None + try: + with open(path, "r", encoding="utf-8") as f: + rec = json.load(f) + ts = float(rec.get("ts", 0)) + ttl = rec.get("ttl") + if ttl is not None: + try: + ttl_f = float(ttl) + except Exception: + ttl_f = None + if ttl_f is not None and time.time() > ts + ttl_f: + try: + os.remove(path) + except Exception: + pass + return None + return rec.get("value") + except Exception: + return None + + +def set(prefix: str, payload: Any, value: Any, ttl: Optional[float] = None) -> None: + _ensure_cache_dir() + key = make_cache_key(prefix, payload) + path = _cache_path(key) + rec = {"ts": time.time(), "ttl": ttl, "value": value} + try: + with open(path, "w", encoding="utf-8") as f: + json.dump(rec, f, ensure_ascii=False) + except Exception: + pass diff --git a/AI Drug safety/ai_drug_safety/cli.py b/AI Drug safety/ai_drug_safety/cli.py new file mode 100644 index 0000000..5134b78 --- /dev/null +++ b/AI Drug safety/ai_drug_safety/cli.py @@ -0,0 +1,26 @@ +try: + from dotenv import load_dotenv + + load_dotenv() +except Exception: + pass + +import argparse +import json +from .agent import run_agent + + +def main(): + parser = argparse.ArgumentParser(description="AI Drug Safety Agent CLI") + parser.add_argument("drug", help="Drug name or prescription text") + parser.add_argument("--age", type=int, default=0, help="Patient age") + parser.add_argument("--conditions", type=str, default="", help="Comma-separated conditions") + # Mock data removed; CLI always uses real data sources. + args = parser.parse_args() + conditions = [c.strip() for c in args.conditions.split(",") if c.strip()] + patient = {"age": args.age, "conditions": conditions} + res = run_agent(args.drug, patient_info=patient, use_mock=False) + print(json.dumps(res, indent=2)) + +if __name__ == "__main__": + main() diff --git a/AI Drug safety/ai_drug_safety/data/beers.json b/AI Drug safety/ai_drug_safety/data/beers.json new file mode 100644 index 0000000..1dc1578 --- /dev/null +++ b/AI Drug safety/ai_drug_safety/data/beers.json @@ -0,0 +1,26 @@ +[ + { + "name": "diazepam", + "warning": "Increased sensitivity and risk of cognitive impairment, delirium, falls and fractures in older adults.", + "recommendation": "Avoid in older adults.", + "evidence": "Beers Criteria" + }, + { + "name": "diphenhydramine", + "warning": "Strong anticholinergic properties — confusion, dry mouth, constipation, urinary retention.", + "recommendation": "Avoid in older adults.", + "evidence": "Beers Criteria" + }, + { + "name": "nitrofurantoin", + "warning": "Risk of pulmonary toxicity and reduced efficacy with low creatinine clearance.", + "recommendation": "Avoid for long-term suppression in older adults with low CrCl.", + "evidence": "Beers Criteria" + }, + { + "name": "meperidine", + "warning": "High risk of CNS excitation and neurotoxicity (normeperidine).", + "recommendation": "Avoid in older adults.", + "evidence": "Beers Criteria" + } +] diff --git a/AI Drug safety/ai_drug_safety/evaluation.py b/AI Drug safety/ai_drug_safety/evaluation.py new file mode 100644 index 0000000..0388b92 --- /dev/null +++ b/AI Drug safety/ai_drug_safety/evaluation.py @@ -0,0 +1,251 @@ +"""Evaluation utilities for AI Drug Safety. + +Compute clinical metrics (sensitivity/precision for major interactions, +Naranjo concordance, mapping coverage, evidence support) against a +gold-standard CSV and previously-run agent results (JSON in `evaluations/`). + +Usage: + python -m ai_drug_safety.evaluation --gold evaluations/gold_standard_sample.csv --results-dir evaluations +""" +from __future__ import annotations + +import argparse +import csv +import glob +import json +import os +from datetime import datetime +from typing import Dict, List, Optional, Tuple + + +def load_latest_results(results_dir: str) -> str: + pattern = os.path.join(results_dir, "evaluation_real_*.json") + files = glob.glob(pattern) + if not files: + raise FileNotFoundError(f"No results files found in {results_dir}") + files.sort() + return files[-1] + + +def load_results(path: str) -> List[Dict]: + with open(path, "r", encoding="utf-8") as f: + data = json.load(f) + return data.get("results") or [] + + +def load_gold_csv(path: str) -> List[Dict[str, str]]: + with open(path, "r", encoding="utf-8") as f: + reader = csv.DictReader(f) + return [r for r in reader] + + +def _model_flags_major(result: Dict) -> bool: + findings = result.get("findings") or {} + interactions = findings.get("interactions") or [] + for i in interactions: + if isinstance(i, dict): + sev = (i.get("severity") or "").lower() + desc = (i.get("description") or "").lower() + if "major" in sev or "major" in desc or "severe" in desc or "life-threatening" in desc: + return True + else: + text = str(i).lower() + if "major" in text or "severe" in text: + return True + return False + + +def _match_result(gold_row: Dict[str, str], results: List[Dict]) -> Optional[Dict]: + # Prefer RXCUI matching when available + gdrug = (gold_row.get("drug") or "").strip().lower() + grxcui = (gold_row.get("rxcui") or "").strip() + for r in results: + # r could be an error dict + if not isinstance(r, dict): + continue + rdrug = (r.get("drug") or "").strip().lower() + if grxcui and r.get("rxcui") and str(r.get("rxcui")).strip() == grxcui: + return r + if gdrug and rdrug and gdrug == rdrug: + return r + # check normalized findings name + findings = r.get("findings") or {} + fname = (findings.get("name") or "").strip().lower() + if fname and gdrug == fname: + return r + return None + + +def rankdata(a: List[float]) -> List[float]: + # average ranks for ties (1-based) + order = sorted((val, idx) for idx, val in enumerate(a)) + ranks = [0.0] * len(a) + i = 0 + n = len(order) + while i < n: + val = order[i][0] + j = i + idxs = [] + while j < n and order[j][0] == val: + idxs.append(order[j][1]) + j += 1 + avg_rank = (i + 1 + j) / 2.0 + for idx in idxs: + ranks[idx] = avg_rank + i = j + return ranks + + +def pearsonr(x: List[float], y: List[float]) -> float: + n = len(x) + if n == 0: + return 0.0 + mx = sum(x) / n + my = sum(y) / n + num = sum((xi - mx) * (yi - my) for xi, yi in zip(x, y)) + sx = sum((xi - mx) ** 2 for xi in x) + sy = sum((yi - my) ** 2 for yi in y) + den = (sx * sy) ** 0.5 + if den == 0: + return 0.0 + return num / den + + +def spearmanr(x: List[float], y: List[float]) -> float: + if not x or not y or len(x) != len(y): + return 0.0 + rx = rankdata(x) + ry = rankdata(y) + return pearsonr(rx, ry) + + +def compute_metrics(gold_rows: List[Dict[str, str]], results: List[Dict]) -> Dict: + n = len(gold_rows) + tp = fp = fn = tn = 0 + mapped = 0 + evidence_count = 0 + risk_list = [] + naranjo_list = [] + + for row in gold_rows: + gold_major = (row.get("major_interaction") or "").strip().lower() in ("1", "true", "yes") + gold_naranjo = row.get("naranjo_score") + gold_naranjo_val = None + if gold_naranjo is not None and gold_naranjo != "": + try: + gold_naranjo_val = float(gold_naranjo) + except Exception: + gold_naranjo_val = None + + res = _match_result(row, results) + if not res: + # treat as negative prediction (no data) + model_major = False + has_evidence = False + else: + model_major = _model_flags_major(res) + findings = res.get("findings") or {} + has_evidence = bool(findings.get("sources")) + if res.get("rxcui"): + mapped += 1 + # try to get model numeric risk score + rscore = res.get("risk", {}).get("score") + if isinstance(rscore, (int, float)): + risk_list.append(float(rscore)) + # if analysis includes naranjo, try to extract + analysis = res.get("analysis") or {} + if isinstance(analysis, dict) and analysis.get("naranjo_score") is not None: + try: + nval = float(analysis.get("naranjo_score")) + naranjo_list.append(nval) + except Exception: + pass + + if has_evidence: + evidence_count += 1 + + if model_major and gold_major: + tp += 1 + elif model_major and not gold_major: + fp += 1 + elif not model_major and gold_major: + fn += 1 + else: + tn += 1 + + if gold_naranjo_val is not None and risk_list: + # if we have both, ensure lists align later; collect pairs now + pass + + # compute metrics + sens = tp / (tp + fn) if (tp + fn) else None + prec = tp / (tp + fp) if (tp + fp) else None + f1 = 2 * prec * sens / (prec + sens) if (prec and sens) else None + rxcui_coverage = mapped / n if n else 0 + evidence_rate = evidence_count / n if n else 0 + + # compute Naranjo concordance if lists available (use model risk vs gold naranjo) + # Align by matching rows where both exist + paired_risk = [] + paired_naranjo = [] + for row in gold_rows: + res = _match_result(row, results) + if not res: + continue + rscore = res.get("risk", {}).get("score") + if rscore is None: + continue + gn = row.get("naranjo_score") + if gn is None or gn == "": + continue + try: + gnv = float(gn) + except Exception: + continue + paired_risk.append(float(rscore)) + paired_naranjo.append(float(gnv)) + + naranjo_spearman = spearmanr(paired_risk, paired_naranjo) if paired_risk else None + + return { + "n_cases": n, + "tp": tp, + "fp": fp, + "fn": fn, + "tn": tn, + "sensitivity": sens, + "precision": prec, + "f1": f1, + "rxcui_coverage": rxcui_coverage, + "evidence_support_rate": evidence_rate, + "naranjo_spearman": naranjo_spearman, + } + + +def main(): + parser = argparse.ArgumentParser(description="Compute evaluation metrics against a gold CSV and results JSON.") + parser.add_argument("--gold", required=True, help="Gold-standard CSV file") + parser.add_argument("--results-dir", default="evaluations", help="Directory with evaluation JSON results") + parser.add_argument("--out", default=None, help="Optional output JSON file for metrics") + args = parser.parse_args() + + results_path = load_latest_results(args.results_dir) + print(f"Using results file: {results_path}") + results = load_results(results_path) + gold = load_gold_csv(args.gold) + metrics = compute_metrics(gold, results) + print(json.dumps(metrics, indent=2, ensure_ascii=False)) + + if args.out: + with open(args.out, "w", encoding="utf-8") as f: + json.dump(metrics, f, indent=2, ensure_ascii=False) + else: + ts = datetime.utcnow().strftime("%Y%m%dT%H%M%SZ") + outp = os.path.join(args.results_dir, f"evaluation_metrics_{ts}.json") + with open(outp, "w", encoding="utf-8") as f: + json.dump(metrics, f, indent=2, ensure_ascii=False) + print(f"Wrote metrics to: {outp}") + + +if __name__ == "__main__": + main() diff --git a/AI Drug safety/ai_drug_safety/llm_integration.py b/AI Drug safety/ai_drug_safety/llm_integration.py new file mode 100644 index 0000000..2ee77d1 --- /dev/null +++ b/AI Drug safety/ai_drug_safety/llm_integration.py @@ -0,0 +1,78 @@ +import os +import json +import re +from typing import Dict, Any, Optional + + +def analyze_with_langchain(findings: Dict[str, Any], patient_info: Dict[str, Any], risk: Dict[str, Any], model_name: str = "gpt-3.5-turbo", temperature: float = 0.0) -> Dict[str, Any]: + """ + Use LangChain + OpenAI to synthesize a clinical-style decision. + + Requires `OPENAI_API_KEY` in the environment and `langchain` + `openai` + installed. Returns a dict with keys: `conclusion`, `recommendation`, + `reasons`, `explanation`. + """ + try: + try: + # langchain v0.0x path + from langchain.llms import OpenAI as LangOpenAI + except Exception: + # older/newer alias + from langchain import OpenAI as LangOpenAI + from langchain.prompts import PromptTemplate + from langchain.chains import LLMChain + except Exception as e: + raise RuntimeError("langchain is not available") from e + + if not os.getenv("OPENAI_API_KEY"): + raise RuntimeError("OPENAI_API_KEY environment variable is not set") + + payload = { + "drug": findings.get("name"), + "side_effects": findings.get("side_effects", []), + "interactions": findings.get("interactions", []), + "warnings": findings.get("warnings", []), + "patient": patient_info, + "risk": risk, + } + payload_text = json.dumps(payload, indent=2, ensure_ascii=False) + + template = ( + "You are a clinical decision support assistant.\n" + "Given the following structured data, produce a concise JSON object with these keys:\n" + "- conclusion: one of (Low risk, Moderate risk, High risk)\n" + "- recommendation: one short sentence for a clinician or pharmacist\n" + "- reasons: a short list of 1-5 concise reasons derived only from the data (cite the data field in the payload)\n" + "- explanation: one paragraph describing the reasoning and any important caveats\n\n" + "Optional keys (include if available):\n" + "- confidence: integer 0-100 indicating confidence in the conclusion\n" + "- assumptions: list of any missing-data assumptions made by the model\n" + "- naranjo_score: numeric estimate of causality if the model can derive it\n\n" + "Important instructions:\n" + "- Use the structured `risk` data (HAS-BLED, Tisdale) as primary evidence; if you disagree with the numeric scores, explain why.\n" + "- When referring to patient attributes, use the field names from `patient` (e.g., patient.sex, patient.age, patient.conditions).\n" + "- Explicitly consider the patient's sex when it affects risk (for example, female sex increases QT/Tisdale risk); if sex is missing, state the assumption.\n" + "- Base each reason on a specific data point from the payload (e.g., 'Tisdale: baseline_qtc_high', 'Interaction: warfarin + aspirin — major').\n\n" + "Data:\n{payload}\n\n" + "Return JSON only and nothing else." + ) + + prompt = PromptTemplate(input_variables=["payload"], template=template) + + llm = LangOpenAI(temperature=temperature, model_name=model_name) + chain = LLMChain(llm=llm, prompt=prompt) + # Run the chain and try to parse JSON output robustly + output = chain.run(payload=payload_text) + + # Direct JSON parse + try: + return json.loads(output.strip()) + except Exception: + # Extract first JSON-looking block + m = re.search(r"\{.*\}", output, re.S) + if m: + try: + return json.loads(m.group(0)) + except Exception: + pass + raise RuntimeError("LLM did not return valid JSON") diff --git a/AI Drug safety/ai_drug_safety/llm_reasoner.py b/AI Drug safety/ai_drug_safety/llm_reasoner.py new file mode 100644 index 0000000..fd73425 --- /dev/null +++ b/AI Drug safety/ai_drug_safety/llm_reasoner.py @@ -0,0 +1,55 @@ +import os +import json +from typing import Dict, Any +from . import risk_scoring + + +def _deterministic_analysis(findings: Dict[str, Any], patient_info: Dict[str, Any], risk: Dict[str, Any]) -> Dict[str, Any]: + score = risk.get("score", 0) if isinstance(risk, dict) else int(risk or 0) + if score >= 60: + conclusion = "High risk" + recommendation = "Avoid prescribing; seek alternatives." + elif score >= 30: + conclusion = "Moderate risk" + recommendation = "Use with caution; monitor closely." + else: + conclusion = "Low risk" + recommendation = "Likely safe for typical patients." + + reasons = [] + if findings.get("interactions"): + reasons.append(f"{len(findings['interactions'])} reported interaction(s)") + if findings.get("side_effects"): + reasons.append(f"{len(findings['side_effects'])} documented side effect(s)") + reasons.extend([f"condition:{c}" for c in (patient_info.get("conditions") or [])]) + + explanation = f"Computed risk score {score}; factors: {', '.join(risk.get('factors', []))}" + + return { + "conclusion": conclusion, + "recommendation": recommendation, + "reasons": reasons, + "explanation": explanation, + } + + +def analyze(findings: Dict[str, Any], patient_info: Dict[str, Any], risk: Dict[str, Any]) -> Dict[str, Any]: + """ + High-level analyze function: prefer LangChain+OpenAI when available + (requires `OPENAI_API_KEY` and `langchain` installed). Falls back to + deterministic rules when LLM is not available or returns invalid output. + """ + # Prefer LangChain-based reasoning when API key is present + if os.getenv("OPENAI_API_KEY"): + try: + from .llm_integration import analyze_with_langchain + + result = analyze_with_langchain(findings, patient_info, risk) + # basic validation of result shape + if isinstance(result, dict) and {"conclusion", "recommendation", "reasons", "explanation"}.issubset(result.keys()): + return result + except Exception: + # On any failure, fall back to deterministic analysis + pass + + return _deterministic_analysis(findings, patient_info, risk) diff --git a/AI Drug safety/ai_drug_safety/naranjo.py b/AI Drug safety/ai_drug_safety/naranjo.py new file mode 100644 index 0000000..4c4b195 --- /dev/null +++ b/AI Drug safety/ai_drug_safety/naranjo.py @@ -0,0 +1,62 @@ +from typing import Dict, Any, Tuple + +_QUESTION_SCORES = { + 1: {"yes": 1, "no": 0, "unknown": 0}, + 2: {"yes": 2, "no": -1, "unknown": 0}, + 3: {"yes": 1, "no": 0, "unknown": 0}, + 4: {"yes": 2, "no": -1, "unknown": 0}, + 5: {"yes": -1, "no": 2, "unknown": 0}, + 6: {"yes": -1, "no": 1, "unknown": 0}, + 7: {"yes": 1, "no": 0, "unknown": 0}, + 8: {"yes": 1, "no": 0, "unknown": 0}, + 9: {"yes": 1, "no": 0, "unknown": 0}, + 10: {"yes": 1, "no": 0, "unknown": 0}, +} + + +def _normalize_answer(a: Any) -> str: + if a is None: + return "unknown" + if isinstance(a, str): + s = a.strip().lower() + if s in ("yes", "y", "1", "true", "+"): + return "yes" + if s in ("no", "n", "0", "false", "-"): + return "no" + return "unknown" + if isinstance(a, bool): + return "yes" if a else "no" + if isinstance(a, (int, float)): + return "yes" if a > 0 else "no" + return "unknown" + + +def naranjo_score(answers: Dict[str, Any]) -> Tuple[int, str]: + """ + Compute the Naranjo adverse drug reaction (ADR) probability score. + + `answers` may contain keys 'q1'..'q10' or 1..10 mapping to yes/no/unknown values. + Returns (score, category) where category is one of: 'definite','probable','possible','doubtful'. + """ + total = 0 + for i in range(1, 11): + key1 = f"q{i}" + key2 = i + val = None + if key1 in answers: + val = answers[key1] + elif key2 in answers: + val = answers[key2] + a = _normalize_answer(val) + score_map = _QUESTION_SCORES.get(i, {}) + total += score_map.get(a, 0) + + if total >= 9: + cat = "definite" + elif total >= 5: + cat = "probable" + elif total >= 1: + cat = "possible" + else: + cat = "doubtful" + return total, cat diff --git a/AI Drug safety/ai_drug_safety/risk_scoring.py b/AI Drug safety/ai_drug_safety/risk_scoring.py new file mode 100644 index 0000000..347cd17 --- /dev/null +++ b/AI Drug safety/ai_drug_safety/risk_scoring.py @@ -0,0 +1,346 @@ +from typing import Dict, Any, List +import os +import json + +_BEERS_DATA = None + + +def _load_beers(): + global _BEERS_DATA + if _BEERS_DATA is not None: + return _BEERS_DATA + try: + p = os.path.join(os.path.dirname(__file__), "data", "beers.json") + with open(p, "r", encoding="utf-8") as f: + _BEERS_DATA = json.load(f) + except Exception: + _BEERS_DATA = [] + # Try to augment entries with RxNorm RXCUI values for robust matching. + try: + from .rxnorm import get_rxcui + except Exception: + get_rxcui = None + + if get_rxcui: + for entry in _BEERS_DATA: + if entry.get("_rxcui") is None: + try: + entry['_rxcui'] = get_rxcui(entry.get("name", "")) + except Exception: + entry['_rxcui'] = None + + return _BEERS_DATA + + +def _find_beers_matches(drug_name: str) -> List[Dict[str, str]]: + name = (drug_name or "").strip() + matches: List[Dict[str, str]] = [] + if not name: + return matches + + # First, attempt RXCUI-based matching using RxNorm + try: + from .rxnorm import get_rxcui as _get_rxcui + except Exception: + _get_rxcui = None + + if _get_rxcui: + try: + input_rxcui = _get_rxcui(name) + except Exception: + input_rxcui = None + if input_rxcui: + for entry in _load_beers(): + erxc = entry.get("_rxcui") + if erxc and erxc == input_rxcui: + matches.append(entry) + if matches: + return matches + + # Fallback: simple substring matching (case-insensitive) + lname = name.lower() + for entry in _load_beers(): + en = entry.get("name", "").lower() + if not en: + continue + if en in lname or lname in en: + matches.append(entry) + return matches + + +def _contains_keyword_list(sources, keywords): + if not sources: + return False + if isinstance(sources, str): + hay = sources.lower() + for k in keywords: + if k in hay: + return True + return False + # list-like + for itm in sources: + if not itm: + continue + if _contains_keyword_list(str(itm), keywords): + return True + return False + + +def _compute_has_bled(patient_info: Dict[str, Any]) -> Dict[str, Any]: + """Compute HAS-BLED score when possible from patient_info. + + Accepts best-effort inputs: `age`, `conditions` (list), `medications` (list or comma string), + `labile_inr` (bool), `alcohol_use` (bool). + Returns dict: {score, components, category}. + """ + age = int(patient_info.get("age") or 0) + conditions = patient_info.get("conditions") or [] + meds = patient_info.get("medications") or [] + if isinstance(meds, str): + meds = [m.strip() for m in meds.split(",") if m.strip()] + + def has_cond(keywords): + return _contains_keyword_list(conditions, keywords) or _contains_keyword_list(meds, keywords) + + score = 0 + components = {} + + # Hypertension + htn = has_cond(["hypertension", "htn"]) or bool(patient_info.get("hypertension")) + components["hypertension"] = bool(htn) + if htn: + score += 1 + + # Abnormal renal / liver (count separately) + renal = has_cond(["renal", "ckd", "kidney", "dialysis"]) or bool(patient_info.get("renal_dysfunction")) + liver = has_cond(["liver", "hepatic", "cirrhosis"]) or bool(patient_info.get("liver_disease")) + components["renal"] = bool(renal) + components["liver"] = bool(liver) + if renal: + score += 1 + if liver: + score += 1 + + # Stroke history + stroke = has_cond(["stroke", "tia"]) or bool(patient_info.get("stroke")) + components["stroke_history"] = bool(stroke) + if stroke: + score += 1 + + # Bleeding history + bleed = has_cond(["bleed", "hemorrhage", "haemorrhage", "gib"]) or bool(patient_info.get("bleeding_history")) + components["bleeding_history"] = bool(bleed) + if bleed: + score += 1 + + # Labile INR + labile = bool(patient_info.get("labile_inr")) + components["labile_inr"] = labile + if labile: + score += 1 + + # Elderly + elderly = age >= 65 + components["elderly"] = elderly + if elderly: + score += 1 + + # Drugs / alcohol (antiplatelet/NSAID or alcohol abuse) + antiplatelet_keywords = ["aspirin", "clopidogrel", "prasugrel", "ticagrelor", "naproxen", "ibuprofen", "diclofenac"] + drugs_flag = _contains_keyword_list(meds, antiplatelet_keywords) or has_cond(["antiplatelet", "nsaid"]) or bool(patient_info.get("concomitant_antiplatelet")) + alcohol = bool(patient_info.get("alcohol_use")) or has_cond(["alcoholism", "alcohol abuse"]) + components["drugs_or_alcohol"] = bool(drugs_flag or alcohol) + if drugs_flag or alcohol: + score += 1 + + # Category + if score >= 3: + category = "high" + elif score == 2: + category = "moderate" + else: + category = "low" + + return {"score": score, "components": components, "category": category} + + +def _compute_tisdale(patient_info: Dict[str, Any], findings: Dict[str, Any]) -> Dict[str, Any]: + """Best-effort Tisdale QT risk score approximation using available data. + + Tisdale categories: low (<=6), moderate (7-10), high (>=11). + """ + age = int(patient_info.get("age") or 0) + sex = (patient_info.get("sex") or "").strip().lower() + conditions = patient_info.get("conditions") or [] + meds = patient_info.get("medications") or [] + if isinstance(meds, str): + meds = [m.strip() for m in meds.split(",") if m.strip()] + + score = 0 + components = {} + + # Age >=68 + if age >= 68: + score += 1 + components["age>=68"] = True + else: + components["age>=68"] = False + + # Female + if sex in ("female", "f"): + score += 1 + components["female"] = True + else: + components["female"] = False + + # Loop diuretic + loop_kw = ["furosemide", "bumetanide", "torsemide"] + loop_flag = _contains_keyword_list(meds, loop_kw) or _contains_keyword_list(conditions, ["loop diuretic"]) + if loop_flag: + score += 1 + components["loop_diuretic"] = True + else: + components["loop_diuretic"] = False + + # Serum K (if provided) + try: + k = float(patient_info.get("serum_k", patient_info.get("potassium", "")) or 0) + except Exception: + k = None + if k is not None and k != 0: + if k <= 3.5: + score += 2 + components["k_low"] = True + else: + components["k_low"] = False + else: + components["k_low"] = None + + # Baseline QTc + try: + qtc = int(patient_info.get("baseline_qtc") or 0) + except Exception: + qtc = None + if qtc and qtc >= 450: + score += 2 + components["baseline_qtc_high"] = True + else: + components["baseline_qtc_high"] = False + + # Acute MI + mi = _contains_keyword_list(conditions, ["mi", "myocardial infarction"]) or bool(patient_info.get("acute_mi")) + if mi: + score += 2 + components["acute_mi"] = True + else: + components["acute_mi"] = False + + # QT-prolonging drugs count (best-effort) + qt_prolongers = set(["amiodarone", "sotalol", "haloperidol", "droperidol", "citalopram", "escitalopram", "fluoroquinolone", "levofloxacin", "moxifloxacin", "clarithromycin", "erythromycin"]) + present = 0 + for m in meds: + if not m: + continue + ml = m.lower() + for qd in qt_prolongers: + if qd in ml: + present += 1 + break + # also check interactions list + for inter in (findings.get("interactions") or []): + dname = (inter.get("drug") if isinstance(inter, dict) else str(inter)).lower() + for qd in qt_prolongers: + if qd in dname: + present += 1 + break + + components["qt_prolonging_drugs_count"] = present + if present > 1: + score += 3 + elif present == 1: + score += 2 + + # Sepsis + sepsis = _contains_keyword_list(conditions, ["sepsis"]) or bool(patient_info.get("sepsis")) + if sepsis: + score += 3 + components["sepsis"] = True + else: + components["sepsis"] = False + + # Category + if score >= 11: + category = "high" + elif score >= 7: + category = "moderate" + else: + category = "low" + + return {"score": score, "components": components, "category": category} + + +def score_risk(findings: Dict[str, Any], patient_info: Dict[str, Any]) -> Dict[str, Any]: + """ + Compute clinically meaningful risk outputs. + + Returns a dict with: + - `score`: integer 0-100 (backwards-compatible summary; derived from clinical scores) + - `factors`: short list of contributing factors (strings) + - `beers_matches`: list of matching Beers entries + - `clinical`: dict with clinical scores (HAS-BLED, Tisdale) and flags + """ + age = int(patient_info.get("age") or 0) + conditions = patient_info.get("conditions") or [] + meds = patient_info.get("medications") or [] + if isinstance(meds, str): + meds = [m.strip() for m in meds.split(",") if m.strip()] + + factors: List[str] = [] + + # Detect major interaction presence and collect interaction-based factors + major_interaction = False + for inter in findings.get("interactions", []): + sev = (inter.get("severity") or "").lower() + if "major" in sev or "major" in (inter.get("description") or "").lower(): + major_interaction = True + factors.append(f"major interaction with {inter.get('drug')}") + + # Beers criteria + drug_name = (findings.get("name") or "").strip() + beers_matches = [] + if age >= 65 and drug_name: + beers_matches = _find_beers_matches(drug_name) + for m in beers_matches: + factors.append(f"Beers:{m.get('name')}") + + # Clinical scores + has_bled = _compute_has_bled(patient_info) + tisdale = _compute_tisdale(patient_info, findings) + + # Compose a backward-compatible integer score from clinical metrics + # Normalize HAS-BLED (max 9) and Tisdale (use typical max 20) to 0-100 + hb_norm = int(min(9, has_bled.get("score", 0)) / 9 * 100) if has_bled else 0 + td_norm = int(min(20, tisdale.get("score", 0)) / 20 * 100) if tisdale else 0 + + # Base on: presence of major interactions (75 points), then max of clinical norms + legacy_score = 0 + if major_interaction: + legacy_score = max(legacy_score, 75) + legacy_score = max(legacy_score, hb_norm, td_norm) + + # Small additional weight for age and conditions + if age >= 65: + legacy_score = min(100, legacy_score + 5) + if conditions: + legacy_score = min(100, legacy_score + min(10, len(conditions) * 2)) + + return { + "score": int(legacy_score), + "factors": factors, + "beers_matches": [m.get("name") for m in beers_matches], + "clinical": { + "has_bled": has_bled, + "tisdale": tisdale, + "major_interaction": major_interaction, + }, + } diff --git a/AI Drug safety/ai_drug_safety/rxnorm.py b/AI Drug safety/ai_drug_safety/rxnorm.py new file mode 100644 index 0000000..f7bb891 --- /dev/null +++ b/AI Drug safety/ai_drug_safety/rxnorm.py @@ -0,0 +1,59 @@ +import urllib.parse +import urllib.request +import json +from typing import Optional + +from .cache import get as cache_get, set as cache_set + +# Cache TTL for RxNorm lookups (1 week) +DEFAULT_TTL = 7 * 24 * 3600 + + +def get_rxcui(drug_name: str, ttl: int = DEFAULT_TTL) -> Optional[str]: + """Return an RXCUI for `drug_name` using RxNav (RxNorm) lookup. + + Uses a file-based cache to avoid repeated network requests. Returns + `None` if unable to resolve. + """ + if not drug_name: + return None + key = drug_name.strip().lower() + cached = cache_get("rxnav_rxcui", key) + if cached is not None: + return cached + + base = "https://rxnav.nlm.nih.gov/REST/rxcui.json?name=" + urllib.parse.quote(drug_name) + try: + with urllib.request.urlopen(base, timeout=8) as resp: + parsed = json.loads(resp.read().decode("utf-8")) + ids = parsed.get("idGroup", {}).get("rxnormId") or [] + if ids: + rxcui = str(ids[0]) + cache_set("rxnav_rxcui", key, rxcui, ttl=ttl) + return rxcui + except Exception: + # network or parsing error — fall through to approximate lookup + pass + + # Fallback: approximateTerm to get candidate rxcui + try: + url = ( + "https://rxnav.nlm.nih.gov/REST/approximateTerm.json?term=" + + urllib.parse.quote(drug_name) + + "&maxEntries=1" + ) + with urllib.request.urlopen(url, timeout=8) as resp: + parsed = json.loads(resp.read().decode("utf-8")) + cand = parsed.get("approximateGroup", {}).get("candidate") or [] + if isinstance(cand, list) and cand: + c = cand[0] + rxcui = c.get("rxcui") + if rxcui: + cache_set("rxnav_rxcui", key, str(rxcui), ttl=ttl) + return str(rxcui) + except Exception: + pass + + # Negative cache for a short time to avoid repeated failures + cache_set("rxnav_rxcui", key, None, ttl=60 * 10) + return None diff --git a/AI Drug safety/ai_drug_safety/web.py b/AI Drug safety/ai_drug_safety/web.py new file mode 100644 index 0000000..b8b3eb9 --- /dev/null +++ b/AI Drug safety/ai_drug_safety/web.py @@ -0,0 +1,58 @@ +try: + from fastapi import FastAPI + from fastapi.responses import HTMLResponse, JSONResponse + from typing import Optional + has_fastapi = True +except Exception: + FastAPI = None + HTMLResponse = None + JSONResponse = None + Optional = None + has_fastapi = False + +from .agent_chain import run_chain_agent + + +if has_fastapi: + app = FastAPI(title="AI Drug Safety Agent") + + _INDEX_HTML = """ + + + AI Drug Safety + +

AI Drug Safety — Quick Check

+
+
+
+
+ + +
+

Use the `/analyze` endpoint programmatically for JSON results.

+ + + """ + + + @app.get("/", response_class=HTMLResponse) + async def index(): + return _INDEX_HTML + + + @app.get("/analyze") + async def analyze(drug: str, age: Optional[int] = 0, conditions: Optional[str] = ""): + """Analyze a drug for a given (optional) patient profile. Uses real sources. + + Query params: + - `drug` (required) + - `age` (optional int) + - `conditions` (optional comma-separated list) + """ + patient = {"age": int(age or 0), "conditions": [c.strip() for c in (conditions or "").split(",") if c.strip()]} + result = run_chain_agent(drug, patient_info=patient, use_mock=False) + return JSONResponse(result) +else: + # Provide a helpful import-time message when FastAPI is not installed. + app = None + diff --git a/AI Drug safety/docs/annotation_protocol.md b/AI Drug safety/docs/annotation_protocol.md new file mode 100644 index 0000000..5731fde --- /dev/null +++ b/AI Drug safety/docs/annotation_protocol.md @@ -0,0 +1,71 @@ +# Clinical Annotation Protocol — AI Drug Safety + +Purpose +------- +This document defines the protocol for clinician annotation of a gold-standard +dataset used to evaluate the AI Drug Safety agent. The goal is to produce +high-quality, reproducible annotations for interaction detection, adverse +outcomes and causality (Naranjo-like scoring) to support clinical evaluation. + +Scope +----- +- Single-drug evaluations: annotate known/suspected interactions and adverse + events for a given indexed drug. +- Prefer authoritative sources (product labels, openFDA, guideline statements) + for evidence extraction during annotation. + +Annotator Requirements +---------------------- +- Clinical background (pharmacist, clinician) with experience in drug safety. +- Familiarity with common drug interaction categories (major/moderate/minor) and + Naranjo ADR causality principles. +- Two independent annotators per case; adjudication by a senior reviewer if + disagreement persists. + +Data Fields and Definitions +--------------------------- +- `id`: Unique identifier for the case/row. +- `drug`: Free-text drug name to be evaluated (input to the system). +- `rxcui`: Optional normalized RxNorm RXCUI for the indexed drug. +- `major_interaction` (boolean): True if there exists at least one clinically + important (major/serious) interaction for this drug in the context of the + presented patient (or generally if patient unspecified). +- `major_interaction_targets` (list): Semicolon-separated list of interacting + drugs considered major (e.g., `warfarin;dabigatran`). +- `naranjo_score` (integer, 0-13): Annotator-assigned Naranjo causality score + when an ADR/outcome is associated with the drug. Use the standard Naranjo + questionnaire and record the resulting integer. +- `adverse_outcome` (boolean): True if there is at least one documented severe + ADR or outcome (hospitalization, death, life-threatening event) linked to the + drug in the reference evidence. +- `annotator_id`, `annotator_date`, `notes`: Administrative and reasoning notes. + +Annotation Process +------------------ +1. Read the drug name and any provided patient context. +2. Search authoritative sources: product label (DailyMed/openFDA), RxNorm, + clinical guidelines, and peer-reviewed literature only as needed. +3. For interactions, record only clinically meaningful interactions (those that + would alter prescribing or require monitoring). Use `major` for life- or + organ-threatening interactions (e.g., increased bleeding risk with + anticoagulants). +4. For Naranjo scoring, apply the standard 10-question instrument and record + the integer total. If no ADR is being adjudicated, leave blank. +5. Save the source citations (URL or document title) in `notes` or a separate + evidence file. + +Adjudication and Quality Control +-------------------------------- +- Compute inter-rater agreement (Cohen's kappa) for key binary labels + (`major_interaction`, `adverse_outcome`). Aim for kappa ≥ 0.8. +- Disagreements should be adjudicated by a senior clinician with ties broken + by consensus and documented rationale. + +Example Row +----------- +`1,aspirin,1191,True,warfarin,5,False,clin1,Verified in openFDA label; increased bleeding with warfarin` + +Notes +----- +This protocol prioritizes clinical significance and reproducibility. Store +annotations in CSV format (UTF-8) and retain raw evidence links for auditing. diff --git a/AI Drug safety/docs/evaluation_spec.md b/AI Drug safety/docs/evaluation_spec.md new file mode 100644 index 0000000..6f42869 --- /dev/null +++ b/AI Drug safety/docs/evaluation_spec.md @@ -0,0 +1,61 @@ +# Evaluation Specification — AI Drug Safety + +Purpose +------- +Define the primary and secondary endpoints, metrics, and analysis plan for a +clinical evaluation of the AI Drug Safety agent. This specification is intended +to be used together with the annotation protocol and a clinician-annotated +gold-standard dataset. + +Primary Objectives +------------------ +- Measure sensitivity (recall) for detection of clinically important (major) + drug–drug interactions. Priority is to minimize false negatives for major + interactions. +- Measure precision (positive predictive value) for the flagged major + interactions. + +Secondary Objectives +-------------------- +- Score discrimination and calibration of the agent's numeric risk score + (AUROC, AUPRC, Brier score, calibration plots). +- Agreement between model Naranjo-like scores and clinician Naranjo scores + (Spearman correlation, mean absolute error). +- Normalization coverage (RXCUI mapping rate) and evidence support rate. + +Metrics +------- +- Sensitivity = TP / (TP + FN) for `major_interaction` detection. +- Precision = TP / (TP + FP) for `major_interaction` detection. +- F1-score for `major_interaction` detection. +- AUROC / AUPRC for risk score where outcome labels exist. +- Calibration metrics: Brier score, calibration-in-the-large, calibration slope. +- Spearman's rho between model and clinician Naranjo scores. +- RXCUI mapping rate = (# records mapped to RXCUI) / total. +- Evidence support rate = (# claims with ≥1 authoritative source) / total. + +Analysis Plan +------------- +1. Pre-specify the primary endpoint (major interaction sensitivity) and + analysis threshold. +2. Use cross-validation or internal/external split: use internal (development) + set for tuning, and external held-out validation for reporting final + performance. +3. Report point estimates with 95% confidence intervals (bootstrap for small + datasets if necessary). +4. Present stratified performance by age group, polypharmacy (>5 drugs), and + comorbidity count. + +Sample Size & Power (Guidance) +------------------------------ +- Aim for at least 200–500 annotated cases for robust estimates of sensitivity + and precision when the prevalence of events is moderate. For low-prevalence + major interactions, oversample positive cases or use enriched cohorts. + +Reporting +--------- +- Include confusion matrices, ROC and PR curves, calibration plots, DCA net + benefit plots (if outcomes available), and exemplar failure cases with + evidence citations. +- Provide the annotation protocol, raw annotations (redacted if needed), and + analysis scripts to enable reproducibility. diff --git a/AI Drug safety/evaluations/adjudicated_gold_20260430T235016Z.csv b/AI Drug safety/evaluations/adjudicated_gold_20260430T235016Z.csv new file mode 100644 index 0000000..81bc3db --- /dev/null +++ b/AI Drug safety/evaluations/adjudicated_gold_20260430T235016Z.csv @@ -0,0 +1,16 @@ +id,drug,major_interaction_A,major_interaction_B,major_interaction_final,major_interaction_agree,adverse_outcome_A,adverse_outcome_B,adverse_outcome_final,adverse_outcome_agree,notes_A,notes_B +1,aspirin,True,True,True,True,False,False,False,True,Increased bleeding risk with anticoagulants,Agrees: bleeding risk with anticoagulants +10,sertraline,False,False,False,True,False,False,False,True,Possible interactions but major ones are rare,Agrees +11,fluoxetine,True,True,True,True,False,False,False,True,Serotonin syndrome risk with MAOIs,Agrees +12,digoxin,True,True,True,True,True,True,True,True,Narrow therapeutic index; major toxicity possible,Agrees +13,furosemide,False,False,False,True,False,False,False,True,Loop diuretic; monitor electrolytes,Agrees +14,spironolactone,True,True,True,True,True,False,,False,Hyperkalemia risk with ACEi/ARB,Disagrees on adverse_outcome severity +15,insulin,False,False,False,True,True,True,True,True,Hypoglycaemia can be severe,Agrees that severe hypoglycaemia is possible +2,warfarin,True,True,True,True,True,True,True,True,High bleeding risk when combined with NSAIDs,Agrees +3,ibuprofen,False,True,,False,False,False,False,True,Typical NSAID effects; context-dependent,Considers NSAID-NSAID interactions major in elderly +4,metformin,False,False,False,True,False,False,False,True,Low interaction profile,Agrees +5,lisinopril,True,False,,False,False,False,False,True,Hyperkalemia risk with potassium-sparing diuretics,Does not consider spironolactone interaction as major +6,simvastatin,True,True,True,True,False,False,False,True,CYP3A4 inhibitors increase myopathy risk,Agrees +7,atorvastatin,True,True,True,True,False,False,False,True,Interaction with strong CYP3A4 inhibitors,Agrees +8,clopidogrel,True,True,True,True,True,True,True,True,Additive bleeding with antiplatelets,Agrees +9,amiodarone,True,True,True,True,True,True,True,True,Multiple major interactions; QT and arrhythmia risk,Agrees diff --git a/AI Drug safety/evaluations/adjudicated_gold_converted.csv b/AI Drug safety/evaluations/adjudicated_gold_converted.csv new file mode 100644 index 0000000..a0ee06a --- /dev/null +++ b/AI Drug safety/evaluations/adjudicated_gold_converted.csv @@ -0,0 +1,16 @@ +id,drug,rxcui,major_interaction,naranjo_score,adverse_outcome,notes +1,aspirin,,True,,False,Increased bleeding risk with anticoagulants | Agrees: bleeding risk with anticoagulants +10,sertraline,,False,,False,Possible interactions but major ones are rare | Agrees +11,fluoxetine,,True,,False,Serotonin syndrome risk with MAOIs | Agrees +12,digoxin,,True,,True,Narrow therapeutic index; major toxicity possible | Agrees +13,furosemide,,False,,False,Loop diuretic; monitor electrolytes | Agrees +14,spironolactone,,True,,,Hyperkalemia risk with ACEi/ARB | Disagrees on adverse_outcome severity +15,insulin,,False,,True,Hypoglycaemia can be severe | Agrees that severe hypoglycaemia is possible +2,warfarin,,True,,True,High bleeding risk when combined with NSAIDs | Agrees +3,ibuprofen,,,,False,Typical NSAID effects; context-dependent | Considers NSAID-NSAID interactions major in elderly +4,metformin,,False,,False,Low interaction profile | Agrees +5,lisinopril,,,,False,Hyperkalemia risk with potassium-sparing diuretics | Does not consider spironolactone interaction as major +6,simvastatin,,True,,False,CYP3A4 inhibitors increase myopathy risk | Agrees +7,atorvastatin,,True,,False,Interaction with strong CYP3A4 inhibitors | Agrees +8,clopidogrel,,True,,True,Additive bleeding with antiplatelets | Agrees +9,amiodarone,,True,,True,Multiple major interactions; QT and arrhythmia risk | Agrees diff --git a/AI Drug safety/evaluations/adjudication_report_20260430T235016Z.json b/AI Drug safety/evaluations/adjudication_report_20260430T235016Z.json new file mode 100644 index 0000000..151fc2a --- /dev/null +++ b/AI Drug safety/evaluations/adjudication_report_20260430T235016Z.json @@ -0,0 +1,32 @@ +{ + "n_pairs": 15, + "major": { + "confusion": { + "n00": 4, + "n01": 1, + "n10": 1, + "n11": 9 + }, + "kappa": 0.7000000000000001, + "bootstrap_ci": { + "2.5%": 0.18918918918918906, + "97.5%": 1.0 + } + }, + "adverse": { + "confusion": { + "n00": 9, + "n01": 0, + "n10": 1, + "n11": 5 + }, + "kappa": 0.8571428571428572, + "bootstrap_ci": { + "2.5%": 0.4827586206896551, + "97.5%": 1.0 + } + }, + "annotator_a_file": "evaluations/annotations_annotatorA.csv", + "annotator_b_file": "evaluations/annotations_annotatorB.csv", + "adjudicator_file": null +} \ No newline at end of file diff --git a/AI Drug safety/evaluations/annotations_annotatorA.csv b/AI Drug safety/evaluations/annotations_annotatorA.csv new file mode 100644 index 0000000..043c293 --- /dev/null +++ b/AI Drug safety/evaluations/annotations_annotatorA.csv @@ -0,0 +1,16 @@ +id,drug,rxcui,major_interaction,adverse_outcome,naranjo_score,annotator_id,notes +1,aspirin,1191,True,False,5,annotatorA,Increased bleeding risk with anticoagulants +2,warfarin,3094,True,True,9,annotatorA,High bleeding risk when combined with NSAIDs +3,ibuprofen,5640,False,False,2,annotatorA,Typical NSAID effects; context-dependent +4,metformin,860975,False,False,1,annotatorA,Low interaction profile +5,lisinopril,83367,True,False,3,annotatorA,Hyperkalemia risk with potassium-sparing diuretics +6,simvastatin,5271,True,False,3,annotatorA,CYP3A4 inhibitors increase myopathy risk +7,atorvastatin,31619,True,False,3,annotatorA,Interaction with strong CYP3A4 inhibitors +8,clopidogrel,135565,True,True,6,annotatorA,Additive bleeding with antiplatelets +9,amiodarone,197361,True,True,7,annotatorA,Multiple major interactions; QT and arrhythmia risk +10,sertraline,22072,False,False,2,annotatorA,Possible interactions but major ones are rare +11,fluoxetine,4446,True,False,4,annotatorA,Serotonin syndrome risk with MAOIs +12,digoxin,5976,True,True,8,annotatorA,Narrow therapeutic index; major toxicity possible +13,furosemide,3440,False,False,1,annotatorA,Loop diuretic; monitor electrolytes +14,spironolactone,997,True,True,5,annotatorA,Hyperkalemia risk with ACEi/ARB +15,insulin,75506,False,True,7,annotatorA,Hypoglycaemia can be severe \ No newline at end of file diff --git a/AI Drug safety/evaluations/annotations_annotatorB.csv b/AI Drug safety/evaluations/annotations_annotatorB.csv new file mode 100644 index 0000000..dbdcb67 --- /dev/null +++ b/AI Drug safety/evaluations/annotations_annotatorB.csv @@ -0,0 +1,16 @@ +id,drug,rxcui,major_interaction,adverse_outcome,naranjo_score,annotator_id,notes +1,aspirin,1191,True,False,5,annotatorB,Agrees: bleeding risk with anticoagulants +2,warfarin,3094,True,True,8,annotatorB,Agrees +3,ibuprofen,5640,True,False,3,annotatorB,Considers NSAID-NSAID interactions major in elderly +4,metformin,860975,False,False,1,annotatorB,Agrees +5,lisinopril,83367,False,False,2,annotatorB,Does not consider spironolactone interaction as major +6,simvastatin,5271,True,False,3,annotatorB,Agrees +7,atorvastatin,31619,True,False,3,annotatorB,Agrees +8,clopidogrel,135565,True,True,6,annotatorB,Agrees +9,amiodarone,197361,True,True,7,annotatorB,Agrees +10,sertraline,22072,False,False,2,annotatorB,Agrees +11,fluoxetine,4446,True,False,4,annotatorB,Agrees +12,digoxin,5976,True,True,8,annotatorB,Agrees +13,furosemide,3440,False,False,1,annotatorB,Agrees +14,spironolactone,997,True,False,4,annotatorB,Disagrees on adverse_outcome severity +15,insulin,75506,False,True,7,annotatorB,Agrees that severe hypoglycaemia is possible \ No newline at end of file diff --git a/AI Drug safety/evaluations/evaluation_metrics_20260501T151852Z.json b/AI Drug safety/evaluations/evaluation_metrics_20260501T151852Z.json new file mode 100644 index 0000000..ef24e00 --- /dev/null +++ b/AI Drug safety/evaluations/evaluation_metrics_20260501T151852Z.json @@ -0,0 +1,13 @@ +{ + "n_cases": 15, + "tp": 0, + "fp": 0, + "fn": 9, + "tn": 6, + "sensitivity": 0.0, + "precision": null, + "f1": null, + "rxcui_coverage": 0.3333333333333333, + "evidence_support_rate": 0.3333333333333333, + "naranjo_spearman": null +} \ No newline at end of file diff --git a/AI Drug safety/evaluations/evaluation_real_20260501T151845Z.json b/AI Drug safety/evaluations/evaluation_real_20260501T151845Z.json new file mode 100644 index 0000000..de186f4 --- /dev/null +++ b/AI Drug safety/evaluations/evaluation_real_20260501T151845Z.json @@ -0,0 +1,312 @@ +{ + "summary": { + "n_drugs": 5, + "rxcui_mapped": 5, + "rxcui_coverage": 1.0, + "openfda_label_count": 5, + "any_source_count": 5, + "avg_interactions_per_drug": 0.6, + "avg_side_effects_per_drug": 10.6, + "avg_risk_score": 14.2 + }, + "results": [ + { + "drug": "aspirin", + "rxcui": "1191", + "findings": { + "name": "Low Dose Aspirin", + "side_effects": [ + "Dyspnoea", + "Drug hypersensitivity", + "Back pain", + "Cerebrovascular accident", + "Blood pressure increased", + "Pain", + "Oedema peripheral", + "Fluid retention", + "Hypertension", + "Dehydration" + ], + "interactions": [], + "warnings": [ + "Warnings Reye's syndrome : Children and teenagers who have or are recovering from chicken pox or flu-like symptoms should not use this product. When using this product, if changes in behavior with nausea and vomiting occur, consult a doctor because these symptoms could be an early sign of Reye's syndrome, a rare but serious illness. Allergy alert : Aspirin may cause a severe allergic reaction, which may include: hives facial swelling shock asthma (wheezing) Stomach bleeding warning: This product contains an NSAID, which may cause severe stomach bleeding. The chance is higher if you: are age 60 or older have had stomach ulcers or bleeding problems take a blood thinning (anticoagulant) or steroid drug take other drugs containing prescription or nonprescription NSAIDs [aspirin, ibuprofen, naproxen, or others] have 3 or more alcoholic drinks every day while using this product take more or for a longer time than directed Do not use if you are allergic to aspirin or any other pain reliever/fever reducer. if you have ever had an allergic reaction to his product or any of its ingredients Ask a doctor before use if stomach bleeding warning applies to you you have a history of stomach problems, such as heartburn you have high blood pressure, heart disease, liver cirrhosis, or kidney disease you are taking a diuretic you have asthma Ask a doctor or pharmacist before use if you are taking a prescription drug for gout diabetes arthritis Stop use and ask a doctor if an allergic reaction occurs. Seek medical help right away. you experience any of the following signs of stomach bleeding: feel faint vomit blood have bloody or black stool have stomach pain that does not get better pain gets worse or lasts more than 10 days redness or swelling is present ringing in the ears or loss of hearing occurs new symptoms occur These could be signs of a serious condition If pregnant or breast-feeding, ask a health professional before use. It is especially important not to use aspirin during the last 3 months of pregnancy unless definitely directed to do so by a doctor because it may cause problems in the unborn child or complications during delivery. Keep out of reach of children. In case of overdose, get medical help or contact a Poison Control Center (1-800-222-1222) right away." + ], + "sources": [ + { + "name": "openFDA label", + "url": "https://api.fda.gov/drug/label" + }, + { + "name": "openFDA events", + "url": "https://api.fda.gov/drug/event" + } + ], + "rxcui": "1191" + }, + "risk": { + "score": 16, + "factors": [], + "beers_matches": [], + "clinical": { + "has_bled": { + "score": 1, + "components": { + "hypertension": false, + "renal": false, + "liver": false, + "stroke_history": false, + "bleeding_history": false, + "labile_inr": false, + "elderly": true, + "drugs_or_alcohol": false + }, + "category": "low" + }, + "tisdale": { + "score": 0, + "components": { + "age>=68": false, + "female": false, + "loop_diuretic": false, + "k_low": null, + "baseline_qtc_high": false, + "acute_mi": false, + "qt_prolonging_drugs_count": 0, + "sepsis": false + }, + "category": "low" + }, + "major_interaction": false + } + }, + "analysis": { + "conclusion": "Low risk", + "recommendation": "Likely safe for typical patients.", + "reasons": [ + "10 documented side effect(s)" + ], + "explanation": "Computed risk score 16; factors: " + } + }, + { + "drug": "warfarin", + "rxcui": "11289", + "findings": { + "name": "Warfarin Sodium", + "side_effects": [ + "6 ADVERSE REACTIONS The following serious adverse reactions to warfarin sodium are discussed in greater detail in other sections of the labeling: Hemorrhage [see Boxed Warning, Warnings and Precautions (5.1), and Overdosage ( 10 )] Tissue Necrosis [see Warnings and Precautions ( 5.2 )] Calciphylaxis [see Warnings and Precautions ( 5.3 )] Acute Kidney Injury [see Warnings and Precautions ( 5.4 )] Systemic Atheroemboli and Cholesterol Microemboli [see Warnings and Precautions ( 5.5 )] Limb Ischemia, Necrosis, and Gangrene in Patients with HIT and HITTS [see Warnings and Precautions ( 5.6 )] Other Clinical Settings with Increased Risks [see Warnings and Precautions ( 5.8 )] Other adverse reactions to warfarin sodium include: Immune system disorders: hypersensitivity/allergic reactions (including urticaria and anaphylactic reactions) Vascular disorders: vasculitis Hepatobiliary disorders: hepatitis, elevated liver enzymes. Cholestatic hepatitis has been associated with concomitant administration of warfarin sodium and ticlopidine. Gastrointestinal disorders: nausea, vomiting, diarrhea, taste perversion, abdominal pain, flatulence, bloating Skin disorders: rash, dermatitis (including bullous eruptions), pruritus, alopecia Respiratory disorders: tracheal or tracheobronchial calcification General disorders: chills Most common adverse reactions to warfarin sodium are fatal and nonfatal hemorrhage from any tissue or organ. ( 6 ) To report SUSPECTED ADVERSE REACTIONS, contact Teva at 1-888-838-2872 or FDA at 1-800-FDA-1088 or www.fda.gov/medwatch.", + "Drug ineffective", + "Haemorrhage", + "Haematuria", + "Subdural haematoma", + "Pulmonary arterial hypertension", + "Otorrhoea", + "International normalised ratio fluctuation", + "Splenomegaly", + "Dyspnoea", + "Insomnia" + ], + "interactions": [ + { + "drug": "7 DRUG INTERACTIONS Concomitant use of drugs that increase bleeding risk, antibiotics, antifungals, botanical (herbal) products, and inhibitors and inducers of CYP2C9, 1A2, or 3A4. ( 7 ) Consult labeling of all concurrently used drugs for complete information about interactions with warfarin sodium or increased risks for bleeding. ( 7 ) 7.1 General Information Drugs may interact with warfarin sodium through pharmacodynamic or pharmacokinetic mechanisms. Pharmacodynamic mechanisms for drug interactions with warfarin sodium are synergism (impaired hemostasis, reduced clotting factor synthesis), competitive antagonism (vitamin K), and alteration of the physiologic control loop for vitamin K metabolism (hereditary resistance). Pharmacokinetic mechanisms for drug interactions with warfarin sodium are mainly enzyme induction, enzyme inhibition, and reduced plasma protein binding. It is important to note that some drugs may interact by more than one mechanism. More frequent INR monitoring should be performed when starting or stopping other drugs, including botanicals, or when changing dosages of other drugs, including drugs intended for short-term use (e.g., antibiotics, antifungals, corticosteroids) [ see Boxed Warning ]. Consult the labeling of all concurrently used drugs to obtain further information about interactions with warfarin sodium or adverse reactions pertaining to bleeding. 7.2 CYP450 Interactions CYP450 isozymes involved in the metabolism of warfarin include CYP2C9, 2C19, 2C8, 2C18, 1A2, and 3A4. The more potent warfarin S -enantiomer is metabolized by CYP2C9 while the R -enantiomer is metabolized by CYP1A2 and 3A4. Inhibitors of CYP2C9, 1A2, and/or 3A4 have the potential to increase the effect (increase INR) of warfarin by increasing the exposure of warfarin. Inducers of CYP2C9, 1A2, and/or 3A4 have the potential to decrease the effect (decrease INR) of warfarin by decreasing the exposure of warfarin. Examples of inhibitors and inducers of CYP2C9, 1A2, and 3A4 are below in Table 2 ; however, this list should not be considered all-inclusive. Consult the labeling of all concurrently used drugs to obtain further information about CYP450 interaction potential. The CYP450 inhibition and induction potential should be considered when starting, stopping, or changing dose of concomitant medications. Closely monitor INR if a concomitant drug is a CYP2C9, 1A2, and/or 3A4 inhibitor or inducer. Table 2: Examples of CYP450 Interactions with Warfarin Enzyme Inhibitors Inducers CYP2C9 amiodarone, capecitabine, cotrimoxazole, etravirine, fluconazole, fluvastatin, fluvoxamine, metronidazole, miconazole, oxandrolone, sulfinpyrazone, tigecycline, voriconazole, zafirlukast aprepitant, bosentan, carbamazepine, phenobarbital, rifampin CYP1A2 acyclovir, allopurinol, caffeine, cimetidine, ciprofloxacin, disulfiram, enoxacin, famotidine, fluvoxamine, methoxsalen, mexiletine, norfloxacin, oral contraceptives, phenylpropanolamine, propafenone, propranolol, terbinafine, thiabendazole, ticlopidine, verapamil, zileuton montelukast, moricizine, omeprazole, phenobarbital, phenytoin, cigarette smoking CYP3A4 alprazolam, amiodarone, amlodipine, amprenavir, aprepitant, atorvastatin, atazanavir, bicalutamide, cilostazol, cimetidine, ciprofloxacin, clarithromycin, conivaptan, cyclosporine, darunavir/ritonavir, diltiazem, erythromycin, fluconazole, fluoxetine, fluvoxamine, fosamprenavir, imatinib, indinavir, isoniazid, itraconazole, ketoconazole, lopinavir/ritonavir, nefazodone, nelfinavir, nilotinib, oral contraceptives, posaconazole, ranitidine, ranolazine, ritonavir, saquinavir, telithromycin, tipranavir, voriconazole, zileuton armodafinil, amprenavir, aprepitant, bosentan, carbamazepine, efavirenz, etravirine, modafinil, nafcillin, phenytoin, pioglitazone, prednisone, rifampin, rufinamide 7.3 Drugs that Increase Bleeding Risk Examples of drugs known to increase the risk of bleeding are presented in Table 3 . Because bleeding risk is increased when these drugs are used concomitantly with warfarin, closely monitor patients receiving any such drug with warfarin. Table 3: Drugs that Can Increase the Risk of Bleeding Drug Class Specific Drugs Anticoagulants argatroban, dabigatran, bivalirudin, desirudin, heparin, lepirudin Antiplatelet Agents aspirin, cilostazol, clopidogrel, dipyridamole, prasugrel, ticlopidine Non-steroidal Anti-Inflammatory Agents celecoxib, diclofenac, diflunisal, fenoprofen, ibuprofen, indomethacin, ketoprofen, ketorolac, mefenamic acid, naproxen, oxaprozin, piroxicam, sulindac Serotonin Reuptake Inhibitors citalopram, desvenlafaxine, duloxetine, escitalopram, fluoxetine, fluvoxamine, milnacipran, paroxetine, sertraline, venlafaxine, vilazodone 7.4 Antibiotics and Antifungals There have been reports of changes in INR in patients taking warfarin and antibiotics or antifungals, but clinical pharmacokinetic studies have not shown consistent effects of these agents on plasma concentrations of warfarin. Closely monitor INR when starting or stopping any antibiotic or antifungal in patients taking warfarin. 7.5 Botanical (Herbal) Products and Foods More frequent INR monitoring should be performed when starting or stopping botanicals. Few adequate, well-controlled studies evaluating the potential for metabolic and/or pharmacologic interactions between botanicals and warfarin sodium exist. Due to a lack of manufacturing standardization with botanical medicinal preparations, the amount of active ingredients may vary. This could further confound the ability to assess potential interactions and effects on anticoagulation. Some botanicals may cause bleeding events when taken alone (e.g., garlic and Ginkgo biloba) and may have anticoagulant, antiplatelet, and/or fibrinolytic properties. These effects would be expected to be additive to the anticoagulant effects of warfarin sodium. Conversely, some botanicals may decrease the effects of warfarin sodium (e.g., co-enzyme Q 10 , St. John’s wort, ginseng). Some botanicals and foods can interact with warfarin sodium through CYP450 interactions (e.g., echinacea, grapefruit juice, ginkgo, goldenseal, St. John’s wort). The amount of vitamin K in food may affect therapy with warfarin sodium. Advise patients taking warfarin sodium to eat a normal, balanced diet maintaining a consistent amount of vitamin K. Patients taking warfarin sodium should avoid drastic changes in dietary habits, such as eating large amounts of green leafy vegetables.", + "severity": "unknown", + "description": "7 DRUG INTERACTIONS Concomitant use of drugs that increase bleeding risk, antibiotics, antifungals, botanical (herbal) products, and inhibitors and inducers of CYP2C9, 1A2, or 3A4. ( 7 ) Consult labeling of all concurrently used drugs for complete information about interactions with warfarin sodium or increased risks for bleeding. ( 7 ) 7.1 General Information Drugs may interact with warfarin sodium through pharmacodynamic or pharmacokinetic mechanisms. Pharmacodynamic mechanisms for drug interactions with warfarin sodium are synergism (impaired hemostasis, reduced clotting factor synthesis), competitive antagonism (vitamin K), and alteration of the physiologic control loop for vitamin K metabolism (hereditary resistance). Pharmacokinetic mechanisms for drug interactions with warfarin sodium are mainly enzyme induction, enzyme inhibition, and reduced plasma protein binding. It is important to note that some drugs may interact by more than one mechanism. More frequent INR monitoring should be performed when starting or stopping other drugs, including botanicals, or when changing dosages of other drugs, including drugs intended for short-term use (e.g., antibiotics, antifungals, corticosteroids) [ see Boxed Warning ]. Consult the labeling of all concurrently used drugs to obtain further information about interactions with warfarin sodium or adverse reactions pertaining to bleeding. 7.2 CYP450 Interactions CYP450 isozymes involved in the metabolism of warfarin include CYP2C9, 2C19, 2C8, 2C18, 1A2, and 3A4. The more potent warfarin S -enantiomer is metabolized by CYP2C9 while the R -enantiomer is metabolized by CYP1A2 and 3A4. Inhibitors of CYP2C9, 1A2, and/or 3A4 have the potential to increase the effect (increase INR) of warfarin by increasing the exposure of warfarin. Inducers of CYP2C9, 1A2, and/or 3A4 have the potential to decrease the effect (decrease INR) of warfarin by decreasing the exposure of warfarin. Examples of inhibitors and inducers of CYP2C9, 1A2, and 3A4 are below in Table 2 ; however, this list should not be considered all-inclusive. Consult the labeling of all concurrently used drugs to obtain further information about CYP450 interaction potential. The CYP450 inhibition and induction potential should be considered when starting, stopping, or changing dose of concomitant medications. Closely monitor INR if a concomitant drug is a CYP2C9, 1A2, and/or 3A4 inhibitor or inducer. Table 2: Examples of CYP450 Interactions with Warfarin Enzyme Inhibitors Inducers CYP2C9 amiodarone, capecitabine, cotrimoxazole, etravirine, fluconazole, fluvastatin, fluvoxamine, metronidazole, miconazole, oxandrolone, sulfinpyrazone, tigecycline, voriconazole, zafirlukast aprepitant, bosentan, carbamazepine, phenobarbital, rifampin CYP1A2 acyclovir, allopurinol, caffeine, cimetidine, ciprofloxacin, disulfiram, enoxacin, famotidine, fluvoxamine, methoxsalen, mexiletine, norfloxacin, oral contraceptives, phenylpropanolamine, propafenone, propranolol, terbinafine, thiabendazole, ticlopidine, verapamil, zileuton montelukast, moricizine, omeprazole, phenobarbital, phenytoin, cigarette smoking CYP3A4 alprazolam, amiodarone, amlodipine, amprenavir, aprepitant, atorvastatin, atazanavir, bicalutamide, cilostazol, cimetidine, ciprofloxacin, clarithromycin, conivaptan, cyclosporine, darunavir/ritonavir, diltiazem, erythromycin, fluconazole, fluoxetine, fluvoxamine, fosamprenavir, imatinib, indinavir, isoniazid, itraconazole, ketoconazole, lopinavir/ritonavir, nefazodone, nelfinavir, nilotinib, oral contraceptives, posaconazole, ranitidine, ranolazine, ritonavir, saquinavir, telithromycin, tipranavir, voriconazole, zileuton armodafinil, amprenavir, aprepitant, bosentan, carbamazepine, efavirenz, etravirine, modafinil, nafcillin, phenytoin, pioglitazone, prednisone, rifampin, rufinamide 7.3 Drugs that Increase Bleeding Risk Examples of drugs known to increase the risk of bleeding are presented in Table 3 . Because bleeding risk is increased when these drugs are used concomitantly with warfarin, closely monitor patients receiving any such drug with warfarin. Table 3: Drugs that Can Increase the Risk of Bleeding Drug Class Specific Drugs Anticoagulants argatroban, dabigatran, bivalirudin, desirudin, heparin, lepirudin Antiplatelet Agents aspirin, cilostazol, clopidogrel, dipyridamole, prasugrel, ticlopidine Non-steroidal Anti-Inflammatory Agents celecoxib, diclofenac, diflunisal, fenoprofen, ibuprofen, indomethacin, ketoprofen, ketorolac, mefenamic acid, naproxen, oxaprozin, piroxicam, sulindac Serotonin Reuptake Inhibitors citalopram, desvenlafaxine, duloxetine, escitalopram, fluoxetine, fluvoxamine, milnacipran, paroxetine, sertraline, venlafaxine, vilazodone 7.4 Antibiotics and Antifungals There have been reports of changes in INR in patients taking warfarin and antibiotics or antifungals, but clinical pharmacokinetic studies have not shown consistent effects of these agents on plasma concentrations of warfarin. Closely monitor INR when starting or stopping any antibiotic or antifungal in patients taking warfarin. 7.5 Botanical (Herbal) Products and Foods More frequent INR monitoring should be performed when starting or stopping botanicals. Few adequate, well-controlled studies evaluating the potential for metabolic and/or pharmacologic interactions between botanicals and warfarin sodium exist. Due to a lack of manufacturing standardization with botanical medicinal preparations, the amount of active ingredients may vary. This could further confound the ability to assess potential interactions and effects on anticoagulation. Some botanicals may cause bleeding events when taken alone (e.g., garlic and Ginkgo biloba) and may have anticoagulant, antiplatelet, and/or fibrinolytic properties. These effects would be expected to be additive to the anticoagulant effects of warfarin sodium. Conversely, some botanicals may decrease the effects of warfarin sodium (e.g., co-enzyme Q 10 , St. John’s wort, ginseng). Some botanicals and foods can interact with warfarin sodium through CYP450 interactions (e.g., echinacea, grapefruit juice, ginkgo, goldenseal, St. John’s wort). The amount of vitamin K in food may affect therapy with warfarin sodium. Advise patients taking warfarin sodium to eat a normal, balanced diet maintaining a consistent amount of vitamin K. Patients taking warfarin sodium should avoid drastic changes in dietary habits, such as eating large amounts of green leafy vegetables." + } + ], + "warnings": [ + "WARNING: BLEEDING RISK Warfarin sodium can cause major or fatal bleeding [see Warnings and Precautions ( 5.1 )] . Perform regular monitoring of INR in all treated patients [see Dosage and Administration ( 2.1 )] . Drugs, dietary changes, and other factors affect INR levels achieved with warfarin sodium therapy [see Drug Interactions ( 7 )] . Instruct patients about prevention measures to minimize risk of bleeding and to report signs and symptoms of bleeding [see Patient Counseling Information ( 17 )] . WARNING: BLEEDING RISK See full prescribing information for complete boxed warning. Warfarin sodium can cause major or fatal bleeding. ( 5.1 ) Perform regular monitoring of INR in all treated patients. ( 2.1 ) Drugs, dietary changes, and other factors affect INR levels achieved with warfarin sodium therapy. ( 7 ) Instruct patients about prevention measures to minimize risk of bleeding and to report signs and symptoms of bleeding. ( 17 )" + ], + "sources": [ + { + "name": "openFDA label", + "url": "https://api.fda.gov/drug/label" + }, + { + "name": "openFDA events", + "url": "https://api.fda.gov/drug/event" + } + ], + "rxcui": "11289" + }, + "risk": { + "score": 15, + "factors": [ + "age>=65" + ], + "beers_matches": [] + }, + "analysis": { + "conclusion": "Low risk", + "recommendation": "Likely safe for typical patients.", + "reasons": [ + "1 reported interaction(s)", + "11 documented side effect(s)" + ], + "explanation": "Computed risk score 15; factors: age>=65" + } + }, + { + "drug": "ibuprofen", + "rxcui": "5640", + "findings": { + "name": "Ibuprofen Dye Free", + "side_effects": [ + "Disease progression", + "Malaise", + "Dyspepsia", + "Renal impairment", + "Cholecystectomy", + "Nephrolithiasis", + "Biliary tract disorder", + "Dyspnoea", + "Pain", + "Feeling abnormal" + ], + "interactions": [], + "warnings": [ + "Warnings Allergy alert: Ibuprofen may cause a severe allergic reaction, especially in people allergic to aspirin. Symptoms may include: rash facial swelling asthma (wheezing) hives skin reddening shock blisters If an allergic reaction occurs, stop use and seek medical help right away. Stomach bleeding warning: This product contains an NSAID, which may cause severe stomach bleeding. The chance is higher if you: take more or for a longer time than directed take a blood thinning (anticoagulant) or steroid drug have had stomach ulcers or bleeding problems have 3 or more alcoholic drinks every day while using this product are age 60 or older take other drugs containing prescription or nonprescription NSAIDs [aspirin, ibuprofen, naproxen, or others] Heart attack and stroke warning: NSAIDs, except aspirin, increase the risk of heart attack, heart failure, and stroke. These can be fatal. The risk is higher if you use more than directed or for longer than directed. Do not use if you have ever had an allergic reaction to any other pain reliever/fever reducer right before or after heart surgery Ask a doctor before use if stomach bleeding warning applies to you you have a history of stomach problems, such as heartburn you have high blood pressure, heart disease, liver cirrhosis, kidney disease, asthma, or had a stroke you are taking a diuretic you have problems or serious side effects from taking pain relievers or fever reducers Ask a doctor or pharmacist before use if you are under a doctor's care for any serious condition taking aspirin for heart attack or stroke, because ibuprofen may decrease this benefit of aspirin taking any other drug When using this product take with food or milk if stomach upset occurs Stop use and ask a doctor if you experience any of the following signs of stomach bleeding: feel faint have bloody or black stools vomit blood have stomach pain that does not get better you have symptoms of heart problems or stroke: chest pain slurred speech leg swelling trouble breathing weakness in one part or side of body pain gets worse or lasts more than 10 days fever gets worse or lasts more than 3 days redness or swelling is present in the painful area any new symptoms appear If pregnant or breast-feeding, ask a health professional before use. It is especially important not to use ibuprofen at 20 weeks or later in pregnancy unless definitely directed to do so by a doctor because it may cause problems in the unborn child or complications during delivery. Keep out of reach of children. In case of overdose, get medical help or contact a Poison Control Center right away." + ], + "sources": [ + { + "name": "openFDA label", + "url": "https://api.fda.gov/drug/label" + }, + { + "name": "openFDA events", + "url": "https://api.fda.gov/drug/event" + } + ], + "rxcui": "5640" + }, + "risk": { + "score": 10, + "factors": [ + "age>=65" + ], + "beers_matches": [] + }, + "analysis": { + "conclusion": "Low risk", + "recommendation": "Likely safe for typical patients.", + "reasons": [ + "10 documented side effect(s)" + ], + "explanation": "Computed risk score 10; factors: age>=65" + } + }, + { + "drug": "metformin", + "rxcui": "6809", + "findings": { + "name": "Metformin Hydrochloride", + "side_effects": [ + "6 ADVERSE REACTIONS The following adverse reactions are also discussed elsewhere in the labeling: Lactic Acidosis [ see Boxed Warning and Warnings and Precautions (5.1) ]. Vitamin B12 Deficiency [ see Warnings and Precautions (5.2) ]. Hypoglycemia [ see Warnings and Precautions (5.3) For metformin hydrochloride tablets, the most common adverse reactions (>5.0%) are diarrhea, nausea/vomiting, flatulence, asthenia, indigestion, abdominal discomfort, and headache. ( 6.1 ) To report SUSPECTED ADVERSE REACTIONS, contact Granules Pharmaceuticals Inc at 1-877-770-3183 or FDA at 1-800-FDA-1088 or www.fda.gov/medwatch 6.1 Clinical Studies Experience Because clinical trials are conducted under widely varying conditions, adverse reaction rates observed in the clinical trials of a drug cannot be directly compared to rates in the clinical trials of another drug and may not reflect the rates observed in practice. Metformin Hydrochloride Tablets In a U.S. clinical trial of metformin hydrochloride tablets in patients with type 2 diabetes mellitus, a total of 141 patients received metformin hydrochloride tablets up to 2550 mg per day. Adverse reactions reported in greater than 5% of metformin hydrochloride tablets treated patients and that were more common than in placebo-treated patients, are listed in Table 1. Table 1: Adverse Reactions from a Clinical Trial of Metformin Hydrochloride Tablets Occurring >5% and More Common than Placebo in Patients with Type 2 Diabetes Mellitus Metformin Hydrochloride Tablets (n=141) Placebo (n=145) Diarrhea 53% 12% Nausea/Vomiting 26% 8% Flatulence 12% 6% Asthenia 9% 6% Indigestion 7% 4% Abdominal Discomfort 6% 5% Headache 6% 5% Diarrhea led to discontinuation of metformin hydrochloride tablets in 6% of patients. Additionally, the following adverse reactions were reported in ≥1 % to ≤5% of metformin hydrochloride tablets treated patients and were more commonly reported with metformin hydrochloride tablets than placebo: abnormal stools, hypoglycemia, myalgia, lightheaded, dyspnea, nail disorder, rash, sweating increased, taste disorder, chest discomfort, chills, flu syndrome, flushing, palpitation. In metformin hydrochloride tablets clinical trials of 29-week duration, a decrease to subnormal levels of previously normal serum vitamin B 12 levels was observed in approximately 7% of patients. Pediatric Patients In clinical trials with metformin hydrochloride tablets in pediatric patients with type 2 diabetes mellitus, the profile of adverse reactions was similar to that observed in adults. 6.2 Postmarketing Experience The following adverse reactions have been identified during post approval use of metformin. Because these reactions are reported voluntarily from a population of uncertain size, it is not always possible to reliably estimate their frequency or establish a causal relationship to drug exposure. Cholestatic, hepatocellular, and mixed hepatocellular liver injury have been reported with postmarketing use of metformin.", + "Gait disturbance", + "Unevaluable event", + "Medication residue present", + "Osteomyelitis", + "Erysipelas", + "Sepsis", + "Balance disorder", + "Weight increased", + "Splenomegaly", + "Dyspnoea" + ], + "interactions": [ + { + "drug": "7 DRUG INTERACTIONS Table 3 presents clinically significant drug interactions with metformin hydrochloride tablets. Table 3: Clinically Significant Drug Interactions with Metformin Hydrochloride Tablets Carbonic Anhydrase Inhibitors Clinical Impact: Carbonic anhydrase inhibitors frequently cause a decrease in serum bicarbonate and induce non-anion gap, hyperchloremic metabolic acidosis. Concomitant use of these drugs with metformin hydrochloride tablets may increase the risk for lactic acidosis. Intervention: Consider more frequent monitoring of these patients. Examples: Topiramate, zonisamide, acetazolamide or dichlorphenamide. Drugs that Reduce metformin hydrochloride tablet Clearance Clinical Impact: Concomitant use of drugs that interfere with common renal tubular transport systems involved in the renal elimination of metformin (e.g., organic cationic transporter-2 [OCT2] / multidrug and toxin extrusion [MATE] inhibitors) could increase systemic exposure to metformin and may increase the risk for lactic acidosis [ see Clinical Pharmacology (12.3) ]. Intervention: Consider the benefits and risks of concomitant use with metformin hydrochloride tablets. Examples: Ranolazine, vandetanib, dolutegravir, and cimetidine. Alcohol Clinical Impact: Alcohol is known to potentiate the effect of metformin on lactate metabolism. Intervention: Warn patients against excessive alcohol intake while receiving metformin hydrochloride tablets. Insulin Secretagogues or Insulin Clinical Impact: Coadministration of metformin hydrochloride tablets with an insulin secretagogue (e.g., sulfonylurea) or insulin may increase the risk of hypoglycemia. Intervention: Patients receiving an insulin secretagogue or insulin may require lower doses of the insulin secretagogue or insulin. Drugs Affecting Glycemic Control Clinical Impact: Certain drugs tend to produce hyperglycemia and may lead to loss of glycemic control. Intervention: When such drugs are administered to a patient receiving metformin hydrochloride tablets, observe the patient closely for loss of blood glucose control. When such drugs are withdrawn from a patient receiving metformin hydrochloride tablets, observe the patient closely for hypoglycemia. Examples: Thiazides and other diuretics, corticosteroids, phenothiazines, thyroid products, estrogens, oral contraceptives, phenytoin, nicotinic acid, sympathomimetics, calcium channel blockers, and isoniazid. Carbonic anhydrase inhibitors may increase risk of lactic acidosis. Consider more frequent monitoring ( 7 ) Drugs that reduce metformin clearance (such as ranolazine, vandetanib, dolutegravir, and cimetidine) may increase the accumulation of metformin. Consider the benefits and risks of concomitant use ( 7 ) Alcohol can potentiate the effect of metformin on lactate metabolism. Warn patients against excessive alcohol intake ( 7 )", + "severity": "unknown", + "description": "7 DRUG INTERACTIONS Table 3 presents clinically significant drug interactions with metformin hydrochloride tablets. Table 3: Clinically Significant Drug Interactions with Metformin Hydrochloride Tablets Carbonic Anhydrase Inhibitors Clinical Impact: Carbonic anhydrase inhibitors frequently cause a decrease in serum bicarbonate and induce non-anion gap, hyperchloremic metabolic acidosis. Concomitant use of these drugs with metformin hydrochloride tablets may increase the risk for lactic acidosis. Intervention: Consider more frequent monitoring of these patients. Examples: Topiramate, zonisamide, acetazolamide or dichlorphenamide. Drugs that Reduce metformin hydrochloride tablet Clearance Clinical Impact: Concomitant use of drugs that interfere with common renal tubular transport systems involved in the renal elimination of metformin (e.g., organic cationic transporter-2 [OCT2] / multidrug and toxin extrusion [MATE] inhibitors) could increase systemic exposure to metformin and may increase the risk for lactic acidosis [ see Clinical Pharmacology (12.3) ]. Intervention: Consider the benefits and risks of concomitant use with metformin hydrochloride tablets. Examples: Ranolazine, vandetanib, dolutegravir, and cimetidine. Alcohol Clinical Impact: Alcohol is known to potentiate the effect of metformin on lactate metabolism. Intervention: Warn patients against excessive alcohol intake while receiving metformin hydrochloride tablets. Insulin Secretagogues or Insulin Clinical Impact: Coadministration of metformin hydrochloride tablets with an insulin secretagogue (e.g., sulfonylurea) or insulin may increase the risk of hypoglycemia. Intervention: Patients receiving an insulin secretagogue or insulin may require lower doses of the insulin secretagogue or insulin. Drugs Affecting Glycemic Control Clinical Impact: Certain drugs tend to produce hyperglycemia and may lead to loss of glycemic control. Intervention: When such drugs are administered to a patient receiving metformin hydrochloride tablets, observe the patient closely for loss of blood glucose control. When such drugs are withdrawn from a patient receiving metformin hydrochloride tablets, observe the patient closely for hypoglycemia. Examples: Thiazides and other diuretics, corticosteroids, phenothiazines, thyroid products, estrogens, oral contraceptives, phenytoin, nicotinic acid, sympathomimetics, calcium channel blockers, and isoniazid. Carbonic anhydrase inhibitors may increase risk of lactic acidosis. Consider more frequent monitoring ( 7 ) Drugs that reduce metformin clearance (such as ranolazine, vandetanib, dolutegravir, and cimetidine) may increase the accumulation of metformin. Consider the benefits and risks of concomitant use ( 7 ) Alcohol can potentiate the effect of metformin on lactate metabolism. Warn patients against excessive alcohol intake ( 7 )" + } + ], + "warnings": [ + "BOXED WARNING WARNING: LACTIC ACIDOSIS Postmarketing cases of metformin-associated lactic acidosis have resulted in death, hypothermia, hypotension, and resistant bradyarrhythmias. The onset of metformin­ associated lactic acidosis is often subtle, accompanied only by nonspecific symptoms such as malaise, myalgias, respiratory distress, somnolence, and abdominal pain. Metformin­ associated lactic acidosis was characterized by elevated blood lactate levels (>5 mmol/Liter), anion gap acidosis (without evidence of ketonuria or ketonemia), an increased lactate/pyruvate ratio; and metformin plasma levels generally >5 mcg/mL [see Warnings and Precautions (5.1) ]. Risk factors for metformin-associated lactic acidosis include renal impairment, concomitant use of certain drugs (e.g. carbonic anhydrase inhibitors such as topiramate), age 65 years old or greater, having a radiological study with contrast, surgery and other procedures, hypoxic states (e.g., acute congestive heart failure), excessive alcohol intake, and hepatic impairment. Steps to reduce the risk of and manage metformin-associated lactic acidosis in these high risk groups are provided [see Dosage and Administration (2.3), (2.7 ) , C ontraindications (4) , Warnings and Precautions (5.1) ]. If metformin-associated lactic acidosis is suspected, immediately discontinue metformin hydrochloride tablets and institute general supportive measures in a hospital setting. Prompt hemodialysis is recommended [ see Warnings and Precautions (5.1 ) ]. WARNING: LACTIC ACIDOSIS See full prescribing information for complete boxed warning. Postmarketing cases of metformin-associated lactic acidosis have resulted in death, hypothermia, hypotension, and resistant bradyarrhythmias. Symptoms included malaise, myalgias, respiratory distress, somnolence, and abdominal pain. Laboratory abnormalities included elevated blood lactate levels, anion gap acidosis, increased lactate/pyruvate ratio; and metformin plasma levels generally >5 mcg/mL. ( 5.1 ) Risk factors include renal impairment, concomitant use of certain drugs, age >65 years old, radiological studies with contrast, surgery and other procedures, hypoxic states, excessive alcohol intake, and hepatic impairment. Steps to reduce the risk of and manage metformin­ associated lactic acidosis in these high risk groups are provided in the Full Prescribing Information. ( 5.1 ) If lactic acidosis is suspected, discontinue metformin hydrochloride tablets and institute general supportive measures in a hospital setting. Prompt hemodialysis is recommended. ( 5.1 )" + ], + "sources": [ + { + "name": "openFDA label", + "url": "https://api.fda.gov/drug/label" + }, + { + "name": "openFDA events", + "url": "https://api.fda.gov/drug/event" + } + ], + "rxcui": "6809" + }, + "risk": { + "score": 15, + "factors": [ + "age>=65" + ], + "beers_matches": [] + }, + "analysis": { + "conclusion": "Low risk", + "recommendation": "Likely safe for typical patients.", + "reasons": [ + "1 reported interaction(s)", + "11 documented side effect(s)" + ], + "explanation": "Computed risk score 15; factors: age>=65" + } + }, + { + "drug": "lisinopril", + "rxcui": "29046", + "findings": { + "name": "Lisinopril and Hydrochlorothiazide", + "side_effects": [ + "ADVERSE REACTIONS Lisinopril and hydrochlorothiazide tablets have been evaluated for safety in 930 patients including 100 patients treated for 50 weeks or more. In clinical trials with lisinopril and hydrochlorothiazide tablets no adverse experiences peculiar to this combination drug have been observed. Adverse experiences that have occurred have been limited to those that have been previously reported with lisinopril or hydrochlorothiazide. The most frequent clinical adverse experiences in controlled trials (including open label extensions) with any combination of lisinopril and hydrochlorothiazide tablets were: dizziness (7.5%), headache (5.2%), cough (3.9%), fatigue (3.7%) and orthostatic effects (3.2%) all of which were more common than in placebo-treated patients. Generally, adverse experiences were mild and transient in nature; but see WARNINGS regarding angioedema and excessive hypotension or syncope. Discontinuation of therapy due to adverse effects was required in 4.4% of patients principally because of dizziness, cough, fatigue and muscle cramps. Adverse experiences occurring in greater than one percent of patients treated with lisinopril plus hydrochlorothiazide in controlled clinical trials are shown below. Percent of Patients in Controlled Studies Clinical adverse experiences occurring in 0.3% to 1.0% of patients in controlled trials included: Lisinopril-Hydrochlorothiazide (n=930) Incidence (discontinuation) Placebo (n=207) Incidence Dizziness 7.5 (0.8) 1.9 Headache 5.2 (0.3) 1.9 Cough 3.9 (0.6) 1.0 Fatigue 3.7 (0.4) 1.0 Orthostatic Effects 3.2 (0.1) 1.0 Diarrhea 2.5 (0.2) 2.4 Nausea 2.2 (0.1) 2.4 Upper Respiratory Infection 2.2 (0.0) 0.0 Muscle Cramps 2.0 (0.4) 0.5 Asthenia 1.8 (0.2) 1.0 Paresthesia 1.5 (0.1) 0.0 Hypotension 1.4 (0.3) 0.5 Vomiting 1.4 (0.1) 0.5 Dyspepsia 1.3 (0.0) 0.0 Rash 1.2 (0.1) 0.5 Impotence 1.2 (0.3) 0.0 Body as a Whole: Chest pain, abdominal pain, syncope, chest discomfort, fever, trauma, virus infection. Cardiovascular: Palpitation, orthostatic hypotension. Digestive: Gastrointestinal cramps, dry mouth, constipation, heartburn. Musculoskeletal: Back pain, shoulder pain, knee pain, back strain, myalgia, foot pain. Nervous/Psychiatric: Decreased libido, vertigo, depression, somnolence. Respiratory: Common cold, nasal congestion, influenza, bronchitis, pharyngeal pain, dyspnea, pulmonary congestion, chronic sinusitis, allergic rhinitis, pharyngeal discomfort. Skin: Flushing, pruritus, skin inflammation, diaphoresis. Special Senses: Blurred vision, tinnitus, otalgia. Urogenital: Urinary tract infection. Angioedema: Angioedema has been reported in patients receiving PRINZIDE, with an incidence higher in Black than in non-Black patients. Angioedema associated with laryngeal edema may be fatal. If angioedema of the face, extremities, lips, tongue, glottis and/or larynx occurs, treatment with PRINZIDE should be discontinued and appropriate therapy instituted immediately. In rare cases, intestinal angioedema has been reported with angiotensin converting enzyme inhibitors including lisinopril. (See WARNINGS ). Hypotension: In clinical trials, adverse effects relating to hypotension occurred as follows: hypotension (1.4 percent), orthostatic hypotension (0.5 percent), other orthostatic effects (3.2 percent). In addition syncope occurred in 0.8 percent of patients (See WARNINGS ). Cough: See PRECAUTIONS - Cough . Clinical Laboratory Test Findings Serum Electrolytes: (See PRECAUTIONS ). Creatinine, Blood Urea Nitrogen: Minor reversible increases in blood urea nitrogen and serum creatinine were observed in patients with essential hypertension treated with lisinopril and hydrochlorothiazide tablets. More marked increases have also been reported and were more likely to occur in patients with renal artery stenosis (See PRECAUTIONS ). Serum Uric Acid, Glucose, Magnesium, Cholesterol, Triglycerides and Calcium: (See PRECAUTIONS ). Hemoglobin and Hematocrit: Small decreases in hemoglobin and hematocrit (mean decreases of approximately 0.5 g% and 1.5 vol%, respectively) occurred frequently in hypertensive patients treated with lisinopril and hydrochlorothiazide tablets but were rarely of clinical importance unless another cause of anemia coexisted. In clinical trials, 0.4% of patients discontinued therapy due to anemia. Liver Function Tests: Rarely, elevations of liver enzymes and/or serum bilirubin have occurred. (See WARNINGS, Hepatic Failure ). Other adverse reactions that have been reported with the individual components are listed below: Lisinopril - In clinical trials adverse reactions which occurred with lisinopril were also seen with lisinopril and hydrochlorothiazide tablets. In addition, and since lisinopril has been marketed, the following adverse reactions have been reported with lisinopril and should be considered potential adverse reactions for lisinopril and hydrochlorothiazide tablets: Body as a Whole: Anaphylactoid reactions (see WARNINGS, Anaphylactoid and Possibly Related Reactions ), malaise, edema, facial edema, pain, pelvic pain, flank pain, chills; Cardiovascular: Cardiac arrest, myocardial infarction or cerebrovascular accident, possibly secondary to excessive hypotension in high risk patients (see WARNINGS, Hypotension ), pulmonary embolism and infarction, worsening of heart failure, arrhythmias (including tachycardia, ventricular tachycardia, atrial tachycardia, atrial fibrillation, bradycardia, and premature ventricular contractions), angina pectoris, transient ischemic attacks, paroxysmal nocturnal dyspnea, decreased blood pressure, peripheral edema, vasculitis; Digestive: Pancreatitis, hepatitis (hepatocellular or cholestatic jaundice) (see WARNINGS, Hepatic Failure ), gastritis, anorexia, flatulence, increased salivation; Endocrine: Diabetes mellitus, syndrome of inappropriate antidiuretic hormone secretion (SIADH); Hematologic: Rare cases of neutropenia, thrombocytopenia, and bone marrow depression have been reported. Hemolytic anemia has been reported; a causal relationship to lisinopril cannot be excluded; Metabolic: Gout, weight loss, dehydration, fluid overload, weight gain; Musculoskeletal: Arthritis, arthralgia, neck pain, hip pain, joint pain, leg pain, arm pain, lumbago; Nervous System/Psychiatric: Ataxia, memory impairment, tremor, insomnia, stroke, nervousness, confusion, peripheral neuropathy (e.g., paresthesia, dysesthesia), spasm, hypersomnia, irritability, mood alterations (including depressive symptoms); hallucinations ; Respiratory: Malignant lung neoplasms, hemoptysis, pulmonary edema, pulmonary infiltrates, eosinophilic pneumonitis, bronchospasm, asthma, pleural effusion, pneumonia, wheezing, orthopnea, painful respiration, epistaxis, laryngitis, sinusitis, pharyngitis, rhinitis, rhinorrhea, chest sound abnormalities; Skin: Urticaria, alopecia, herpes zoster, photosensitivity, skin lesions, skin infections, pemphigus, erythema, psoriasis. Other severe skin reactions (including toxic epidermal necrolysis, Stevens-Johnson syndrome and cutaneous pseudolymphoma) have been reported rarely; causal relationship has not been established; Special Senses: Visual loss, diplopia, photophobia, taste disturbances, olfactory disturbances; Urogenital: Acute renal failure, oliguria, anuria, uremia, progressive azotemia, renal dysfunction (see PRECAUTIONS and DOSAGE AND ADMINISTRATION ), pyelonephritis, dysuria, breast pain. Miscellaneous: A symptom complex has been reported which may include a positive ANA, an elevated erythrocyte sedimentation rate, arthralgia/arthritis, myalgia, fever, vasculitis, leukocytosis, eosinophilia, photosensitivity, rash, and other dermatological manifestations. Fetal/Neonatal Morbidity and Mortality: See WARNINGS , Pregnancy, Lisinopril, Fetal/Neonatal Morbidity and Mortality . Hydrochlorothiazide - Body as a Whole: Weakness; Digestive: Anorexia, gastric irritation, cramping, jaundice (intrahepatic cholestatic jaundice) (See WARNINGS, Hepatic Failure ), pancreatitis, sialadenitis, constipation; Hematologic: Leukopenia, agranulocytosis, thrombocytopenia, aplastic anemia, hemolytic anemia; Musculoskeletal: Muscle spasm; Nervous System/Psychiatric: Restlessness; Renal: Renal failure, renal dysfunction, interstitial nephritis (see WARNINGS ); Skin: Erythema multiforme including Stevens-Johnson syndrome, exfoliative dermatitis including toxic epidermal necrolysis, alopecia; Special Senses: Xanthopsia; Hypersensitivity: Purpura, photosensitivity, urticaria, necrotizing angiitis (vasculitis and cutaneous vasculitis), respiratory distress including pneumonitis and pulmonary edema, anaphylactic reactions. Non-melanoma Skin Cancer Hydrochlorothiazide is associated with an increased risk of non-melanoma skin cancer. In a study conducted in the Sentinel System, increased risk was predominantly for squamous cell carcinoma (SCC) and in white patients taking large cumulative doses. The increased risk for SCC in the overall population was approximately 1 additional case per 16,000 patients per year, and for white patients taking a cumulative dose of ≥50,000 mg the risk increase was approximately 1 additional SCC case for every 6,700 patients per year.", + "Haemoglobin decreased", + "Fatigue", + "Insomnia", + "Dehydration", + "Vomiting", + "Drug hypersensitivity", + "Oedema peripheral", + "Fluid retention", + "Pulmonary embolism", + "Anaemia" + ], + "interactions": [ + { + "drug": "Drug Interactions Lisinopril Hypotension - Patients on Diuretic Therapy: Patients on diuretics and especially those in whom diuretic therapy was recently instituted, may occasionally experience an excessive reduction of blood pressure after initiation of therapy with lisinopril. The possibility of hypotensive effects with lisinopril can be minimized by either discontinuing the diuretic or increasing the salt intake prior to initiation of treatment with lisinopril. If it is necessary to continue the diuretic, initiate therapy with lisinopril at a dose of 5 mg daily, and provide close medical supervision after the initial dose for at least two hours and until blood pressure has stabilized for at least an additional hour. (See WARNINGS , and DOSAGE AND ADMINISTRATION .) When a diuretic is added to the therapy of a patient receiving lisinopril, an additional antihypertensive effect is usually observed (See DOSAGE AND ADMINISTRATION .) Non-steroidal Anti-inflammatory Agents Including Selective Cyclooxygenase-2 (COX-2) Inhibitors: In patients who are elderly, volume-depleted (including those on diuretic therapy), or with compromised renal function, co-administration of NSAIDs, including selective COX-2 inhibitors, with ACE inhibitors, including lisinopril, may result in deterioration of renal function, including possible acute renal failure. These effects are usually reversible. Monitor renal function periodically in patients receiving lisinopril and NSAID therapy. The antihypertensive effect of ACE inhibitors, including lisinopril, may be attenuated by NSAIDs. Dual Blockade of the Renin-Angiotensin System (RAS): Dual blockade of the RAS with angiotensin receptor blockers, ACE inhibitors, or direct renin inhibitors (such as aliskiren) is associated with increased risk of hypotension, syncope, hyperkalemia, and changes in renal function (including acute renal failure) compared to monotherapy. The VA NEPHRON trial enrolled 1448 patients with type 2 diabetes, elevated urinary-albumin-to-creatinine ratio, and decreased estimated glomerular filtration rate (GFR 30 to 89.9 ml/min), randomized them to lisinopril or placebo on a background of losartan therapy and followed them for a median of 2.2 years. Patients receiving the combination of losartan and lisinopril did not obtain any additional benefit compared to monotherapy for the combined endpoint of decline in GFR, end state renal disease, or death, but experienced an increased incidence of hyperkalemia and acute kidney injury compared with the monotherapy group. In general, avoid combined use of RAS inhibitors. Monitor blood pressure, renal function, and electrolytes in patients on lisinopril and hydrochlorothiazide tablets and other agents that affect the RAS. Do not coadminister aliskiren with lisinopril and hydrochlorothiazide tablets in patients with diabetes. Avoid use of aliskiren with PRINZIDE in patients with renal impairment (GFR <60 ml/min). Other Agents: Lisinopril has been used concomitantly with nitrates and/or digoxin without evidence of clinically significant adverse interactions. No meaningful clinically important pharmacokinetic interactions occurred when lisinopril was used concomitantly with propranolol, digoxin, or hydrochlorothiazide. The presence of food in the stomach does not alter the bioavailability of lisinopril. Agents Increasing Serum Potassium: Lisinopril attenuates potassium loss caused by thiazide-type diuretics. Use of lisinopril with potassium-sparing diuretics (e.g., spironolactone, eplerenone, triamterene, or amiloride), potassium supplements, or potassium-containing salt substitutes may lead to significant increases in serum potassium. Therefore, if concomitant use of these agents is indicated, because of demonstrated hypokalemia, they should be used with caution and with frequent monitoring of serum potassium. Lithium: Lithium toxicity has been reported in patients receiving lithium concomitantly with drugs which cause elimination of sodium, including ACE inhibitors. Lithium toxicity was usually reversible upon discontinuation of lithium and the ACE inhibitor. It is recommended that serum lithium levels be monitored frequently if lisinopril is administered concomitantly with lithium. Gold: Nitritoid reactions (symptoms include facial flushing, nausea, vomiting and hypotension) have been reported rarely in patients on therapy with injectable gold (sodium aurothiomalate) and concomitant ACE inhibitor therapy including lisinopril and hydrochlorothiazide tablets. mTOR (mammalian target of rapamycin) inhibitors: Patients receiving coadministration of ACE inhibitor and mTOR inhibitor (e.g., temsirolimus, sirolimus, everolimus) therapy may be at increased risk for angioedema. (see WARNINGS ) Neprilysin Inhibitors: Patients taking concomitant neprilysin inhibitors may be at increased risk for angioedema. (see WARNINGS ) Hydrochlorothiazide When administered concurrently the following drugs may interact with thiazide diuretics. Alcohol, barbiturates, or narcotics - potentiation of orthostatic hypotension may occur. Antidiabetic drugs (oral agents and insulin) - dosage adjustment of the antidiabetic drug may be required. Other antihypertensive drugs - additive effect or potentiation. Cholestyramine and colestipol resins - Absorption of hydrochlorothiazide is impaired in the presence of anionic exchange resins. Single doses of either cholestyramine or colestipol resins bind the hydrochlorothiazide and reduce its absorption from the gastrointestinal tract by up to 85% and 43%, respectively. Corticosteroids, ACTH - intensified electrolyte depletion, particularly hypokalemia. Pressor amines (e.g., norepinephrine) - possible decreased response to pressor amines but not sufficient to preclude their use. Skeletal muscle relaxants, nondepolarizing (e.g., tubocurarine) - possible increased responsiveness to the muscle relaxant. Lithium - should not generally be given with diuretics. Diuretic agents reduce the renal clearance of lithium and add a high risk of lithium toxicity. Refer to the package insert for lithium preparations before use of such preparations with lisinopril and hydrochlorothiazide tablets. Non-steroidal Anti-inflammatoryDrugs - In some patients, the administration of a non-steroidal anti-inflammatory agent can reduce the diuretic, natriuretic, and antihypertensive effects of loop, potassium-sparing and thiazide diuretics. Therefore, when lisinopril and hydrochlorothiazide tablets and non-steroidal anti-inflammatory agents are used concomitantly, the patient should be observed closely to determine if the desired effect of lisinopril and hydrochlorothiazide tablets is obtained.", + "severity": "unknown", + "description": "Drug Interactions Lisinopril Hypotension - Patients on Diuretic Therapy: Patients on diuretics and especially those in whom diuretic therapy was recently instituted, may occasionally experience an excessive reduction of blood pressure after initiation of therapy with lisinopril. The possibility of hypotensive effects with lisinopril can be minimized by either discontinuing the diuretic or increasing the salt intake prior to initiation of treatment with lisinopril. If it is necessary to continue the diuretic, initiate therapy with lisinopril at a dose of 5 mg daily, and provide close medical supervision after the initial dose for at least two hours and until blood pressure has stabilized for at least an additional hour. (See WARNINGS , and DOSAGE AND ADMINISTRATION .) When a diuretic is added to the therapy of a patient receiving lisinopril, an additional antihypertensive effect is usually observed (See DOSAGE AND ADMINISTRATION .) Non-steroidal Anti-inflammatory Agents Including Selective Cyclooxygenase-2 (COX-2) Inhibitors: In patients who are elderly, volume-depleted (including those on diuretic therapy), or with compromised renal function, co-administration of NSAIDs, including selective COX-2 inhibitors, with ACE inhibitors, including lisinopril, may result in deterioration of renal function, including possible acute renal failure. These effects are usually reversible. Monitor renal function periodically in patients receiving lisinopril and NSAID therapy. The antihypertensive effect of ACE inhibitors, including lisinopril, may be attenuated by NSAIDs. Dual Blockade of the Renin-Angiotensin System (RAS): Dual blockade of the RAS with angiotensin receptor blockers, ACE inhibitors, or direct renin inhibitors (such as aliskiren) is associated with increased risk of hypotension, syncope, hyperkalemia, and changes in renal function (including acute renal failure) compared to monotherapy. The VA NEPHRON trial enrolled 1448 patients with type 2 diabetes, elevated urinary-albumin-to-creatinine ratio, and decreased estimated glomerular filtration rate (GFR 30 to 89.9 ml/min), randomized them to lisinopril or placebo on a background of losartan therapy and followed them for a median of 2.2 years. Patients receiving the combination of losartan and lisinopril did not obtain any additional benefit compared to monotherapy for the combined endpoint of decline in GFR, end state renal disease, or death, but experienced an increased incidence of hyperkalemia and acute kidney injury compared with the monotherapy group. In general, avoid combined use of RAS inhibitors. Monitor blood pressure, renal function, and electrolytes in patients on lisinopril and hydrochlorothiazide tablets and other agents that affect the RAS. Do not coadminister aliskiren with lisinopril and hydrochlorothiazide tablets in patients with diabetes. Avoid use of aliskiren with PRINZIDE in patients with renal impairment (GFR <60 ml/min). Other Agents: Lisinopril has been used concomitantly with nitrates and/or digoxin without evidence of clinically significant adverse interactions. No meaningful clinically important pharmacokinetic interactions occurred when lisinopril was used concomitantly with propranolol, digoxin, or hydrochlorothiazide. The presence of food in the stomach does not alter the bioavailability of lisinopril. Agents Increasing Serum Potassium: Lisinopril attenuates potassium loss caused by thiazide-type diuretics. Use of lisinopril with potassium-sparing diuretics (e.g., spironolactone, eplerenone, triamterene, or amiloride), potassium supplements, or potassium-containing salt substitutes may lead to significant increases in serum potassium. Therefore, if concomitant use of these agents is indicated, because of demonstrated hypokalemia, they should be used with caution and with frequent monitoring of serum potassium. Lithium: Lithium toxicity has been reported in patients receiving lithium concomitantly with drugs which cause elimination of sodium, including ACE inhibitors. Lithium toxicity was usually reversible upon discontinuation of lithium and the ACE inhibitor. It is recommended that serum lithium levels be monitored frequently if lisinopril is administered concomitantly with lithium. Gold: Nitritoid reactions (symptoms include facial flushing, nausea, vomiting and hypotension) have been reported rarely in patients on therapy with injectable gold (sodium aurothiomalate) and concomitant ACE inhibitor therapy including lisinopril and hydrochlorothiazide tablets. mTOR (mammalian target of rapamycin) inhibitors: Patients receiving coadministration of ACE inhibitor and mTOR inhibitor (e.g., temsirolimus, sirolimus, everolimus) therapy may be at increased risk for angioedema. (see WARNINGS ) Neprilysin Inhibitors: Patients taking concomitant neprilysin inhibitors may be at increased risk for angioedema. (see WARNINGS ) Hydrochlorothiazide When administered concurrently the following drugs may interact with thiazide diuretics. Alcohol, barbiturates, or narcotics - potentiation of orthostatic hypotension may occur. Antidiabetic drugs (oral agents and insulin) - dosage adjustment of the antidiabetic drug may be required. Other antihypertensive drugs - additive effect or potentiation. Cholestyramine and colestipol resins - Absorption of hydrochlorothiazide is impaired in the presence of anionic exchange resins. Single doses of either cholestyramine or colestipol resins bind the hydrochlorothiazide and reduce its absorption from the gastrointestinal tract by up to 85% and 43%, respectively. Corticosteroids, ACTH - intensified electrolyte depletion, particularly hypokalemia. Pressor amines (e.g., norepinephrine) - possible decreased response to pressor amines but not sufficient to preclude their use. Skeletal muscle relaxants, nondepolarizing (e.g., tubocurarine) - possible increased responsiveness to the muscle relaxant. Lithium - should not generally be given with diuretics. Diuretic agents reduce the renal clearance of lithium and add a high risk of lithium toxicity. Refer to the package insert for lithium preparations before use of such preparations with lisinopril and hydrochlorothiazide tablets. Non-steroidal Anti-inflammatoryDrugs - In some patients, the administration of a non-steroidal anti-inflammatory agent can reduce the diuretic, natriuretic, and antihypertensive effects of loop, potassium-sparing and thiazide diuretics. Therefore, when lisinopril and hydrochlorothiazide tablets and non-steroidal anti-inflammatory agents are used concomitantly, the patient should be observed closely to determine if the desired effect of lisinopril and hydrochlorothiazide tablets is obtained." + } + ], + "warnings": [ + "WARNINGS General Lisinopril Anaphylactoid and Possibly Related Reactions: Presumably because angiotensin-converting enzyme inhibitors affect the metabolism of eicosanoids and polypeptides, including endogenous bradykinin, patients receiving ACE inhibitors (including lisinopril and hydrochlorothiazide tablets) may be subject to a variety of adverse reactions, some of them serious. Head and Neck Angioedema: Angioedema of the face, extremities, lips, tongue, glottis and/or larynx has been reported rarely in patients treated with angiotensin converting enzyme inhibitors, including lisinopril. This may occur at any time during treatment. ACE inhibitors have been associated with a higher rate of angioedema in Black than in non-Black patients. In such cases lisinopril and hydrochlorothiazide tablets should be promptly discontinued and appropriate therapy and monitoring should be provided until complete and sustained resolution of signs and symptoms has occurred. Even in those instances where swelling of only the tongue is involved, without respiratory distress, patients may require prolonged observation since treatment with antihistamines and corticosteroids may not be sufficient. Very rarely, fatalities have been reported due to angioedema associated with laryngeal edema or tongue edema. Patients with involvement of the tongue, glottis or larynx are likely to experience airway obstruction, especially those with a history of airway surgery. Where there is involvement of the tongue, glottis or larynx, likely to cause airway obstruction, subcutaneous epinephrine solution 1:1000 (0.3 mL to 0.5 mL) and/or measures necessary to ensure a patent airway should be promptly provided (See ADVERSE REACTIONS . ) Patients with a history of angioedema unrelated to ACE-inhibitor therapy may be at increased risk of angioedema while receiving an ACE inhibitor (see also INDICATIONS AND USAGE and CONTRAINDICATIONS ). Patients receiving coadministration of ACE inhibitor and mTOR (mammalian target of rapamycin) inhibitor (e.g., temsirolimus, sirolimus, everolimus) therapy or a neprilysin inhibitor may be at increased risk for angioedema (see PRECAUTIONS ). Intestinal Angioedema : Intestinal angioedema has been reported in patients treated with ACE inhibitors. These patients presented with abdominal pain (with or without nausea or vomiting); in some cases there was no prior history of facial angioedema and C-1 esterase levels were normal. The angioedema was diagnosed by procedures including abdominal CT scan or ultrasound, or at surgery, and symptoms resolved after stopping the ACE inhibitor. Intestinal angioedema should be included in the differential diagnosis of patients on ACE inhibitors presenting with abdominal pain. Anaphylactoid reactions during desensitization: Two patients undergoing desensitizing treatment with hymenoptera venom while receiving ACE inhibitors sustained life-threatening anaphylactoid reactions. In the same patients, these reactions were avoided when ACE inhibitors were temporarily withheld, but they reappeared upon inadvertent rechallenge. Anaphylactoid reactions during membrane exposure: Sudden and potentially life-threatening anaphylactoid reactions have been reported in some patients dialyzed with high-flux membranes and treated concomitantly with an ACE inhibitor. In such patients, dialysis must be stopped immediately, and aggressive therapy for anaphylactoid reactions must be initiated. Symptoms have not been relieved by antihistamines in these situations. In these patients, consideration should be given to using a different type of dialysis membrane or a different class of antihypertensive agent. Anaphylactoid reactions have also been reported in patients undergoing low-density lipoprotein apheresis with dextran sulfate absorption. Hypotension and Related Effects Excessive hypotension was rarely seen in uncomplicated hypertensive patients but is a possible consequence of lisinopril use in salt/volume-depleted persons such as those treated vigorously with diuretics or patients on dialysis. (See PRECAUTIONS, Drug Interactions and ADVERSE REACTIONS .) Syncope has been reported in 0.8 percent of patients receiving lisinopril and hydrochlorothiazide tablets. In patients with hypertension receiving lisinopril alone, the incidence of syncope was 0.1 percent. The overall incidence of syncope may be reduced by proper titration of the individual components. (See PRECAUTIONS, Drug Interactions , ADVERSE REACTIONS and DOSAGE AND ADMINISTRATION .) In patients with severe congestive heart failure, with or without associated renal insufficiency, excessive hypotension has been observed and may be associated with oliguria and/or progressive azotemia, and rarely with acute renal failure and/or death. Because of the potential fall in blood pressure in these patients, therapy should be started under very close medical supervision. Such patients should be followed closely for the first two weeks of treatment and whenever the dose of lisinopril and/or diuretic is increased. Similar considerations apply to patients with ischemic heart or cerebrovascular disease in whom an excessive fall in blood pressure could result in a myocardial infarction or cerebrovascular accident. If hypotension occurs, the patient should be placed in supine position and, if necessary, receive an intravenous infusion of normal saline. A transient hypotensive response is not a contraindication to further doses which usually can be given without difficulty once the blood pressure has increased after volume expansion. Neutropenia/Agranulocytosis Another angiotensin-converting enzyme inhibitor, captopril, has been shown to cause agranulocytosis and bone marrow depression, rarely in uncomplicated patients but more frequently in patients with renal impairment, especially if they also have a collagen vascular disease. Available data from clinical trials of lisinopril are insufficient to show that lisinopril does not cause agranulocytosis at similar rates. Marketing experience has revealed rare cases of neutropenia and bone marrow depression in which a causal relationship to lisinopril cannot be excluded. Periodic monitoring of white blood cell counts in patients with collagen vascular disease and renal disease should be considered. Hepatic Failure Rarely, ACE inhibitors have been associated with a syndrome that starts with cholestatic jaundice or hepatitis and progresses to fulminant hepatic necrosis, and (sometimes) death. The mechanism of this syndrome is not understood. Patients receiving ACE inhibitors who develop jaundice or marked elevations of hepatic enzymes should discontinue the ACE inhibitor and receive appropriate medical follow-up. Hydrochlorothiazide Thiazides should be used with caution in severe renal disease. In patients with renal disease, thiazides may precipitate azotemia. Cumulative effects of the drug may develop in patients with impaired renal function. Thiazides should be used with caution in patients with impaired hepatic function or progressive liver disease, since minor alterations of fluid and electrolyte balance may precipitate hepatic coma. Sensitivity reactions may occur in patients with or without a history of allergy or bronchial asthma. The possibility of exacerbation or activation of systemic lupus erythematosus has been reported. Lithium generally should not be given with thiazides (see PRECAUTIONS, Drug Interactions , Lisinopril and Hydrochlorothiazide ). Acute Myopia and Secondary Angle-Closure Glaucoma : Hydrochlorothiazide, a sulfonamide, can cause an idiosyncratic reaction, resulting in acute transient myopia and acute angle-closure glaucoma. Symptoms include acute onset of decreased visual acuity or ocular pain and typically occur within hours to weeks of drug initiation. Untreated acute angle-closure glaucoma can lead to permanent vision loss. The primary treatment is to discontinue hydrochlorothiazide as rapidly as possible. Prompt medical or surgical treatments may need to be considered if the intraocular pressure remains uncontrolled. Risk factors for developing acute angle-closure glaucoma may include a history of sulfonamide or penicillin allergy. Fetal Toxicity Use of drugs that act on the renin-angiotensin system during the second and third trimesters of pregnancy reduces fetal renal function and increases fetal and neonatal morbidity and death. Resulting oligohydramnios can be associated with fetal lung hypoplasia and skeletal deformations. Potential neonatal adverse effects include skull hypoplasia, anuria, hypotension, renal failure, and death. When pregnancy is detected, discontinue lisinopril and hydrochlorothiazide tablets as soon as possible. These adverse outcomes are usually associated with the use of these drugs in the second and third trimester of pregnancy. Most epidemiologic studies examining fetal abnormalities after exposure to antihypertensive use in the first trimester have not distinguished drugs affecting the renin-angiotensin system from other antihypertensive agents. Appropriate management of maternal hypertension during pregnancy is important to optimize outcomes for both mother and fetus. In the unusual case that there is no appropriate alternative therapy to drugs affecting the renin-angiotensin system for a particular patient, apprise the mother of the potential risk to the fetus. Perform serial ultrasound examinations to assess the intra-amniotic environment. If oligohydramnios is observed, discontinue lisinopril and hydrochlorothiazide tablets, unless it is considered lifesaving for the mother. Fetal testing may be appropriate, based on the week of pregnancy. Patients and physicians should be aware, however, that oligohydramnios may not appear until after the fetus has sustained irreversible injury. Closely observe infants with histories of in utero exposure to lisinopril and hydrochlorothiazide tablets for hypotension, oliguria, and hyperkalemia ( See PRECAUTIONS, Pediatric Use ). Lisinopril-Hydrochlorothiazide Teratogenicity studies were conducted in mice and rats with up to 90 mg/kg/day of lisinopril in combination with 10 mg/kg/day of hydrochlorothiazide. This dose of lisinopril is 5 times (in mice) and 10 times (in rats) the maximum recommended human daily dose (MRHDD) when compared on a body surface area basis (mg/m 2 ); the dose of hydrochlorothiazide is 0.9 times (in mice) and 1.8 times (in rats) the MRHDD. Maternal or fetotoxic effects were not seen in mice with the combination. In rats decreased maternal weight gain and decreased fetal weight occurred down to 3/10 mg/kg/day (the lowest dose tested). Associated with the decreased fetal weight was a delay in fetal ossification. The decreased fetal weight and delay in fetal ossification were not seen in saline-supplemented animals given 90/10 mg/kg/day. No teratogenic effects of lisinopril were seen in studies of pregnant mice, rats, and rabbits. On a body surface area basis, the doses used were up 55 times, 33 times, and 0.15 times, respectively, the MRHDD. Hydrochlorothiazide Studies in which hydrochlorothiazide was orally administered to pregnant mice and rats during their respective periods of major organogenesis at doses up to 3000 and 1000 mg/kg/day, respectively, provided no evidence of harm to the fetus. These doses are more than 150 times the MRHDD on a body surface area basis. Thiazides cross the placental barrier and appear in cord blood. There is a risk of fetal or neonatal jaundice, thrombocytopenia and possibly other adverse reactions that have occurred in adults." + ], + "sources": [ + { + "name": "openFDA label", + "url": "https://api.fda.gov/drug/label" + }, + { + "name": "openFDA events", + "url": "https://api.fda.gov/drug/event" + } + ], + "rxcui": "29046" + }, + "risk": { + "score": 15, + "factors": [ + "age>=65" + ], + "beers_matches": [] + }, + "analysis": { + "conclusion": "Low risk", + "recommendation": "Likely safe for typical patients.", + "reasons": [ + "1 reported interaction(s)", + "11 documented side effect(s)" + ], + "explanation": "Computed risk score 15; factors: age>=65" + } + } + ] +} \ No newline at end of file diff --git a/AI Drug safety/evaluations/gold_standard_sample.csv b/AI Drug safety/evaluations/gold_standard_sample.csv new file mode 100644 index 0000000..922a5b3 --- /dev/null +++ b/AI Drug safety/evaluations/gold_standard_sample.csv @@ -0,0 +1,6 @@ +id,drug,rxcui,major_interaction,major_interaction_targets,naranjo_score,adverse_outcome,annotator_id,notes +1,aspirin,1191,True,warfarin,5,False,clin1,Verified increased bleeding risk with warfarin in label +2,warfarin,,True,aspirin;ibuprofen,7,True,clin2,High bleed risk; multiple interacting agents +3,ibuprofen,,False,,2,False,clin1,Typical NSAID adverse effects; major interactions are context-dependent +4,metformin,,False,,1,False,clin2,Low interaction profile when used alone +5,lisinopril,,True,spironolactone,3,False,clin1,Hyperkalemia risk with potassium-sparing diuretics \ No newline at end of file diff --git a/AI Drug safety/requirements-streamlit.txt b/AI Drug safety/requirements-streamlit.txt new file mode 100644 index 0000000..e251330 --- /dev/null +++ b/AI Drug safety/requirements-streamlit.txt @@ -0,0 +1 @@ +streamlit \ No newline at end of file diff --git a/AI Drug safety/requirements.txt b/AI Drug safety/requirements.txt new file mode 100644 index 0000000..08845eb --- /dev/null +++ b/AI Drug safety/requirements.txt @@ -0,0 +1,7 @@ +streamlit +fastapi +uvicorn[standard] +langchain +openai +python-dotenv +requests diff --git a/AI Drug safety/run_real_evaluation.py b/AI Drug safety/run_real_evaluation.py new file mode 100644 index 0000000..a9a53fd --- /dev/null +++ b/AI Drug safety/run_real_evaluation.py @@ -0,0 +1,86 @@ +#!/usr/bin/env python3 +"""Simple real-data evaluation harness. + +Runs the agent on a small list of real drugs (or a provided file) using +`use_mock=False` and reports operational metrics useful for clinical +evaluation (mapping coverage, OpenFDA availability, counts of interactions/SEs). + +This is a lightweight smoke/evaluation script — for formal clinical +evaluation follow the spec in the TODOs and create an annotated gold set. +""" +try: + from dotenv import load_dotenv + load_dotenv() +except Exception: + pass + +import argparse +import json +import os +import time +from datetime import datetime + +from ai_drug_safety.agent_chain import run_chain_agent + + +def load_drugs(path: str): + if not path: + return ["aspirin", "warfarin", "ibuprofen", "metformin", "lisinopril"] + if not os.path.exists(path): + raise FileNotFoundError(path) + with open(path, "r", encoding="utf-8") as f: + return [l.strip() for l in f if l.strip()] + + +def summarize(results): + n = len(results) + mapped = sum(1 for r in results if r.get("rxcui")) + openfda_label = sum(1 for r in results if any("openfda" in (s.get("name","") or "").lower() for s in r.get("findings",{}).get("sources",[]))) + any_source = sum(1 for r in results if r.get("findings") and r["findings"].get("sources")) + total_interactions = sum(len(r.get("findings",{}).get("interactions",[])) for r in results) + total_side_effects = sum(len(r.get("findings",{}).get("side_effects",[])) for r in results) + risk_scores = [r.get("risk", {}).get("score") for r in results if r.get("risk") and isinstance(r.get("risk").get("score"), (int, float))] + avg_risk = sum(risk_scores)/len(risk_scores) if risk_scores else None + return { + "n_drugs": n, + "rxcui_mapped": mapped, + "rxcui_coverage": mapped / n if n else 0, + "openfda_label_count": openfda_label, + "any_source_count": any_source, + "avg_interactions_per_drug": total_interactions / n if n else 0, + "avg_side_effects_per_drug": total_side_effects / n if n else 0, + "avg_risk_score": avg_risk, + } + + +def main(): + parser = argparse.ArgumentParser(description="Run a small real-data evaluation.") + parser.add_argument("--drugs-file", help="Optional file with one drug name per line") + parser.add_argument("--out-dir", default="evaluations", help="Directory to write results") + parser.add_argument("--age", type=int, default=65, help="Default patient age for risk scoring") + args = parser.parse_args() + + drugs = load_drugs(args.drugs_file) + os.makedirs(args.out_dir, exist_ok=True) + results = [] + for d in drugs: + print(f"Querying: {d}") + try: + res = run_chain_agent(d, patient_info={"age": args.age, "conditions": []}, use_mock=False) + results.append(res) + except Exception as e: + results.append({"drug": d, "error": str(e)}) + time.sleep(0.2) + + summary = summarize(results) + ts = datetime.utcnow().strftime("%Y%m%dT%H%M%SZ") + out_path = os.path.join(args.out_dir, f"evaluation_real_{ts}.json") + with open(out_path, "w", encoding="utf-8") as f: + json.dump({"summary": summary, "results": results}, f, indent=2, ensure_ascii=False) + + print(json.dumps(summary, indent=2, ensure_ascii=False)) + print(f"Saved full results to: {out_path}") + + +if __name__ == "__main__": + main() diff --git a/AI Drug safety/run_web.py b/AI Drug safety/run_web.py new file mode 100644 index 0000000..efe4c80 --- /dev/null +++ b/AI Drug safety/run_web.py @@ -0,0 +1,11 @@ +try: + from dotenv import load_dotenv + + load_dotenv() +except Exception: + pass + +import uvicorn + +if __name__ == "__main__": + uvicorn.run("ai_drug_safety.web:app", host="127.0.0.1", port=8000, reload=False) diff --git a/AI Drug safety/scripts/convert_adjudicated_to_gold.py b/AI Drug safety/scripts/convert_adjudicated_to_gold.py new file mode 100644 index 0000000..e498c55 --- /dev/null +++ b/AI Drug safety/scripts/convert_adjudicated_to_gold.py @@ -0,0 +1,47 @@ +#!/usr/bin/env python3 +"""Convert adjudicated adjudicated_gold_*.csv to a simple gold-standard CSV +compatible with `ai_drug_safety.evaluation`. + +Writes `evaluations/adjudicated_gold_converted.csv` with fields: +id,drug,rxcui,major_interaction,naranjo_score,adverse_outcome,notes +""" +import glob +import csv +import os + + +def find_latest_adjudicated(directory="evaluations"): + pattern = os.path.join(directory, "adjudicated_gold_*.csv") + files = glob.glob(pattern) + if not files: + raise FileNotFoundError("No adjudicated_gold_*.csv files found in evaluations/") + files.sort() + return files[-1] + + +def convert(inpath, outpath): + with open(inpath, newline='', encoding='utf-8') as inf: + reader = csv.DictReader(inf) + rows = list(reader) + + with open(outpath, 'w', newline='', encoding='utf-8') as outf: + fieldnames = ['id', 'drug', 'rxcui', 'major_interaction', 'naranjo_score', 'adverse_outcome', 'notes'] + writer = csv.DictWriter(outf, fieldnames=fieldnames) + writer.writeheader() + for r in rows: + writer.writerow({ + 'id': r.get('id',''), + 'drug': r.get('drug',''), + 'rxcui': r.get('rxcui',''), + 'major_interaction': r.get('major_interaction_final',''), + 'naranjo_score': '', + 'adverse_outcome': r.get('adverse_outcome_final',''), + 'notes': (r.get('notes_A','') + ' | ' + r.get('notes_B','')).strip(' | '), + }) + + +if __name__ == '__main__': + latest = find_latest_adjudicated() + out = os.path.join('evaluations', 'adjudicated_gold_converted.csv') + convert(latest, out) + print(f'Wrote converted gold: {out}') diff --git a/AI Drug safety/streamlit_app.py b/AI Drug safety/streamlit_app.py new file mode 100644 index 0000000..84b3202 --- /dev/null +++ b/AI Drug safety/streamlit_app.py @@ -0,0 +1,240 @@ +#!/usr/bin/env python3 +"""Streamlit UI for AI Drug Safety testing. + +Run with: + python -m pip install -r requirements-streamlit.txt + streamlit run streamlit_app.py + +This provides a single/multiple-drug and batch tester that calls the project's +`run_chain_agent` function and shows structured results. +""" +import os +import json +import time +import re +from typing import List + +import streamlit as st + +from ai_drug_safety.agent_chain import run_chain_agent + +try: + from dotenv import load_dotenv + load_dotenv() +except Exception: + pass + + +def model_flags_major(res: dict) -> bool: + findings = res.get("findings") or {} + interactions = findings.get("interactions") or [] + for i in interactions: + try: + if isinstance(i, dict): + sev = (i.get("severity") or "").lower() + desc = (i.get("description") or "").lower() + if "major" in sev or "major" in desc or "severe" in desc or "life-threatening" in desc: + return True + else: + text = str(i).lower() + if "major" in text or "severe" in text: + return True + except Exception: + continue + return False + + +def analyze_drug(drug: str, patient_info: dict, use_mock: bool): + return run_chain_agent(drug, patient_info=patient_info, use_mock=use_mock) + + +def analyze_batch(drugs: List[str], patient_info: dict, use_mock: bool): + results = [] + progress = st.progress(0) + for i, d in enumerate(drugs): + with st.spinner(f"Analyzing {d} ({i+1}/{len(drugs)})"): + try: + r = analyze_drug(d, patient_info, use_mock) + except Exception as e: + r = {"drug": d, "error": str(e)} + results.append(r) + progress.progress((i + 1) / len(drugs)) + return results + + +def parse_drugs_input(text: str) -> List[str]: + if not text: + return [] + parts = re.split(r"[;,\n]+", text) + return [p.strip() for p in parts if p.strip()] + + +def main(): + st.set_page_config(page_title="AI Drug Safety — Tester", layout="wide") + st.title("AI Drug Safety — Test Interface") + st.markdown("Enter one or multiple drug names (comma/semicolon/newline separated) to analyze side effects, interactions, and clinical risk scores.") + + col1, col2 = st.columns([2, 1]) + with col1: + mode = st.radio("Mode", ("Single/Multiple", "Batch")) + uploaded = None + if mode == "Single/Multiple": + drug_input = st.text_input("Drug name(s) — comma/semicolon/newline separated", value="aspirin") + uploaded = st.file_uploader("Or upload a text/CSV file (one drug per line)", type=["txt", "csv"]) + if uploaded is not None: + try: + drug_input = uploaded.read().decode("utf-8") + except Exception: + st.error("Unable to read uploaded file.") + else: + drugs_text = st.text_area("Paste one drug per line", height=200) + uploaded = st.file_uploader("Or upload a text/CSV file (one drug per line)", type=["txt", "csv"]) + if uploaded is not None: + try: + drugs_text = uploaded.read().decode("utf-8") + except Exception: + st.error("Unable to read uploaded file.") + + with col2: + age = st.number_input("Patient age", min_value=0, max_value=130, value=65) + sex = st.radio("Sex", ("female", "male"), index=0) + conditions_raw = st.text_input("Patient conditions (comma-separated)", value="") + + with st.expander("Patient flags (optional)"): + hypertension = st.checkbox("Hypertension", value=False) + renal = st.checkbox("Renal dysfunction", value=False) + liver = st.checkbox("Liver disease", value=False) + stroke_history = st.checkbox("Stroke/TIA history", value=False) + bleeding_history = st.checkbox("Bleeding history", value=False) + labile_inr = st.checkbox("Labile INR", value=False) + drugs_or_alcohol = st.checkbox("Drugs or alcohol risk", value=False) + + # Force real data usage (openFDA / real sources). Mock is disabled by request. + use_mock = False + llm_enabled = bool(os.getenv("OPENAI_API_KEY")) + if llm_enabled: + st.success("LLM enabled (OPENAI_API_KEY found).") + else: + st.warning("LLM disabled (OPENAI_API_KEY not set). Using deterministic reasoning.") + run_btn = st.button("Analyze") + + conditions = [c.strip() for c in conditions_raw.split(",") if c.strip()] + + # Build patient_info mapping from UI + patient_info = { + "age": age, + "sex": sex, + "conditions": conditions, + "hypertension": hypertension if 'hypertension' in locals() else False, + "renal_dysfunction": renal if 'renal' in locals() else False, + "liver_disease": liver if 'liver' in locals() else False, + "stroke": stroke_history if 'stroke_history' in locals() else False, + "bleeding_history": bleeding_history if 'bleeding_history' in locals() else False, + "labile_inr": labile_inr if 'labile_inr' in locals() else False, + "concomitant_antiplatelet": drugs_or_alcohol if 'drugs_or_alcohol' in locals() else False, + "alcohol_use": drugs_or_alcohol if 'drugs_or_alcohol' in locals() else False, + # `elderly` is derived from `age` (age >= 65) in clinical scoring; do not collect as a separate flag here. + } + + if run_btn: + if mode == "Single/Multiple": + drugs = parse_drugs_input(drug_input) + if not drugs: + st.error("Please enter at least one drug name.") + elif len(drugs) == 1: + d = drugs[0] + with st.spinner("Analyzing..."): + try: + res = analyze_drug(d, patient_info, use_mock) + except Exception as e: + st.exception(e) + return + + st.subheader("Result") + st.write("Drug:", res.get("drug")) + st.write("RXCUI:", res.get("rxcui")) + sources = [(s.get("name") or "") for s in (res.get("findings") or {}).get("sources", [])] + st.write("Sources:", sources) + + with st.expander("Full JSON"): + st.json(res) + + risk = res.get("risk") or {} + if risk.get("clinical"): + st.subheader("Clinical Risk Scores") + st.write(risk.get("clinical")) + + findings = res.get("findings") or {} + st.subheader("Side Effects") + st.write(findings.get("side_effects") or []) + st.subheader("Interactions") + st.write(findings.get("interactions") or []) + else: + results = analyze_batch(drugs, patient_info, use_mock) + st.success("Multi-drug analysis complete.") + + table = [] + for r in results: + table.append({ + "drug": r.get("drug"), + "rxcui": r.get("rxcui"), + "major_flag": model_flags_major(r), + "has_sources": bool((r.get("findings") or {}).get("sources")), + }) + + st.subheader("Summary") + st.dataframe(table) + + with st.expander("Full results (JSON)"): + st.json(results) + + if st.button("Save results to evaluations"): + os.makedirs("evaluations", exist_ok=True) + ts = time.strftime("%Y%m%dT%H%M%SZ", time.gmtime()) + outp = os.path.join("evaluations", f"evaluation_streamlit_{ts}.json") + with open(outp, "w", encoding="utf-8") as f: + json.dump({"summary": {"n_drugs": len(drugs)}, "results": results}, f, indent=2, ensure_ascii=False) + st.success(f"Saved to {outp}") + else: + # Batch path (paste/upload) + drugs = [] + if 'drugs_text' in locals(): + drugs = [d.strip() for d in (drugs_text or "").splitlines() if d.strip()] + if uploaded is not None and not drugs: + try: + content = uploaded.read().decode("utf-8") + drugs = [d.strip() for d in content.splitlines() if d.strip()] + except Exception: + pass + if not drugs: + st.error("Please paste or upload a list of drugs for batch mode.") + else: + results = analyze_batch(drugs, patient_info, use_mock) + st.success("Batch analysis complete.") + + table = [] + for r in results: + table.append({ + "drug": r.get("drug"), + "rxcui": r.get("rxcui"), + "major_flag": model_flags_major(r), + "has_sources": bool((r.get("findings") or {}).get("sources")), + }) + + st.subheader("Summary") + st.dataframe(table) + + with st.expander("Full results (JSON)"): + st.json(results) + + if st.button("Save results to evaluations"): + os.makedirs("evaluations", exist_ok=True) + ts = time.strftime("%Y%m%dT%H%M%SZ", time.gmtime()) + outp = os.path.join("evaluations", f"evaluation_streamlit_{ts}.json") + with open(outp, "w", encoding="utf-8") as f: + json.dump({"summary": {"n_drugs": len(drugs)}, "results": results}, f, indent=2, ensure_ascii=False) + st.success(f"Saved to {outp}") + + +if __name__ == "__main__": + main() From d1d5cc8dd2abbf75783aeac489e8d5eec033d5da Mon Sep 17 00:00:00 2001 From: Ahmed Aziz Ammar <129754857+ahmed200346@users.noreply.github.com> Date: Sun, 3 May 2026 20:37:31 +0200 Subject: [PATCH 2/2] Update README branche 3D Printer Wiss --- README.md | 78 +++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 76 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 3980a84..2ba202c 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,76 @@ -# DX-LAB -An Agentic System for Drug Discovery: A virtual laboratory that integrates multiple AI agents to discover, design, evaluate, and optimize drug candidates—without physical experimentation. +# AI Drug Safety Agent + +This repository contains a Python scaffold for an AI Drug Safety Agent that +fetches real drug data (openFDA / optional DrugBank), computes clinical risk +scores (HAS-BLED, Tisdale), and synthesizes a concise clinical recommendation +using an LLM when available. + +Requirements +------------ + +Install dependencies into a virtual environment (recommended): + +```bash +python -m venv .venv +source .venv/bin/activate # or .venv\Scripts\activate on Windows +pip install -r requirements.txt +``` + +Quick start +----------- + +Run the Streamlit tester UI: + +```bash +streamlit run streamlit_app.py +``` + +Run the FastAPI web shim (optional): + +```bash +``` + +CLI example: + +```bash +python -m ai_drug_safety.cli aspirin --age 70 --conditions "hypertension" +``` + +Environment / secrets +--------------------- + +Set `OPENAI_API_KEY` and `DRUGBANK_API_KEY` in your local environment or a +`.env` file at the project root. IMPORTANT: do not commit secrets. The +repository `.gitignore` contains `.env` and `.venv` entries — ensure you do not +push actual API keys to GitHub. + +What I changed for you +---------------------- + +- Added `requirements.txt` with core dependencies. +- Updated the Streamlit UI to accept `sex` and relevant clinical flags. +- Improved the LLM prompt to request `confidence` and `assumptions` fields + and explicitly consider `patient.sex` when relevant. + +What to keep (recommended) +-------------------------- + +- `ai_drug_safety/` — core package and clinical logic (keep). +- `evaluations/` — adjudicated gold and annotation CSVs (keep for evaluation). +- `streamlit_app.py` — interactive tester UI (keep if you use it). + +Optional files you can remove +---------------------------- + +- `run_example.py` — tiny demo script (non-essential). +- `.cache/` — cache files from previous runs (safe to remove if you want a clean repo). +- `__pycache__/` directories — Python bytecode caches (safe to remove). + +Pushing to GitHub +------------------ + +Before you push, ensure: +- `.env` does not contain secrets (remove or redact keys). +- `.venv/` is not committed (use `.gitignore`). + +If you want, I can remove non-essential files now (e.g., `run_example.py`, `.cache/`).