Graph-native drug interaction reasoning — Jac walkers traverse a pharmaceutical knowledge graph, LLM handles only NLP and narrative.
JacHacks Spring 2026 · Consumer Healthcare track · Best Use of Jac · Best Demo
Adverse drug interactions send 125,000 Americans to the hospital every year and cause over 1.5 million preventable injuries. Most people taking multiple medications have no idea which combinations are dangerous. Existing checkers are glorified database lookups — they don't reason, they don't explain, and they don't understand plain English.
PharmaGraph is a conversational drug interaction checker that accepts plain-English prescription descriptions and reasons about them using a Jac graph traversal engine. The graph IS the reasoning. LLM handles only what it's actually good at: parsing natural language and writing plain-English explanations.
"My doctor put me on warfarin and fluconazole for a fungal infection..."
↓
[Jac LLM Walker] → parse_prescription() by llm()
↓
[Jac Graph Walker] → traverses Drug–Interaction–Drug edges
↓
[Jac Graph Walker] → checks Condition contraindications
↓
[Jac LLM Walker] → generate_risk_summary() by llm()
↓
Severity 5 CONTRAINDICATED — warfarin + fluconazole
⚠ Fluconazole inhibits CYP2C9, massively increasing warfarin levels
Traditional drug checkers query a database. PharmaGraph uses Jac walkers to traverse a live pharmaceutical knowledge graph — the same architecture you'd use for multi-hop reasoning in any domain.
| Feature | Traditional Checker | PharmaGraph |
|---|---|---|
| Drug lookup | DB query | Graph node traversal |
| Interaction check | JOIN query | Walker traverses edges |
| Condition check | Another query | Walker visits Condition nodes |
| Patient profile | Session cookie | MedProfile node in graph |
| Explanation | Static text | LLM reads graph output |
root
├── Drug(warfarin) ──[Interaction]── Drug(aspirin)
│ └──[Interaction]── Drug(fluconazole)
├── Drug(ibuprofen) ──[Interaction]── Drug(lithium)
├── Condition(renal_failure)
│ └── contraindicated_drugs: [metformin, ...]
└── MedProfile(user)
├──> Drug(warfarin) ← walker links matched drugs
├──> Drug(aspirin)
└──> RiskReport(...) ← walker creates after analysis
walker report_walker {
can run with `root entry {
# Step 1: LLM extracts drug names from natural language
extracted = parse_prescription(self.raw_text) by llm()
# Step 2: Walker links matched Drug nodes to MedProfile
profile ++> matched_drug_node
# Step 3: Walker checks all_interactions from data layer
for inter in get_all_interactions() {
if both drugs in user profile → flag pair
}
# Step 4: Walker traverses Condition nodes
for cond in [-->(`?Condition)] {
if cond matches user conditions → flag contraindication
}
# Step 5: LLM generates patient-readable report
summary = generate_risk_summary(pairs, contras, drugs) by llm()
# Step 6: Walker creates RiskReport node in graph
profile ++> RiskReport(flagged_pairs, summary, ...)
}
}Six walkers total:
seed_graph— loads drug/condition data into the graph on startupget_graph_data— traverses all Drug nodes + interaction pairs for visualizationparse_rx— standalone: extract drugs from a prescription via LLMinteraction_walk— standalone: run interaction check on current profilecondition_walk— standalone: check condition contraindicationsreport_walker— full pipeline: all of the above in one traversal
- Natural language input — describe your medications in plain English
- Graph traversal reasoning — Jac walker traverses 49 drugs, 50+ known interactions
- Condition contraindications — 8 medical conditions cross-referenced
- Severity scoring — 5-level scale (1 Minimal → 5 Contraindicated)
- LLM narrative — Claude generates a patient-readable plain-English report
- Live graph visualization — D3.js force-directed graph with animated traversal
- Persistent profile — MedProfile node persists in the Jac graph across requests
- Traversal replay — watch the walker move through the graph in real-time
| Layer | Technology |
|---|---|
| Language | Jac (jaclang 0.9.0) |
| Backend | jac-cloud 0.2.11 (auto FastAPI from walkers) |
| LLM | byLLM 0.4.7 → Claude claude-sonnet-4-20250514 |
| Frontend | React 18 + D3.js v7 + Vite |
| Data | 49 drugs · 50 interactions · 8 conditions (JSON) |
- Python 3.11+
- Node.js 18+
- An Anthropic API key
git clone https://github.com/nishantr14/jac.git
cd jac
pip install jaclang==0.9.0 jac-cloud==0.2.11 byllm==0.4.7 python-dotenv
cd frontend && npm install && cd ..echo ANTHROPIC_API_KEY=sk-ant-YOUR_KEY_HERE > .envjac serve main.jac --host 0.0.0.0 --port 8000The server auto-seeds 49 drugs and 50 interactions into the graph on first run.
cd frontend && npm run devTry these in the app:
Scenario 1 — The Pharmacist's Nightmare
Taking warfarin for my heart, the doctor also put me on fluconazole for a
fungal infection, and I take aspirin every morning plus ibuprofen for back pain
Finds: 3 flagged interactions including a Severity 5 (warfarin + fluconazole)
Scenario 2 — SSRI + MAOI (Serotonin Syndrome Risk)
Patient takes fluoxetine 20mg daily for depression and was just prescribed
phenelzine by a different psychiatrist
Finds: Severity 5 — CONTRAINDICATED. Life-threatening serotonin syndrome.
Scenario 3 — Cardiac Polypharmacy
I take digoxin and amiodarone for my heart arrhythmia. Also on simvastatin
for cholesterol and clarithromycin for a chest infection
Finds: Multiple Severity 4 interactions — digoxin toxicity + statin myopathy risk
jac/
├── main.jac # Entry point, auto-seeds on startup
├── graph/
│ ├── nodes.jac # Drug, Condition, MedProfile, RiskReport
│ └── edges.jac # Interaction edge
├── walkers/
│ ├── seed_graph.jac # Loads data into Jac graph
│ ├── get_graph_data.jac # Returns graph for D3 visualization
│ ├── parse_rx.jac # LLM prescription parser
│ ├── interaction_walk.jac # Graph traversal interaction checker
│ ├── condition_walk.jac # Condition contraindication checker
│ └── report_walker.jac # Full pipeline orchestrator
├── llm/
│ └── functions.jac # byLLM function definitions
├── data/
│ └── interactions.json # Drug/interaction/condition database
├── data_loader.py # Cached Python data access layer
├── llm_config.py # byllm Model instance
├── frontend/
│ └── src/
│ ├── App.jsx
│ ├── api.js
│ └── components/
│ ├── DrugGraph.jsx # D3 force graph + traversal animation
│ ├── PrescriptionInput.jsx
│ └── RiskReport.jsx
└── tests/
└── e2e_test.py # 11 end-to-end tests
jac-cloud auto-generates REST endpoints from walker definitions:
| Endpoint | Method | Description |
|---|---|---|
POST /walker/seed_graph |
POST | Load drug data into graph |
POST /walker/get_graph_data |
POST | Get all nodes + links for visualization |
POST /walker/report_walker |
POST | Full analysis pipeline |
POST /walker/parse_rx |
POST | Extract drugs from free text |
POST /walker/interaction_walk |
POST | Check interactions for current profile |
POST /walker/condition_walk |
POST | Check condition contraindications |
Example:
curl -X POST http://localhost:8000/walker/report_walker \
-H "Content-Type: application/json" \
-d '{"raw_text": "Taking warfarin and aspirin daily"}'- Consumer Healthcare (primary) — helps everyday people understand their medication risks at home
- Best Use of Jac — graph walkers as the medical reasoning engine; byLLM for NLP only
- Best Demo — live graph traversal animation shows the walker moving through the drug interaction network
Built at JacHacks Spring 2026.
PharmaGraph is not a medical device and does not provide medical advice. Always consult your doctor or pharmacist.