# Project export: Splice Sentinel

This document was generated by HackStack to give an AI agent context about a hackathon project. Sections are labeled with their provenance; content marked as truncated was cut to keep this document small.

## Project metadata

- Hackathon: UC Berkeley AI Hackathon 2026
- Tagline: Drug combinations have hidden risks. SpliceSentinel checks live FDA data, finds similar patient cases, and explains dangers before doctors prescribe.
- Devpost: https://devpost.com/software/splice-sentinel
- GitHub: https://github.com/AnushaAgarwal27/SpliceSentinel
- Video: https://www.youtube.com/embed/1BcvrHAf5Lk?enablejsapi=1&hl=en_US&rel=0&start=&version=3&wmode=transparent
- Team: 2 GitHub contributor(s) — Anusha Agarwal (50 commits), Claude Haiku 4.5 (43 commits)

## Devpost submission (written by the team)

### Inspiration

Studies show adverse drug interactions cause over a million preventable hospital admissions annually in the US. Despite this, clinicians lack quick access to real-world evidence at the point of care. The FDA's FAERS database contains over 10 million adverse event reports—but it's buried behind technical barriers and complex statistics. I built SpliceSentinel to bridge that gap: making FDA evidence instantly actionable in plain English during the prescribing decision.

### What it does

SpliceSentinel helps a doctor check the safety of a drug combination using real FDA data. The doctor uploads a patient's medical records and prescriptions, and the tool: Extracts medications and history from the documents using Claude Pulls real adverse event reports for that drug combo from FDA FAERS Flags statistically significant risks using the Proportional Reporting Ratio (PRR ≥ 2) Surfaces similar real patient cases with matching conditions Writes a clinical summary explaining the risk in plain English The result: a doctor gets evidence-backed answers in seconds instead of hours of manual research.

### How we built it

The backend runs on Python and FastAPI, with Claude handling document parsing, FDA data analysis, and the final plain-English summary. Sentry tracks performance and errors. The frontend is React, with drag-and-drop upload and an interactive graph of adverse events. Results are cached in JSON for speed.

### Challenges we ran into

Files weren't fully saved before we read them. Temporary files were sometimes read before they finished writing to disk. Fixed with explicit file.flush() and os.fsync() calls. Multi-file upload kept breaking. React wasn't applying the multiple attribute consistently. Fixed by separating the input elements and adding deduplication logic. Hundreds of adverse events were impossible to read at a glance. Solved with an interactive, color-coded network graph with search and hover tooltips. Sentry needs real signal, not noise. We had to instrument FDA queries carefully so slow calls were tracked meaningfully without adding unnecessary overhead.

### Accomplishments we're proud of

A statistically sound signal detection method (PRR), not just raw event counts Reliable file handling that holds up with real, messy medical documents A visualization that makes thousands of FDA data points understandable at a glance Full observability into performance and errors via Sentry A complete, working full-stack app — not just a prototype

### What we learned

How FDA adverse event reporting actually works, and where its limits are How to reliably parse real-world medical documents (encoding issues, large files, messy formatting) How to turn statistics into clear, trustworthy clinical language without oversimplifying How to use Sentry to catch slowness, not just crashes — critical in a healthcare context How much careful I/O handling and error recovery matters once you're handling real medical data

### What's next

A faster way for individual doctors to onboard — no IT department or hospital procurement needed More data sources — FDA drug labels, contraindication databases, clinical literature A second opinion mode so a doctor can quickly sanity-check a prescribing decision before signing off Mobile app so a doctor can use it at the bedside, not just at a desk

## README (from the GitHub repository)

# 🔬 Drug Interaction Checker

A hackathon-ready tool for doctors to check if a drug combination is dangerous for a specific patient using **real FDA adverse event data**.

## 🎯 What It Does

1. **Doctor enters:** 2 drugs, patient age/sex/conditions/meds
2. **System queries:** openFDA FAERS database (30M+ adverse events)
3. **Calculates:** Statistical signal (PRR) for dangerous combinations
4. **Finds:** Real similar patient cases from FDA data
5. **Generates:** AI summaries & clinical documentation (with Claude API)

**Result:** Beautiful progressive reveal showing investigation happening in real-time

## 🏗️ Architecture

```
Frontend (React + Framer Motion)
    ↓ (axios calls)
Backend (FastAPI + async)
    ↓
openFDA API (real data, no key required)
    ↓
Claude API (optional, for narratives)
```

## 📦 What's Built

### PARTS 1-3: Core Functionality (Ready Now ✅)
- **PART 1:** openFDA query layer (1,600+ reports tested)
- **PART 2:** PRR signal detection (266 elevated signals found)
- **PART 3:** Patient similarity scorer (5 similar cases found)

### PARTS 4-7: Backend Complete ✅
- **PART 4:** Claude AI integration (narratives + notes)
- **PART 5:** Confidence/data-quality handling
- **PART 6:** Arize Phoenix observability
- **PART 7:** Complete FastAPI endpoints

### PARTS 8-15: Beautiful Frontend ✅
- **PART 8:** Check combination form
- **PART 9:** Live query progress indicator
- **PART 10:** Signal results table
- **PART 11:** Similar cases cards (staggered animation)
- **PART 12:** AI narrative summary
- **PART 13:** Copy-ready clinical note
- **PART 14:** Progressive reveal container
- **PART 15:** Framer Motion animations

## 🚀 Quick Start

### Prerequisites
- Python 3.9+
- Node.js 16+

### Backend Setup

```bash
# Install Python dependencies
cd backend
python3 -m pip install -r requirements.txt

# Copy env template and add your APIs (optional)
cp .env.example .env

# Run backend
python3 app.py
# Runs on http://localhost:8000
```

### Frontend Setup

```bash
# Install Node dependencies
cd frontend
npm install

# Run dev server
npm run dev
# Opens http://localhost:3000
```

### Test PARTS 1-3 (No API key needed!)

```bash
cd backend
python3 test_parts_1_to_3.py
```

Expected output:
- 1,628 Warfarin + Ibuprofen reports
- 266 elevated signals
- 5 similar patient cases found

## 🔑 Optional: Add Claude (PART 4)

1. Buy $5 API credit from https://console.anthropic.com/account/keys
2. Update `.env`:
   ```
   ANTHROPIC_API_KEY=your_key_here
   ```
3. Restart backend
4. Claude narratives & clinical notes auto-activate

**Cost:** ~$0.006 per drug check (833 checks for $5)

## 📊 Test Case: Warfarin + Ibuprofen

A real, dangerous combination. Expected results:

```
✅ 1,628 combination reports
⚠️  266 elevated signals
🔴 Top: INR increased (PRR 218.89x vs Warfarin)
👥 Similar cases found: 5
```

Try with patient profile:
- Age: 67
- Sex: Male
- Conditions: Atrial fibrillation, Hypertension
- Current meds: Warfarin, Metoprolol

## 🎨 Frontend Features

- **Progressive Reveal UI:** Results appear step-by-step as data loads
- **Smooth Animations:** Framer Motion for polished feel
- **Responsive Design:** Tailwind CSS for all screen sizes
- **Copy-Ready Clinical Note:** One-click clipboard for EHR integration
- **Real-Time Progress:** Visual indicators showing what's happening

## 📈 Data Confidence Handling

```
HIGH (≥30 reports):   Results reliable
MODERATE (10-29):     Interpret cautiously  
LOW (<10):            May be random, flag explicitly
```

## ⚠️ Known Limitations

- Simplified PRR (not Omega shrinkage/BCPNN used in published research)
- Patient data manually entered (not from real EHR)
- FAERS narratives vary in availability/quality
- Tool is for clinical **review**, not **recommendations**

## 🏥 FDA Data

- Source: openFDA Drug Adverse Event API
- Database: FAERS (30M+ reports since 2004)
- Updates: Daily
- Coverage: US adverse events + international
- No API key required for low-volume queries

## 📚 Example Reactions Detected

For Warfarin + Ibuprofen:
- International Normalised Ratio increased (PRR: 218×)
- Completed suicide (PRR: 999×)
- Pulmonary embolism (PRR: 231×)
- Gastrointestinal bleeding (detected reliably)

## 🎯 Next Steps for Hackathon

1. ✅ Run both servers
2. ✅ Test form with Warfarin + Ibuprofen
3. ✅ Watch progressive reveal animation
4. ✅ Copy clinical note to clipboard
5. 🎁 (Optional) Add Claude API key for AI summaries

## 📝 Architecture Notes

**Why FastAPI + async?**
- Parallel openFDA queries (fast)
- Graceful Claude API calls (slow network-bound)
- Real-time feel with progress updates

**Why Framer Motion?**
- Industry-standard React animations
- 60fps smooth reveals
- Small bundle (~45kb)

**Why Tailwind?**
- Rapid styling (no CSS writing)
- Consistent design tokens
- Mobile-first responsive

## 🔗 Resources

- [openFDA API Docs](https://open.fda.gov/apis/drug/event/)
- [Anthropic Claude API](https://console.anthropic.com)
- [FAERS Database](https://fis.fda.gov/sense/app/955320cf-cc2d-402f-a7f3-67ce44754cab)
- [PRR Methods](https://pharmacovigilance.nhs.uk/signal-detection/)

## 📜 License

Built for hackathon. Use at your own risk. Not for clinical decisions.

---

**Built with:** Python, FastAPI, React, Tailwind, Framer Motion, openFDA, Claude AI

**Status:** Ready for demo 🚀


## Detected evidence (automated analysis)

Indexed codebase: 199 recognized source files, 818 KB.
- Anthropic (technology) — detected in the code
- CSS (language) — detected in the code
- FastAPI (technology) — detected in the code
- HTML (language) — detected in the code
- JavaScript (language) — detected in the code
- Python (language) — detected in the code
- React (technology) — detected in the code
- Tailwind CSS (technology) — detected in the code
- AI coding agent: Claude Code — evidence: config files committed to the repository; commit authorship or trailers

## Codebase structure (from repository index)

### Files (120 of 217)

```
.agents/skills/phoenix-cli/references/axial-coding.md
.agents/skills/phoenix-cli/references/open-coding.md
.agents/skills/phoenix-cli/SKILL.md
.agents/skills/phoenix-evals/references/axial-coding.md
.agents/skills/phoenix-evals/references/common-mistakes-python.md
.agents/skills/phoenix-evals/references/error-analysis-multi-turn.md
.agents/skills/phoenix-evals/references/error-analysis.md
.agents/skills/phoenix-evals/references/evaluate-dataframe-python.md
.agents/skills/phoenix-evals/references/evaluators-code-python.md
.agents/skills/phoenix-evals/references/evaluators-code-typescript.md
.agents/skills/phoenix-evals/references/evaluators-custom-templates.md
.agents/skills/phoenix-evals/references/evaluators-llm-python.md
.agents/skills/phoenix-evals/references/evaluators-llm-typescript.md
.agents/skills/phoenix-evals/references/evaluators-overview.md
.agents/skills/phoenix-evals/references/evaluators-pre-built.md
.agents/skills/phoenix-evals/references/evaluators-rag.md
.agents/skills/phoenix-evals/references/experiments-datasets-python.md
.agents/skills/phoenix-evals/references/experiments-datasets-typescript.md
.agents/skills/phoenix-evals/references/experiments-overview.md
.agents/skills/phoenix-evals/references/experiments-running-python.md
.agents/skills/phoenix-evals/references/experiments-running-typescript.md
.agents/skills/phoenix-evals/references/experiments-synthetic-python.md
.agents/skills/phoenix-evals/references/experiments-synthetic-typescript.md
.agents/skills/phoenix-evals/references/fundamentals-anti-patterns.md
.agents/skills/phoenix-evals/references/fundamentals-model-selection.md
.agents/skills/phoenix-evals/references/fundamentals.md
.agents/skills/phoenix-evals/references/observe-sampling-python.md
.agents/skills/phoenix-evals/references/observe-sampling-typescript.md
.agents/skills/phoenix-evals/references/observe-tracing-setup.md
.agents/skills/phoenix-evals/references/production-continuous.md
.agents/skills/phoenix-evals/references/production-guardrails.md
.agents/skills/phoenix-evals/references/production-overview.md
.agents/skills/phoenix-evals/references/setup-python.md
.agents/skills/phoenix-evals/references/setup-typescript.md
.agents/skills/phoenix-evals/references/validation-evaluators-python.md
.agents/skills/phoenix-evals/references/validation-evaluators-typescript.md
.agents/skills/phoenix-evals/references/validation.md
.agents/skills/phoenix-evals/SKILL.md
.agents/skills/phoenix-tracing/README.md
.agents/skills/phoenix-tracing/references/annotations-overview.md
.agents/skills/phoenix-tracing/references/annotations-python.md
.agents/skills/phoenix-tracing/references/annotations-typescript.md
.agents/skills/phoenix-tracing/references/fundamentals-flattening.md
.agents/skills/phoenix-tracing/references/fundamentals-overview.md
.agents/skills/phoenix-tracing/references/fundamentals-required-attributes.md
.agents/skills/phoenix-tracing/references/fundamentals-universal-attributes.md
.agents/skills/phoenix-tracing/references/instrumentation-atif-python.md
.agents/skills/phoenix-tracing/references/instrumentation-auto-python.md
.agents/skills/phoenix-tracing/references/instrumentation-auto-typescript.md
.agents/skills/phoenix-tracing/references/instrumentation-manual-python.md
.agents/skills/phoenix-tracing/references/instrumentation-manual-typescript.md
.agents/skills/phoenix-tracing/references/metadata-python.md
.agents/skills/phoenix-tracing/references/metadata-typescript.md
.agents/skills/phoenix-tracing/references/production-python.md
.agents/skills/phoenix-tracing/references/production-typescript.md
.agents/skills/phoenix-tracing/references/projects-python.md
.agents/skills/phoenix-tracing/references/projects-typescript.md
.agents/skills/phoenix-tracing/references/sessions-python.md
.agents/skills/phoenix-tracing/references/sessions-typescript.md
.agents/skills/phoenix-tracing/references/setup-python.md
.agents/skills/phoenix-tracing/references/setup-typescript.md
.agents/skills/phoenix-tracing/references/span-agent.md
.agents/skills/phoenix-tracing/references/span-chain.md
.agents/skills/phoenix-tracing/references/span-embedding.md
.agents/skills/phoenix-tracing/references/span-evaluator.md
.agents/skills/phoenix-tracing/references/span-guardrail.md
.agents/skills/phoenix-tracing/references/span-llm.md
.agents/skills/phoenix-tracing/references/span-reranker.md
.agents/skills/phoenix-tracing/references/span-retriever.md
.agents/skills/phoenix-tracing/references/span-tool.md
.agents/skills/phoenix-tracing/SKILL.md
.claude/scheduled_tasks.lock
.gitignore
backend.py
backend/.agents/skills/phoenix-cli/references/axial-coding.md
backend/.agents/skills/phoenix-cli/references/open-coding.md
backend/.agents/skills/phoenix-cli/SKILL.md
backend/.agents/skills/phoenix-evals/references/axial-coding.md
backend/.agents/skills/phoenix-evals/references/common-mistakes-python.md
backend/.agents/skills/phoenix-evals/references/error-analysis-multi-turn.md
backend/.agents/skills/phoenix-evals/references/error-analysis.md
backend/.agents/skills/phoenix-evals/references/evaluate-dataframe-python.md
backend/.agents/skills/phoenix-evals/references/evaluators-code-python.md
backend/.agents/skills/phoenix-evals/references/evaluators-code-typescript.md
backend/.agents/skills/phoenix-evals/references/evaluators-custom-templates.md
backend/.agents/skills/phoenix-evals/references/evaluators-llm-python.md
backend/.agents/skills/phoenix-evals/references/evaluators-llm-typescript.md
backend/.agents/skills/phoenix-evals/references/evaluators-overview.md
backend/.agents/skills/phoenix-evals/references/evaluators-pre-built.md
backend/.agents/skills/phoenix-evals/references/evaluators-rag.md
backend/.agents/skills/phoenix-evals/references/experiments-datasets-python.md
backend/.agents/skills/phoenix-evals/references/experiments-datasets-typescript.md
backend/.agents/skills/phoenix-evals/references/experiments-overview.md
backend/.agents/skills/phoenix-evals/references/experiments-running-python.md
backend/.agents/skills/phoenix-evals/references/experiments-running-typescript.md
backend/.agents/skills/phoenix-evals/references/experiments-synthetic-python.md
backend/.agents/skills/phoenix-evals/references/experiments-synthetic-typescript.md
backend/.agents/skills/phoenix-evals/references/fundamentals-anti-patterns.md
backend/.agents/skills/phoenix-evals/references/fundamentals-model-selection.md
backend/.agents/skills/phoenix-evals/references/fundamentals.md
backend/.agents/skills/phoenix-evals/references/observe-sampling-python.md
backend/.agents/skills/phoenix-evals/references/observe-sampling-typescript.md
backend/.agents/skills/phoenix-evals/references/observe-tracing-setup.md
backend/.agents/skills/phoenix-evals/references/production-continuous.md
backend/.agents/skills/phoenix-evals/references/production-guardrails.md
backend/.agents/skills/phoenix-evals/references/production-overview.md
backend/.agents/skills/phoenix-evals/references/setup-python.md
backend/.agents/skills/phoenix-evals/references/setup-typescript.md
backend/.agents/skills/phoenix-evals/references/validation-evaluators-python.md
backend/.agents/skills/phoenix-evals/references/validation-evaluators-typescript.md
backend/.agents/skills/phoenix-evals/references/validation.md
backend/.agents/skills/phoenix-evals/SKILL.md
backend/.agents/skills/phoenix-tracing/README.md
backend/.agents/skills/phoenix-tracing/references/annotations-overview.md
backend/.agents/skills/phoenix-tracing/references/annotations-python.md
backend/.agents/skills/phoenix-tracing/references/annotations-typescript.md
backend/.agents/skills/phoenix-tracing/references/fundamentals-flattening.md
backend/.agents/skills/phoenix-tracing/references/fundamentals-overview.md
backend/.agents/skills/phoenix-tracing/references/fundamentals-required-attributes.md
backend/.agents/skills/phoenix-tracing/references/fundamentals-universal-attributes.md
[97 more files omitted for size]
```

### Dependencies

- backend/requirements.txt: anthropic@==0.25.0, arize-phoenix@==1.3.0, fastapi@==0.104.1, httpx@==0.25.0, openinference-instrumentation-anthropic@==0.1.16, opentelemetry-api@==1.21.0, opentelemetry-exporter-otlp@==0.42b0, opentelemetry-sdk@==1.21.0, pydantic@==2.5.0, PyPDF2@==4.0.1, python-dotenv@==1.0.0, python-multipart@==0.0.6, sentry-sdk[fastapi]@>=2.0.0, uvicorn@==0.24.0
- frontend/package.json: @vitejs/plugin-react@^4.2.1, autoprefixer@^10.4.16, axios@^1.6.0, clsx@^2.0.0, framer-motion@^11.0.0, lucide-react@^0.292.0, postcss@^8.4.31, react@^18.2.0, react-dom@^18.2.0, react-force-graph-2d@^1.29.1, recharts@^3.8.1, tailwindcss@^3.4.1, vite@^5.0.8
- package.json: react@^18.2.0, react-dom@^18.2.0, react-force-graph-2d@^1.29.1, react-scripts@5.0.1
- requirements.txt: arize-phoenix@==1.3.0, fastapi@==0.104.1, google-generativeai@==0.3.0, httpx@==0.25.0, pydantic@==2.5.0, python-dotenv@==1.0.0, sentry-sdk[fastapi]@>=2.0.0, uvicorn@==0.24.0

### Recent commits (newest first)

- Integrate Sentry monitoring, add multi-report uploads, improve UI/UX, optimize API performance
- UI improvements: fix file uploads, update colors, add search, simplify text, remove proof section
- Fix file upload issues and add multi-report support with test data
- Add Sentry monitoring
- Fix upload flow duplication bug caused by React.StrictMode double-invoke
- Merge origin/main with upload flow
- Remove .env.example for security
- Document correct backend startup: use backend/app.py
- Fix API route: restore /api/check-combination endpoint
- Enable Phoenix Cloud tracing with register() and clean up
- Optimize UX and enhance data transparency
- Merge remote main, keeping local design system changes
- Redesign app with premium biotech aesthetic and enhance FileUploadPage
- Update button styling to biotech palette in Similar Cases
- Apply biotech design palette to App header and layout
- Elevate landing page to premium biotech aesthetic
- Clarify section names to avoid confusion
- Remove duplicate Copy Report ID button
- Add direct OpenFDA API link for JSON verification
- Merge remote main, keeping local versions

## Key source files (fetched from GitHub, selected and truncated for size)

### PROOF_FOR_JUDGES.md

```markdown
# 🔬 Drug Interaction Checker - Proof of Real FDA Data

## How to Verify This App Uses REAL FDA Data

### Quick Demo (60 seconds)

#### Step 1: Run the test script
```bash
cd /Users/anushaagarwal/drug-interaction-checker
python3 test_fda_api.py
```

**Shows:**
- ✅ Direct connection to FDA API
- ✅ Status 200 (success)
- ✅ **1,628 real Warfarin + Ibuprofen adverse event reports**
- ✅ Real patient data (age 82, serious event, blood clot)

---

### Step 2: View Raw FDA Data in Browser
Visit this endpoint directly:
```
http://localhost:8000/debug/fda-raw/warfarin/ibuprofen
```

**Shows:**
- Drug combination: Warfarin + Ibuprofen
- Total reports found: **1,628**
- Sample patient reports with real data
- Actual adverse reactions documented

---

### Step 3: Use the Full App
1. Go to `http://localhost:3000`
2. Enter:
   - Drug A: Warfarin
   - Drug B: Ibuprofen
   - Age: 67
3. Click "Check Combination"

**Shows:**
- Real FDA data being processed
- 266 elevated signals detected
- Statistical analysis (PRR scores)
- Similar real patient cases

---

## 🔐 How We Verify It's Real (Not Fake Data)

### The FDA API is Public and Verifiable
**All data comes from:** `https://api.fda.gov/drug/event.json`

You can verify this yourself:
```bash
# Open in browser or curl
https://api.fda.gov/drug/event.json?search=patient.drug.medicinalproduct:"WARFARIN"+AND+patient.drug.medicinalproduct:"IBUPROFEN"&limit=1
```

This will show you the SAME data our app uses.

### What Makes It Real:
✅ **Government Source** - FDA's official FAERS database  
✅ **Public API** - Anyone can query it (no secret sauce)  
✅ **Verifiable Records** - Each report has SafetyReportID  
✅ **Real Patient Cases** - Age, sex, reactions, outcomes  
✅ **No Mock Data** - We literally call FDA's servers in real-time  

---

## 📊 Example Output

When you query "Warfarin + Ibuprofen", the system finds:

```json
{
  "status": "success",
  "drug_a": "warfarin",
  "drug_b": "ibuprofen",
  "total_reports": 1628,
  "sample_reactions": [
    "Deep vein thrombosis",
    "International normalised ratio increased",
    "Pulmonary embolism",
    "Bleeding"
  ]
}
```

**Each report contains:**
- Patient age & sex
- Serious event indicator (hospitalization, death, etc.)
- Actual adverse reactions experienced
- Transmission date to FDA
- Report ID (can be looked up in FDA database)

---

## 🎯 For Judges: Verification Checklist

- [ ] Run `python3 test_fda_api.py` - see 1,628 real reports
- [ ] Visit `http://localhost:8000/debug/fda-raw/warfarin/ibuprofen` - see raw JSON
- [ ] Use the app to check a combination - see FDA data processed
- [ ] Query FDA API directly to verify we're using official data
- [ ] Check SafetyReportID in our results against FDA's database

---

## 💡 Why This Matters for a Hackathon

**Most drug interaction checkers use:**
- ❌ Medical textbooks (outdated, limited)
- ❌ Pharmacy databases (expensive, limited access)
- ❌ Simulated data (fake examples)

**Our app uses:**
- ✅ **Real adverse event re
[truncated — 598 more characters]
```

### TESTING_FILE_UPLOAD.md

```markdown
# 🧪 Testing File Upload Feature

## What We Built

Your app now has **TWO ways** to input patient data:

### Method 1: Manual Entry ✏️
Traditional form - type in each field

### Method 2: Upload Documents 📄  
Upload patient report + prescription → Claude AI extracts structured data

---

## How to Test File Upload

### Step 1: Go to App
```
http://localhost:3000
```

### Step 2: Click "📄 Upload Documents" Tab
You'll see two file upload boxes:
- Patient Medical Report
- Proposed Prescription

### Step 3: Upload Mock Files
We created mock files in your project:
```
/Users/anushaagarwal/drug-interaction-checker/MOCK_PATIENT_REPORT.txt
/Users/anushaagarwal/drug-interaction-checker/MOCK_PRESCRIPTION.txt
```

Upload both files.

### Step 4: Click "📋 Extract Patient Data"
The backend will:
1. Read both text files
2. Send to Claude AI
3. Parse structured fields:
   - Patient age: **67**
   - Sex: **Male**  
   - Conditions: **Atrial fibrillation, Hypertension, Type 2 Diabetes**
   - Current meds: **Warfarin, Metoprolol, Lisinopril, Metformin**
   - Proposed drug: **Ibuprofen**
   - Illness: **Lower back pain from degenerative disc disease**

### Step 5: Review & Confirm
The app shows extracted fields. You can:
- ✓ Accept as-is (click "Confirm & Check Risk")
- ✏️ Edit any fields before checking
- ↺ Upload different files

### Step 6: See Real FDA Results
Once confirmed, it queries FDA with:
- **Drug A:** Ibuprofen (proposed)
- **Drug B:** Warfarin (first in current meds)
- **Patient context:** 67M, AFib, on anticoagulation

Results will show:
- ⚠️ **ELEVATED SIGNALS** found
- Real adverse event data
- Similar patient cases
- Risk assessment

---

## Why This Matters

### Traditional Drug Checker
```
"Check Warfarin + Ibuprofen"
→ Shows generic data
→ No patient context
→ Not clinically useful
```

### Your App (Now)
```
1. Doctor uploads: Patient report + New prescription
2. AI auto-extracts: Age, conditions, current meds
3. FDA query with context: "67-year-old on anticoagulation"
4. Real results: "266 elevated signals - HIGH RISK"
5. Doctor sees: Similar real cases from FDA
```

---

## Claude AI Extraction

When you upload documents, here's what happens:

**Input to Claude:**
```
"I have a 67-year-old male with:
- Atrial fibrillation (on Warfarin)
- Hypertension
- Type 2 Diabetes
He's taking: Warfarin, Metoprolol, Lisinopril, Metformin
Doctor wants to prescribe: Ibuprofen
Reason: Lower back pain"
```

**Claude Returns:**
```json
{
  "patient_age": 67,
  "patient_sex": "Male",
  "patient_conditions": ["Atrial fibrillation", "Hypertension", "Type 2 Diabetes"],
  "patient_current_meds": ["Warfarin", "Metoprolol", "Lisinopril", "Metformin"],
  "proposed_drug": "Ibuprofen",
  "illness_indication": "Lower back pain from degenerative disc disease",
  "confidence": 0.95
}
```

---

## For Your Hackathon Demo

**Show the Judges:**

1. **"Manual Entry" Tab**
   - Type: Warfarin + Ibuprofen
   - Shows: Generic drug combo risk

2. **"Upload Documents" Tab**  
 
[truncated — 2009 more characters]
```

### requirements.txt

```
fastapi==0.104.1
uvicorn==0.24.0
httpx==0.25.0
google-generativeai==0.3.0
pydantic==2.5.0
python-dotenv==1.0.0
arize-phoenix==1.3.0
sentry-sdk[fastapi]>=2.0.0

```

### package.json

```
{
  "name": "splice-sentinel",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "react": "^18.2.0",
    "react-dom": "^18.2.0",
    "react-force-graph-2d": "^1.29.1",
    "react-scripts": "5.0.1"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject"
  },
  "eslintConfig": {
    "extends": [
      "react-app"
    ]
  },
  "browserslist": {
    "production": [
      ">0.2%",
      "not dead",
      "not op_mini all"
    ],
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ]
  }
}

```

### backend/requirements.txt

```
fastapi==0.104.1
uvicorn==0.24.0
httpx==0.25.0
anthropic==0.25.0
python-dotenv==1.0.0
pydantic==2.5.0
arize-phoenix==1.3.0
openinference-instrumentation-anthropic==0.1.16
opentelemetry-api==1.21.0
opentelemetry-sdk==1.21.0
opentelemetry-exporter-otlp==0.42b0
PyPDF2==4.0.1
python-multipart==0.0.6
sentry-sdk[fastapi]>=2.0.0

```

### frontend/package.json

```
{
  "name": "drug-interaction-checker",
  "version": "1.0.0",
  "private": true,
  "type": "module",
  "scripts": {
    "dev": "vite",
    "build": "vite build",
    "preview": "vite preview"
  },
  "dependencies": {
    "axios": "^1.6.0",
    "clsx": "^2.0.0",
    "framer-motion": "^11.0.0",
    "lucide-react": "^0.292.0",
    "react": "^18.2.0",
    "react-dom": "^18.2.0",
    "react-force-graph-2d": "^1.29.1",
    "recharts": "^3.8.1"
  },
  "devDependencies": {
    "@vitejs/plugin-react": "^4.2.1",
    "autoprefixer": "^10.4.16",
    "postcss": "^8.4.31",
    "tailwindcss": "^3.4.1",
    "vite": "^5.0.8"
  }
}

```

### src/index.js

```javascript
import React from 'react';
import ReactDOM from 'react-dom/client';
import './App.css';
import App from './App';

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
);

```

### src/App.jsx

```javascript
import { useEffect, useMemo, useRef, useState } from 'react';
import ForceGraph2D from 'react-force-graph-2d';
import './App.css';

const DEFAULT_VISIBLE_REACTIONS = 20;

const getReactionPRR = (data) => Number(data?.prr) || 0;
const getReactionCount = (data) => Number(data?.count) || 0;

function ReactionsNetworkGraph({ result }) {
  const graphRef = useRef(null);
  const containerRef = useRef(null);
  const [showAll, setShowAll] = useState(false);
  const [hoverNode, setHoverNode] = useState(null);
  const [selectedNode, setSelectedNode] = useState(null);
  const [graphWidth, setGraphWidth] = useState(820);
  const activeNode = hoverNode || selectedNode;

  const reactions = useMemo(() => Object.entries(result.elevated_reactions || {}), [result.elevated_reactions]);
  const visibleReactions = useMemo(() => {
    return [...reactions]
      .sort(([, a], [, b]) => {
        const prrDelta = getReactionPRR(b) - getReactionPRR(a);
        return prrDelta !== 0 ? prrDelta : getReactionCount(b) - getReactionCount(a);
      })
      .slice(0, showAll ? reactions.length : DEFAULT_VISIBLE_REACTIONS);
  }, [reactions, showAll]);

  const graphData = useMemo(() => {
    const maxCount = Math.max(...visibleReactions.map(([, data]) => getReactionCount(data)), 1);
    const maxPRR = Math.max(...visibleReactions.map(([, data]) => getReactionPRR(data)), 1);
    const drugAId = `drug:${result.drug_a}:a`;
    const drugBId = `drug:${result.drug_b}:b`;

    const nodes = [
      {
        id: drugAId,
        name: result.drug_a,
        type: 'drug',
        count: result.total_reports,
        val: 15,
        fx: -48,
        fy: 0,
      },
      {
        id: drugBId,
        name: result.drug_b,
        type: 'drug',
        count: result.total_reports,
        val: 15,
        fx: 48,
        fy: 0,
      },
    ];
    const links = [];

    visibleReactions.forEach(([reaction, data], index) => {
      const count = getReactionCount(data);
      const prr = getReactionPRR(data);
      const strength = Math.max(0.12, ((Math.sqrt(count / maxCount) + Math.sqrt(Math.min(prr, maxPRR) / maxPRR)) / 2));
      const reactionId = `reaction:${reaction}:${index}`;

      nodes.push({
        id: reactionId,
        name: reaction,
        type: 'reaction',
        count,
        prr,
        elevated: prr >= 2,
        val: 6 + Math.sqrt(count / maxCount) * 11,
      });

      links.push(
        { source: drugAId, target: reactionId, value: strength, prr, count },
        { source: drugBId, target: reactionId, value: strength, prr, count }
      );
    });

    return { nodes, links };
  }, [result, visibleReactions]);

  const connectedNodeIds = useMemo(() => {
    if (!selectedNode) return new Set();

    const ids = new Set([selectedNode.id]);
    graphData.links.forEach((link) => {
      const sourceId = typeof link.source === 'object' ? link.source.id : link.source;
      const targetId = typeof link.target === 'object' ? link.target.id : link.target;
      if (sourceId === selectedNode.id) ids.add(targetId);
      if (targetId === selectedNode.id) ids.add(sourceId);
    });
    return ids;
  }, [graphData.links, selectedNode]);

  useEffect(() => {
    const updateWidth = () => {
      if (containerRef.current) {
        setGraphWidth(Math.max(320, Math.floor(containerRef.current.clientWidth)));
      }
    };

    updateWidth();
    window.addEventListener('resize', updateWidth);
    return () => window.removeEventListener('resize', updateWidth);
  }, []);

  useEffect(() => {
    const graph = graphRef.current;
    if (!graph) return;

    graph.d3Force('link')?.distance((link) => 220 - link.value * 80);
    graph.d3Force('charge')?.strength(-360);
    graph.d3Force('center')?.strength?.(0.06);
  }, [graphData]);

  return (
    <div className="reactions-network">
      <div className="network-header">
        <div>
          <h3>Reaction Association Network</h3>
          <p>Hover for count/PRR details. Click a node to isolate its direct connections.</p>
        </div>
        {reactions.length > DEFAULT_VISIBLE_REACTIONS && (
          <button
            type="button"
            className="network-toggle"
            onClick={() => {
              setShowAll((current) => !current);
              setSelectedNode(null);
              graphRef.current?.zoomToFit?.(500, 40);
            }}
          >
            {showAll ? `Show Top ${DEFAULT_VISIBLE_REACTIONS}` : `Show All ${reactions.length}`}
          </button>
        )}
      </div>

      <div className="network-legend">
        <span><b className="legend-drug" /> Drug</span>
        <span><b className="legend-reaction" /> Reaction</span>
        <span><b className="legend-signal" /> PRR >= 2</span>
      </div>

      <div ref={containerRef} className="network-canvas">
        {activeNode && (
          <div className="network-tooltip">
            <div className="network-tooltip-kind">
              {activeNode.type === 'drug' ? 'Drug node' : activeNode.elevated ? 'Elevated reaction' : 'Reaction'}
            </div>
            <div className="network-tooltip-title">{activeNode.name}</div>
            {activeNode.type === 'drug' ? (
              <div className="network-tooltip-row">Combination reports: {Number(activeNode.count || 0).toLocaleString()}</div>
            ) : (
              <>
                <div className="network-tooltip-row">Reports: {Number(activeNode.count || 0).toLocaleString()}</div>
                <div className="network-tooltip-row">PRR: {activeNode.prr.toFixed(2)}x</div>
              </>
            )}
          </div>
        )}
        <ForceGraph2D
          ref={graphRef}
          graphData={graphData}
          width={graphWidth}
          height={540}
          backgroundColor="#0A0A0B"
          cooldownTicks={140}
          d3VelocityDecay={0.32}
          onEngineStop={() => graphRef.current?.zoomToFit?.(600, 74)}
          linkColor={(link) => {
            const sourceId = typeof link.source === 'o
[truncated — 9185 more characters]
```

### frontend/src/index.jsx

```javascript
import ReactDOM from 'react-dom/client'
import App from './App.jsx'
import './App.css'

ReactDOM.createRoot(document.getElementById('root')).render(
  <App />
)

```

### backend/app.py

```python
"""
Drug Interaction Checker - FastAPI Backend

A hackathon tool for doctors to check if a drug combination is dangerous
for a specific patient, using real FDA adverse event data.

Architecture:
  PART 1: openFDA query service
  PART 2: Signal detection (PRR)
  PART 3: Patient similarity scorer
  PART 4: Claude narrative + note generation
  PART 5: Confidence handling
  PART 6: Arize Phoenix observability
  PART 7: FastAPI endpoints

Run:
  uvicorn app:app --reload --host 0.0.0.0 --port 8000
"""

# ============================================================================
# PART 6: Initialize Phoenix tracing FIRST (before importing services)
# Using OpenTelemetry directly for Phoenix Cloud (avoids hdbscan dependency)
# ============================================================================
import os
import sys
from dotenv import load_dotenv

load_dotenv()

from sentry_utils import capture_exception, init_sentry, track_operation

init_sentry()

# ============================================================================
# Now import services (they will use instrumented Anthropic)
# ============================================================================
from fastapi import FastAPI, HTTPException, UploadFile, File, WebSocket
from fastapi.middleware.cors import CORSMiddleware
from pydantic import BaseModel
from typing import Optional, List
import tempfile
import json
from datetime import datetime

from config import ANTHROPIC_API_KEY, HOST, PORT, PRR_THRESHOLD
from services.openfda_service import check_drug_combination
from services.signal_detection import find_elevated_signals, calculate_data_confidence
from services.similarity_scorer import find_similar_cases
from services.claude_service import summarize_narrative_pattern, generate_clinical_note
from services.document_parser import parse_patient_data_with_claude, get_mock_patient_data
from data_store import store_query_result, get_trending_signals, get_combo_history, get_new_signals_since

# Initialize FastAPI app
app = FastAPI(
    title="Drug Interaction Checker",
    description="Real FDA adverse event data for drug combinations",
    version="1.0.0"
)

# CORS middleware for React frontend
app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)


# ============================================================================
# Request/Response Models
# ============================================================================

class DrugCombinationRequest(BaseModel):
    """Input: Doctor's check request."""
    drug_a: str
    drug_b: Optional[str] = None  # Optional: if provided, use it; otherwise use patient_current_meds
    patient_age: Optional[int] = None
    patient_sex: Optional[str] = None  # "M", "F", or None
    patient_conditions: Optional[List[str]] = None
    patient_current_meds: Optional[List[str]] = None


class ReactionSignal(BaseModel):
    """A flagged reaction with PRR data."""
    reaction: str
    combo_count: int
    rate_in_combo: float
    prr_vs_drug_a: float
    prr_vs_drug_b: float


class SimilarCase(BaseModel):
    """A similar patient case from FAERS."""
    similarity_score: float
    reason: str
    reaction: str
    safetyreportid: str
    days_to_onset: Optional[int] = None
    case_age: Optional[int] = None
    case_sex: Optional[str] = None


class CheckResult(BaseModel):
    """Result of drug combination check (all PARTS combined)."""
    drug_a: str
    drug_b: str
    combo_total: int
    confidence: str
    confidence_message: str
    signals: List[ReactionSignal]
    similar_cases: List[SimilarCase] = []  # PART 3
    narrative_summary: str = ""  # PART 4
    clinical_note: str = ""  # PART 4


# ============================================================================
# Endpoints
# ============================================================================

@app.get("/health")
async def health():
    """Health check."""
    return {"status": "ok", "service": "drug-interaction-checker"}


@app.get("/debug/fda-raw/{drug_a}/{drug_b}")
async def debug_fda_raw(drug_a: str, drug_b: str):
    """Debug endpoint: Show raw FDA data for a drug combination."""
    try:
        data = await check_drug_combination(drug_a.upper(), drug_b.upper())
        return {
            "status": "success",
            "drug_a": drug_a,
            "drug_b": drug_b,
            "total_reports": data["combo_total"],
            "sample_reports": data["combo_reports"][:3] if data["combo_reports"] else [],
            "total_reactions_found": len(data["combo_reactions"]),
            "sample_reactions": list(data["combo_reactions"].keys())[:10],
            "raw_fda_response": data["combo_reports"][:1] if data["combo_reports"] else None,
            "note": "This is raw FDA FAERS data - real adverse event reports"
        }
    except Exception as e:
        capture_exception(e, endpoint="/debug/fda-raw", drug_a=drug_a, drug_b=drug_b)
        raise HTTPException(status_code=502, detail=f"FDA API error: {str(e)}")


@app.get("/debug/similar-cases/{drug_a}/{drug_b}")
async def debug_similar_cases(drug_a: str, drug_b: str):
    """Debug endpoint: Show how the top 5 similar cases were selected and scored."""
    try:
        data = await check_drug_combination(drug_a.upper(), drug_b.upper())
        similar_cases = data.get("similar_cases", [])

        return {
            "status": "success",
            "drug_combination": f"{drug_a.upper()} + {drug_b.upper()}",
            "total_reports_analyzed": data["combo_total"],
            "top_similar_cases_found": len(similar_cases),
            "similar_cases": [
                {
                    "rank": i + 1,
                    "similarity_score": case["similarity_score"],
                    "match_quality": "High" if case["similarity_score"] >= 50 else "Moderate" if case["similarity_score"] >= 40 else "Fair",
                    "reaction_reported": case["reaction"
[truncated — 20743 more characters]
```

[190 more indexed source files omitted to keep this export small. The full file list is in the Codebase structure section above.]