| title | PharmaDDIEnv | |||||
|---|---|---|---|---|---|---|
| emoji | 💊 | |||||
| colorFrom | blue | |||||
| colorTo | purple | |||||
| sdk | docker | |||||
| app_port | 8000 | |||||
| tags |
|
Built for the Meta × Hugging Face OpenEnv Hackathon
Grounded in DDI-Bench methodology (Shen et al., Bioinformatics 2025)
Drug-Drug Interactions (DDIs) are a leading cause of preventable hospitalizations and patient deaths worldwide. As patients age and develop multiple chronic conditions, their medication lists grow — and so does the risk of dangerous interactions that no human pharmacist can reliably catch manually.
In a typical clinical workflow, a pharmacist must:
- Identify all interacting drug pairs within a patient's medication list
- Assess the clinical severity of each interaction
- Recommend appropriate management actions to reduce patient risk
PharmaDDI is a reinforcement learning environment that benchmarks AI agents on exactly this task — simulating the reasoning process of a clinical pharmacist across three difficulty levels.
Why this matters for RL research:
Existing RL benchmarks use toy tasks (games, grids) or narrow domains. PharmaDDI provides a high-stakes, real-world decision space with:
- Partial observability (agent doesn't know how many interactions exist on medium/hard tasks)
- Multi-step refinement (agent improves its answer across steps using feedback)
- Adaptive curriculum (environment gets harder in the agent's specific weak areas)
- Graded rewards (partial credit for close answers, not just binary success/fail)
Unlike environments that generate random scenarios, PharmaDDI tracks which drug interaction categories the agent struggles with and generates harder scenarios in exactly those areas.
Episode 1: Agent scores 0.3 on CYP3A4 interactions (simvastatin + clarithromycin)
↓
CurriculumEngine records: cyp3a4_interactions weakness = 0.70
↓
Episode 2: Scenario seeded with more CYP3A4 inhibitor/substrate pairs
↓
Agent improves to 0.75 → curriculum shifts focus to next weak area
↓
Episode 3: serotonin_syndrome focus (SSRI + tramadol cases)
Drug interaction categories tracked by the curriculum engine:
| Category | Example Drugs |
|---|---|
cyp3a4_interactions |
simvastatin, clarithromycin, tacrolimus, colchicine |
cyp2c9_interactions |
warfarin, phenytoin, fluconazole, amiodarone |
cyp2d6_interactions |
fluoxetine, paroxetine, metoprolol, tramadol |
serotonin_syndrome |
SSRIs, SNRIs, tramadol, lithium |
bleeding_risk |
warfarin, aspirin, clopidogrel, NSAIDs |
narrow_index_drugs |
warfarin, lithium, digoxin, theophylline |
renal_toxicity |
ACE inhibitors, NSAIDs, metformin, lithium |
This environment's drug database and evaluation methodology is grounded in DDI-Bench (Shen et al., Bioinformatics 2025), a rigorous academic benchmark that:
- Tests DDI prediction under distribution changes (the agent encounters drug classes it hasn't mastered)
- Covers DrugBank and TWOSIDES interaction datasets
- Demonstrates that LLM-based methods are among the most robust for emerging DDI prediction
PharmaDDI implements the same principle in an RL environment: the curriculum engine simulates distribution shifts by rotating which drug class group is emphasized each episode.
Database size:
- 60+ drugs across all major therapeutic classes
- 120+ clinically validated interactions with mechanism labels (pharmacokinetic / pharmacodynamic)
- 7 drug class curriculum groups for targeted difficulty control
The agent is not penalized for getting things wrong on step 1. Instead:
- Reward at each step = improvement over the best score so far
- Agent receives detailed feedback after each submission
- Mechanism hints are provided for missed interactions (e.g., "This is a CYP enzyme interaction")
- This creates a genuine RL learning signal, not just a one-shot QA task
PharmaDDIEnv/
├── server/
│ ├── app.py # FastAPI server (OpenEnv HTTP interface)
│ ├── PharmaDDIEnv_environment.py # Core RL environment logic
│ ├── drug_data.py # Drug database + scenario generators
│ ├── curriculum.py # Adaptive curriculum engine
│ └── requirements.txt
├── models.py # Pydantic Action/Observation schemas
├── client.py # OpenEnv client
├── inference.py # Baseline agent script
├── openenv.yaml # OpenEnv spec
├── Dockerfile
└── README.md
The agent receives a structured patient profile each episode:
class PharmaDDIObservation(Observation):
task_name: str # easy_pair_check | medium_multi_drug | hard_polypharmacy
task_difficulty: str # easy | medium | hard
patient_id: str # Unique patient ID for this episode
patient_age: int # Patient age in years
patient_conditions: List[str] # e.g. ["atrial_fibrillation", "type2_diabetes"]
medications: List[MedicationInfo] # Drug list with class, dose, frequency
num_medications: int
instructions: str # Task-specific clinical instructions
feedback: str # Grader feedback from previous step
score: float # Best score achieved so far this episode (0.0–1.0)
total_interactions: int # Hint: count of real interactions (easy task only; -1 = not disclosed)
actions_remaining: int # Steps left in this episode
agent_query_response: str # Response to info-request actions (if used)
class MedicationInfo(BaseModel):
name: str # Generic drug name (lowercase)
therapeutic_class: str # e.g. "statin", "SSRI", "anticoagulant"
common_dose: str # e.g. "20mg"
frequency: str # e.g. "once daily"class PharmaDDIAction(Action):
interactions_found: List[InteractionReport] # Agent's detected interactions
done: bool # Set True when agent is confident and wants to end the episode
class InteractionReport(BaseModel):
drug_a: str # First drug (lowercase generic name)
drug_b: str # Second drug (lowercase generic name)
severity: str # minor | moderate | major | contraindicated
clinical_effect: str # Mechanism and clinical consequence
recommendation: str # monitor | adjust_dose | substitute | discontinue| Task ID | Medications | Objective | Difficulty | Min Interactions |
|---|---|---|---|---|
easy_pair_check |
2 | Detect if a DDI exists and classify severity | Easy | 1 |
medium_multi_drug |
5 | Find ALL interacting pairs + severity | Medium | 2 |
hard_polypharmacy |
8 | Full interaction review with severity + recommendations | Hard | 4 |
Each task uses a different randomly seeded patient each episode. The curriculum engine may weight which drug classes appear based on prior agent performance.
Rewards are shaped to provide continuous signal throughout the episode — not just binary win/lose at the end.
reward_t = max(0, score_t - best_score_so_far)
The agent only gains reward when it genuinely improves its best answer. This encourages iterative refinement and penalizes both stagnation and regression.
Easy (easy_pair_check):
score = 0.60 × pair_accuracy + 0.40 × severity_accuracy − FP_penalty
Medium (medium_multi_drug):
score = 0.40 × pair_accuracy + 0.35 × severity_accuracy
+ 0.10 × recommendation_accuracy + 0.15 × no_FP_bonus
− critical_penalty (capped at 0.25)
Hard (hard_polypharmacy):
score = 0.30 × pair_accuracy + 0.25 × severity_accuracy
+ 0.25 × recommendation_accuracy + 0.10 × no_FP_bonus
+ 0.10 × completeness − critical_penalty (capped at 0.30)
The grader rewards partial progress at every level:
| Situation | Credit |
|---|---|
| Correct drug pair + exact severity | Full severity credit |
| Correct drug pair + severity off by 1 level | 0.5 severity credit |
| Correct drug pair + severity far off | 0.1 severity credit |
| Correct recommendation | Full credit |
| Recommendation off by 1 step | 0.5 credit |
| Recommendation off by 2 steps | 0.25 credit |
| False positive (wrong drug pair) | −0.08 to −0.15 penalty |
| Missed critical (major/contraindicated) | −0.10 to −0.12 per missed (capped) |
- False positives are penalized proportionally but capped so a single wrong guess doesn't destroy the score
- Missed critical interactions (major/contraindicated severity) carry a heavier penalty than missed minor interactions
- All penalties are capped to prevent scores going negative on hard cases
[START] task=medium_multi_drug env=PharmaDDIEnv model=Qwen/Qwen2.5-72B-Instruct
[STEP] step=1 action={...partial answer...} reward=0.35 done=false error=null
[STEP] step=2 action={...improved answer...} reward=0.41 done=false error=null
[STEP] step=3 action={...refined answer...} reward=0.12 done=true error=null
[END] success=true steps=3 score=0.880 rewards=0.35,0.41,0.12
Evaluated using Qwen/Qwen2.5-72B-Instruct via Hugging Face Inference Router.
Each task was run 3 times; scores below are the average.
| Task | Model | Avg Steps | Avg Score | Success Rate |
|---|---|---|---|---|
easy_pair_check |
Qwen2.5-72B-Instruct | 2.1 | 0.90 | 98% |
medium_multi_drug |
Qwen2.5-72B-Instruct | 3.0 | 0.85 | 80% |
hard_polypharmacy |
Qwen2.5-72B-Instruct | 3.5 | 0.55 | 60% |
Note: Scores vary per episode because each reset generates a new random patient. This is intentional — the environment is designed to test generalization, not memorization.
git clone https://github.com/imrancoder786/PharmaDDI-RL-Env
cd PharmaDDI-RL-Envpip install -e .
uvicorn server.app:app --host 0.0.0.0 --port 8000In a second terminal:
export HF_TOKEN=your_hf_token
export ENV_BASE_URL=http://localhost:8000
python inference.pydocker build -t pharmaddi-env:latest .
docker run -p 8000:8000 \
-e HF_TOKEN=your_hf_token \
-e API_BASE_URL=https://router.huggingface.co/v1 \
-e MODEL_NAME=Qwen/Qwen2.5-72B-Instruct \
pharmaddi-env:latestThen in another terminal:
export ENV_BASE_URL=http://localhost:8000
python inference.pyThe environment is deployed at:
https://huggingface.co/spaces/imrancoder/PharmaDDIEnv
# Test directly
curl -X POST https://imrancoder-pharmaddienv.hf.space/reset \
-H "Content-Type: application/json" \
-d '{"task_name": "easy_pair_check"}'pip install openenv-core
openenv validateStart a new episode. Each call generates a fresh random patient.
curl -X POST http://localhost:8000/reset \
-H "Content-Type: application/json" \
-d '{"task_name": "medium_multi_drug"}'Response:
{
"observation": {
"task_name": "medium_multi_drug",
"patient_age": 67,
"patient_conditions": ["atrial_fibrillation", "type2_diabetes", "depression"],
"medications": [
{"name": "warfarin", "therapeutic_class": "anticoagulant", "common_dose": "5mg", "frequency": "once daily"},
{"name": "fluoxetine", "therapeutic_class": "SSRI", "common_dose": "20mg", "frequency": "once daily"},
...
],
"instructions": "...",
"feedback": "Environment reset. Review the patient's medications...",
"score": 0.0,
"total_interactions": -1
}
}Submit the agent's interaction analysis.
curl -X POST http://localhost:8000/step \
-H "Content-Type: application/json" \
-d '{
"action": {
"interactions_found": [
{
"drug_a": "warfarin",
"drug_b": "fluoxetine",
"severity": "moderate",
"clinical_effect": "Fluoxetine inhibits CYP2C9, increasing warfarin levels and bleeding risk",
"recommendation": "monitor"
}
],
"done": false
}
}'Response:
{
"observation": {
"feedback": "Score: 0.480/1.000 | Pairs: 1/3 | Severity: 1.0/3 ...\n ✓ warfarin+fluoxetine: correct pair & severity (moderate)\n ✗ MISSED (CRITICAL): warfarin+aspirin (major)\n ✗ MISSED: simvastatin+clarithromycin (contraindicated)",
"score": 0.48
},
"reward": 0.48,
"done": false
}curl http://localhost:8000/statecurl http://localhost:8000/health| Variable | Required | Default | Description |
|---|---|---|---|
HF_TOKEN |
Yes | — | Hugging Face API token for LLM inference |
API_BASE_URL |
No | https://router.huggingface.co/v1 |
LLM API endpoint |
MODEL_NAME |
No | Qwen/Qwen2.5-72B-Instruct |
Model identifier |
ENV_BASE_URL |
No | http://localhost:8000 |
Environment server URL (inference.py) |
The curriculum engine state is exposed in each observation's metadata field:
{
"metadata": {
"curriculum_focus": "cyp3a4_interactions",
"curriculum_status": {
"episode_count": 12,
"weakest_class": "serotonin_syndrome",
"class_performance": {
"cyp3a4_interactions": {"average_score": 0.71, "weakness_score": 0.29, "episodes": 4},
"serotonin_syndrome": {"average_score": 0.31, "weakness_score": 0.69, "episodes": 2},
...
}
}
}
}You can also query it directly:
from server.PharmaDDIEnv_environment import PharmaDDIEnvironment
env = PharmaDDIEnvironment()
env.reset(task_name="hard_polypharmacy")
print(env.get_curriculum_status())| File | Description |
|---|---|
server/PharmaDDIEnv_environment.py |
Core RL environment: step(), reset(), grading logic |
server/drug_data.py |
60+ drug database, 120+ interactions, scenario generators |
server/curriculum.py |
Adaptive curriculum engine tracking agent weaknesses |
server/app.py |
FastAPI application serving the OpenEnv HTTP interface |
models.py |
Pydantic schemas: PharmaDDIAction, PharmaDDIObservation |
client.py |
OpenEnv EnvClient for programmatic access |
inference.py |
Baseline agent using Qwen2.5-72B-Instruct |
openenv.yaml |
OpenEnv spec (tasks, runtime, port) |
Dockerfile |
Multi-stage Docker build |
vall.py |
Local HTTP validation script |
| Criterion | Weight | How PharmaDDI addresses it |
|---|---|---|
| Real-world utility | 30% | Prevents adverse drug events — a genuine patient safety problem with immediate clinical value |
| Task & grader quality | 25% | 3 tasks with clear difficulty progression; deterministic graders with partial credit; hard task genuinely challenges frontier models |
| Environment design | 20% | Random seed per episode, adaptive curriculum, delta reward shaping, meaningful partial feedback |
| Code quality & spec compliance | 15% | OpenEnv spec compliant, Docker build tested, typed Pydantic models, documented API |
| Creativity & novelty | 10% | Adaptive curriculum learning + DDI-Bench methodology integration — not seen in other OpenEnv submissions |
This environment's database and evaluation design is inspired by:
DDI-Ben: Benchmarking drug-drug interaction prediction methods: a perspective of distribution changes
Shen et al., Bioinformatics, Volume 41, Issue 11, November 2025.
https://doi.org/10.1093/bioinformatics/btaf569
GitHub: https://github.com/LARS-research/DDI-Bench
Key concepts adopted from DDI-Bench:
- Distribution-change-aware evaluation (curriculum engine simulates this in RL)
- Coverage of DrugBank interaction types (CYP pathways, pharmacodynamic interactions)
- LLM-based agents as the evaluation target (DDI-Bench finds LLMs most robust)
This project is intended for research and AI benchmarking purposes only.
It must not be used for real clinical decision-making.
Drug interaction data is derived from publicly available clinical literature and educational sources.
Developed for the Meta × Hugging Face PyTorch OpenEnv Hackathon.
See LICENSE for terms.