Skill: aipoch-ai/automated-soap-note-generator (version 0.1.0)

--- name: automated-soap-note-generator description: Transform unstructured clinical input (dictation, transcripts, or rough notes) into standardized SOAP (Subjective, Objective, Assessment, Plan) medical documentation. Use ONLY for initial documentation draft generation; ALL output requires physician review before entering patient records. Not for complex cases requiring nuanced clinical reasoning. allowed-tools: [Read, Write, Bash, Edit] license: MIT metadata: skill-author: AIPOCH --- # Automated SOAP Note Generator ## Overview AI-powered clinical documentation tool that converts unstructured clinical input into professionally formatted SOAP notes compliant with medical documentation standards. **Key Capabilities:** - **Intelligent Parsing**: Extracts structured information from free-text clinical narratives - **SOAP Classification**: Automatically categorizes content into Subjective, Objective, Assessment, Plan sections - **Medical Entity Recognition**: Identifies symptoms, diagnoses, medications, procedures, and anatomical locations - **Temporal Analysis**: Extracts timeline information (onset, duration, progression) - **Template Generation**: Produces standardized SOAP format suitable for EHR integration - **Multi-modal Input**: Accepts text dictation, transcripts, or clinical notes ## When to Use **✅ Use this skill when:** - Converting physician dictation into structured SOAP format for efficiency - Processing audio-to-text transcripts from patient encounters - Transforming consultation rough notes into formal documentation - Generating initial draft documentation to reduce administrative burden - Standardizing clinical encounter summaries for consistency - Creating preliminary notes for routine follow-up visits **❌ Do NOT use when:** - Input contains PHI that hasn't been de-identified for testing/training - Complex psychiatric cases requiring nuanced mental status documentation → Use specialized psychiatric documentation tools - Surgical procedures requiring operative report detail → Use `operative-report-generator` - Patient requires nuanced clinical reasoning beyond text extraction - Legal or forensic documentation requiring exact transcription → Use verbatim transcription services - Critical care situations requiring real-time precise documentation - Cases requiring differential diagnosis prioritization without physician input **⚠️ ALWAYS Required:** - Physician review and approval before entering into patient record - Verification of medical facts and clinical accuracy - Confirmation of medication names, dosages, and instructions ## Integration with Other Skills **Upstream Skills:** - `medical-scribe-dictation`: Convert physician verbal dictation to text input - `ehr-semantic-compressor`: Summarize lengthy EHR notes for SOAP generation - `dicom-anonymizer`: Prepare imaging reports for SOAP inclusion - `audio-script-writer`: Convert audio recordings to text format **Downstream Skills:** - `medical-email-polisher`: Professional communication of SOAP summaries to patients - `clinical-data-cleaner`: Standardize extracted data for research databases - `hipaa-compliance-auditor`: Verify de-identification before sharing documentation - `discharge-summary-writer`: Generate discharge summaries from SOAP encounters - `referral-letter-generator`: Create referral letters based on Assessment and Plan sections **Complete Workflow:** ``` Medical Scribe Dictation (audio→text) → Automated SOAP Note Generator (this skill) → Physician Review → EHR Entry / Medical Email Polisher (patient communication) / Referral Letter Generator (referrals) ``` ## Core Capabilities ### 1. Input Processing and Preprocessing Handle various input formats and prepare for NLP analysis: ```python from scripts.soap_generator import SOAPNoteGenerator generator = SOAPNoteGenerator() # Process text input soap_note = generator.generate( input_text="Patient presents with 2-day history of chest pain, radiating to left arm...", patient_id="P12345", encounter_date="2026-01-15", provider="Dr. Smith" ) # Process from audio transcript soap_note = generator.generate_from_transcript( transcript_path="consultation_transcript.txt", patient_id="P12345" ) ``` **Input Preprocessing Steps:** 1. **Text Cleaning**: Remove filler words ("um", "uh"), timestamps, speaker labels 2. **Sentence Segmentation**: Split into clinically meaningful segments 3. **Normalization**: Standardize abbreviations and medical shorthand 4. **Encoding Detection**: Handle various file formats (UTF-8, ASCII, etc.) **Parameters:** | Parameter | Type | Required | Description | Default | |-----------|------|----------|-------------|---------| | `input_text` | str | Yes* | Raw clinical text or dictation | None | | `transcript_path` | str | Yes* | Path to transcript file | None | | `patient_id` | str | No | Patient identifier (MUST be de-identified for testing) | None | | `encounter_date` | str | No | Date in ISO 8601 format (YYYY-MM-DD) | Current date | | `provider` | str | No | Healthcare provider name | None | | `specialty` | str | No | Medical specialty context | "general" | | `verbose` | bool | No | Include confidence scores | False | *Either `input_text` or `transcript_path` required **Best Practices:** - Always verify input text quality (clear audio → better transcription → better SOAP) - Remove patient identifiers before processing unless in secure environment - Split long encounters (>30 minutes) into logical segments - Flag ambiguous abbreviations for manual review ### 2. Medical Named Entity Recognition (NER) Identify and extract medical concepts from unstructured text: ```python # Extract entities with context entities = generator.extract_medical_entities( "Patient has history of hypertension and diabetes, currently taking lisinopril 10mg daily and metformin 500mg BID" ) # Returns structured entities: # { # "diagnoses": ["hypertension", "diabetes mellitus"], # "medications": [ # {"name": "lisinopril", "dose": "10mg", "frequency": "daily"}, # {"name": "metformin", "dose": "500mg", "frequency": "BID"} # ] # } ``` **Entity Types Recognized:** | Category | Examples | Notes | |----------|----------|-------| | **Diagnoses** | diabetes, hypertension, pneumonia | ICD-10 compatible where possible | | **Symptoms** | chest pain, headache, nausea | Includes severity modifiers | | **Medications** | metformin, lisinopril, aspirin | Extracts dose, route, frequency | | **Procedures** | ECG, CT scan, blood draw | Includes body site | | **Anatomy** | left arm, chest, abdomen | Laterality and location | | **Lab Values** | glucose 120, BP 140/90 | Units and reference ranges | | **Temporal** | yesterday, 3 days ago, chronic | Normalized to relative dates | **Common Issues and Solutions:** **Issue: Missed medications** - Symptom: Generic names not recognized (e.g., "water pill" for diuretic) - Solution: Manual review required; tool flags colloquial terms for verification **Issue: Ambiguous abbreviations** - Symptom: "SOB" could be shortness of breath or something else - Solution: Context-aware disambiguation; flag uncertain cases **Issue: Misspelled drug names** - Symptom: "metfomin" instead of "metformin" - Solution: Fuzzy matching with confidence threshold; flag low-confidence matches ### 3. SOAP Section Classification Automatically categorize sentences into appropriate SOAP sections: ```python # Classify content into SOAP sections classified = generator.classify_soap_sections( "Patient reports chest pain for 2 days. Physical exam shows BP 140/90. Likely angina. Schedule stress test and start aspirin 81mg daily." ) # Output structure: # { # "Subjective": ["Patient reports chest pain for 2 days"], # "Objective": ["Physical exam shows BP 140/90"], # "Assessment": ["Likely angina"], # "Plan": ["Schedule stress test", "start aspirin 81mg daily"] # } ``` **Classification Rules:** | Section | Content Type | Examples | |---------|--------------|----------| | **S** - Subjective | Patient-reported information | "Patient states...", "Patient reports...", "Complains of..." | | **O** - Objective | Observable/measurable findings | Vital signs, physical exam, lab results, imaging | | **A** - Assessment | Clinical interpretation | Diagnosis, differential, clinical impression | | **P** - Plan | Actions to be taken | Medications, procedures, follow-up, patient education | **Multi-label Handling:** Some sentences span multiple sections (e.g., "Patient reports chest pain [S], which was sharp and 8/10 [S], with ECG showing ST elevation [O]") - Tool splits compound sentences at conjunctions - Assigns primary and secondary labels with confidence scores **Best Practices:** - Review classification accuracy, especially for complex multi-part statements - Manually verify Assessment section (most critical for patient care) - Ensure temporal context preserved (recent vs. chronic symptoms) ### 4. Temporal Information Extraction Parse and normalize timeline information: ```python # Extract temporal relationships timeline = generator.extract_temporal_info( "Patient had chest pain starting 3 days ago, worsening since yesterday. Had similar episode 2 months ago that resolved with rest." ) # Returns: # { # "onset": "3 days ago", # "progression": "worsening", # "previous_episodes": [ # {"time": "2 months ago", "resolution": "with rest"} # ] # } ``` **Temporal Elements Extracted:** - **Onset**: When symptoms started ("2 days ago", "this morning") - **Duration**: How long symptoms lasted ("for 3 hours", "ongoing") - **Frequency**: How often symptoms occur ("daily", "intermittently") - **Progression**: Getting better/worse/stable - **Prior Episodes**: Previous similar events - **Context**: "before meals", "with exertion", "at night" **Normalization:** Converts relative dates to standardized format: - "yesterday" → Encounter date minus 1 day - "3 days ago" → Specific date calculated - "chronic" → Flagged for chronic condition tracking ### 5. Negation and Uncertainty Detection Critical for accurate medical documentation: ```python # Detect negations and uncertainties analysis = generator.analyze_certainty( "Patient denies chest pain. No shortness of breath. Possibly had fever yesterday but not sure." ) # Identifies: # - "denies chest pain" → Negative finding (important!) # - "No shortness of breath" → Negative finding # - "Possibly had fever" → Uncertain finding (flag for verification) ``` **Detection Categories:** | Type | Cues | Action | |------|------|--------| | **Negation** | denies, no, without, absent | Mark as negative finding | | **Uncertainty** | possibly, maybe, uncertain, ? | Flag for physician review | | **Hypothetical** | if, would, could | Note as conditional | | **Family History** | family history of, mother had | Separate from patient findings | **⚠️ Critical:** Negation errors are high-risk (e.g., missing "denies" → documenting symptom they don't have) - Always verify negative findings in Subjective section - Uncertain findings must be explicitly marked for review ### 6. Structured SOAP Generation Produce final formatted output: ```python # Generate complete SOAP note soap_output = generator.generate_soap_document( structured_data=classified, format="markdown", # Options: markdown, json, hl7, text include_metadata=True ) ``` **Output Format:** ```markdown # SOAP Note **Patient ID:** P12345 **Date:** 2026-01-15 **Provider:** Dr. Smith ## Subjective Patient reports [extracted symptoms with duration]. History of [chronic conditions]. Currently taking [medications]. Patient denies [negative findings]. ## Objective **Vital Signs:** [BP, HR, RR, Temp, O2Sat] **Physical Examination:** [Exam findings by system] **Laboratory/Data:** [Relevant results] ## Assessment [Primary diagnosis/differential] [Clinical reasoning summary] ## Plan 1. [Action item 1] 2. [Action item 2] 3. [Follow-up instructions] --- *Generated by AI. REQUIRES PHYSICIAN REVIEW before entry into patient record.* ``` **Export Formats:** | Format | Use Case | Notes | |--------|----------|-------| | **Markdown** | Human review, documentation | Default, readable | | **JSON** | System integration, research | Structured data | | **HL7 FHIR** | EHR integration | Healthcare standard | | **Plain Text** | Simple documentation | Minimal formatting | | **CSV** | Data analysis, research | Tabular data export | ## Complete Workflow Example **From audio dictation to reviewed SOAP note:** ```bash # Step 1: Process audio to text (using medical-scribe-dictation or external) # Assuming you have transcript: consultation.txt # Step 2: Generate SOAP note python scripts/main.py \ --input-file consultation.txt \ --patient-id P12345 \ --provider "Dr. Smith" \ --specialty "cardiology" \ --output soap_draft.md \ --format markdown # Step 3: Review output # - Open soap_draft.md # - Verify medical accuracy # - Correct any errors # - Add missing clinical reasoning # Step 4: Finalize (after physician approval) # - Copy approved content to EHR # - Or use for patient communication ``` **Python API Usage:** ```python from scripts.soap_generator import SOAPNoteGenerator from scripts.post_processor import ReviewFormatter # Initialize generator = SOAPNoteGenerator() reviewer = ReviewFormatter() # Generate draft with open("dictation.txt", "r") as f: raw_text = f.read() draft = generator.generate( input_text=raw_text, patient_id="P12345", encounter_date="2026-01-15", provider="Dr. Smith", specialty="internal_medicine" ) # Add physician review markers marked_draft = reviewer.add_review_markers(draft) # Save with warning header reviewer.save_with_disclaimer( marked_draft, output_path="soap_draft_review.md", disclaimer="REQUIRES PHYSICIAN REVIEW - NOT FOR DIRECT ENTRY" ) ``` **Expected Output Files:** ``` output/ ├── soap_draft.md # Generated SOAP note ├── entities_extracted.json # Structured medical entities ├── classification_report.txt # Confidence scores for each section └── review_checklist.md # Items requiring manual verification ``` ## Quality Checklist **Pre-generation Checks:** - [ ] Input text is legible (not garbled transcription) - [ ] Audio quality was sufficient (if from dictation) - [ ] Patient identifiers handled per HIPAA guidelines - [ ] No obvious transcription errors (medication names make sense) **During Generation:** - [ ] All medications recognized and dosages extracted - [ ] Temporal information correctly normalized - [ ] Negations properly detected (denies = negative finding) - [ ] Uncertain statements flagged for review - [ ] SOAP sections logically organized **Post-generation Review (PHYSICIAN MUST CHECK):** - [ ] **CRITICAL**: Medical facts are accurate - [ ] **CRITICAL**: Medication names, dosages, and frequencies correct - [ ] **CRITICAL**: Assessment section reflects clinical reasoning - [ ] Allergies correctly documented - [ ] Vital signs accurately transcribed - [ ] Physical exam findings complete - [ ] Plan includes all necessary actions - [ ] Follow-up instructions clear and appropriate - [ ] No fabricated information (hallucinations) **Before EHR Entry:** - [ ] Physician has reviewed and approved - [ ] Corrections made as needed - [ ] Signed/attested by responsible provider - [ ] Metadata complete (date, provider, encounter type) ## Common Pitfalls **Input Quality Issues:** - ❌ **Poor audio quality** (background noise, mumbling) → Garbled transcription → Inaccurate SOAP - ✅ Ensure quiet environment for dictation; use high-quality microphone - ❌ **Incomplete dictation** (provider trails off, changes subject) → Missing information - ✅ Dictate in complete sentences; pause between distinct thoughts - ❌ **Heavy accents or fast speech** → Transcription errors - ✅ Speak clearly; review transcription immediately if possible **Medical Accuracy Issues:** - ❌ **Medication name confusion** ("Lipitor" vs "lipid lowerer") → Wrong drug documented - ✅ Always verify medication names; use generic names when possible - ❌ **Missed negations** ("denies chest pain" → "has chest pain") → Critical error - ✅ Carefully review Subjective section for negative findings - ❌ **Temporal confusion** ("pain since yesterday" vs "pain until yesterday") → Wrong timeline - ✅ Verify onset, duration, and progression with patient - ❌ **Uncertain findings documented as certain** ("possibly pneumonia" → "pneumonia") - ✅ Flag all uncertain language for clarification **Documentation Issues:** - ❌ **Hallucinated information** (AI adds details not in input) → False documentation - ✅ Compare output directly with source material - ❌ **Missing context** ("continue meds" without specifying which ones) - ✅ Ensure plan is specific and actionable - ❌ **Generic assessments** ("patient is stable" without specifics) - ✅ Add clinical reasoning to Assessment section **Compliance Issues:** - ❌ **Entering AI-generated text without review** → Legal/medical liability - ✅ NEVER enter into patient record without physician approval - ❌ **Including PHI in unsecured processing** → HIPAA violation - ✅ Use only in HIPAA-compliant environments **Process Issues:** - ❌ **Not saving original input** → Cannot verify if questions arise - ✅ Retain original dictation/transcript - ❌ **No audit trail** → Cannot track AI involvement - ✅ Document that SOAP was AI-assisted in metadata ## Troubleshooting **Problem: Poor entity recognition** - Symptoms: Medications or diagnoses not detected - Causes: Specialized terminology, misspellings, rare conditions - Solutions: - Use generic drug names when possible - Check `references/medical_terminology.md` for supported terms - Manually add missing entities during review **Problem: Wrong SOAP classification** - Symptoms: Physical exam findings in Subjective; symptoms in Objective - Causes: Ambiguous phrasing ("Patient appears in pain") - Solutions: - Rephrase input for clarity ("Patient reports pain level 8/10") - Manually move sentences to correct sections - Check classification confidence scores **Problem: Missing temporal information** - Symptoms: All events seem to happen "now" - Causes: Unclear time references ("recently", "a while ago") - Solutions: - Use specific dates or durations in dictation - Manually add timeline during review - Ask patient for clarification on timing **Problem: Inappropriate certainty level** - Symptoms: "Possibly" removed; "definitely" added - Causes: AI over-confident in uncertain situations - Solutions: - Preserve physician's uncertainty language - Add qualifiers back during review - Flag all diagnostic statements for verification **Problem: Formatting errors in output** - Symptoms: Garbled text, wrong encoding, missing sections - Causes: Special characters, non-ASCII text, file encoding issues - Solutions: - Save input as UTF-8 - Avoid special symbols in medication names - Check output file encoding **Problem: Processing fails or hangs** - Symptoms: Script crashes, timeout errors - Causes: Very long input (>5000 words), complex nested clauses - Solutions: - Split very long encounters into sections - Simplify complex sentences - Increase timeout limit for large inputs ## References Available in `references/` directory: - `clinical_guidelines.md` - Standards for medical documentation - `sample_soap_notes.md` - Example SOAP notes by specialty - `medical_terminology.md` - Supported medical terms and abbreviations - `nlp_pipeline_documentation.md` - Technical details of NLP processing - `hipaa_compliance_guide.md` - Guidelines for safe handling of PHI - `specialty_specific_templates.md` - Templates for cardiology, orthopedics, etc. ## Scripts Located in `scripts/` directory: - `main.py` - CLI interface for SOAP generation - `soap_generator.py` - Core SOAP generation logic - `entity_extractor.py` - Medical NER module - `soap_classifier.py` - Section classification engine - `temporal_parser.py` - Timeline extraction - `negation_detector.py` - Negation and uncertainty detection - `post_processor.py` - Output formatting and review markers - `batch_processor.py` - Process multiple encounters - `validator.py` - Quality checks and compliance validation ## Performance and Resources **Typical Processing Time:** - Short encounter (<5 min dictation): 10-15 seconds - Standard visit (10-15 min): 30-45 seconds - Complex case (30+ min): 1-2 minutes **System Requirements:** - **RAM**: 4 GB minimum, 8 GB recommended for large batches - **Storage**: ~500 MB for models and dependencies - **CPU**: Multi-core processor recommended for batch processing - **GPU**: Not required but speeds up NLP processing if available **Supported Input Sizes:** - Text: Up to 10,000 words per encounter - File: Up to 10 MB text files - Audio transcript: Up to 2 hours of clinical encounter ## Limitations - **Not a diagnostic tool**: Cannot make medical decisions or diagnoses - **Specialty coverage**: Best performance in internal medicine, family practice; variable in highly specialized fields - **Language**: Optimized for English; limited support for other languages - **Context window**: May lose context in very long, complex encounters - **Ambiguity**: Struggles with highly ambiguous or contradictory input - **Rare conditions**: May not recognize very rare diseases or new medications - **Non-verbal cues**: Cannot interpret tone, emphasis, or non-verbal information from audio ## Regulatory and Legal Notes - **FDA Status**: This tool is NOT FDA-approved as a medical device - **HIPAA Compliance**: Must be used in HIPAA-compliant environment - **Liability**: User (physician/healthcare provider) retains full responsibility for final documentation - **Documentation**: Must disclose AI assistance in medical record per institutional policy - **Malpractice**: AI-generated content does not replace clinical judgment ## Version History - **v1.0.0** (Current): Initial release with core SOAP generation capabilities - Planned: Enhanced specialty-specific models, multi-language support, EHR direct integration ## Parameters | Parameter | Type | Default | Required | Description | |-----------|------|---------|----------|-------------| | `--input`, `-i` | string | - | No | Input clinical text directly | | `--input-file`, `-f` | string | - | No | Path to input text file | | `--output`, `-o` | string | - | No | Output file path | | `--patient-id`, `-p` | string | - | No | Patient identifier | | `--provider` | string | - | No | Healthcare provider name | | `--format` | string | markdown | No | Output format (markdown, json) | ## Usage ### Basic Usage ```bash # Generate SOAP from text python scripts/main.py --input "Patient reports chest pain..." --output note.md # From file python scripts/main.py --input-file consultation.txt --patient-id P12345 --provider "Dr. Smith" # JSON output python scripts/main.py --input-file notes.txt --format json --output note.json ``` ## Risk Assessment | Risk Indicator | Assessment | Level | |----------------|------------|-------| | Code Execution | Python script executed locally | Medium | | Network Access | No external API calls | Low | | File System Access | Read input files, write output files | Low | | Data Exposure | May process PHI (Protected Health Information) | High | | HIPAA Compliance | Must be used in compliant environment | High | ## Security Checklist - [x] No hardcoded credentials or API keys - [x] No unauthorized file system access - [x] Output does not contain hardcoded PHI - [x] Prompt injection protections in place - [x] Input validation for file paths - [x] Error messages sanitized - [x] **CRITICAL**: HIPAA compliance required for PHI ## Prerequisites ```bash # Python 3.7+ # No external packages required (uses standard library) ``` ## Evaluation Criteria ### Success Metrics - [x] Successfully parses unstructured clinical text - [x] Correctly categorizes into SOAP sections - [x] Extracts medical entities (symptoms, diagnoses, medications) - [x] Generates properly formatted output ### Test Cases 1. **Text Input**: Clinical text → Properly formatted SOAP note 2. **File Input**: Text file → Complete SOAP note with metadata 3. **JSON Output**: Text input → Valid JSON with all fields ## Lifecycle Status - **Current Stage**: Draft - **Next Review Date**: 2026-03-06 - **Known Issues**: None - **Planned Improvements**: - Enhanced entity recognition - Specialty-specific templates - EHR integration support --- **⚠️ CRITICAL REMINDER: All AI-generated SOAP notes REQUIRE physician review and approval before entry into patient records. This tool assists documentation but does not replace clinical judgment or medical decision-making.**

path: references/clinical_guidelines.md
content: # Clinical Documentation Guidelines

## SOAP Note Standards

### Subjective (S)
**Purpose**: Document patient-reported information

**Required Elements**:
- Chief complaint (CC)
- History of present illness (HPI)
  - Onset
  - Duration
  - Character
  - Aggravating/Alleviating factors
  - Associated symptoms
  - Severity (1-10 scale)
- Review of systems (ROS) - relevant
- Past medical history (PMH)
- Medications
- Allergies
- Social history (relevant)
- Family history (relevant)

**Writing Tips**:
- Use patient's own words when appropriate
- Document negative findings with "denies"
- Include temporal information

### Objective (O)
**Purpose**: Document observable, measurable data

**Required Elements**:
- Vital signs (with time)
- Physical examination findings
- Laboratory results
- Imaging results
- Other diagnostic data

**Writing Tips**:
- Use standard medical terminology
- Include measurements and values
- Note "WNL" (Within Normal Limits) for normal findings
- Describe abnormal findings specifically

### Assessment (A)
**Purpose**: Clinical interpretation and diagnosis

**Required Elements**:
- Primary diagnosis
- Differential diagnoses
- Problem list
- Status/changes from previous encounters

**Writing Tips**:
- Be specific with diagnoses
- Include diagnostic reasoning
- Address all problems identified
- Note stability/instability

### Plan (P)
**Purpose**: Treatment and management strategy

**Required Elements**:
- Medications (drug, dose, route, frequency)
- Procedures/tests ordered
- Consultations/referrals
- Patient education
- Follow-up plan

**Writing Tips**:
- Be specific with dosing
- Include patient instructions
- Specify timeframe for follow-up
- Document informed consent

## Medical Documentation Principles

### 1. Clarity
- Use unambiguous language
- Avoid abbreviations that could be misinterpreted
- Write legibly (if handwritten)

### 2. Completeness
- Document all relevant information
- Include negative findings
- Record all decisions and reasoning

### 3. Timeliness
- Document during or immediately after encounter
- Include date and time for all entries

### 4. Accuracy
- Verify all facts before recording
- Correct errors properly (single line through, initial, date)
- Never alter records after the fact

### 5. Objectivity
- Distinguish fact from opinion
- Quote patient when documenting subjective information

## Legal Considerations

- All entries must be dated and signed
- Use permanent ink for handwritten notes
- Never leave blank spaces
- Document informed consent discussions
- Include patient non-compliance if applicable

## Quality Metrics

Effective SOAP notes should be:
- **Specific**: Avoid vague terms like "good" or "normal"
- **Measurable**: Include quantifiable data
- **Actionable**: Clear next steps
- **Relevant**: Focus on pertinent information
- **Time-bound**: Include timelines for follow-up


path: references/medical_terminology.md
content: # Medical Terminology Reference

## Common Medical Abbreviations

### Vital Signs & Measurements
| Abbreviation | Meaning |
|--------------|---------|
| BP | Blood Pressure |
| HR | Heart Rate |
| RR | Respiratory Rate |
| Temp | Temperature |
| SpO2 | Oxygen Saturation |
| Wt | Weight |
| Ht | Height |
| BMI | Body Mass Index |

### Common Medical Terms
| Term | Definition |
|------|------------|
| Afebrile | Without fever |
| Ambulatory | Able to walk |
| Bilateral | Affecting both sides |
| Chronic | Long-lasting |
| Distal | Farther from center |
| Edema | Swelling |
| Proximal | Closer to center |
| Unilateral | Affecting one side |

### Prescription Abbreviations
| Abbreviation | Meaning |
|--------------|---------|
| BID | Twice daily |
| TID | Three times daily |
| QID | Four times daily |
| PRN | As needed |
| PO | By mouth |
| IM | Intramuscular |
| IV | Intravenous |

## Symptom Vocabulary

### Pain Descriptors
- Sharp
- Dull
- Burning
- Throbbing
- Cramping
- Shooting
- Aching
- Stabbing

### Severity Scale
- Mild (1-3/10)
- Moderate (4-6/10)
- Severe (7-10/10)

### Onset Patterns
- Acute (sudden)
- Chronic (long-standing)
- Gradual
- Intermittent
- Constant

## Body Systems Terminology

### Cardiovascular
| Term | Meaning |
|------|---------|
| Arrhythmia | Irregular heartbeat |
| Bradycardia | Slow heart rate |
| Hypertension | High blood pressure |
| Hypotension | Low blood pressure |
| Tachycardia | Fast heart rate |
| Murmur | Abnormal heart sound |

### Respiratory
| Term | Meaning |
|------|---------|
| Apnea | Cessation of breathing |
| Dyspnea | Difficulty breathing |
| Tachypnea | Rapid breathing |
| Wheezing | Whistling breathing sound |
| Rales | Crackling lung sounds |
| Rhonchi | Coarse rattling sounds |

### Neurological
| Term | Meaning |
|------|---------|
| Aphasia | Speech difficulty |
| Ataxia | Loss of coordination |
| Paresthesia | Numbness/tingling |
| Syncope | Fainting |
| Vertigo | Dizziness |

### Gastrointestinal
| Term | Meaning |
|------|---------|
| Dysphagia | Difficulty swallowing |
| Nausea | Sensation of vomiting |
| Reflux | Backward flow |
| Ascites | Fluid in abdomen |

## Common Medications

### Drug Classes
| Class | Examples |
|-------|----------|
| ACE Inhibitors | Lisinopril, Enalapril |
| Beta Blockers | Metoprolol, Atenolol |
| Statins | Atorvastatin, Simvastatin |
| NSAIDs | Ibuprofen, Naproxen |
| Opioids | Morphine, Oxycodone |
| Antibiotics | Amoxicillin, Azithromycin |

## Diagnostic Terms

### Imaging
| Term | Description |
|------|-------------|
| X-ray | Radiographic imaging |
| CT | Computed tomography |
| MRI | Magnetic resonance imaging |
| Ultrasound | Sonographic imaging |
| PET | Positron emission tomography |

### Laboratory
| Term | Description |
|------|-------------|
| CBC | Complete blood count |
| BMP | Basic metabolic panel |
| CMP | Comprehensive metabolic panel |
| LFT | Liver function tests |
| Hgb A1c | Glycated hemoglobin |
| Lipid panel | Cholesterol profile |

## Assessment/Plan Terminology

### Diagnostic Language
- Rule out (R/O)
- Consistent with
- Suggestive of
- Compatible with
- Secondary to

### Treatment Language
- Initiate
- Continue
- Discontinue
- Titrate
- Monitor
- Reassess
- Refer

## Common Medical Conditions

### Cardiovascular
- Hypertension (HTN)
- Coronary Artery Disease (CAD)
- Myocardial Infarction (MI)
- Congestive Heart Failure (CHF)
- Atrial Fibrillation (AFib)

### Endocrine
- Diabetes Mellitus Type 1/2 (DM)
- Hypothyroidism
- Hyperthyroidism
- Cushing's Syndrome

### Respiratory
- Asthma
- Chronic Obstructive Pulmonary Disease (COPD)
- Pneumonia
- Bronchitis

### Neurological
- Migraine
- Seizure Disorder
- Stroke
- Multiple Sclerosis (MS)

### Mental Health
- Generalized Anxiety Disorder (GAD)
- Major Depressive Disorder (MDD)
- Bipolar Disorder
- Schizophrenia

## Negation Terms
Terms indicating absence of a finding:
- Denies
- No evidence of
- Without
- Absence of
- Negative for
- Ruled out


path: references/sample_soap_notes.md
content: # Sample SOAP Notes

## Example 1: Cardiology Consultation

### Input (Raw Dictation)
```
45-year-old male here for follow-up after MI last month. Says he's feeling much better now. 
Walking 30 minutes daily without chest pain. Taking his medications as prescribed - 
metoprolol 50mg twice daily, atorvastatin 40mg daily, aspirin 81mg daily. No side effects. 
Vitals today: BP 128/78, HR 72 regular. Exam shows clear lungs, regular heart sounds, 
no edema. Assessment: Post-MI recovery progressing well, NYHA Class I. Plan: Continue 
current medications, repeat lipid panel in 3 months, follow up in 6 weeks, return sooner 
if chest pain recurs.
```

### Output (Structured SOAP)

**Subjective**
45-year-old male presents for post-MI follow-up. Patient reports significant improvement in symptoms. Currently walking 30 minutes daily without chest pain or shortness of breath. Adherent to medication regimen: metoprolol 50mg BID, atorvastatin 40mg daily, aspirin 81mg daily. Denies medication side effects.

**Objective**
- Vitals: BP 128/78 mmHg, HR 72 bpm (regular)
- Physical Exam: Lungs clear bilaterally, heart sounds regular, no peripheral edema

**Assessment**
1. Post-myocardial infarction - recovery phase
2. NYHA Class I (no limitation of physical activity)
3. Good medication compliance

**Plan**
1. Continue current medications:
   - Metoprolol 50mg PO BID
   - Atorvastatin 40mg PO daily
   - Aspirin 81mg PO daily
2. Laboratory: Fasting lipid panel in 3 months
3. Follow-up: Return in 6 weeks
4. Patient Education: Return immediately if chest pain recurs

---

## Example 2: Primary Care Visit - Diabetes Management

### Input (Raw Dictation)
```
62-year-old female with Type 2 diabetes here for routine follow-up. Home glucose logs 
show fasting readings 120-140. No hypoglycemic episodes. Reports good adherence to 
metformin 1000mg twice daily. Diet improved, walking 20 minutes most days. Weight 
down 3 pounds since last visit. Vitals: Weight 174 lbs, BP 138/84, HR 76. Exam 
unremarkable. Hgb A1c from last week is 7.2%, improved from 7.8%. Assessment: Type 2 
diabetes mellitus, well controlled. Plan: Continue metformin, recheck A1c in 3 months, 
continue lifestyle modifications, follow up in 3 months.
```

### Output (Structured SOAP)

**Subjective**
62-year-old female with Type 2 DM presents for routine follow-up. Home glucose monitoring shows fasting glucose 120-140 mg/dL. Denies hypoglycemic episodes. Reports excellent medication compliance with metformin 1000mg BID. Improved dietary habits with increased physical activity (walking 20 minutes most days). Reports 3-pound weight loss since last visit.

**Objective**
- Vitals: Weight 174 lbs, BP 138/84 mmHg, HR 76 bpm
- Physical Exam: Unremarkable
- Labs: Hgb A1c 7.2% (improved from 7.8% 3 months ago)

**Assessment**
1. Type 2 Diabetes Mellitus - well controlled
2. Improved glycemic control (A1c 7.2%)
3. Successful lifestyle modifications

**Plan**
1. Medications: Continue metformin 1000mg PO BID
2. Monitoring: Recheck Hgb A1c in 3 months
3. Lifestyle: Continue current diet and exercise regimen
4. Follow-up: Return in 3 months

---

## Example 3: Emergency Department Visit

### Input (Raw Dictation)
```
28-year-old female came in with right ankle injury after twisting it playing soccer 2 
hours ago. Heard a pop, immediate swelling. Pain 7/10, can't bear weight. No other 
injuries. No numbness or tingling. Vitals stable. Exam shows significant swelling 
over lateral ankle, tenderness over ATFL, positive anterior drawer. Distal pulses 
intact, sensation normal. X-ray negative for fracture. Assessment: Right ankle sprain, 
Grade 2. Plan: RICE protocol, ace wrap, crutches, ibuprofen 600mg q6h PRN pain, 
refer to orthopedics, follow up in 1 week or sooner if worsening.
```

### Output (Structured SOAP)

**Subjective**
28-year-old female presents with acute right ankle injury sustained 2 hours ago during soccer. Reports twisting mechanism with audible "pop" and immediate swelling. Pain rated 7/10. Unable to bear weight on affected limb. Denies numbness, tingling, or other injuries.

**Objective**
- Vitals: Stable
- Physical Exam:
  - Significant swelling over lateral right ankle
  - Tenderness over anterior talofibular ligament (ATFL)
  - Positive anterior drawer test
  - Distal pulses intact
  - Sensation normal
- Imaging: X-ray negative for fracture

**Assessment**
1. Right ankle sprain, Grade 2
2. ATFL injury

**Plan**
1. Conservative management:
   - RICE protocol (Rest, Ice, Compression, Elevation)
   - Ace wrap compression
   - Crutches for ambulation
2. Medications: Ibuprofen 600mg PO q6h PRN pain
3. Referral: Orthopedics for follow-up
4. Follow-up: Return in 1 week or sooner if symptoms worsen

---

## Example 4: Psychiatry Initial Evaluation

### Input (Raw Dictation)
```
34-year-old male referred by PCP for anxiety evaluation. Reports 6-month history of 
excessive worry about work performance, difficulty sleeping, muscle tension. Using 
alcohol 2-3 drinks nightly to relax. No suicidal thoughts. FH positive for anxiety 
in mother. Vitals stable. Mental status exam shows anxious affect, racing thoughts, 
no psychosis. Assessment: Generalized Anxiety Disorder, mild-moderate. Alcohol use 
as coping mechanism. Plan: Start sertraline 25mg daily, CBT referral, reduce alcohol, 
follow up in 2 weeks to assess medication tolerance.
```

### Output (Structured SOAP)

**Subjective**
34-year-old male presents for initial psychiatric evaluation (referred by PCP). Reports 6-month history of excessive worry about work performance. Associated symptoms include difficulty initiating and maintaining sleep, and persistent muscle tension. Using alcohol 2-3 drinks nightly as self-treatment for anxiety. Denies suicidal ideation, homicidal ideation, or psychotic symptoms. Family history positive for anxiety disorder in mother.

**Objective**
- Vitals: Stable
- Mental Status Exam:
  - Appearance: Well-groomed
  - Affect: Anxious
  - Thought process: Racing thoughts
  - Thought content: No psychosis, no SI/HI
  - Judgment/Insight: Fair

**Assessment**
1. Generalized Anxiety Disorder (F41.1) - mild to moderate severity
2. Alcohol use as maladaptive coping mechanism
3. Insomnia secondary to anxiety

**Plan**
1. Pharmacotherapy: Initiate sertraline 25mg PO daily, titrate as tolerated
2. Psychotherapy: Refer for Cognitive Behavioral Therapy (CBT)
3. Lifestyle: Patient counseled on reducing alcohol consumption
4. Safety: No current safety concerns
5. Follow-up: Return in 2 weeks to assess medication tolerance and efficacy


path: requirements.txt
content: dataclasses
enum


path: scripts/main.py
content: #!/usr/bin/env python3
"""
Automated SOAP Note Generator

Transforms unstructured clinical notes/dictation into standardized SOAP format.
Uses medical NLP techniques for entity extraction and section classification.

Usage:
    python main.py --input "clinical text" --output output.md
    python main.py --audio consultation.wav --output output.md
"""

import argparse
import re
import json
from datetime import datetime
from dataclasses import dataclass, asdict
from typing import Dict, List, Optional, Tuple
from enum import Enum


class SOAPSection(Enum):
    """SOAP note sections."""
    SUBJECTIVE = "Subjective"
    OBJECTIVE = "Objective"
    ASSESSMENT = "Assessment"
    PLAN = "Plan"


@dataclass
class MedicalEntity:
    """Represents a medical entity extracted from text."""
    text: str
    label: str  # e.g., 'SYMPTOM', 'DIAGNOSIS', 'MEDICATION', 'PROCEDURE'
    start: int
    end: int
    attributes: Dict = None


@dataclass
class SOAPNote:
    """Structured SOAP note data class."""
    patient_id: Optional[str]
    encounter_date: Optional[str]
    provider: Optional[str]
    subjective: str
    objective: str
    assessment: str
    plan: str
    raw_input: str = ""
    entities: List[MedicalEntity] = None

    def to_dict(self) -> Dict:
        """Convert to dictionary."""
        return {
            "patient_id": self.patient_id,
            "encounter_date": self.encounter_date,
            "provider": self.provider,
            "subjective": self.subjective,
            "objective": self.objective,
            "assessment": self.assessment,
            "plan": self.plan,
            "raw_input": self.raw_input,
            "entities": [asdict(e) for e in (self.entities or [])]
        }

    def to_markdown(self) -> str:
        """Generate formatted SOAP note in markdown."""
        md = ["# SOAP Note"]
        
        if self.patient_id:
            md.append(f"**Patient ID:** {self.patient_id}")
        if self.encounter_date:
            md.append(f"**Date:** {self.encounter_date}")
        if self.provider:
            md.append(f"**Provider:** {self.provider}")
        
        md.append("")
        md.append("## Subjective")
        md.append(self.subjective or "No subjective information provided.")
        
        md.append("")
        md.append("## Objective")
        md.append(self.objective or "No objective findings documented.")
        
        md.append("")
        md.append("## Assessment")
        md.append(self.assessment or "Assessment pending.")
        
        md.append("")
        md.append("## Plan")
        md.append(self.plan or "Plan to be determined.")
        
        return "\n".join(md)


class MedicalNLPProcessor:
    """Medical NLP processing utilities."""
    
    # Medical terminology patterns for rule-based extraction
    SYMPTOM_KEYWORDS = [
        "pain", "ache", "discomfort", "nausea", "vomiting", "fever", "chills",
        "headache", "dizziness", "fatigue", "weakness", "numbness", "tingling",
        "shortness of breath", "cough", "sore throat", "congestion", "rash",
        "swelling", "bruising", "bleeding", "diarrhea", "constipation"
    ]
    
    DIAGNOSIS_KEYWORDS = [
        "diagnosed with", "diagnosis", "condition", "disease", "disorder",
        "infection", "syndrome", "hypertension", "diabetes", "asthma",
        "pneumonia", "bronchitis", "migraine", "arthritis"
    ]
    
    MEDICATION_KEYWORDS = [
        "prescribed", "medication", "drug", "mg", "tablet", "capsule",
        "aspirin", "ibuprofen", "acetaminophen", "antibiotic", "insulin",
        "lisinopril", "metformin", "omeprazole", "amlodipine"
    ]
    
    PROCEDURE_KEYWORDS = [
        "procedure", "surgery", "operation", "biopsy", "x-ray", "mri",
        "ct scan", "ultrasound", "blood test", "urinalysis", "ecg", "ekg"
    ]
    
    VITAL_SIGN_PATTERNS = {
        "bp": r"\b(?:BP|blood pressure)[\s:]*?(\d{2,3})/(\d{2,3})\b",
        "hr": r"\b(?:HR|heart rate|pulse)[\s:]*?(\d{2,3})\b",
        "rr": r"\b(?:RR|respiratory rate)[\s:]*?(\d{1,2})\b",
        "temp": r"\b(?:temp|temperature)[\s:]*?(\d{2}\.?\d?)[°\s]*(?:F|C)?\b",
        "spo2": r"\b(?:SpO2|oxygen saturation)[\s:]*?(\d{2,3})%?\b",
        "weight": r"\b(?:weight|wt)[\s:]*?(\d{2,3}(?:\.\d)?)\s*(?:kg|lbs?)\b"
    }
    
    NEGATION_PATTERNS = [
        r"\bno\s+(\w+)",
        r"\bdenies\s+(\w+)",
        r"\bdenied\s+(\w+)",
        r"\bwithout\s+(\w+)",
        r"\bnot\s+\w+\s+(\w+)"
    ]

    def extract_entities(self, text: str) -> List[MedicalEntity]:
        """Extract medical entities from text."""
        entities = []
        text_lower = text.lower()
        
        # Extract symptoms
        for symptom in self.SYMPTOM_KEYWORDS:
            for match in re.finditer(r'\b' + re.escape(symptom) + r'\b', text_lower):
                start = match.start()
                end = match.end()
                entities.append(MedicalEntity(
                    text=text[start:end],
                    label="SYMPTOM",
                    start=start,
                    end=end
                ))
        
        # Extract medications
        for med in self.MEDICATION_KEYWORDS:
            for match in re.finditer(r'\b' + re.escape(med) + r'\b', text_lower):
                start = match.start()
                end = match.end()
                entities.append(MedicalEntity(
                    text=text[start:end],
                    label="MEDICATION",
                    start=start,
                    end=end
                ))
        
        # Extract diagnoses
        for diag in self.DIAGNOSIS_KEYWORDS:
            for match in re.finditer(r'\b' + re.escape(diag) + r'\b', text_lower):
                start = match.start()
                end = match.end()
                entities.append(MedicalEntity(
                    text=text[start:end],
                    label="DIAGNOSIS",
                    start=start,
                    end=end
                ))
        
        # Extract procedures
        for proc in self.PROCEDURE_KEYWORDS:
            for match in re.finditer(r'\b' + re.escape(proc) + r'\b', text_lower):
                start = match.start()
                end = match.end()
                entities.append(MedicalEntity(
                    text=text[start:end],
                    label="PROCEDURE",
                    start=start,
                    end=end
                ))
        
        return entities
    
    def extract_vital_signs(self, text: str) -> Dict[str, str]:
        """Extract vital signs from text."""
        vitals = {}
        for vital_name, pattern in self.VITAL_SIGN_PATTERNS.items():
            matches = re.findall(pattern, text, re.IGNORECASE)
            if matches:
                vitals[vital_name] = matches[0] if isinstance(matches[0], str) else f"{matches[0][0]}/{matches[0][1]}"
        return vitals
    
    def detect_negation(self, text: str) -> List[str]:
        """Detect negated findings."""
        negated = []
        for pattern in self.NEGATION_PATTERNS:
            matches = re.findall(pattern, text, re.IGNORECASE)
            negated.extend(matches)
        return negated


class SOAPNoteGenerator:
    """Main SOAP note generator class."""
    
    # Section classification keywords
    SUBJECTIVE_INDICATORS = [
        "patient reports", "patient states", "patient complains", "c/o",
        "history of", "h/o", "symptoms", "feels", "experiencing",
        "onset", "duration", "frequency", "severity", "character",
        "aggravated by", "relieved by", "associated with", "denies"
    ]
    
    OBJECTIVE_INDICATORS = [
        "exam reveals", "physical exam", "vital signs", "vitals",
        "bp", "blood pressure", "heart rate", "pulse", "respiratory rate",
        "temperature", "afebrile", "alert", "oriented", "mucous membranes",
        "lungs clear", "heart regular", "abdomen soft", "no tenderness"
    ]
    
    ASSESSMENT_INDICATORS = [
        "assessment", "impression", "diagnosis", "differential",
        "likely", "probable", "suspected", "appears to be", "consistent with",
        "rule out", "r/o", "possible", "consider", "most likely"
    ]
    
    PLAN_INDICATORS = [
        "plan", "will", "continue", "start", "initiate", "discontinue",
        "prescribe", "refer", "follow up", "follow-up", "return",
        "monitor", "recheck", "labs", "imaging", "schedule"
    ]
    
    def __init__(self):
        self.nlp = MedicalNLPProcessor()
    
    def classify_section(self, sentence: str) -> SOAPSection:
        """Classify which SOAP section a sentence belongs to."""
        sentence_lower = sentence.lower()
        
        scores = {
            SOAPSection.SUBJECTIVE: 0,
            SOAPSection.OBJECTIVE: 0,
            SOAPSection.ASSESSMENT: 0,
            SOAPSection.PLAN: 0
        }
        
        # Score each section
        for indicator in self.SUBJECTIVE_INDICATORS:
            if indicator in sentence_lower:
                scores[SOAPSection.SUBJECTIVE] += 1
        
        for indicator in self.OBJECTIVE_INDICATORS:
            if indicator in sentence_lower:
                scores[SOAPSection.OBJECTIVE] += 1
        
        for indicator in self.ASSESSMENT_INDICATORS:
            if indicator in sentence_lower:
                scores[SOAPSection.ASSESSMENT] += 1
        
        for indicator in self.PLAN_INDICATORS:
            if indicator in sentence_lower:
                scores[SOAPSection.PLAN] += 1
        
        return max(scores, key=scores.get)
    
    def parse_sentences(self, text: str) -> List[str]:
        """Split text into sentences."""
        # Simple sentence splitting
        sentences = re.split(r'(?<=[.!?])\s+', text)
        return [s.strip() for s in sentences if s.strip()]
    
    def extract_subjective(self, text: str, sentences: List[str]) -> str:
        """Extract subjective section content."""
        subjective_sentences = []
        
        for sentence in sentences:
            if self.classify_section(sentence) == SOAPSection.SUBJECTIVE:
                subjective_sentences.append(sentence)
        
        # If no subjective section detected, look for patient-reported content
        if not subjective_sentences:
            for sentence in sentences:
                lower = sentence.lower()
                if any(x in lower for x in ["patient", "pt", "reports", "states", "complains"]):
                    subjective_sentences.append(sentence)
        
        return " ".join(subjective_sentences) if subjective_sentences else ""
    
    def extract_objective(self, text: str, sentences: List[str]) -> str:
        """Extract objective section content."""
        objective_sentences = []
        
        for sentence in sentences:
            if self.classify_section(sentence) == SOAPSection.OBJECTIVE:
                objective_sentences.append(sentence)
        
        # Add vital signs if found
        vitals = self.nlp.extract_vital_signs(text)
        if vitals:
            vital_text = "Vital signs: " + ", ".join([f"{k}: {v}" for k, v in vitals.items()])
            objective_sentences.insert(0, vital_text)
        
        return " ".join(objective_sentences) if objective_sentences else ""
    
    def extract_assessment(self, text: str, sentences: List[str]) -> str:
        """Extract assessment section content."""
        assessment_sentences = []
        
        for sentence in sentences:
            if self.classify_section(sentence) == SOAPSection.ASSESSMENT:
                assessment_sentences.append(sentence)
        
        # Look for diagnosis patterns
        if not assessment_sentences:
            for sentence in sentences:
                lower = sentence.lower()
                if any(x in lower for x in ["diagnosis", "impression", "assessment", "likely"]):
                    assessment_sentences.append(sentence)
        
        return " ".join(assessment_sentences) if assessment_sentences else ""
    
    def extract_plan(self, text: str, sentences: List[str]) -> str:
        """Extract plan section content."""
        plan_sentences = []
        
        for sentence in sentences:
            if self.classify_section(sentence) == SOAPSection.PLAN:
                plan_sentences.append(sentence)
        
        # Look for medication/treatment patterns
        if not plan_sentences:
            for sentence in sentences:
                lower = sentence.lower()
                if any(x in lower for x in ["prescribed", "started", "continue", "follow up"]):
                    plan_sentences.append(sentence)
        
        return " ".join(plan_sentences) if plan_sentences else ""
    
    def generate(self, input_text: str, patient_id: Optional[str] = None,
                 encounter_date: Optional[str] = None,
                 provider: Optional[str] = None) -> SOAPNote:
        """
        Generate a SOAP note from unstructured clinical text.
        
        Args:
            input_text: Raw clinical text/dictation
            patient_id: Patient identifier
            encounter_date: Date of encounter
            provider: Healthcare provider name
            
        Returns:
            SOAPNote object with structured sections
        """
        # Parse sentences
        sentences = self.parse_sentences(input_text)
        
        # Extract entities
        entities = self.nlp.extract_entities(input_text)
        
        # Extract each section
        subjective = self.extract_subjective(input_text, sentences)
        objective = self.extract_objective(input_text, sentences)
        assessment = self.extract_assessment(input_text, sentences)
        plan = self.extract_plan(input_text, sentences)
        
        return SOAPNote(
            patient_id=patient_id,
            encounter_date=encounter_date or datetime.now().strftime("%Y-%m-%d"),
            provider=provider,
            subjective=subjective,
            objective=objective,
            assessment=assessment,
            plan=plan,
            raw_input=input_text,
            entities=entities
        )
    
    def generate_from_audio(self, audio_path: str, **kwargs) -> SOAPNote:
        """
        Generate SOAP note from audio file (placeholder - requires ASR integration).
        
        Args:
            audio_path: Path to audio file
            **kwargs: Additional arguments for generate()
            
        Returns:
            SOAPNote object
        """
        # This is a placeholder - would integrate with ASR service
        raise NotImplementedError(
            "Audio transcription requires integration with ASR service. "
            "Use generate() with pre-transcribed text instead."
        )


def main():
    """Command-line interface."""
    parser = argparse.ArgumentParser(
        description="Generate SOAP notes from clinical text"
    )
    parser.add_argument("--input", "-i", help="Input clinical text")
    parser.add_argument("--input-file", "-f", help="Path to input text file")
    parser.add_argument("--output", "-o", help="Output file path")
    parser.add_argument("--patient-id", "-p", help="Patient identifier")
    parser.add_argument("--provider", help="Healthcare provider name")
    parser.add_argument("--format", choices=["markdown", "json"], 
                        default="markdown", help="Output format")
    
    args = parser.parse_args()
    
    # Get input text
    if args.input:
        input_text = args.input
    elif args.input_file:
        with open(args.input_file, 'r') as f:
            input_text = f.read()
    else:
        # Demo mode with sample input
        input_text = (
            "Patient is a 45-year-old male presenting with chest pain for 2 hours. "
            "Pain is 8/10, crushing quality, radiates to left arm. "
            "Patient reports shortness of breath and diaphoresis. "
            "Vital signs: BP 160/95, HR 105, RR 22, Temp 98.6F. "
            "Physical exam reveals diaphoretic patient, chest clear bilaterally. "
            "Assessment: Acute coronary syndrome, possible STEMI. "
            "Plan: Activate cath lab, give aspirin 325mg, nitroglycerin 0.4mg SL."
        )
        print("Demo mode - using sample input...\n")
    
    # Generate SOAP note
    generator = SOAPNoteGenerator()
    soap_note = generator.generate(
        input_text=input_text,
        patient_id=args.patient_id,
        provider=args.provider
    )
    
    # Format output
    if args.format == "json":
        output = json.dumps(soap_note.to_dict(), indent=2)
    else:
        output = soap_note.to_markdown()
    
    # Write or print output
    if args.output:
        with open(args.output, 'w') as f:
            f.write(output)
        print(f"SOAP note written to {args.output}")
    else:
        print(output)


if __name__ == "__main__":
    main()


Security scan verdicts — ClawScan: suspicious | static analysis: clean | VirusTotal: clean | SkillSpector: suspicious (MEDIUM)