This cookbook details how to deploy a localized, containerized otolaryngology, neurotology, and audiology decision-support engine for ENT clinics, audiology suites, and emergency departments to ingest pure-tone audiograms ($\text{PTA}$, $250 - 8000\text{ Hz}$), word recognition scores ($\text{WRS}$), and clinical neurotologic exam findings, evaluate the 2019 AAO-HNS Sudden Sensorineural Hearing Loss ($\text{SSNHL}$) Diagnostic Criteria (verifying $\ge 30\text{ dB}$ sensorineural loss across $\ge 3$ contiguous frequencies within $72\text{ hours}$), calculate Weight-Adjusted High-Dose Oral Prednisone Regimens, gate Intratympanic ($\text{IT}$) Dexamethasone Salvage Injections, enforce MRI IAC Vestibular Schwannoma Rule-Out Sentinels, and track Siegel Recovery Metrics according to AAO-HNS consensus guidelines without external cloud API reliance.
Sudden Sensorineural Hearing Loss ($\text{SSNHL}$) is an otologic emergency presenting as rapid-onset, unexplained sensorineural hearing loss:
[Pure-Tone Audiometry: 250 - 8000 Hz (Affected vs Contralateral), WRS %, Onset Hours]
│
▼
[AAO-HNS 2019 Criteria Gate: >= 30 dB Loss across >= 3 Contiguous Frequencies]
│
▼
[Sensorineural vs Conductive Classifier: Air-Bone Gap (ABG < 10-15 dB)]
│
▼
[Systemic vs Intratympanic Triage: Oral Prednisone 60mg vs IT Dexamethasone]
│
▼
[MRI IAC Vestibular Schwannoma Sentinel & Siegel Recovery Tracking Engine]
Install required scientific Python and audiological modeling packages:
pip install numpy scipy pandas torch torchvision matplotlib
"""
Cookbook 302: Offline Otolaryngology SSNHL Audiometry & Intratympanic Steroid Engine
OpenPHR Clinical AI Working Group (https://openphr.org)
"""
import math
import numpy as np
import pandas as pd
from typing import Dict, List, Tuple, Optional
from dataclasses import dataclass
@dataclass
class AudiologyExamTelemetry:
patient_id: str
age_years: float
weight_kg: float # e.g. 72.0 kg
affected_ear: str # "Left" or "Right"
onset_duration_hours: float # e.g. 36.0 hours (<= 72h = sudden)
# Audiometric Air Conduction Thresholds (dB HL) across standard octaves
# Affected Ear Thresholds
affected_ac_250hz: float = 55.0
affected_ac_500hz: float = 65.0
affected_ac_1000hz: float = 70.0
affected_ac_2000hz: float = 65.0
affected_ac_4000hz: float = 60.0
affected_ac_8000hz: float = 70.0
# Contralateral Baseline Ear Thresholds (Normal or baseline)
contralateral_ac_250hz: float = 15.0
contralateral_ac_500hz: float = 15.0
contralateral_ac_1000hz: float = 10.0
contralateral_ac_2000hz: float = 15.0
contralateral_ac_4000hz: float = 20.0
contralateral_ac_8000hz: float = 20.0
# Bone Conduction Thresholds (for Air-Bone Gap evaluation)
affected_bc_500hz: float = 60.0 # ABG = 5 dB (Sensorineural!)
affected_bc_1000hz: float = 65.0 # ABG = 5 dB
affected_bc_2000hz: float = 60.0 # ABG = 5 dB
# Speech Audiometry
word_recognition_score_wrs_percent: float = 32.0 # Normal > 90%; Severe discrimination loss
# Associated Otologic & Neurologic Symptoms
concurrent_vertigo_present: bool = True # Negative prognostic factor for recovery
concurrent_tinnitus_present: bool = True
concurrent_aural_fullness_present: bool = True
cranial_nerve_deficit_or_ataxia: bool = False # Flag for AICA stroke / CPA tumor
# Systemic Medical Co-Morbidities
uncontrolled_diabetes_mellitus: bool = False
active_peptic_ulcer_disease: bool = False
severe_psychiatric_disorder: bool = False
@dataclass
class SSNHLEvaluationReport:
patient_id: str
aao_hns_ssnhl_confirmed: bool
contiguous_frequency_drop_db: float
affected_pure_tone_average_pta: float # 4-frequency PTA (500, 1000, 2000, 4000 Hz)
hearing_loss_severity_grade: str # "Mild", "Moderate", "Moderately Severe", "Severe", "Profound"
primary_treatment_pathway: str # "Oral Prednisone Taper", "Intratympanic Dexamethasone Injections", "Combined Therapy"
steroid_prescription_orders: List[str]
imaging_and_diagnostic_orders: List[str]
prognostic_and_safety_sentinels: List[str]
clinical_aao_hns_directive: str
class OtolaryngologySSNHLEngine:
"""
Offline clinical engine for AAO-HNS 2019 SSNHL diagnostic verification,
air-bone gap sensorineural confirmation, oral vs intratympanic steroid dosing,
and MRI IAC acoustic neuroma screening.
"""
OCTAVE_FREQUENCIES = [250, 500, 1000, 2000, 4000, 8000]
def compute_pta(self, ac_500: float, ac_1000: float, ac_2000: float, ac_4000: float) -> float:
# Standard 4-frequency PTA
return round(float(np.mean([ac_500, ac_1000, ac_2000, ac_4000])), 1)
def classify_severity(self, pta: float) -> str:
if pta >= 91.0:
return "Profound Hearing Loss (PTA >= 91 dB HL)"
elif pta >= 71.0:
return "Severe Hearing Loss (PTA 71 - 90 dB HL)"
elif pta >= 56.0:
return "Moderately Severe Hearing Loss (PTA 56 - 70 dB HL)"
elif pta >= 41.0:
return "Moderate Hearing Loss (PTA 41 - 55 dB HL)"
elif pta >= 26.0:
return "Mild Hearing Loss (PTA 26 - 40 dB HL)"
else:
return "Normal Hearing Sensitivity (PTA <= 25 dB HL)"
def verify_aao_hns_criteria(self, d: AudiologyExamTelemetry) -> Tuple[bool, float, List[int]]:
aff = [d.affected_ac_250hz, d.affected_ac_500hz, d.affected_ac_1000hz, d.affected_ac_2000hz, d.affected_ac_4000hz, d.affected_ac_8000hz]
norm = [d.contralateral_ac_250hz, d.contralateral_ac_500hz, d.contralateral_ac_1000hz, d.contralateral_ac_2000hz, d.contralateral_ac_4000hz, d.contralateral_ac_8000hz]
drops = [a - n for a, n in zip(aff, norm)]
# Check for >= 3 contiguous frequencies with drop >= 30 dB
max_drop = 0.0
matching_freqs = []
confirmed = False
for i in range(len(drops) - 2):
window = drops[i:i+3]
if all(val >= 30.0 for val in window) and (d.onset_duration_hours <= 72.0):
confirmed = True
max_drop = max(max_drop, float(np.mean(window)))
matching_freqs = self.OCTAVE_FREQUENCIES[i:i+3]
return confirmed, round(max_drop, 1), matching_freqs
def check_sensorineural_nature(self, d: AudiologyExamTelemetry) -> bool:
# Check Air-Bone Gap at 500, 1000, 2000 Hz (< 15 dB is sensorineural)
abg_500 = d.affected_ac_500hz - d.affected_bc_500hz
abg_1000 = d.affected_ac_1000hz - d.affected_bc_1000hz
abg_2000 = d.affected_ac_2000hz - d.affected_bc_2000hz
mean_abg = np.mean([abg_500, abg_1000, abg_2000])
return bool(mean_abg < 15.0)
def determine_steroid_pathway(self, d: AudiologyExamTelemetry, is_ssnhl: bool) -> Tuple[str, List[str]]:
orders = []
contraindicated = d.uncontrolled_diabetes_mellitus or d.active_peptic_ulcer_disease or d.severe_psychiatric_disorder
if not is_ssnhl:
return "Observation / Non-Steroid Management", ["Patient does not meet AAO-HNS 2019 criteria for SSNHL; evaluate for conductive loss or chronic presbycusis."]
if contraindicated:
pathway = "Primary Intratympanic Corticosteroid Injections (Oral Steroids Contraindicated)"
orders.append("1. PRIMARY INTRATYMPANIC INJECTION: Dexamethasone 10-24 mg/mL (0.5 mL) injected into posteroinferior middle ear under microscopy.")
orders.append("2. FREQUENCY: Administer 3 to 4 intratympanic injections over 10-14 days (spaced every 3-4 days).")
orders.append("3. POST-INJECTION POSITIONING: Maintain patient in supine position with head rotated 45 degrees away from affected ear for 25-30 minutes without swallowing or speaking.")
else:
pathway = "First-Line High-Dose Oral Prednisone Therapy"
pred_dose = min(60.0, round(1.0 * d.weight_kg, 0))
orders.append(f"1. ORAL PREDNISONE: Prednisone {pred_dose:.0f} mg PO once daily in the morning with breakfast for 10 consecutive days.")
orders.append(f"2. PREDNISONE TAPER: Step-down taper: 40 mg daily x 2 days -> 20 mg daily x 2 days -> 10 mg daily x 2 days -> discontinue.")
orders.append("3. GASTROPROTECTION: Omeprazole 20 mg PO daily for gastric ulcer prophylaxis.")
orders.append("4. INTRATYMPANIC SALVAGE GATING: Schedule follow-up audiogram at completion of oral steroid course (Day 14-21); if incomplete recovery (PTA gain < 15 dB or PTA > 45 dB), initiate Intratympanic Dexamethasone Salvage injections immediately.")
return pathway, orders
def evaluate_case(self, data: AudiologyExamTelemetry) -> SSNHLEvaluationReport:
is_ssnhl, drop_db, matched_freqs = self.verify_aao_hns_criteria(data)
is_snhl = self.check_sensorineural_nature(data)
pta = self.compute_pta(data.affected_ac_500hz, data.affected_ac_1000hz, data.affected_ac_2000hz, data.affected_ac_4000hz)
sev_grade = self.classify_severity(pta)
pathway, rx_orders = self.determine_steroid_pathway(data, is_ssnhl and is_snhl)
imaging = []
imaging.append("1. CONTRAST-ENHANCED MRI IAC / CPA: Order MRI Brain & Internal Auditory Canals with and without gadolinium to rule out Vestibular Schwannoma (Acoustic Neuroma) and demyelinating disease (Mandatory per AAO-HNS 2019 guidelines).")
if data.cranial_nerve_deficit_or_ataxia:
imaging.append("2. STAT STROKE PROTOCOL: Order emergent MRI Brain DWI / MRA Head & Neck to evaluate for anterior inferior cerebellar artery (AICA) territory brainstem/cerebellar infarction.")
sentinels = []
if data.concurrent_vertigo_present:
sentinels.append("PROGNOSTIC WARNING: Concurrent vertigo indicates labyrinthine/vestibular involvement and is an established negative prognostic predictor for spontaneous hearing recovery.")
if data.word_recognition_score_wrs_percent < 50.0:
sentinels.append(f"SEVERE SPEECH DISCRIMINATION DEFICIT (WRS = {data.word_recognition_score_wrs_percent}%): Flag for early counseling on auditory rehabilitation, CROS hearing aids, or cochlear implant evaluation if non-responsive to steroid rescue.")
directives = []
directives.append(f"AAO-HNS 2019 STATUS: {'CONFIRMED SSNHL' if (is_ssnhl and is_snhl) else 'UNCONFIRMED'}.")
directives.append(f"AUDIOMETRY: {data.affected_ear} Ear PTA = {pta} dB HL ({sev_grade}) | WRS = {data.word_recognition_score_wrs_percent}%.")
directives.append(f"THERAPEUTIC PLAN: {pathway}.")
return SSNHLEvaluationReport(
patient_id=data.patient_id,
aao_hns_ssnhl_confirmed=(is_ssnhl and is_snhl),
contiguous_frequency_drop_db=drop_db,
affected_pure_tone_average_pta=pta,
hearing_loss_severity_grade=sev_grade,
primary_treatment_pathway=pathway,
steroid_prescription_orders=rx_orders,
imaging_and_diagnostic_orders=imaging,
prognostic_and_safety_sentinels=sentinels,
clinical_aao_hns_directive=" ".join(directives)
)
# Example Execution & Verification
if __name__ == "__main__":
engine = OtolaryngologySSNHLEngine()
print("=" * 80)
print("OpenPHR Clinical Otolaryngology SSNHL Audiometry & Intratympanic Steroid Engine")
print("=" * 80)
# Test Case 1: 42-year-old male with sudden Left-sided hearing loss for 36 hours (Weight 72 kg)
# Audiogram: Left ear thresholds 55, 65, 70, 65, 60, 70 dB HL (vs Right ear 10-20 dB HL) -> Drop >= 50 dB across 6 contiguous frequencies!
# Air-Bone Gap: 5 dB (Sensorineural confirmed) | WRS: 32% (Severe speech discrimination loss) | Vertigo + Tinnitus
# Management: Confirmed SSNHL (PTA 65 dB HL = Moderately Severe) -> Oral Prednisone 60 mg/day x 10d + Taper -> MRI IAC!
oto1 = AudiologyExamTelemetry(
patient_id="OTO-SSNHL-9104",
age_years=42.0,
weight_kg=72.0,
affected_ear="Left",
onset_duration_hours=36.0,
affected_ac_250hz=55.0,
affected_ac_500hz=65.0,
affected_ac_1000hz=70.0,
affected_ac_2000hz=65.0,
affected_ac_4000hz=60.0,
affected_ac_8000hz=70.0,
contralateral_ac_250hz=15.0,
contralateral_ac_500hz=15.0,
contralateral_ac_1000hz=10.0,
contralateral_ac_2000hz=15.0,
contralateral_ac_4000hz=20.0,
contralateral_ac_8000hz=20.0,
affected_bc_500hz=60.0,
affected_bc_1000hz=65.0,
affected_bc_2000hz=60.0,
word_recognition_score_wrs_percent=32.0,
concurrent_vertigo_present=True,
concurrent_tinnitus_present=True
)
rep1 = engine.evaluate_case(oto1)
print(f"\n[Patient {rep1.patient_id} - Neurotology Report]")
print(f"AAO-HNS SSNHL Confirmed: {rep1.aao_hns_ssnhl_confirmed}")
print(f"Affected Pure-Tone Average: {rep1.affected_pure_tone_average_pta} dB HL ({rep1.hearing_loss_severity_grade})")
print(f"Treatment Strategy: {rep1.primary_treatment_pathway}")
print("\nPharmacotherapy Orders:")
for o in rep1.steroid_prescription_orders:
print(f" • {o}")
print("\nImaging & Diagnostic Orders:")
for i in rep1.imaging_and_diagnostic_orders:
print(f" • {i}")
print("\nPrognostic Sentinels:")
for s in rep1.prognostic_and_safety_sentinels:
print(f" {s}")
print(f"\nAAO-HNS Consensus Directive:\n{rep1.clinical_aao_hns_directive}")
# Test Case 2: 58-year-old female with brittle Type 1 Diabetes (Oral Steroids Contraindicated -> IT Dexamethasone!)
oto2 = AudiologyExamTelemetry(
patient_id="OTO-SSNHL-1042",
age_years=58.0,
weight_kg=64.0,
affected_ear="Right",
onset_duration_hours=24.0,
affected_ac_500hz=70.0,
affected_ac_1000hz=75.0,
affected_ac_2000hz=70.0,
affected_ac_4000hz=65.0,
contralateral_ac_500hz=15.0,
contralateral_ac_1000hz=15.0,
contralateral_ac_2000hz=15.0,
contralateral_ac_4000hz=15.0,
uncontrolled_diabetes_mellitus=True # Oral steroid contraindicated!
)
rep2 = engine.evaluate_case(oto2)
print(f"\n[Patient {rep2.patient_id}] - Strategy: {rep2.primary_treatment_pathway}")