
Tooluniverse Adverse Event Detection
- 321 installs
- 1.6k repo stars
- Updated August 4, 2026
- mims-harvard/tooluniverse
tooluniverse-adverse-event-detection is a biomedical agent skill that detects and quantifies adverse drug event signals from FDA FAERS data with PRR, ROR, and IC statistics for developers building automated pharmacovigil
About
tooluniverse-adverse-event-detection is a mims-harvard/ToolUniverse skill that runs a nine-phase pharmacovigilance pipeline for post-market drug safety. It resolves drugs to ChEMBL and DrugBank IDs, profiles FAERS reactions, calculates disproportionality metrics (PRR, ROR, IC) with 95% confidence intervals for at least 15–20 top adverse events, mines FDA labels, compares class safety, queries literature, and outputs a quantitative Safety Signal Score from 0–100 with T1–T4 evidence grading. Signal criteria require PRR ≥ 2.0, lower CI > 1.0, and N ≥ 3. Bundled references include PHASE_DETAILS.md, REPORT_TEMPLATE.md, TOOL_REFERENCE.md, and QUICK_START.md plus a test_adverse_event_detection.py file. Use it when agents must produce regulatory-grade safety signal reports rather than informal drug side-effect summaries.
- Pharmacovigilance signal detection
- Adverse event entity extraction
- Safety database querying
- Harvard ToolUniverse tool wrappers
- Automated pharmacology safety review
Tooluniverse Adverse Event Detection by the numbers
- 321 all-time installs (skills.sh)
- +6 installs in the week ending Aug 4, 2026 (Skillselion tracking)
- Ranked #2,232 of 16,546 AI & Agent Building skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/mims-harvard/tooluniverse --skill tooluniverse-adverse-event-detectionAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 321 |
|---|---|
| repo stars | ★ 1.6k |
| Last updated | August 4, 2026 |
| Repository | mims-harvard/tooluniverse ↗ |
How do you detect adverse drug event signals from FAERS?
Equip coding agents to detect, query, and summarize adverse drug events from pharmacovigilance sources, safety corpora, and biomedical APIs during automated R&D and safety review workflows.
Who is it for?
Biomedical engineers and computational safety analysts building agent-driven post-market surveillance, drug safety assessment, or regulatory decision-support workflows.
Skip if: Clinical trial adverse-event coding during live patient care, or teams needing only informal drug side-effect summaries without statistical signal detection.
When should I use this skill?
User asks for FAERS safety signals, pharmacovigilance reports, disproportionality analysis, or comparative drug adverse-event profiling
What you get
Pharmacovigilance markdown reports, disproportionality statistics, Safety Signal Scores (0–100), and T1–T4 evidence grades
- safety signal reports
- disproportionality tables
- Safety Signal Scores
By the numbers
- Runs a 9-phase pharmacovigilance workflow
- Safety Signal Score ranges 0–100 across 4 weighted components
- Analyzes at least 15–20 top adverse events per drug with PRR/ROR/IC
Files
COMPUTE, DON'T DESCRIBE
When analysis requires computation (statistics, data processing, scoring, enrichment), write and run Python code via Bash. Don't describe what you would do — execute it and report actual results. Use ToolUniverse tools to retrieve data, then Python (pandas, scipy, statsmodels, matplotlib) to analyze it.
Adverse Drug Event Signal Detection & Analysis
Automated pipeline for detecting, quantifying, and contextualizing adverse drug event signals using FAERS disproportionality analysis, FDA label mining, mechanism-based prediction, and literature evidence. Produces a quantitative Safety Signal Score (0-100) for regulatory and clinical decision-making.
KEY PRINCIPLES: 1. Signal quantification first - Every adverse event must have PRR/ROR/IC with confidence intervals 2. Serious events priority - Deaths, hospitalizations, life-threatening events always analyzed first 3. Multi-source triangulation - FAERS + FDA labels + OpenTargets + DrugBank + literature 4. Context-aware assessment - Distinguish drug-specific vs class-wide vs confounding signals 5. Report-first approach - Create report file FIRST, update progressively 6. Evidence grading mandatory - T1 (regulatory/boxed warning) through T4 (computational) 7. English-first queries - Always use English drug names in tool calls, respond in user's language
REASONING STRATEGY — Start Here: Start with the signal: What adverse event was reported more than expected? (PRR >= 2.0, N >= 3, lower CI > 1.0 is the threshold). Then ask three questions in order: 1. Biologically plausible? Given the drug's mechanism of action and targets, does this adverse event make sense? An off-target kinase inhibitor causing cardiac events is plausible; a topical agent causing systemic toxicity needs more scrutiny. LOOK UP DON'T GUESS — use OpenTargets_get_drug_mechanisms_of_action_by_chemblId and drugbank_get_targets_by_drug_name_or_drugbank_id to check targets before asserting plausibility. 2. Timing consistent? Acute reactions (within hours/days) suggest immune or direct pharmacologic mechanism. Delayed reactions (weeks/months) suggest cumulative toxicity or idiosyncratic response. Check FAERS time-to-onset distribution. 3. Could confounders explain it? Patients taking this drug likely have the underlying disease — compare against background rate in that population, not the general population. Class-wide signals (appearing for all drugs in the class) suggest mechanism-based rather than molecule-specific toxicity.
Causality Assessment — Naranjo Algorithm Reasoning: When determining whether an adverse event is drug-caused (not just associated), apply these steps systematically. LOOK UP DON'T GUESS — search FAERS and FDA labels for each criterion: 1. Prior reports? Are there previous conclusive reports of this reaction? Check FDA label (FDA_get_adverse_reactions_by_drug_name) and literature (PubMed_search_articles). Yes = +1. 2. Temporal relationship? Did the AE appear after drug administration? Onset within expected pharmacokinetic window (1-5 half-lives) = +2. Use FAERS_stratify_by_demographics for time-to-onset data. 3. Dechallenge? Did the AE improve when the drug was stopped? Positive dechallenge = +1. Look for rechallenge/dechallenge case reports in literature. 4. Rechallenge? Did the AE reappear when the drug was restarted? Positive rechallenge = +2 (strongest single piece of evidence for causality). 5. Alternative causes? Could the underlying disease, concomitant drugs, or other factors explain the AE? Check drugbank_get_drug_interactions_by_drug_name_or_id for interacting drugs. 6. Dose-response? Did the reaction worsen with higher doses or improve with lower doses? Dose-dependent AEs suggest on-target toxicity. 7. Drug level confirmation? Was the drug detected in body fluids at toxic concentrations?
- Score: Definite (>=9), Probable (5-8), Possible (1-4), Doubtful (<=0).
- Even without individual patient data, you can estimate causality from aggregate FAERS signals + label evidence + mechanistic plausibility.
Reference files (in this directory):
PHASE_DETAILS.md- Detailed tool calls, code examples, and output templates per phaseREPORT_TEMPLATE.md- Full report template and completeness checklistTOOL_REFERENCE.md- Tool parameter reference and fallback chainsQUICK_START.md- Quick examples and common drug names
---
When to Use
Apply when user asks:
- "What are the safety signals for [drug]?"
- "Detect adverse events for [drug]"
- "Is [drug] associated with [adverse event]?"
- "What are the FAERS signals for [drug]?"
- "Compare safety of [drug A] vs [drug B] for [adverse event]"
- "What are the serious adverse events for [drug]?"
- "Are there emerging safety signals for [drug]?"
- "Post-market surveillance report for [drug]"
- "Pharmacovigilance signal detection for [drug]"
Differentiation from tooluniverse-pharmacovigilance: This skill focuses specifically on signal detection and quantification using disproportionality analysis (PRR, ROR, IC) with statistical rigor, produces a quantitative Safety Signal Score (0-100), and performs comparative safety analysis across drug classes.
---
Workflow Overview
Phase 0: Input Parsing & Drug Disambiguation
Parse drug name, resolve to ChEMBL ID, DrugBank ID
Identify drug class, mechanism, and approved indications
|
Phase 1: FAERS Adverse Event Profiling
Top adverse events by frequency
Seriousness and outcome distributions
Demographics (age, sex, country)
|
Phase 2: Disproportionality Analysis (Signal Detection)
Calculate PRR, ROR, IC with 95% CI for each AE
Apply signal detection criteria
Classify signal strength (Strong/Moderate/Weak/None)
|
Phase 3: FDA Label Safety Information
Boxed warnings, contraindications
Warnings and precautions, adverse reactions
Drug interactions, special populations
|
Phase 4: Mechanism-Based Adverse Event Context
Target-based AE prediction (OpenTargets safety)
Off-target effects, ADMET predictions
Drug class effects comparison
|
Phase 5: Comparative Safety Analysis
Compare to drugs in same class
Identify unique vs class-wide signals
Head-to-head disproportionality comparison
|
Phase 6: Drug-Drug Interactions & Risk Factors
Known DDIs causing AEs
Pharmacogenomic risk factors (PharmGKB)
FDA PGx biomarkers
|
Phase 7: Literature Evidence
PubMed safety studies, case reports
OpenAlex citation analysis
Preprint emerging signals (EuropePMC)
|
Phase 8: Risk Assessment & Safety Signal Score
Calculate Safety Signal Score (0-100)
Evidence grading (T1-T4) for each signal
Clinical significance assessment
|
Phase 9: Report Synthesis & Recommendations
Monitoring recommendations
Risk mitigation strategies
Completeness checklist---
Phase Summaries
Phase 0: Input Parsing & Drug Disambiguation
Resolve drug name to ChEMBL ID, DrugBank ID. Get mechanism of action, blackbox warning status, targets, and approved indications.
- Tools:
OpenTargets_get_drug_chembId_by_generic_name,OpenTargets_get_drug_mechanisms_of_action_by_chemblId,OpenTargets_get_drug_blackbox_status_by_chembl_ID,drugbank_get_safety_by_drug_name_or_drugbank_id,drugbank_get_targets_by_drug_name_or_drugbank_id,OpenTargets_get_drug_indications_by_chemblId
Phase 1: FAERS Adverse Event Profiling
Query FAERS for top adverse events, seriousness distribution, outcomes, demographics, and death-related events. Filter serious events by type (death, hospitalization, life-threatening). Get MedDRA hierarchy rollup.
- Tools:
FAERS_count_reactions_by_drug_event,FAERS_count_seriousness_by_drug_event,FAERS_count_outcomes_by_drug_event,FAERS_count_patient_age_distribution,FAERS_count_death_related_by_drug,FAERS_count_reportercountry_by_drug_event,FAERS_filter_serious_events,FAERS_rollup_meddra_hierarchy
Phase 2: Disproportionality Analysis (Signal Detection)
CRITICAL PHASE. For each top adverse event (at least 15-20), calculate PRR, ROR, IC with 95% CI. Classify signal strength. Stratify strong signals by demographics.
- Tools:
FAERS_calculate_disproportionality,FAERS_stratify_by_demographics - MedDRA term level note:
FAERS_count_reactions_by_drug_eventfilters by MedDRA Lowest Level Term (reactionmeddraverse) whileFAERS_calculate_disproportionalityuses Preferred Terms. Case counts can differ dramatically — always use disproportionality analysis as the primary signal metric, not raw counts. - Signal criteria: PRR >= 2.0 AND lower CI > 1.0 AND N >= 3
- Strength: Strong (PRR >= 5), Moderate (PRR 3-5), Weak (PRR 2-3)
- See
PHASE_DETAILS.mdfor full signal classification table
Phase 3: FDA Label Safety Information
Extract boxed warnings, contraindications, warnings/precautions, adverse reactions, drug interactions, and special population info. Note: {error: {code: "NOT_FOUND"}} is normal when a section does not exist.
- Tools:
FDA_get_boxed_warning_info_by_drug_name,FDA_get_contraindications_by_drug_name,FDA_get_warnings_by_drug_name,FDA_get_adverse_reactions_by_drug_name,FDA_get_drug_interactions_by_drug_name,FDA_get_pregnancy_or_breastfeeding_info_by_drug_name,FDA_get_geriatric_use_info_by_drug_name,FDA_get_pediatric_use_info_by_drug_name,FDA_get_pharmacogenomics_info_by_drug_name
Phase 4: Mechanism-Based Adverse Event Context
Get target safety profile, OpenTargets adverse events, ADMET toxicity predictions (if SMILES available), and drug warnings.
- Tools:
OpenTargets_get_target_safety_profile_by_ensemblID,OpenTargets_get_drug_adverse_events_by_chemblId,ADMETAI_predict_toxicity,ADMETAI_predict_CYP_interactions,OpenTargets_get_drug_warnings_by_chemblId
Phase 5: Comparative Safety Analysis
Head-to-head comparison with class members using FAERS_compare_drugs. Aggregate class AEs. Identify class-wide vs drug-specific signals.
- Tools:
FAERS_compare_drugs,FAERS_count_additive_adverse_reactions,FAERS_count_additive_seriousness_classification
Phase 6: Drug-Drug Interactions & Risk Factors
Extract DDIs from FDA label, DrugBank, and DailyMed. Query PharmGKB for pharmacogenomic risk factors and dosing guidelines. Check FDA PGx biomarkers.
- Tools:
FDA_get_drug_interactions_by_drug_name,drugbank_get_drug_interactions_by_drug_name_or_id,DailyMed_parse_drug_interactions,PharmGKB_search_drugs,PharmGKB_get_drug_details,PharmGKB_get_dosing_guidelines,fda_pharmacogenomic_biomarkers
Phase 7: Literature Evidence
Search PubMed, OpenAlex, and EuropePMC for safety studies, case reports, and preprints.
- Tools:
PubMed_search_articles,openalex_search_works,EuropePMC_search_articles
Phase 8: Risk Assessment & Safety Signal Score
Calculate Safety Signal Score (0-100) from four components: FAERS signal strength (0-35), serious AEs (0-30), FDA label warnings (0-25), literature evidence (0-10). Grade each signal T1-T4. See PHASE_DETAILS.md for scoring rubric.
Phase 9: Report Synthesis
Generate comprehensive markdown report with executive summary, all phase outputs, monitoring recommendations, risk mitigation strategies, patient counseling points, and completeness checklist. See REPORT_TEMPLATE.md for full template.
---
Edge Cases
- No FAERS reports: Skip Phases 1-2; rely on FDA label, mechanism predictions, literature
- Generic vs Brand name: Try both in FAERS; use
OpenTargets_get_drug_chembId_by_generic_nameto resolve - Drug combinations: Use
FAERS_count_additive_adverse_reactionsfor aggregate class analysis - Confounding by indication: Compare AE profile to the disease being treated; note limitation in report
- Drugs with boxed warnings: Score component automatically 25/25 for label warnings; prioritize boxed warning events
Adverse Event Detection - Phase Details
Detailed tool calls, code examples, and output templates for each analysis phase.
---
Phase 0: Input Parsing & Drug Disambiguation
0.1 Resolve Drug Identity
# Step 1: Get ChEMBL ID from drug name
chembl_result = tu.tools.OpenTargets_get_drug_chembId_by_generic_name(drugName="atorvastatin")
# Response: {data: {search: {hits: [{id: "CHEMBL1487", name: "ATORVASTATIN", description: "..."}]}}}
chembl_id = chembl_result['data']['search']['hits'][0]['id'] # "CHEMBL1487"
# Step 2: Get drug mechanism of action
moa = tu.tools.OpenTargets_get_drug_mechanisms_of_action_by_chemblId(chemblId=chembl_id)
# Response: {data: {drug: {mechanismsOfAction: {rows: [{mechanismOfAction: "HMG-CoA reductase inhibitor", actionType: "INHIBITOR", targetName: "...", targets: [{id: "ENSG00000113161", approvedSymbol: "HMGCR"}]}]}}}}
# Step 3: Get blackbox warning status
blackbox = tu.tools.OpenTargets_get_drug_blackbox_status_by_chembl_ID(chemblId=chembl_id)
# Response: {data: {drug: {name: "ATORVASTATIN", hasBeenWithdrawn: false, blackBoxWarning: false}}}
# Step 4: Get DrugBank info (safety, toxicity)
drugbank = tu.tools.drugbank_get_safety_by_drug_name_or_drugbank_id(
query="atorvastatin", case_sensitive=False, exact_match=False, limit=3
)
# Response: {results: [{drug_name: "Atorvastatin", drugbank_id: "DB01076", toxicity: "...", food_interactions: "..."}]}
# Step 5: Get DrugBank targets
targets = tu.tools.drugbank_get_targets_by_drug_name_or_drugbank_id(
query="atorvastatin", case_sensitive=False, exact_match=False, limit=3
)
# Step 6: Get approved indications
indications = tu.tools.OpenTargets_get_drug_indications_by_chemblId(chemblId=chembl_id)0.2 Output for Report
## 1. Drug Identification
| Property | Value |
|----------|-------|
| **Generic Name** | Atorvastatin |
| **ChEMBL ID** | CHEMBL1487 |
| **DrugBank ID** | DB01076 |
| **Drug Class** | HMG-CoA reductase inhibitor (Statin) |
| **Mechanism** | HMG-CoA reductase inhibitor (target: HMGCR) |
| **Primary Target** | HMGCR (ENSG00000113161) |
| **Black Box Warning** | No |
| **Withdrawn** | No |
*Source: OpenTargets, DrugBank*---
Phase 1: FAERS Adverse Event Profiling
1.1 Query FAERS for Adverse Events
# Get top adverse event reactions (returns list of {term, count})
reactions = tu.tools.FAERS_count_reactions_by_drug_event(medicinalproduct="ATORVASTATIN")
# Get seriousness classification
seriousness = tu.tools.FAERS_count_seriousness_by_drug_event(medicinalproduct="ATORVASTATIN")
# Get outcome distribution
outcomes = tu.tools.FAERS_count_outcomes_by_drug_event(medicinalproduct="ATORVASTATIN")
# Get age distribution
age_dist = tu.tools.FAERS_count_patient_age_distribution(medicinalproduct="ATORVASTATIN")
# Get death-related events
deaths = tu.tools.FAERS_count_death_related_by_drug(medicinalproduct="ATORVASTATIN")
# Get reporter country distribution
countries = tu.tools.FAERS_count_reportercountry_by_drug_event(medicinalproduct="ATORVASTATIN")1.2 Get Serious Events Breakdown
# Filter serious events - all types
serious_all = tu.tools.FAERS_filter_serious_events(
operation="filter_serious_events",
drug_name="ATORVASTATIN",
seriousness_type="all"
)
# Death-related serious events
serious_death = tu.tools.FAERS_filter_serious_events(
operation="filter_serious_events",
drug_name="ATORVASTATIN",
seriousness_type="death"
)
# Hospitalization-related
serious_hosp = tu.tools.FAERS_filter_serious_events(
operation="filter_serious_events",
drug_name="ATORVASTATIN",
seriousness_type="hospitalization"
)
# Life-threatening
serious_lt = tu.tools.FAERS_filter_serious_events(
operation="filter_serious_events",
drug_name="ATORVASTATIN",
seriousness_type="life_threatening"
)1.3 MedDRA Hierarchy Rollup
meddra = tu.tools.FAERS_rollup_meddra_hierarchy(
operation="rollup_meddra_hierarchy",
drug_name="ATORVASTATIN"
)1.4 Output for Report
## 2. FAERS Adverse Event Profile
### 2.1 Overview
- **Total reports**: 326,261 (Serious: 242,757 | Non-serious: 83,504)
- **Fatal outcomes**: 22,128
- **Primary reporter countries**: US (170,963), GB (40,079), CA (16,492)
### 2.2 Top 10 Adverse Events by Frequency
| Rank | Adverse Event | Reports | % of Total |
|------|---------------|---------|------------|
| 1 | Fatigue | 19,171 | 5.9% |
| 2 | Diarrhoea | 17,127 | 5.2% |
| ... | ... | ... | ... |
### 2.3 Outcome Distribution
| Outcome | Count | Percentage |
|---------|-------|------------|
| Unknown | 162,310 | 39.6% |
| Fatal | 22,128 | 5.4% |
### 2.4 Age Distribution
| Age Group | Reports | Percentage |
|-----------|---------|------------|
| Elderly | 38,510 | 61.3% |
| Adult | 24,302 | 38.7% |
*Source: FAERS via FAERS_count_reactions_by_drug_event, FAERS_count_seriousness_by_drug_event*---
Phase 2: Disproportionality Analysis (Signal Detection)
2.1 Calculate Signal Metrics
CRITICAL: This is the core of the skill. For each top adverse event (at least top 15-20), calculate PRR, ROR, and IC with 95% confidence intervals.
top_events = ["Rhabdomyolysis", "Myalgia", "Hepatotoxicity", "Diabetes mellitus",
"Acute kidney injury", "Myopathy", "Pancreatitis"]
for event in top_events:
result = tu.tools.FAERS_calculate_disproportionality(
operation="calculate_disproportionality",
drug_name="ATORVASTATIN",
adverse_event=event
)
# Response structure:
# {
# status: "success",
# drug_name: "ATORVASTATIN",
# adverse_event: "Rhabdomyolysis",
# contingency_table: {a_drug_and_event, b_drug_no_event, c_no_drug_event, d_no_drug_no_event},
# metrics: {
# ROR: {value: 4.825, ci_95_lower: 4.622, ci_95_upper: 5.037},
# PRR: {value: 4.79, ci_95_lower: 4.59, ci_95_upper: 4.998},
# IC: {value: 2.194, ci_95_lower: 2.136, ci_95_upper: 2.252}
# },
# signal_detection: {signal_detected: true, signal_strength: "Strong signal", criteria: "..."}
# }2.2 Signal Detection Criteria
Proportional Reporting Ratio (PRR):
- PRR = (a/(a+b)) / (c/(c+d))
- Signal: PRR >= 2.0 AND lower 95% CI > 1.0 AND case count >= 3
Reporting Odds Ratio (ROR):
- ROR = (ad) / (bc)
- Signal: Lower 95% CI > 1.0
Information Component (IC):
- IC = log2(observed/expected)
- Signal: Lower 95% CI > 0
2.3 Signal Strength Classification
| Strength | PRR | ROR Lower CI | IC Lower CI | Clinical Action |
|---|---|---|---|---|
| Strong | >= 5.0 | >= 3.0 | >= 2.0 | Immediate investigation required |
| Moderate | 3.0-4.9 | 2.0-2.9 | 1.0-1.9 | Active monitoring recommended |
| Weak | 2.0-2.9 | 1.0-1.9 | 0-0.9 | Routine monitoring, watch for trends |
| No signal | < 2.0 | < 1.0 | < 0 | Standard pharmacovigilance |
2.4 Demographic Stratification of Key Signals
result = tu.tools.FAERS_stratify_by_demographics(
operation="stratify_by_demographics",
drug_name="ATORVASTATIN",
adverse_event="Rhabdomyolysis",
stratify_by="sex" # Options: sex, age, country
)Note on sex codes: group 0 = Unknown, group 1 = Male, group 2 = Female.
2.5 Output for Report
## 3. Disproportionality Analysis (Signal Detection)
### 3.1 Signal Detection Summary
| Adverse Event | Cases (a) | PRR | PRR 95% CI | ROR | ROR 95% CI | IC | Signal |
|---------------|-----------|-----|------------|-----|------------|-----|--------|
| Rhabdomyolysis | 2,226 | 4.79 | 4.59-5.00 | 4.83 | 4.62-5.04 | 2.19 | **STRONG** |
| Myopathy | 1,234 | 6.12 | 5.72-6.55 | 6.18 | 5.77-6.62 | 2.54 | **STRONG** |
*Source: FAERS via FAERS_calculate_disproportionality, FAERS_stratify_by_demographics*---
Phase 3: FDA Label Safety Information
3.1 Extract Label Sections
boxed = tu.tools.FDA_get_boxed_warning_info_by_drug_name(drug_name="atorvastatin")
contras = tu.tools.FDA_get_contraindications_by_drug_name(drug_name="atorvastatin")
warnings = tu.tools.FDA_get_warnings_by_drug_name(drug_name="atorvastatin")
adverse_rxns = tu.tools.FDA_get_adverse_reactions_by_drug_name(drug_name="atorvastatin")
interactions = tu.tools.FDA_get_drug_interactions_by_drug_name(drug_name="atorvastatin")
pregnancy = tu.tools.FDA_get_pregnancy_or_breastfeeding_info_by_drug_name(drug_name="atorvastatin")
geriatric = tu.tools.FDA_get_geriatric_use_info_by_drug_name(drug_name="atorvastatin")
pediatric = tu.tools.FDA_get_pediatric_use_info_by_drug_name(drug_name="atorvastatin")
pgx_label = tu.tools.FDA_get_pharmacogenomics_info_by_drug_name(drug_name="atorvastatin")3.2 Handling No Results
IMPORTANT: FDA label tools return {error: {code: "NOT_FOUND"}} when a section does not exist. This is NORMAL for many drugs.
if isinstance(boxed, dict) and 'error' in boxed:
boxed_warning_text = "None (no boxed warning for this drug)"
else:
boxed_warning_text = boxed['results'][0].get('boxed_warning', ['None'])[0]---
Phase 4: Mechanism-Based Adverse Event Context
4.1 Target Safety Profile
target_id = "ENSG00000113161" # HMGCR from Phase 0
safety = tu.tools.OpenTargets_get_target_safety_profile_by_ensemblID(ensemblId=target_id)
ot_aes = tu.tools.OpenTargets_get_drug_adverse_events_by_chemblId(chemblId="CHEMBL1487")4.2 ADMET Predictions (if SMILES available)
toxicity = tu.tools.ADMETAI_predict_toxicity(smiles=[smiles])
cyp = tu.tools.ADMETAI_predict_CYP_interactions(smiles=[smiles])4.3 Drug Warnings from OpenTargets
warnings = tu.tools.OpenTargets_get_drug_warnings_by_chemblId(chemblId="CHEMBL1487")---
Phase 5: Comparative Safety Analysis
5.1 Compare to Drug Class
comparison = tu.tools.FAERS_compare_drugs(
operation="compare_drugs",
drug1="ATORVASTATIN",
drug2="SIMVASTATIN",
adverse_event="Rhabdomyolysis"
)
# Aggregate adverse events across drug class
class_drugs = ["ATORVASTATIN", "SIMVASTATIN", "ROSUVASTATIN", "PRAVASTATIN"]
class_aes = tu.tools.FAERS_count_additive_adverse_reactions(
medicinalproducts=class_drugs
)
class_serious = tu.tools.FAERS_count_additive_seriousness_classification(
medicinalproducts=class_drugs
)---
Phase 6: Drug-Drug Interactions & Risk Factors
6.1 Drug-Drug Interactions
ddi_label = tu.tools.FDA_get_drug_interactions_by_drug_name(drug_name="atorvastatin")
ddi_db = tu.tools.drugbank_get_drug_interactions_by_drug_name_or_id(
query="atorvastatin", case_sensitive=False, exact_match=False, limit=3
)
ddi_dailymed = tu.tools.DailyMed_parse_drug_interactions(drug_name="atorvastatin")6.2 Pharmacogenomic Risk Factors
pgx_search = tu.tools.PharmGKB_search_drugs(query="atorvastatin")
pgx_details = tu.tools.PharmGKB_get_drug_details(drug_id="PA448500")
dosing = tu.tools.PharmGKB_get_dosing_guidelines(gene="SLCO1B1")
fda_pgx = tu.tools.fda_pharmacogenomic_biomarkers(drug_name="atorvastatin", limit=10)---
Phase 7: Literature Evidence
7.1 Search Published Literature
pubmed = tu.tools.PubMed_search_articles(
query='atorvastatin adverse events safety rhabdomyolysis',
limit=20
)
openalex = tu.tools.openalex_search_works(
query="atorvastatin safety adverse events",
limit=15
)
preprints = tu.tools.EuropePMC_search_articles(
query="atorvastatin safety signal",
source="PPR",
pageSize=10
)---
Phase 8: Risk Assessment & Safety Signal Score
8.1 Safety Signal Score Calculation (0-100)
Component 1: FAERS Signal Strength (0-35 points)
If any signal has PRR >= 5 AND ROR lower CI >= 3: 35 points
If any signal has PRR 3-5 AND ROR lower CI 2-3: 20 points
If any signal has PRR 2-3 AND ROR lower CI 1-2: 10 points
If no signals detected: 0 pointsComponent 2: Serious Adverse Events (0-30 points)
Deaths reported with high count (>100): 30 points
Deaths reported with low count (1-100): 25 points
Life-threatening events: 20 points
Hospitalizations only: 15 points
Non-serious only: 0 pointsComponent 3: FDA Label Warnings (0-25 points)
Boxed warning present: 25 points
Drug withdrawn or restricted: 25 points
Contraindications present: 15 points
Warnings and precautions: 10 points
Adverse reactions only: 5 points
No label warnings: 0 pointsComponent 4: Literature Evidence (0-10 points)
Meta-analyses confirming safety signals: 10 points
Multiple RCTs with safety concerns: 7 points
Case reports/case series: 4 points
No published safety concerns: 0 pointsTotal Score Interpretation:
| Score Range | Interpretation | Action |
|---|---|---|
| 75-100 | High concern | Serious safety signals; requires immediate regulatory attention |
| 50-74 | Moderate concern | Significant monitoring needed; consider risk mitigation |
| 25-49 | Low-moderate concern | Routine enhanced monitoring; standard risk management |
| 0-24 | Low concern | Standard safety profile; routine pharmacovigilance |
8.2 Evidence Grading
| Tier | Criteria | Example |
|---|---|---|
| T1 | Boxed warning, confirmed by RCTs, PRR > 10 | Metformin: Lactic acidosis |
| T2 | Label warning + FAERS signal (PRR 3-10) + published studies | Atorvastatin: Rhabdomyolysis |
| T3 | FAERS signal (PRR 2-3) + case reports | Atorvastatin: Pancreatitis |
| T4 | Computational prediction only (ADMET) or weak signal | ADMETAI hepatotoxicity prediction |
Adverse Drug Event Signal Detection - Quick Start
What This Skill Does
Detects and quantifies adverse drug event signals using FDA FAERS disproportionality analysis (PRR, ROR, IC), FDA label mining, mechanism-based prediction, and literature evidence. Produces a Safety Signal Score (0-100) for clinical and regulatory decision-making.
Quick Examples
Example 1: Full Safety Signal Report
"Detect adverse event signals for atorvastatin"
The skill will: 1. Resolve atorvastatin to CHEMBL1487 / DB01076 2. Query FAERS for top adverse events (fatigue, diarrhoea, myalgia, rhabdomyolysis, ...) 3. Calculate PRR/ROR/IC for each significant event with 95% CI 4. Extract FDA label warnings and contraindications 5. Compare to statin class (simvastatin, rosuvastatin, pravastatin) 6. Check PharmGKB for pharmacogenomic risk factors (SLCO1B1) 7. Search PubMed for safety literature 8. Calculate Safety Signal Score (e.g., 62/100 = Moderate Concern) 9. Generate comprehensive markdown report
Example 2: Specific Adverse Event Investigation
"Is pembrolizumab associated with myocarditis? Calculate the disproportionality."
The skill will focus on: 1. Resolve pembrolizumab to ChEMBL ID 2. Run FAERS_calculate_disproportionality(drug_name="PEMBROLIZUMAB", adverse_event="Myocarditis") 3. Get PRR, ROR, IC with 95% CI and signal strength 4. Stratify by demographics (age, sex) 5. Check FDA label for myocarditis warnings 6. Search literature for immune checkpoint inhibitor cardiotoxicity
Example 3: Drug Class Comparison
"Compare safety of apixaban vs warfarin for bleeding events"
The skill will: 1. Run FAERS_compare_drugs(drug1="APIXABAN", drug2="WARFARIN", adverse_event="Haemorrhage") 2. Compare PRR/ROR/IC side-by-side 3. Repeat for related events: GI haemorrhage, intracranial haemorrhage, epistaxis 4. Generate comparative safety table
Example 4: Emerging Signal Detection
"Are there emerging safety signals for semaglutide not in the label?"
The skill will: 1. Get all FAERS signals for semaglutide 2. Calculate disproportionality for top 20 events 3. Extract all FDA label warnings 4. Identify signals in FAERS that are NOT in the label = potential emerging signals 5. Search recent literature and preprints for confirmation 6. Rank by signal strength and case count
Example 5: Post-Market Surveillance Report
"Generate a pharmacovigilance signal detection report for methotrexate"
Produces full 10-section report with:
- Executive summary
- FAERS profile with >15 adverse events
- PRR/ROR/IC for all significant events
- FDA label safety extraction
- Target safety profile
- Drug class comparison
- PGx risk factors
- Literature evidence
- Safety Signal Score
- Clinical recommendations
Key Outputs
1. Safety Signal Score (0-100): Quantitative risk metric
- 75-100: High concern
- 50-74: Moderate concern
- 25-49: Low-moderate concern
- 0-24: Low concern
2. Disproportionality Table: PRR, ROR, IC with 95% CI for each AE
3. Signal Strength Classification: Strong/Moderate/Weak/None for each event
4. Evidence Grading: T1 (regulatory) to T4 (computational) for each finding
5. Markdown Report: Saved to [DRUG]_adverse_event_report.md
Common Drug Names to Try
| Drug | Class | Expected Key Signals |
|---|---|---|
| Atorvastatin | Statin | Rhabdomyolysis, myalgia, hepatotoxicity |
| Metformin | Biguanide | Lactic acidosis |
| Warfarin | Anticoagulant | Hemorrhage |
| Pembrolizumab | Anti-PD-1 | Immune-mediated AEs (colitis, pneumonitis) |
| Doxorubicin | Anthracycline | Cardiotoxicity |
| Methotrexate | Antimetabolite | Hepatotoxicity, pancytopenia |
| Ibuprofen | NSAID | GI bleeding, renal injury |
Tips
- Use generic names in UPPERCASE for FAERS queries (e.g., "ATORVASTATIN")
- MedDRA terms are required for specific event queries (e.g., "Rhabdomyolysis", not "muscle breakdown")
- PRR >= 2.0 with lower CI > 1.0 and N >= 3 indicates a signal
- PRR >= 5.0 indicates a strong signal requiring investigation
- Check FDA label to distinguish known vs novel signals
- Drug class comparison helps determine if signal is drug-specific or class-wide
Adverse Event Detection - Report Template
Report File Structure
File: [DRUG]_adverse_event_report.md
# Adverse Drug Event Signal Detection Report: [DRUG]
**Generated**: [Date] | **Drug**: [Generic Name] | **ChEMBL ID**: [ID]
**Safety Signal Score**: [XX/100] ([INTERPRETATION])
---
## Executive Summary
[2-3 paragraph summary of key findings]
**Key Safety Signals**:
1. [Strongest signal with PRR/ROR]
2. [Second signal]
3. [Third signal]
**Regulatory Status**: [Boxed warning Y/N] | [Withdrawn Y/N] | [Restrictions]
---
## 1. Drug Identification
[Phase 0 output]
## 2. FAERS Adverse Event Profile
[Phase 1 output]
## 3. Disproportionality Analysis
[Phase 2 output]
## 4. FDA Label Safety Information
[Phase 3 output]
## 5. Mechanism-Based Context
[Phase 4 output]
## 6. Comparative Safety Analysis
[Phase 5 output]
## 7. Drug-Drug Interactions & PGx Risk
[Phase 6 output]
## 8. Literature Evidence
[Phase 7 output]
## 9. Risk Assessment
[Phase 8 output]
## 10. Clinical Recommendations
### 10.1 Monitoring Recommendations
| Parameter | Frequency | Rationale |
|-----------|-----------|-----------|
| [Lab test] | [Frequency] | [Why] |
### 10.2 Risk Mitigation Strategies
| Risk | Mitigation | Evidence |
|------|-----------|----------|
| [Risk] | [Strategy] | [Source] |
### 10.3 Patient Counseling Points
- [Point 1]
- [Point 2]
### 10.4 Populations at Higher Risk
| Population | Risk Factor | Recommendation |
|-----------|-------------|----------------|
| [Group] | [Factor] | [Action] |
---
## 11. Completeness Checklist
[See below]
## 12. Data Sources
[All tools and databases used with timestamps]---
Completeness Checklist
Phase 0: Drug Disambiguation
- [ ] Generic name resolved
- [ ] ChEMBL ID obtained
- [ ] DrugBank ID obtained
- [ ] Drug class identified
- [ ] Mechanism of action stated
- [ ] Primary target identified
- [ ] Blackbox/withdrawal status checked
Phase 1: FAERS Profiling
- [ ] Top adverse events queried (>=15 events)
- [ ] Seriousness distribution obtained
- [ ] Outcome distribution obtained
- [ ] Age distribution obtained
- [ ] Death-related events counted
- [ ] Reporter country distribution obtained
Phase 2: Disproportionality Analysis
- [ ] PRR calculated for >= 10 adverse events
- [ ] ROR with 95% CI for each event
- [ ] IC with 95% CI for each event
- [ ] Signal strength classified for each
- [ ] Demographics stratified for strong signals
Phase 3: FDA Label
- [ ] Boxed warnings checked (or confirmed none)
- [ ] Contraindications extracted
- [ ] Warnings and precautions extracted
- [ ] Adverse reactions from label
- [ ] Drug interactions from label
- [ ] Special populations (pregnancy, geriatric, pediatric)
Phase 4: Mechanism Context
- [ ] Target safety profile (OpenTargets)
- [ ] OpenTargets adverse events queried
- [ ] ADMET predictions (if SMILES available)
Phase 5: Comparative Analysis
- [ ] At least 1 class comparison performed
- [ ] Class-wide vs drug-specific signals identified
- [ ] Aggregate class AEs computed (if applicable)
Phase 6: DDIs & PGx
- [ ] DDIs from FDA label extracted
- [ ] PharmGKB queried
- [ ] Dosing guidelines checked
- [ ] FDA PGx biomarkers checked
Phase 7: Literature
- [ ] PubMed searched (>=10 articles)
- [ ] OpenAlex citation analysis (if time permits)
- [ ] Key safety publications cited
Phase 8: Risk Assessment
- [ ] Safety Signal Score calculated (0-100)
- [ ] Each signal evidence-graded (T1-T4)
- [ ] Score interpretation provided
Phase 9: Report
- [ ] Report file created and saved
- [ ] Executive summary written
- [ ] Monitoring recommendations provided
- [ ] Risk mitigation strategies listed
- [ ] Patient counseling points included
- [ ] All sources cited
#!/usr/bin/env python3
"""
Comprehensive Test Suite for Adverse Drug Event Signal Detection Skill
Tests all phases of the workflow with real drugs:
- Atorvastatin (statin, common drug with many reports)
- Metformin (biguanide, boxed warning drug)
- Pembrolizumab (immunotherapy, biologic)
- Ibuprofen (NSAID, OTC drug)
Run: python3 test_adverse_event_detection.py
"""
import json
import sys
import time
import traceback
# Track results
results = []
total_tests = 0
passed_tests = 0
failed_tests = 0
start_time = time.time()
def run_test(test_name, test_func):
"""Run a single test and track results."""
global total_tests, passed_tests, failed_tests
total_tests += 1
print(f"\n{'='*70}")
print(f"TEST {total_tests}: {test_name}")
print(f"{'='*70}")
try:
test_func()
passed_tests += 1
results.append((test_name, "PASS", ""))
print(f" RESULT: PASS")
except Exception as e:
failed_tests += 1
tb = traceback.format_exc()
results.append((test_name, "FAIL", str(e)))
print(f" RESULT: FAIL - {e}")
print(f" Traceback:\n{tb}")
def setup():
"""Initialize ToolUniverse."""
from tooluniverse import ToolUniverse
tu = ToolUniverse()
tu.load_tools()
return tu
# Initialize once
print("Initializing ToolUniverse...")
tu = setup()
print(f"Loaded {len(tu.all_tool_dict)} tools")
# ============================================================================
# PHASE 0: Drug Disambiguation Tests
# ============================================================================
def test_01_drug_disambiguation_atorvastatin():
"""Phase 0: Resolve atorvastatin to ChEMBL ID."""
result = tu.tools.OpenTargets_get_drug_chembId_by_generic_name(drugName="atorvastatin")
assert isinstance(result, dict), f"Expected dict, got {type(result)}"
assert "data" in result, f"Missing 'data' key in {result.keys()}"
hits = result["data"]["search"]["hits"]
assert len(hits) > 0, "No hits found for atorvastatin"
chembl_id = hits[0]["id"]
assert chembl_id == "CHEMBL1487", f"Expected CHEMBL1487, got {chembl_id}"
print(f" ChEMBL ID: {chembl_id}")
print(f" Name: {hits[0]['name']}")
def test_02_drug_blackbox_status():
"""Phase 0: Check blackbox/withdrawal status."""
result = tu.tools.OpenTargets_get_drug_blackbox_status_by_chembl_ID(chemblId="CHEMBL1487")
assert isinstance(result, dict), f"Expected dict, got {type(result)}"
drug = result["data"]["drug"]
assert drug["name"] == "ATORVASTATIN", f"Wrong drug: {drug['name']}"
assert drug["blackBoxWarning"] is False, "Atorvastatin should not have blackbox warning"
assert drug["hasBeenWithdrawn"] is False, "Atorvastatin should not be withdrawn"
print(f" Drug: {drug['name']}")
print(f" Blackbox: {drug['blackBoxWarning']}")
print(f" Withdrawn: {drug['hasBeenWithdrawn']}")
def test_03_drug_mechanism_of_action():
"""Phase 0: Get mechanism of action."""
result = tu.tools.OpenTargets_get_drug_mechanisms_of_action_by_chemblId(chemblId="CHEMBL1487")
assert isinstance(result, dict), f"Expected dict, got {type(result)}"
rows = result["data"]["drug"]["mechanismsOfAction"]["rows"]
assert len(rows) > 0, "No MOA found"
moa = rows[0]
assert "HMG-CoA reductase" in moa["mechanismOfAction"], f"Unexpected MOA: {moa['mechanismOfAction']}"
assert moa["actionType"] == "INHIBITOR", f"Expected INHIBITOR, got {moa['actionType']}"
targets = moa.get("targets", [])
assert len(targets) > 0, "No targets found"
print(f" MOA: {moa['mechanismOfAction']}")
print(f" Action: {moa['actionType']}")
print(f" Target: {targets[0]['approvedSymbol']} ({targets[0]['id']})")
def test_04_drugbank_safety():
"""Phase 0: Get DrugBank safety/toxicity info."""
result = tu.tools.drugbank_get_safety_by_drug_name_or_drugbank_id(
query="atorvastatin", case_sensitive=False, exact_match=False, limit=3
)
assert isinstance(result, dict), f"Expected dict, got {type(result)}"
assert result["total_matches"] >= 1, "No DrugBank matches found"
drug = result["results"][0]
assert drug["drug_name"] == "Atorvastatin", f"Wrong drug: {drug['drug_name']}"
assert drug["drugbank_id"] == "DB01076", f"Wrong DrugBank ID: {drug['drugbank_id']}"
assert len(drug["toxicity"]) > 0, "Empty toxicity field"
print(f" DrugBank ID: {drug['drugbank_id']}")
print(f" Toxicity: {drug['toxicity'][:150]}...")
# ============================================================================
# PHASE 1: FAERS Adverse Event Profiling Tests
# ============================================================================
def test_05_faers_top_reactions():
"""Phase 1: Get top adverse event reactions from FAERS."""
result = tu.tools.FAERS_count_reactions_by_drug_event(medicinalproduct="ATORVASTATIN")
assert isinstance(result, list), f"Expected list, got {type(result)}"
assert len(result) >= 10, f"Expected >= 10 reactions, got {len(result)}"
# Verify structure
first = result[0]
assert "term" in first, f"Missing 'term' key"
assert "count" in first, f"Missing 'count' key"
assert isinstance(first["count"], int), f"Count should be int, got {type(first['count'])}"
# Verify myalgia is present (known statin AE)
terms = [r["term"].upper() for r in result]
assert any("MYALGIA" in t for t in terms), f"Expected MYALGIA in results, got top terms: {terms[:10]}"
print(f" Total reactions returned: {len(result)}")
print(f" Top 5: {[(r['term'], r['count']) for r in result[:5]]}")
def test_06_faers_seriousness():
"""Phase 1: Get seriousness distribution."""
result = tu.tools.FAERS_count_seriousness_by_drug_event(medicinalproduct="ATORVASTATIN")
assert isinstance(result, list), f"Expected list, got {type(result)}"
assert len(result) >= 2, f"Expected at least 2 categories (Serious/Non-serious)"
terms = {r["term"]: r["count"] for r in result}
assert "Serious" in terms, "Missing 'Serious' category"
assert "Non-serious" in terms, "Missing 'Non-serious' category"
assert terms["Serious"] > 0, "Serious count should be > 0"
print(f" Serious: {terms['Serious']}")
print(f" Non-serious: {terms['Non-serious']}")
print(f" Serious %: {terms['Serious'] / (terms['Serious'] + terms['Non-serious']) * 100:.1f}%")
def test_07_faers_outcomes():
"""Phase 1: Get outcome distribution."""
result = tu.tools.FAERS_count_outcomes_by_drug_event(medicinalproduct="ATORVASTATIN")
assert isinstance(result, list), f"Expected list, got {type(result)}"
terms = {r["term"]: r["count"] for r in result}
assert "Fatal" in terms, "Missing 'Fatal' outcome"
assert terms["Fatal"] > 0, "Fatal count should be > 0 for atorvastatin"
print(f" Outcomes: {json.dumps(terms, indent=2)}")
def test_08_faers_age_distribution():
"""Phase 1: Get age distribution."""
result = tu.tools.FAERS_count_patient_age_distribution(medicinalproduct="ATORVASTATIN")
assert isinstance(result, list), f"Expected list, got {type(result)}"
terms = {r["term"]: r["count"] for r in result}
assert "Elderly" in terms or "Adult" in terms, "Missing expected age groups"
print(f" Age distribution: {json.dumps(terms, indent=2)}")
def test_09_faers_death_counts():
"""Phase 1: Get death-related counts."""
result = tu.tools.FAERS_count_death_related_by_drug(medicinalproduct="ATORVASTATIN")
assert isinstance(result, list), f"Expected list, got {type(result)}"
terms = {r["term"]: r["count"] for r in result}
assert "death" in terms, "Missing 'death' category"
assert "alive" in terms, "Missing 'alive' category"
print(f" Alive: {terms['alive']}")
print(f" Death: {terms['death']}")
def test_10_faers_reporter_country():
"""Phase 1: Get reporter country distribution."""
result = tu.tools.FAERS_count_reportercountry_by_drug_event(medicinalproduct="ATORVASTATIN")
assert isinstance(result, list), f"Expected list, got {type(result)}"
assert len(result) >= 5, "Expected multiple countries"
# US should be top reporter
top_country = result[0]["term"]
assert top_country == "US", f"Expected US as top reporter, got {top_country}"
print(f" Top 5 countries: {[(r['term'], r['count']) for r in result[:5]]}")
# ============================================================================
# PHASE 2: Disproportionality Analysis Tests
# ============================================================================
def test_11_disproportionality_rhabdomyolysis():
"""Phase 2: Calculate PRR/ROR/IC for atorvastatin + rhabdomyolysis."""
result = tu.tools.FAERS_calculate_disproportionality(
operation="calculate_disproportionality",
drug_name="ATORVASTATIN",
adverse_event="Rhabdomyolysis"
)
assert isinstance(result, dict), f"Expected dict, got {type(result)}"
assert result.get("status") == "success", f"Status not success: {result.get('status')}"
# Check contingency table
ct = result["contingency_table"]
assert ct["a_drug_and_event"] > 0, "a should be > 0"
assert ct["b_drug_no_event"] > 0, "b should be > 0"
# Check metrics
metrics = result["metrics"]
assert "PRR" in metrics, "Missing PRR"
assert "ROR" in metrics, "Missing ROR"
assert "IC" in metrics, "Missing IC"
prr = metrics["PRR"]["value"]
ror = metrics["ROR"]["value"]
ic = metrics["IC"]["value"]
assert prr > 2.0, f"PRR should be > 2 for rhabdomyolysis, got {prr}"
assert ror > 2.0, f"ROR should be > 2 for rhabdomyolysis, got {ror}"
assert ic > 0, f"IC should be > 0 for rhabdomyolysis, got {ic}"
# Check CI
assert metrics["PRR"]["ci_95_lower"] > 1.0, "PRR lower CI should be > 1"
assert metrics["ROR"]["ci_95_lower"] > 1.0, "ROR lower CI should be > 1"
# Check signal detection
sd = result["signal_detection"]
assert sd["signal_detected"] is True, "Signal should be detected"
assert "Strong" in sd["signal_strength"], f"Expected strong signal, got {sd['signal_strength']}"
print(f" PRR: {prr:.3f} (CI: {metrics['PRR']['ci_95_lower']:.3f}-{metrics['PRR']['ci_95_upper']:.3f})")
print(f" ROR: {ror:.3f} (CI: {metrics['ROR']['ci_95_lower']:.3f}-{metrics['ROR']['ci_95_upper']:.3f})")
print(f" IC: {ic:.3f} (CI: {metrics['IC']['ci_95_lower']:.3f}-{metrics['IC']['ci_95_upper']:.3f})")
print(f" Signal: {sd['signal_strength']}")
def test_12_disproportionality_myalgia():
"""Phase 2: Calculate PRR/ROR/IC for atorvastatin + myalgia."""
result = tu.tools.FAERS_calculate_disproportionality(
operation="calculate_disproportionality",
drug_name="ATORVASTATIN",
adverse_event="Myalgia"
)
assert result.get("status") == "success", f"Status not success"
prr = result["metrics"]["PRR"]["value"]
assert prr > 1.0, f"PRR should be > 1 for myalgia, got {prr}"
sd = result["signal_detection"]
assert sd["signal_detected"] is True, "Signal should be detected for myalgia"
print(f" PRR: {prr:.3f}")
print(f" Signal: {sd['signal_strength']}")
def test_13_serious_events_death():
"""Phase 2: Filter serious events (death type)."""
result = tu.tools.FAERS_filter_serious_events(
operation="filter_serious_events",
drug_name="ATORVASTATIN",
seriousness_type="death"
)
assert isinstance(result, dict), f"Expected dict, got {type(result)}"
assert result.get("status") == "success", f"Status not success"
assert result["total_serious_events"] > 0, "Should have death-related events"
assert len(result["top_serious_reactions"]) > 0, "Should have top reactions"
print(f" Total death-related events: {result['total_serious_events']}")
print(f" Top 3: {[(r['reaction'], r['count']) for r in result['top_serious_reactions'][:3]]}")
def test_14_demographic_stratification():
"""Phase 2: Stratify rhabdomyolysis by sex."""
result = tu.tools.FAERS_stratify_by_demographics(
operation="stratify_by_demographics",
drug_name="ATORVASTATIN",
adverse_event="Rhabdomyolysis",
stratify_by="sex"
)
assert isinstance(result, dict), f"Expected dict, got {type(result)}"
assert result.get("status") == "success", f"Status not success"
assert result["total_reports"] > 0, "Should have reports"
strat = result["stratification"]
assert len(strat) >= 2, "Should have at least 2 groups (male/female)"
print(f" Total reports: {result['total_reports']}")
for s in strat:
sex_label = {0: "Unknown", 1: "Male", 2: "Female"}.get(s["group"], f"Code {s['group']}")
print(f" {sex_label}: {s['count']} ({s['percentage']:.1f}%)")
def test_15_meddra_rollup():
"""Phase 2: MedDRA hierarchy rollup."""
result = tu.tools.FAERS_rollup_meddra_hierarchy(
operation="rollup_meddra_hierarchy",
drug_name="ATORVASTATIN"
)
assert isinstance(result, dict), f"Expected dict, got {type(result)}"
assert result.get("status") == "success", f"Status not success"
pt_level = result["meddra_hierarchy"]["PT_level"]
assert len(pt_level) >= 10, f"Expected >= 10 PTs, got {len(pt_level)}"
print(f" Total PTs: {len(pt_level)}")
print(f" Top 5: {[(p['preferred_term'], p['count']) for p in pt_level[:5]]}")
# ============================================================================
# PHASE 3: FDA Label Tests
# ============================================================================
def test_16_fda_boxed_warning_none():
"""Phase 3: Verify atorvastatin has no boxed warning."""
result = tu.tools.FDA_get_boxed_warning_info_by_drug_name(drug_name="atorvastatin")
# Atorvastatin should NOT have a boxed warning
assert isinstance(result, dict), f"Expected dict, got {type(result)}"
if "error" in result:
assert result["error"]["code"] == "NOT_FOUND", f"Unexpected error: {result['error']}"
print(" Confirmed: No boxed warning (NOT_FOUND)")
else:
# Some formulations might return results - check content
print(f" Results returned: {result.get('meta', {}).get('total', 0)}")
def test_17_fda_boxed_warning_present():
"""Phase 3: Verify a drug WITH boxed warning (metformin)."""
result = tu.tools.FDA_get_boxed_warning_info_by_drug_name(drug_name="metformin")
assert isinstance(result, dict), f"Expected dict, got {type(result)}"
if "results" in result and len(result["results"]) > 0:
bw = result["results"][0].get("boxed_warning", [])
if bw:
assert "lactic acidosis" in bw[0].lower() or "metformin" in bw[0].lower(), \
f"Expected lactic acidosis warning, got: {bw[0][:100]}"
print(f" Boxed warning found: {bw[0][:150]}...")
else:
print(" No boxed_warning field in results (metformin may have changed labels)")
else:
print(" Note: Metformin boxed warning not found via this API endpoint")
def test_18_fda_contraindications():
"""Phase 3: Get contraindications."""
result = tu.tools.FDA_get_contraindications_by_drug_name(drug_name="atorvastatin")
assert isinstance(result, dict), f"Expected dict, got {type(result)}"
assert result["meta"]["total"] > 0, "Should have contraindication results"
text = result["results"][0]["contraindications"][0].lower()
assert "liver" in text or "hypersensitivity" in text, f"Expected liver/hypersensitivity, got: {text[:200]}"
print(f" Results: {result['meta']['total']}")
print(f" Text: {text[:200]}...")
def test_19_fda_adverse_reactions():
"""Phase 3: Get adverse reactions from label."""
result = tu.tools.FDA_get_adverse_reactions_by_drug_name(drug_name="atorvastatin")
assert isinstance(result, dict), f"Expected dict, got {type(result)}"
assert result["meta"]["total"] > 0, "Should have adverse reaction results"
text = result["results"][0]["adverse_reactions"][0].lower()
assert "myopathy" in text or "rhabdomyolysis" in text or "adverse" in text, \
f"Expected safety terms in label, got: {text[:200]}"
print(f" Results: {result['meta']['total']}")
print(f" Preview: {text[:200]}...")
def test_20_fda_drug_interactions():
"""Phase 3: Get drug interactions from label."""
result = tu.tools.FDA_get_drug_interactions_by_drug_name(drug_name="atorvastatin")
assert isinstance(result, dict), f"Expected dict, got {type(result)}"
assert result["meta"]["total"] > 0, "Should have drug interaction results"
text = result["results"][0]["drug_interactions"][0].lower()
assert "cyp3a4" in text or "cyclosporine" in text or "interaction" in text, \
f"Expected CYP3A4 or cyclosporine, got: {text[:200]}"
print(f" Results: {result['meta']['total']}")
print(f" Preview: {text[:200]}...")
# ============================================================================
# PHASE 4: Mechanism-Based Context Tests
# ============================================================================
def test_21_opentargets_adverse_events():
"""Phase 4: Get OpenTargets adverse events (FAERS-based significance)."""
result = tu.tools.OpenTargets_get_drug_adverse_events_by_chemblId(chemblId="CHEMBL1487")
assert isinstance(result, dict), f"Expected dict, got {type(result)}"
ae = result["data"]["drug"]["adverseEvents"]
assert ae["count"] > 0, "Should have adverse events"
rows = ae["rows"]
assert len(rows) > 0, "Should have AE rows"
# Check structure
first = rows[0]
assert "name" in first, "Missing 'name' field"
assert "meddraCode" in first, "Missing 'meddraCode' field"
assert "count" in first, "Missing 'count' field"
assert "logLR" in first, "Missing 'logLR' field"
# Myalgia should be top
names = [r["name"] for r in rows]
assert "myalgia" in names or "rhabdomyolysis" in names, \
f"Expected myalgia/rhabdomyolysis, got: {names}"
print(f" Count: {ae['count']}")
top3 = [(r['name'], r['count'], round(r['logLR'], 1)) for r in rows[:3]]
print(f" Top 3: {top3}")
def test_22_target_safety_profile():
"""Phase 4: Get target safety profile for HMGCR."""
result = tu.tools.OpenTargets_get_target_safety_profile_by_ensemblID(ensemblId="ENSG00000113161")
assert isinstance(result, dict), f"Expected dict, got {type(result)}"
target = result["data"]["target"]
assert target["approvedSymbol"] == "HMGCR", f"Wrong target: {target['approvedSymbol']}"
liabilities = target.get("safetyLiabilities", [])
print(f" Target: {target['approvedSymbol']}")
print(f" Safety liabilities: {len(liabilities)}")
for sl in liabilities[:3]:
print(f" - {sl['event']} ({sl['datasource']})")
def test_23_admet_toxicity():
"""Phase 4: Predict toxicity using ADMETAI."""
# Atorvastatin SMILES
smiles = "CC(C)C1=C(C(=C(N1CC[C@H](C[C@H](CC(=O)O)O)O)C2=CC=C(C=C2)F)C3=CC=CC=C3)C(=O)NC4=CC=CC=C4"
result = tu.tools.ADMETAI_predict_toxicity(smiles=[smiles])
assert result is not None, "ADMETAI returned None"
# ADMETAI results may vary in structure
print(f" Result type: {type(result)}")
if isinstance(result, dict):
print(f" Keys: {list(result.keys())[:10]}")
elif isinstance(result, list):
print(f" Items: {len(result)}")
if len(result) > 0:
print(f" First item keys: {list(result[0].keys())[:10] if isinstance(result[0], dict) else 'N/A'}")
# ============================================================================
# PHASE 5: Comparative Safety Analysis Tests
# ============================================================================
def test_24_compare_drugs():
"""Phase 5: Compare atorvastatin vs simvastatin for rhabdomyolysis."""
result = tu.tools.FAERS_compare_drugs(
operation="compare_drugs",
drug1="ATORVASTATIN",
drug2="SIMVASTATIN",
adverse_event="Rhabdomyolysis"
)
assert isinstance(result, dict), f"Expected dict, got {type(result)}"
assert result.get("status") == "success", f"Status not success"
d1 = result["drug1"]
d2 = result["drug2"]
assert d1["name"] == "ATORVASTATIN"
assert d2["name"] == "SIMVASTATIN"
prr1 = d1["metrics"]["PRR"]["value"]
prr2 = d2["metrics"]["PRR"]["value"]
assert prr1 > 0, "PRR1 should be > 0"
assert prr2 > 0, "PRR2 should be > 0"
# Simvastatin should have higher PRR for rhabdomyolysis (known)
print(f" Atorvastatin PRR: {prr1:.3f}")
print(f" Simvastatin PRR: {prr2:.3f}")
print(f" Comparison: {result['comparison']}")
def test_25_aggregate_class_reactions():
"""Phase 5: Aggregate adverse reactions across statin class."""
result = tu.tools.FAERS_count_additive_adverse_reactions(
medicinalproducts=["ATORVASTATIN", "SIMVASTATIN", "ROSUVASTATIN"]
)
assert isinstance(result, list), f"Expected list, got {type(result)}"
assert len(result) >= 10, f"Expected >= 10 reactions, got {len(result)}"
# Check MYALGIA is present (class-wide effect)
terms = [r["term"].upper() for r in result]
assert any("MYALGIA" in t for t in terms), f"Expected MYALGIA in class aggregate"
print(f" Total reactions: {len(result)}")
print(f" Top 3: {[(r['term'], r['count']) for r in result[:3]]}")
def test_26_aggregate_class_seriousness():
"""Phase 5: Aggregate seriousness across statin class."""
result = tu.tools.FAERS_count_additive_seriousness_classification(
medicinalproducts=["ATORVASTATIN", "SIMVASTATIN"]
)
assert isinstance(result, list), f"Expected list, got {type(result)}"
terms = {r["term"]: r["count"] for r in result}
assert "Serious" in terms, "Missing 'Serious' category"
print(f" Class seriousness: {json.dumps(terms, indent=2)}")
# ============================================================================
# PHASE 6: DDIs & PGx Tests
# ============================================================================
def test_27_fda_drug_interactions_label():
"""Phase 6: Get DDIs from FDA label."""
result = tu.tools.FDA_get_drug_interactions_by_drug_name(drug_name="atorvastatin")
assert isinstance(result, dict), f"Expected dict, got {type(result)}"
assert result["meta"]["total"] > 0, "Should have DDI results"
print(f" DDI results: {result['meta']['total']}")
def test_28_pharmgkb_drug_search():
"""Phase 6: Search PharmGKB for atorvastatin."""
result = tu.tools.PharmGKB_search_drugs(query="atorvastatin")
assert isinstance(result, dict), f"Expected dict, got {type(result)}"
assert result["status"] == "success", f"Status not success"
data = result["data"]
assert len(data) > 0, "No PharmGKB results"
drug = data[0]
assert drug["name"] == "atorvastatin", f"Wrong drug: {drug['name']}"
assert drug["id"] == "PA448500", f"Wrong ID: {drug['id']}"
print(f" PharmGKB ID: {drug['id']}")
print(f" Name: {drug['name']}")
print(f" SMILES: {drug.get('smiles', 'N/A')[:80]}...")
def test_29_pharmgkb_drug_details():
"""Phase 6: Get PharmGKB drug details."""
result = tu.tools.PharmGKB_get_drug_details(drug_id="PA448500")
assert isinstance(result, dict), f"Expected dict, got {type(result)}"
assert result["status"] == "success", f"Status not success"
data = result["data"]
assert data["name"] == "atorvastatin", f"Wrong drug: {data['name']}"
print(f" Drug: {data['name']}")
print(f" Types: {data.get('types', [])}")
def test_30_pharmgkb_dosing_guidelines():
"""Phase 6: Get SLCO1B1 dosing guidelines (key statin pharmacogene)."""
result = tu.tools.PharmGKB_get_dosing_guidelines(gene="SLCO1B1")
assert result is not None, "PharmGKB dosing guidelines returned None"
print(f" Result type: {type(result)}")
if isinstance(result, dict):
print(f" Status: {result.get('status', 'N/A')}")
if "data" in result:
data = result["data"]
if isinstance(data, list):
print(f" Guidelines found: {len(data)}")
elif isinstance(data, dict):
print(f" Keys: {list(data.keys())[:10]}")
elif isinstance(result, list):
print(f" Guidelines found: {len(result)}")
# ============================================================================
# PHASE 7: Literature Evidence Tests
# ============================================================================
def test_31_pubmed_search():
"""Phase 7: Search PubMed for atorvastatin safety literature."""
result = tu.tools.PubMed_search_articles(
query="atorvastatin adverse events safety rhabdomyolysis",
limit=5
)
assert isinstance(result, list), f"Expected list, got {type(result)}"
assert len(result) > 0, "No PubMed results"
first = result[0]
assert "pmid" in first, "Missing 'pmid'"
assert "title" in first, "Missing 'title'"
assert "pub_year" in first, "Missing 'pub_year'"
print(f" Articles found: {len(result)}")
for article in result[:3]:
print(f" - [{article['pmid']}] {article['title'][:80]}... ({article['pub_year']})")
def test_32_openalex_search():
"""Phase 7: Search OpenAlex for citation analysis."""
result = tu.tools.openalex_search_works(
query="atorvastatin statin safety adverse events",
limit=5
)
assert result is not None, "OpenAlex returned None"
if isinstance(result, dict):
if "results" in result:
works = result["results"]
elif "data" in result:
works = result["data"]
else:
works = [result]
elif isinstance(result, list):
works = result
else:
works = []
assert len(works) > 0, "No OpenAlex results"
print(f" Works found: {len(works)}")
# ============================================================================
# CROSS-DRUG TESTS (Different drug types)
# ============================================================================
def test_33_metformin_disproportionality():
"""Cross-drug: Metformin + Lactic acidosis (known strong signal)."""
result = tu.tools.FAERS_calculate_disproportionality(
operation="calculate_disproportionality",
drug_name="METFORMIN",
adverse_event="Lactic acidosis"
)
assert result.get("status") == "success"
prr = result["metrics"]["PRR"]["value"]
assert prr > 5.0, f"Expected strong PRR for lactic acidosis, got {prr}"
sd = result["signal_detection"]
assert sd["signal_detected"] is True
print(f" PRR: {prr:.3f}")
print(f" Signal: {sd['signal_strength']}")
def test_34_pembrolizumab_reactions():
"""Cross-drug: Pembrolizumab (biologic/immunotherapy) FAERS profile."""
result = tu.tools.FAERS_count_reactions_by_drug_event(medicinalproduct="PEMBROLIZUMAB")
assert isinstance(result, list), f"Expected list, got {type(result)}"
assert len(result) >= 5, "Expected multiple reactions"
print(f" Reactions: {len(result)}")
print(f" Top 5: {[(r['term'], r['count']) for r in result[:5]]}")
def test_35_pembrolizumab_disproportionality():
"""Cross-drug: Pembrolizumab + immune-related AE."""
result = tu.tools.FAERS_calculate_disproportionality(
operation="calculate_disproportionality",
drug_name="PEMBROLIZUMAB",
adverse_event="Colitis"
)
assert result.get("status") == "success"
prr = result["metrics"]["PRR"]["value"]
assert prr > 1.0, f"Expected PRR > 1 for pembrolizumab colitis"
print(f" PRR: {prr:.3f}")
print(f" Signal: {result['signal_detection']['signal_strength']}")
def test_36_ibuprofen_reactions():
"""Cross-drug: Ibuprofen (NSAID, OTC) FAERS profile."""
result = tu.tools.FAERS_count_reactions_by_drug_event(medicinalproduct="IBUPROFEN")
assert isinstance(result, list), f"Expected list, got {type(result)}"
assert len(result) >= 5, "Expected multiple reactions"
print(f" Reactions: {len(result)}")
print(f" Top 5: {[(r['term'], r['count']) for r in result[:5]]}")
def test_37_ibuprofen_gi_bleeding():
"""Cross-drug: Ibuprofen + GI bleeding (known NSAID effect)."""
result = tu.tools.FAERS_calculate_disproportionality(
operation="calculate_disproportionality",
drug_name="IBUPROFEN",
adverse_event="Gastrointestinal haemorrhage"
)
assert result.get("status") == "success"
prr = result["metrics"]["PRR"]["value"]
sd = result["signal_detection"]
assert sd["signal_detected"] is True, "GI hemorrhage should be a signal for ibuprofen"
print(f" PRR: {prr:.3f}")
print(f" Signal: {sd['signal_strength']}")
def test_38_compare_nsaids():
"""Cross-drug: Compare ibuprofen vs naproxen for GI bleeding."""
result = tu.tools.FAERS_compare_drugs(
operation="compare_drugs",
drug1="IBUPROFEN",
drug2="NAPROXEN",
adverse_event="Gastrointestinal haemorrhage"
)
assert result.get("status") == "success"
d1 = result["drug1"]
d2 = result["drug2"]
print(f" Ibuprofen PRR: {d1['metrics']['PRR']['value']:.3f}")
print(f" Naproxen PRR: {d2['metrics']['PRR']['value']:.3f}")
print(f" Comparison: {result['comparison']}")
# ============================================================================
# EDGE CASE TESTS
# ============================================================================
def test_39_rare_drug_faers():
"""Edge case: Drug with fewer FAERS reports."""
# Tocilizumab - biologic with moderate reports
result = tu.tools.FAERS_count_reactions_by_drug_event(medicinalproduct="TOCILIZUMAB")
assert isinstance(result, list), f"Expected list, got {type(result)}"
print(f" Reactions: {len(result)}")
if len(result) > 0:
print(f" Top 3: {[(r['term'], r['count']) for r in result[:3]]}")
def test_40_individual_case_reports():
"""Edge case: Retrieve individual case reports."""
result = tu.tools.FAERS_search_adverse_event_reports(
medicinalproduct="atorvastatin",
limit=3
)
assert isinstance(result, list), f"Expected list, got {type(result)}"
assert len(result) > 0, "Should have at least 1 report"
report = result[0]
assert "safetyreportid" in report, "Missing safetyreportid"
assert "patient" in report, "Missing patient data"
assert "reaction" in report["patient"], "Missing reaction data"
print(f" Reports: {len(result)}")
print(f" Report ID: {report['safetyreportid']}")
print(f" Reactions: {len(report['patient']['reaction'])}")
def test_41_search_by_drug_and_reaction():
"""Edge case: Search for specific drug-reaction pair reports."""
result = tu.tools.FAERS_search_reports_by_drug_and_reaction(
medicinalproduct="ATORVASTATIN",
reactionmeddrapt="Rhabdomyolysis",
limit=3
)
assert isinstance(result, list), f"Expected list, got {type(result)}"
assert len(result) > 0, "Should have rhabdomyolysis reports for atorvastatin"
for report in result:
reactions = [r["reactionmeddrapt"] for r in report["patient"]["reaction"]]
assert "Rhabdomyolysis" in reactions, f"Expected Rhabdomyolysis in reactions"
print(f" Reports with rhabdomyolysis: {len(result)}")
def test_42_temporal_trends():
"""Edge case: Analyze temporal trends (may return insufficient data)."""
result = tu.tools.FAERS_analyze_temporal_trends(
operation="analyze_temporal_trends",
drug_name="ATORVASTATIN"
)
assert isinstance(result, dict), f"Expected dict, got {type(result)}"
assert result.get("status") == "success", f"Status: {result.get('status')}"
trend = result.get("trend_analysis", {})
print(f" Trend: {trend.get('trend', 'N/A')}")
print(f" Temporal data points: {len(result.get('temporal_data', []))}")
def test_43_safety_signal_score_calculation():
"""Integration: Verify Safety Signal Score components can be computed."""
# Component 1: FAERS Signal Strength (check if PRR >= 5 exists)
disp = tu.tools.FAERS_calculate_disproportionality(
operation="calculate_disproportionality",
drug_name="ATORVASTATIN",
adverse_event="Rhabdomyolysis"
)
prr = disp["metrics"]["PRR"]["value"]
faers_score = 35 if prr >= 5 else (20 if prr >= 3 else (10 if prr >= 2 else 0))
# Component 2: Serious Adverse Events
deaths = tu.tools.FAERS_count_death_related_by_drug(medicinalproduct="ATORVASTATIN")
death_count = next((r["count"] for r in deaths if r["term"] == "death"), 0)
serious_score = 30 if death_count > 100 else (25 if death_count > 0 else 0)
# Component 3: FDA Label Warnings
boxed = tu.tools.FDA_get_boxed_warning_info_by_drug_name(drug_name="atorvastatin")
has_boxed = not (isinstance(boxed, dict) and "error" in boxed)
contras = tu.tools.FDA_get_contraindications_by_drug_name(drug_name="atorvastatin")
has_contras = isinstance(contras, dict) and contras.get("meta", {}).get("total", 0) > 0
label_score = 25 if has_boxed else (15 if has_contras else 5)
# Component 4: Literature (simplified - just check if papers exist)
papers = tu.tools.PubMed_search_articles(
query="atorvastatin adverse events safety",
limit=5
)
lit_score = 7 if len(papers) >= 3 else (4 if len(papers) > 0 else 0)
total_score = faers_score + serious_score + label_score + lit_score
print(f" FAERS Signal Score: {faers_score}/35 (PRR={prr:.2f})")
print(f" Serious Events Score: {serious_score}/30 (deaths={death_count})")
print(f" Label Warning Score: {label_score}/25 (boxed={has_boxed}, contras={has_contras})")
print(f" Literature Score: {lit_score}/10 (papers={len(papers)})")
print(f" TOTAL SAFETY SIGNAL SCORE: {total_score}/100")
assert 0 <= total_score <= 100, f"Score out of range: {total_score}"
assert total_score > 0, "Score should be > 0 for atorvastatin"
# ============================================================================
# Run all tests
# ============================================================================
if __name__ == "__main__":
tests = [
# Phase 0: Drug Disambiguation
("Phase 0: Drug disambiguation (atorvastatin -> CHEMBL1487)", test_01_drug_disambiguation_atorvastatin),
("Phase 0: Blackbox/withdrawal status", test_02_drug_blackbox_status),
("Phase 0: Mechanism of action", test_03_drug_mechanism_of_action),
("Phase 0: DrugBank safety/toxicity", test_04_drugbank_safety),
# Phase 1: FAERS Profiling
("Phase 1: FAERS top reactions", test_05_faers_top_reactions),
("Phase 1: FAERS seriousness distribution", test_06_faers_seriousness),
("Phase 1: FAERS outcome distribution", test_07_faers_outcomes),
("Phase 1: FAERS age distribution", test_08_faers_age_distribution),
("Phase 1: FAERS death counts", test_09_faers_death_counts),
("Phase 1: FAERS reporter country", test_10_faers_reporter_country),
# Phase 2: Disproportionality
("Phase 2: Disproportionality - rhabdomyolysis (strong signal)", test_11_disproportionality_rhabdomyolysis),
("Phase 2: Disproportionality - myalgia", test_12_disproportionality_myalgia),
("Phase 2: Serious events (death)", test_13_serious_events_death),
("Phase 2: Demographic stratification", test_14_demographic_stratification),
("Phase 2: MedDRA hierarchy rollup", test_15_meddra_rollup),
# Phase 3: FDA Label
("Phase 3: Boxed warning - none (atorvastatin)", test_16_fda_boxed_warning_none),
("Phase 3: Boxed warning - present (metformin)", test_17_fda_boxed_warning_present),
("Phase 3: Contraindications", test_18_fda_contraindications),
("Phase 3: Adverse reactions from label", test_19_fda_adverse_reactions),
("Phase 3: Drug interactions from label", test_20_fda_drug_interactions),
# Phase 4: Mechanism Context
("Phase 4: OpenTargets adverse events", test_21_opentargets_adverse_events),
("Phase 4: Target safety profile (HMGCR)", test_22_target_safety_profile),
("Phase 4: ADMET toxicity predictions", test_23_admet_toxicity),
# Phase 5: Comparative Safety
("Phase 5: Compare drugs (atorvastatin vs simvastatin)", test_24_compare_drugs),
("Phase 5: Aggregate class reactions (statins)", test_25_aggregate_class_reactions),
("Phase 5: Aggregate class seriousness", test_26_aggregate_class_seriousness),
# Phase 6: DDIs & PGx
("Phase 6: FDA DDIs from label", test_27_fda_drug_interactions_label),
("Phase 6: PharmGKB drug search", test_28_pharmgkb_drug_search),
("Phase 6: PharmGKB drug details", test_29_pharmgkb_drug_details),
("Phase 6: PharmGKB dosing guidelines (SLCO1B1)", test_30_pharmgkb_dosing_guidelines),
# Phase 7: Literature
("Phase 7: PubMed safety literature", test_31_pubmed_search),
("Phase 7: OpenAlex citation analysis", test_32_openalex_search),
# Cross-drug tests
("Cross-drug: Metformin + lactic acidosis (strong signal)", test_33_metformin_disproportionality),
("Cross-drug: Pembrolizumab FAERS profile", test_34_pembrolizumab_reactions),
("Cross-drug: Pembrolizumab + colitis", test_35_pembrolizumab_disproportionality),
("Cross-drug: Ibuprofen FAERS profile", test_36_ibuprofen_reactions),
("Cross-drug: Ibuprofen + GI bleeding", test_37_ibuprofen_gi_bleeding),
("Cross-drug: Compare NSAIDs (ibuprofen vs naproxen)", test_38_compare_nsaids),
# Edge cases
("Edge case: Rare drug (tocilizumab)", test_39_rare_drug_faers),
("Edge case: Individual case reports", test_40_individual_case_reports),
("Edge case: Search by drug + reaction", test_41_search_by_drug_and_reaction),
("Edge case: Temporal trends", test_42_temporal_trends),
# Integration
("Integration: Safety Signal Score calculation", test_43_safety_signal_score_calculation),
]
print(f"\n{'#'*70}")
print(f"# ADVERSE DRUG EVENT SIGNAL DETECTION - COMPREHENSIVE TEST SUITE")
print(f"# Tests: {len(tests)}")
print(f"# Drugs: Atorvastatin, Metformin, Pembrolizumab, Ibuprofen, Tocilizumab")
print(f"{'#'*70}")
for name, func in tests:
run_test(name, func)
elapsed = time.time() - start_time
print(f"\n{'='*70}")
print(f"TEST SUMMARY")
print(f"{'='*70}")
print(f"Total: {total_tests}")
print(f"Passed: {passed_tests}")
print(f"Failed: {failed_tests}")
print(f"Pass Rate: {passed_tests/total_tests*100:.1f}%")
print(f"Time: {elapsed:.1f}s")
print()
if failed_tests > 0:
print("FAILED TESTS:")
for name, status, error in results:
if status == "FAIL":
print(f" FAIL: {name}")
print(f" {error}")
print(f"\n{'='*70}")
if failed_tests == 0:
print("ALL TESTS PASSED")
else:
print(f"{failed_tests} TEST(S) FAILED")
print(f"{'='*70}")
sys.exit(0 if failed_tests == 0 else 1)
Adverse Event Detection - Tool Parameter Reference
Verified parameter names, response formats, and fallback chains for all tools used in this skill.
---
FAERS Tools (OpenFDA-based)
| Tool | Key Parameters | Notes |
|---|---|---|
FAERS_count_reactions_by_drug_event | medicinalproduct (REQUIRED), patientsex, patientagegroup, occurcountry | Returns [{term, count}] |
FAERS_count_seriousness_by_drug_event | medicinalproduct (REQUIRED), patientsex, patientagegroup, occurcountry | Returns [{term: "Serious"/"Non-serious", count}] |
FAERS_count_outcomes_by_drug_event | medicinalproduct (REQUIRED), patientsex, patientagegroup, occurcountry | Returns [{term: "Fatal"/"Recovered"/..., count}] |
FAERS_count_patient_age_distribution | medicinalproduct (REQUIRED) | Returns [{term: "Elderly"/"Adult"/..., count}] |
FAERS_count_death_related_by_drug | medicinalproduct (REQUIRED) | Returns [{term: "alive"/"death", count}] |
FAERS_count_reportercountry_by_drug_event | medicinalproduct (REQUIRED), patientsex, patientagegroup, serious | Returns [{term: "US"/"GB"/..., count}] |
FAERS_search_adverse_event_reports | medicinalproduct, limit (max 100), skip | Returns individual case reports with patient/drug/reaction data |
FAERS_search_reports_by_drug_and_reaction | medicinalproduct (REQUIRED), reactionmeddrapt (REQUIRED), limit, skip, patientsex, serious | Returns individual reports filtered by specific reaction |
FAERS_search_serious_reports_by_drug | medicinalproduct (REQUIRED), seriousnessdeath, seriousnesshospitalization, seriousnesslifethreatening, seriousnessdisabling, limit | Returns serious event reports |
FAERS Analytics Tools (operation-based)
| Tool | Key Parameters | Notes |
|---|---|---|
FAERS_calculate_disproportionality | operation="calculate_disproportionality", drug_name (REQUIRED), adverse_event (REQUIRED) | Returns PRR, ROR, IC with 95% CI and signal detection |
FAERS_analyze_temporal_trends | operation="analyze_temporal_trends", drug_name (REQUIRED), adverse_event (optional) | Returns yearly counts and trend direction |
FAERS_compare_drugs | operation="compare_drugs", drug1 (REQUIRED), drug2 (REQUIRED), adverse_event (REQUIRED) | Returns PRR/ROR/IC for both drugs side-by-side |
FAERS_filter_serious_events | operation="filter_serious_events", drug_name (REQUIRED), seriousness_type (death/hospitalization/disability/life_threatening/all) | Returns top serious reactions with counts |
FAERS_stratify_by_demographics | operation="stratify_by_demographics", drug_name (REQUIRED), adverse_event (REQUIRED), stratify_by (sex/age/country) | Returns stratified counts and percentages. Sex codes: 0=Unknown, 1=Male, 2=Female |
FAERS_rollup_meddra_hierarchy | operation="rollup_meddra_hierarchy", drug_name (REQUIRED) | Returns top 50 preferred terms with counts |
FAERS Aggregate Tools (multi-drug)
| Tool | Key Parameters | Notes |
|---|---|---|
FAERS_count_additive_adverse_reactions | medicinalproducts (REQUIRED, array), patientsex, patientagegroup, occurcountry, serious, seriousnessdeath | Aggregates AE counts across multiple drugs |
FAERS_count_additive_seriousness_classification | medicinalproducts (REQUIRED, array), patientsex, patientagegroup, occurcountry | Aggregates seriousness across multiple drugs |
FAERS_count_additive_reaction_outcomes | medicinalproducts (REQUIRED, array) | Aggregates outcomes across multiple drugs |
FDA Label Tools
| Tool | Key Parameters | Notes |
|---|---|---|
FDA_get_boxed_warning_info_by_drug_name | drug_name | Returns {error: {code: "NOT_FOUND"}} if no boxed warning |
FDA_get_contraindications_by_drug_name | drug_name | Returns {meta: {total: N}, results: [{contraindications: [...]}]} |
FDA_get_adverse_reactions_by_drug_name | drug_name | Returns {meta: {total: N}, results: [{adverse_reactions: [...]}]} |
FDA_get_warnings_by_drug_name | drug_name | Returns {meta: {total: N}, results: [{warnings: [...]}]} |
FDA_get_drug_interactions_by_drug_name | drug_name | Returns {meta: {total: N}, results: [{drug_interactions: [...]}]} |
FDA_get_pharmacogenomics_info_by_drug_name | drug_name | Returns PGx info from label |
FDA_get_pregnancy_or_breastfeeding_info_by_drug_name | drug_name | Returns pregnancy info |
FDA_get_geriatric_use_info_by_drug_name | drug_name | Returns geriatric use info |
FDA_get_pediatric_use_info_by_drug_name | drug_name | Returns pediatric info |
OpenTargets Tools
| Tool | Key Parameters | Notes |
|---|---|---|
OpenTargets_get_drug_chembId_by_generic_name | drugName | Returns {data: {search: {hits: [{id, name, description}]}}} |
OpenTargets_get_drug_adverse_events_by_chemblId | chemblId | Returns {data: {drug: {adverseEvents: {count, rows: [{name, meddraCode, count, logLR}]}}}} |
OpenTargets_get_drug_blackbox_status_by_chembl_ID | chemblId | Returns {data: {drug: {hasBeenWithdrawn, blackBoxWarning}}} |
OpenTargets_get_drug_warnings_by_chemblId | chemblId | Returns drug warnings (may be empty) |
OpenTargets_get_drug_mechanisms_of_action_by_chemblId | chemblId | Returns {data: {drug: {mechanismsOfAction: {rows: [...]}}}} |
OpenTargets_get_drug_indications_by_chemblId | chemblId | Returns approved and investigational indications |
OpenTargets_get_target_safety_profile_by_ensemblID | ensemblId | Returns {data: {target: {safetyLiabilities: [...]}}} |
DrugBank Tools
| Tool | Key Parameters | Notes |
|---|---|---|
drugbank_get_safety_by_drug_name_or_drugbank_id | query, case_sensitive (bool), exact_match (bool), limit | Returns toxicity, food interactions |
drugbank_get_targets_by_drug_name_or_drugbank_id | query, case_sensitive, exact_match, limit | Returns drug targets |
drugbank_get_drug_interactions_by_drug_name_or_id | query, case_sensitive, exact_match, limit | Returns DDIs |
drugbank_get_pharmacology_by_drug_name_or_drugbank_id | query, case_sensitive, exact_match, limit | Returns pharmacology |
PharmGKB Tools
| Tool | Key Parameters | Notes |
|---|---|---|
PharmGKB_search_drugs | query | Returns {data: [{id, name, smiles}]} |
PharmGKB_get_drug_details | drug_id (e.g., "PA448500") | Returns detailed drug info |
PharmGKB_get_dosing_guidelines | guideline_id, gene (both optional) | Returns dosing guidelines |
PharmGKB_get_clinical_annotations | annotation_id, gene_id (both optional) | Returns clinical annotations |
fda_pharmacogenomic_biomarkers | drug_name, biomarker, limit | Returns {count, results: [...]} |
ADMETAI Tools
| Tool | Key Parameters | Notes |
|---|---|---|
ADMETAI_predict_toxicity | smiles (REQUIRED, array of strings) | Predicts hepatotoxicity, cardiotoxicity, etc. |
ADMETAI_predict_CYP_interactions | smiles (REQUIRED, array) | Predicts CYP inhibition/substrate |
Literature Tools
| Tool | Key Parameters | Notes |
|---|---|---|
PubMed_search_articles | query, limit | Returns list of article dicts |
openalex_search_works | query, limit | Returns works with citation counts |
EuropePMC_search_articles | query, source ("PPR" for preprints), pageSize | Returns articles including preprints |
search_clinical_trials | query_term (REQUIRED), condition, intervention, pageSize | Returns clinical trials |
---
Fallback Chains
| Primary Tool | Fallback 1 | Fallback 2 |
|---|---|---|
FAERS_calculate_disproportionality | Manual calculation from FAERS_count_* data | Literature PRR values |
FAERS_count_reactions_by_drug_event | FAERS_rollup_meddra_hierarchy | OpenTargets adverse events |
FDA_get_boxed_warning_info_by_drug_name | OpenTargets_get_drug_blackbox_status_by_chembl_ID | DrugBank safety |
FDA_get_contraindications_by_drug_name | FDA_get_warnings_by_drug_name | DrugBank safety |
OpenTargets_get_drug_chembId_by_generic_name | ChEMBL_search_drugs | Manual search |
PharmGKB_search_drugs | fda_pharmacogenomic_biomarkers | FDA label PGx section |
PubMed_search_articles | openalex_search_works | EuropePMC_search_articles |
Related skills
How it compares
Pick tooluniverse-adverse-event-detection over tooluniverse-pharmacovigilance when you need rigorous disproportionality statistics and a quantitative 0–100 Safety Signal Score rather than broad safety profiling alone.
FAQ
What statistics does tooluniverse-adverse-event-detection compute?
tooluniverse-adverse-event-detection calculates PRR, ROR, and IC with 95% confidence intervals for top adverse events via FAERS_calculate_disproportionality. Signals require PRR ≥ 2.0, lower CI > 1.0, and at least 3 cases.
What is the Safety Signal Score in tooluniverse-adverse-event-detection?
tooluniverse-adverse-event-detection produces a 0–100 Safety Signal Score from four components: FAERS signal strength (0–35), serious AEs (0–30), FDA label warnings (0–25), and literature evidence (0–10), with T1–T4 grading per signal.
How many phases does tooluniverse-adverse-event-detection run?
tooluniverse-adverse-event-detection executes nine phases from drug disambiguation through FAERS profiling, disproportionality analysis, FDA label mining, mechanism context, comparative safety, DDIs, literature, scoring, and final report synthesis.