Spaces:
Runtime error
Runtime error
Implement LLM-as-a-Judge and CLI Tester
Browse files- ER_MAP/__pycache__/autoplay.cpython-313.pyc +0 -0
- ER_MAP/__pycache__/dashboard.cpython-313.pyc +0 -0
- ER_MAP/cli_tester.py +116 -0
- ER_MAP/dashboard.py +2 -2
- ER_MAP/envs/__pycache__/api_router.cpython-313.pyc +0 -0
- ER_MAP/envs/__pycache__/disease_db.cpython-313.pyc +0 -0
- ER_MAP/envs/__pycache__/empathy_engine.cpython-313.pyc +0 -0
- ER_MAP/envs/__pycache__/randomizer.cpython-313.pyc +0 -0
- ER_MAP/envs/__pycache__/triage_env.cpython-313.pyc +0 -0
- ER_MAP/envs/api_router.py +75 -1
- ER_MAP/envs/disease_db.py +181 -0
- ER_MAP/envs/empathy_engine.py +5 -1
- ER_MAP/envs/randomizer.py +40 -20
- ER_MAP/envs/triage_env.py +163 -80
- ER_MAP/evaluate.py +1 -1
- ER_MAP/openenv.yaml +1 -1
ER_MAP/__pycache__/autoplay.cpython-313.pyc
CHANGED
|
Binary files a/ER_MAP/__pycache__/autoplay.cpython-313.pyc and b/ER_MAP/__pycache__/autoplay.cpython-313.pyc differ
|
|
|
ER_MAP/__pycache__/dashboard.cpython-313.pyc
CHANGED
|
Binary files a/ER_MAP/__pycache__/dashboard.cpython-313.pyc and b/ER_MAP/__pycache__/dashboard.cpython-313.pyc differ
|
|
|
ER_MAP/cli_tester.py
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
import sys
|
| 3 |
+
import json
|
| 4 |
+
import time
|
| 5 |
+
import argparse
|
| 6 |
+
from colorama import init, Fore, Style
|
| 7 |
+
|
| 8 |
+
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
| 9 |
+
|
| 10 |
+
from ER_MAP.envs.triage_env import TriageEnv
|
| 11 |
+
|
| 12 |
+
try:
|
| 13 |
+
from groq import Groq
|
| 14 |
+
GROQ_AVAILABLE = True
|
| 15 |
+
except ImportError:
|
| 16 |
+
GROQ_AVAILABLE = False
|
| 17 |
+
|
| 18 |
+
init(autoreset=True)
|
| 19 |
+
|
| 20 |
+
def print_header(title):
|
| 21 |
+
print(f"\n{Fore.CYAN}{Style.BRIGHT}{'='*60}")
|
| 22 |
+
print(f"{Fore.CYAN}{Style.BRIGHT} {title}")
|
| 23 |
+
print(f"{Fore.CYAN}{Style.BRIGHT}{'='*60}")
|
| 24 |
+
|
| 25 |
+
def run_automated_cli(phase: int):
|
| 26 |
+
groq_key = os.environ.get("GROQ_API_KEY") or os.environ.get("GROQ_NURSE_API_KEY") or os.environ.get("GROQ_PATIENT_API_KEY")
|
| 27 |
+
if not groq_key or not GROQ_AVAILABLE:
|
| 28 |
+
print(f"{Fore.RED}ERROR: GROQ_API_KEY environment variable required and 'groq' package must be installed.")
|
| 29 |
+
return
|
| 30 |
+
|
| 31 |
+
client = Groq(api_key=groq_key)
|
| 32 |
+
|
| 33 |
+
print(f"{Fore.YELLOW}Initializing Environment (Phase {phase})...")
|
| 34 |
+
env = TriageEnv(render_mode="human")
|
| 35 |
+
|
| 36 |
+
obs_json, info = env.reset(options={"phase": phase})
|
| 37 |
+
gt = env.ground_truth
|
| 38 |
+
|
| 39 |
+
print_header(f"GROUND TRUTH GENERATED (PHASE {phase})")
|
| 40 |
+
print(f"{Fore.MAGENTA}Disease: {gt['disease']['true_disease']} | Diff: {gt['disease']['difficulty']} | Emergency: {gt['disease'].get('is_emergency', False)}")
|
| 41 |
+
print(f"Correct Tx: {gt['disease']['correct_treatment']}")
|
| 42 |
+
|
| 43 |
+
print(f"\n{Fore.BLUE}Patient: {gt['patient']['communication']}, {gt['patient']['compliance']}, {gt['patient']['literacy']}")
|
| 44 |
+
print(f"{Fore.GREEN}Nurse: {gt['nurse']['experience']}, {gt['nurse']['bandwidth']}, {gt['nurse']['empathy']}")
|
| 45 |
+
|
| 46 |
+
print_header("AUTOMATED DOCTOR AGENT RUNNING (70B)")
|
| 47 |
+
|
| 48 |
+
messages = [
|
| 49 |
+
{"role": "system", "content": "You are the Doctor. You must output ONLY valid JSON matching the exact schema requested in your prompt. Tools available: speak_to, order_lab, read_soap, update_soap, terminal_discharge."}
|
| 50 |
+
]
|
| 51 |
+
|
| 52 |
+
step_count = 0
|
| 53 |
+
max_steps = 30
|
| 54 |
+
cumulative_reward = 0.0
|
| 55 |
+
|
| 56 |
+
while step_count < max_steps:
|
| 57 |
+
# Provide the observation
|
| 58 |
+
messages.append({"role": "user", "content": obs_json})
|
| 59 |
+
|
| 60 |
+
print(f"\n{Fore.YELLOW}--- Step {step_count + 1} ---")
|
| 61 |
+
print(f"{Fore.WHITE}Doctor 70B is thinking...")
|
| 62 |
+
try:
|
| 63 |
+
completion = client.chat.completions.create(
|
| 64 |
+
model="llama-3.3-70b-versatile",
|
| 65 |
+
messages=messages,
|
| 66 |
+
temperature=0.7,
|
| 67 |
+
response_format={"type": "json_object"}
|
| 68 |
+
)
|
| 69 |
+
raw_action = completion.choices[0].message.content
|
| 70 |
+
messages.append({"role": "assistant", "content": raw_action})
|
| 71 |
+
|
| 72 |
+
# Print Action
|
| 73 |
+
try:
|
| 74 |
+
action_parsed = json.loads(raw_action)
|
| 75 |
+
print(f"{Fore.CYAN}💭 Thought: {action_parsed.get('thought', '')}")
|
| 76 |
+
print(f"{Fore.GREEN}🛠️ Action: {action_parsed.get('tool', '')} -> {json.dumps({k:v for k,v in action_parsed.items() if k not in ['thought', 'tool']})}")
|
| 77 |
+
except:
|
| 78 |
+
print(f"{Fore.GREEN}🛠️ Action (Raw): {raw_action}")
|
| 79 |
+
|
| 80 |
+
# Execute step
|
| 81 |
+
obs_json, reward, done, truncated, info = env.step(raw_action)
|
| 82 |
+
cumulative_reward += reward
|
| 83 |
+
print(f"{Fore.MAGENTA}🪙 Step Reward: {reward:.2f} | Total: {cumulative_reward:.2f}")
|
| 84 |
+
|
| 85 |
+
if done or truncated:
|
| 86 |
+
print(f"\n{Fore.RED}{Style.BRIGHT}=== EPISODE FINISHED ===")
|
| 87 |
+
try:
|
| 88 |
+
final_obs = json.loads(obs_json)
|
| 89 |
+
print(f"Outcome: {final_obs.get('event')}")
|
| 90 |
+
print(f"Message: {final_obs.get('message')}")
|
| 91 |
+
if 'match_ratio' in final_obs:
|
| 92 |
+
print(f"Match Ratio: {final_obs['match_ratio']:.0%}")
|
| 93 |
+
except:
|
| 94 |
+
print(f"Final Obs: {obs_json}")
|
| 95 |
+
break
|
| 96 |
+
|
| 97 |
+
except Exception as e:
|
| 98 |
+
print(f"{Fore.RED}LLM Error: {e}")
|
| 99 |
+
break
|
| 100 |
+
|
| 101 |
+
step_count += 1
|
| 102 |
+
time.sleep(1)
|
| 103 |
+
|
| 104 |
+
if step_count >= max_steps:
|
| 105 |
+
print(f"\n{Fore.RED}{Style.BRIGHT}=== MAX STEPS REACHED ===")
|
| 106 |
+
|
| 107 |
+
if __name__ == "__main__":
|
| 108 |
+
parser = argparse.ArgumentParser(description="ER-MAP Automated CLI Tester")
|
| 109 |
+
parser.add_argument("--phase", type=int, default=1, choices=[1, 2, 3], help="Curriculum phase (1-3)")
|
| 110 |
+
args = parser.parse_args()
|
| 111 |
+
|
| 112 |
+
try:
|
| 113 |
+
run_automated_cli(args.phase)
|
| 114 |
+
except KeyboardInterrupt:
|
| 115 |
+
print("\nExiting...")
|
| 116 |
+
sys.exit(0)
|
ER_MAP/dashboard.py
CHANGED
|
@@ -43,7 +43,7 @@ def get_env():
|
|
| 43 |
from ER_MAP.envs.triage_env import TriageEnv
|
| 44 |
nurse_key = os.environ.get("GROQ_NURSE_API_KEY", "")
|
| 45 |
patient_key = os.environ.get("GROQ_PATIENT_API_KEY", "")
|
| 46 |
-
model = os.environ.get("ERMAP_MODEL", "llama-3.
|
| 47 |
ENV = TriageEnv(nurse_api_key=nurse_key, patient_api_key=patient_key, model=model)
|
| 48 |
return ENV
|
| 49 |
|
|
@@ -53,7 +53,7 @@ def get_doctor():
|
|
| 53 |
if DOCTOR is None:
|
| 54 |
from groq import Groq
|
| 55 |
api_key = os.environ.get("GROQ_DOCTOR_API_KEY", "") or os.environ.get("GROQ_PATIENT_API_KEY", "")
|
| 56 |
-
model = os.environ.get("ERMAP_MODEL", "llama-3.
|
| 57 |
DOCTOR = DoctorBrain(api_key=api_key, model=model)
|
| 58 |
return DOCTOR
|
| 59 |
|
|
|
|
| 43 |
from ER_MAP.envs.triage_env import TriageEnv
|
| 44 |
nurse_key = os.environ.get("GROQ_NURSE_API_KEY", "")
|
| 45 |
patient_key = os.environ.get("GROQ_PATIENT_API_KEY", "")
|
| 46 |
+
model = os.environ.get("ERMAP_MODEL", "llama-3.3-70b-versatile")
|
| 47 |
ENV = TriageEnv(nurse_api_key=nurse_key, patient_api_key=patient_key, model=model)
|
| 48 |
return ENV
|
| 49 |
|
|
|
|
| 53 |
if DOCTOR is None:
|
| 54 |
from groq import Groq
|
| 55 |
api_key = os.environ.get("GROQ_DOCTOR_API_KEY", "") or os.environ.get("GROQ_PATIENT_API_KEY", "")
|
| 56 |
+
model = os.environ.get("ERMAP_MODEL", "llama-3.3-70b-versatile")
|
| 57 |
DOCTOR = DoctorBrain(api_key=api_key, model=model)
|
| 58 |
return DOCTOR
|
| 59 |
|
ER_MAP/envs/__pycache__/api_router.cpython-313.pyc
CHANGED
|
Binary files a/ER_MAP/envs/__pycache__/api_router.cpython-313.pyc and b/ER_MAP/envs/__pycache__/api_router.cpython-313.pyc differ
|
|
|
ER_MAP/envs/__pycache__/disease_db.cpython-313.pyc
CHANGED
|
Binary files a/ER_MAP/envs/__pycache__/disease_db.cpython-313.pyc and b/ER_MAP/envs/__pycache__/disease_db.cpython-313.pyc differ
|
|
|
ER_MAP/envs/__pycache__/empathy_engine.cpython-313.pyc
CHANGED
|
Binary files a/ER_MAP/envs/__pycache__/empathy_engine.cpython-313.pyc and b/ER_MAP/envs/__pycache__/empathy_engine.cpython-313.pyc differ
|
|
|
ER_MAP/envs/__pycache__/randomizer.cpython-313.pyc
CHANGED
|
Binary files a/ER_MAP/envs/__pycache__/randomizer.cpython-313.pyc and b/ER_MAP/envs/__pycache__/randomizer.cpython-313.pyc differ
|
|
|
ER_MAP/envs/__pycache__/triage_env.cpython-313.pyc
CHANGED
|
Binary files a/ER_MAP/envs/__pycache__/triage_env.cpython-313.pyc and b/ER_MAP/envs/__pycache__/triage_env.cpython-313.pyc differ
|
|
|
ER_MAP/envs/api_router.py
CHANGED
|
@@ -28,7 +28,7 @@ except ImportError:
|
|
| 28 |
# ---------------------------------------------------------------------------
|
| 29 |
# Configuration
|
| 30 |
# ---------------------------------------------------------------------------
|
| 31 |
-
DEFAULT_MODEL = "llama-3.
|
| 32 |
MAX_SLIDING_WINDOW_TURNS = 3 # Keep system prompt + last 3 exchanges
|
| 33 |
DEFAULT_MAX_TOKENS = 512
|
| 34 |
DEFAULT_TEMPERATURE = 0.7
|
|
@@ -242,6 +242,80 @@ class AgentRouter:
|
|
| 242 |
|
| 243 |
return parsed
|
| 244 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 245 |
# ----- Mock Responses (for testing without API) -----
|
| 246 |
|
| 247 |
@staticmethod
|
|
|
|
| 28 |
# ---------------------------------------------------------------------------
|
| 29 |
# Configuration
|
| 30 |
# ---------------------------------------------------------------------------
|
| 31 |
+
DEFAULT_MODEL = "llama-3.3-70b-versatile"
|
| 32 |
MAX_SLIDING_WINDOW_TURNS = 3 # Keep system prompt + last 3 exchanges
|
| 33 |
DEFAULT_MAX_TOKENS = 512
|
| 34 |
DEFAULT_TEMPERATURE = 0.7
|
|
|
|
| 242 |
|
| 243 |
return parsed
|
| 244 |
|
| 245 |
+
# ----- LLM-as-a-Judge Treatment Evaluation -----
|
| 246 |
+
|
| 247 |
+
def evaluate_treatment(
|
| 248 |
+
self,
|
| 249 |
+
prescribed_treatment: str,
|
| 250 |
+
correct_treatment: str,
|
| 251 |
+
lethal_treatments: list,
|
| 252 |
+
disease_name: str,
|
| 253 |
+
) -> dict:
|
| 254 |
+
"""
|
| 255 |
+
Use a 70B LLM as a Chief Medical Officer to grade the Doctor's
|
| 256 |
+
prescribed treatment against the ground truth.
|
| 257 |
+
|
| 258 |
+
Returns:
|
| 259 |
+
{"score": float 0.0-1.0, "is_lethal": bool, "reasoning": str}
|
| 260 |
+
"""
|
| 261 |
+
# Pick any available client (prefer nurse client to spread rate limits)
|
| 262 |
+
client = self._clients.get("nurse") or self._clients.get("patient")
|
| 263 |
+
if client is None:
|
| 264 |
+
logger.warning("No API client available for LLM Judge. Returning default score.")
|
| 265 |
+
return {"score": 0.5, "is_lethal": False, "reasoning": "No API client available."}
|
| 266 |
+
|
| 267 |
+
lethal_str = ", ".join(lethal_treatments) if lethal_treatments else "None"
|
| 268 |
+
|
| 269 |
+
judge_prompt = (
|
| 270 |
+
"You are the Chief Medical Officer reviewing an ER Doctor's treatment plan.\n\n"
|
| 271 |
+
f"## Disease (Hidden from Doctor during episode)\n{disease_name}\n\n"
|
| 272 |
+
f"## Gold-Standard Correct Treatment\n{correct_treatment}\n\n"
|
| 273 |
+
f"## Known Lethal / Contraindicated Treatments\n{lethal_str}\n\n"
|
| 274 |
+
f"## Doctor's Prescribed Treatment\n{prescribed_treatment}\n\n"
|
| 275 |
+
"## Your Task\n"
|
| 276 |
+
"Grade the Doctor's treatment plan on a scale of 0.0 to 1.0:\n"
|
| 277 |
+
"- 1.0 = Covers all critical interventions, correct drugs, correct dosing approach\n"
|
| 278 |
+
"- 0.75 = Correct diagnosis and most key interventions, minor omissions\n"
|
| 279 |
+
"- 0.5 = Right diagnosis but significant treatment gaps\n"
|
| 280 |
+
"- 0.25 = Partially relevant but missing critical life-saving steps\n"
|
| 281 |
+
"- 0.0 = Completely wrong or dangerous treatment\n\n"
|
| 282 |
+
"IMPORTANT GRADING RULES:\n"
|
| 283 |
+
"- Accept synonyms and equivalent drugs (e.g., 'broad-spectrum antibiotics' partially covers 'cefoxitin')\n"
|
| 284 |
+
"- Accept equivalent procedures described differently (e.g., 'emergency surgery' = 'emergent surgical exploration')\n"
|
| 285 |
+
"- Penalize missing CRITICAL steps (e.g., missing 'NPO' before surgery, missing specific drug classes)\n"
|
| 286 |
+
"- Heavily penalize if a lethal/contraindicated treatment was prescribed\n\n"
|
| 287 |
+
'Respond ONLY in valid JSON:\n'
|
| 288 |
+
'{"score": <float 0.0-1.0>, "is_lethal": <bool>, "reasoning": "<brief explanation>"}'
|
| 289 |
+
)
|
| 290 |
+
|
| 291 |
+
try:
|
| 292 |
+
completion = client.chat.completions.create(
|
| 293 |
+
model=self.model,
|
| 294 |
+
messages=[
|
| 295 |
+
{"role": "system", "content": "You are a medical evaluation AI. Output ONLY valid JSON."},
|
| 296 |
+
{"role": "user", "content": judge_prompt},
|
| 297 |
+
],
|
| 298 |
+
temperature=0.1, # Low temp for consistent grading
|
| 299 |
+
max_tokens=256,
|
| 300 |
+
response_format={"type": "json_object"},
|
| 301 |
+
)
|
| 302 |
+
raw_text = completion.choices[0].message.content or ""
|
| 303 |
+
parsed = _extract_json_from_text(raw_text)
|
| 304 |
+
|
| 305 |
+
if parsed and "score" in parsed:
|
| 306 |
+
score = max(0.0, min(1.0, float(parsed["score"])))
|
| 307 |
+
is_lethal = bool(parsed.get("is_lethal", False))
|
| 308 |
+
reasoning = parsed.get("reasoning", "")
|
| 309 |
+
logger.info(f"LLM Judge: score={score:.2f}, lethal={is_lethal}, reason={reasoning}")
|
| 310 |
+
return {"score": score, "is_lethal": is_lethal, "reasoning": reasoning}
|
| 311 |
+
else:
|
| 312 |
+
logger.warning(f"LLM Judge returned unparseable response: {raw_text[:200]}")
|
| 313 |
+
return {"score": 0.5, "is_lethal": False, "reasoning": "Judge response unparseable."}
|
| 314 |
+
|
| 315 |
+
except Exception as e:
|
| 316 |
+
logger.error(f"LLM Judge API error: {e}")
|
| 317 |
+
return {"score": 0.5, "is_lethal": False, "reasoning": f"API error: {e}"}
|
| 318 |
+
|
| 319 |
# ----- Mock Responses (for testing without API) -----
|
| 320 |
|
| 321 |
@staticmethod
|
ER_MAP/envs/disease_db.py
CHANGED
|
@@ -521,6 +521,173 @@ VITALS_DB["Severe Hypothermia"] = "HR 32, BP 75/45, RR 6, SpO2 88%, Temp 27.5C -
|
|
| 521 |
LAB_RESULTS_DB["Severe Hypothermia"] = {"BMP": "Na 140, K 3.0, Glucose 50 (LOW), Cr 1.5", "ECG": "Marked sinus bradycardia rate 32, Osborn (J) waves in precordial leads, prolonged QT -- classic hypothermia", "ABG": "pH 7.22, pCO2 50, pO2 55 -- mixed acidosis (temperature corrected)", "CBC": "WBC 4.0, Hgb 16.0 (hemoconcentration), Plt 90"}
|
| 522 |
SOAP_HISTORY_DB["Severe Hypothermia"] = {"HPI": "Homeless 60M found unresponsive outdoors by police on a night with ambient temperature -5C. Unknown down time. Minimally responsive. Cold and rigid. Bystanders report he was seen drinking earlier.", "ROS": {"Neuro": "unresponsive"}, "Past_Medical_History": "Unknown -- homeless, no medical records available. Smells of alcohol.", "Medications": "Unknown", "Allergies": "Unknown", "Social_History": "Homeless, known to frequent shelters, alcohol use suspected", "Physical_Examination": "Unresponsive, GCS 5. Core temp 27.5C. Rigid musculature. Bradycardic, weak pulse. Pupils sluggish. Cold skin. No visible trauma."}
|
| 523 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 524 |
DISEASES_DB["Snakebite Envenomation"] = {"true_disease": "Snakebite Envenomation", "true_symptoms": ["fang marks with local swelling", "progressive edema", "ecchymosis", "metallic taste", "coagulopathy"], "correct_treatment": "CroFab antivenom 4-6 vials IV initial dose, repeat if swelling progresses, mark advancing edge of swelling, IV fluids, tetanus prophylaxis, avoid tourniquets and incision", "lethal_treatments": ["tourniquet", "incision and suction", "ice to wound"], "medical_history": "Outdoor exposure, rural area", "difficulty": "medium", "critical_labs": ["CBC", "coagulation", "BMP", "fibrinogen"]}
|
| 525 |
VITALS_DB["Snakebite Envenomation"] = "HR 115, BP 95/60, RR 22, SpO2 97%, Temp 37.5C -- tachycardic, mildly hypotensive"
|
| 526 |
LAB_RESULTS_DB["Snakebite Envenomation"] = {"CBC": "WBC 15.0, Hgb 12.0, Plt 45 (CRITICAL LOW -- venom-induced thrombocytopenia)", "coagulation": "PT 35, INR 4.5 (CRITICAL), aPTT 85 -- severe coagulopathy from venom", "BMP": "Na 138, K 4.8, Cr 1.5, CK 2500 (myotoxicity)", "fibrinogen": "Fibrinogen: 50 mg/dL (CRITICAL LOW -- consumptive coagulopathy)"}
|
|
@@ -530,3 +697,17 @@ DISEASES_DB["Angioedema"] = {"true_disease": "Angioedema", "true_symptoms": ["ra
|
|
| 530 |
VITALS_DB["Angioedema"] = "HR 95, BP 150/90, RR 24, SpO2 93%, Temp 37.0C -- hypertensive (on ACE inhibitor), hypoxic from airway compromise"
|
| 531 |
LAB_RESULTS_DB["Angioedema"] = {"CBC": "WBC 8.0, Hgb 14.0, Plt 220 -- normal", "C4_level": "C4: 8 mg/dL (LOW -- suggests bradykinin-mediated, not histamine)", "tryptase": "Serum tryptase: 5 ng/mL (normal -- NOT allergic/mast cell mediated, confirms ACE inhibitor cause)"}
|
| 532 |
SOAP_HISTORY_DB["Angioedema"] = {"HPI": "65M presents with 4 hours of progressive swelling of tongue and lips. Now having difficulty speaking and swallowing. Mild stridor noted. He has been on lisinopril for 8 years without prior issues. No urticaria. No known allergen exposure.", "ROS": {"ENT": "tongue and lip swelling, difficulty swallowing, voice change", "Resp": "mild stridor, dyspnea", "Derm": "NO urticaria (important -- suggests bradykinin not histamine)"}, "Past_Medical_History": "HTN on lisinopril x 8 years, Type 2 DM", "Medications": "Lisinopril 20mg daily, metformin 1000mg BID", "Allergies": "NKDA", "Social_History": "Retired engineer, African American (higher risk for ACE inhibitor angioedema), non-smoker", "Physical_Examination": "Significant tongue and lip edema. Voice muffled. Mild inspiratory stridor. No urticaria anywhere. Oropharynx: tongue filling oral cavity, uvula edematous. Lungs clear. Airway assessment: concerning for progression."}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 521 |
LAB_RESULTS_DB["Severe Hypothermia"] = {"BMP": "Na 140, K 3.0, Glucose 50 (LOW), Cr 1.5", "ECG": "Marked sinus bradycardia rate 32, Osborn (J) waves in precordial leads, prolonged QT -- classic hypothermia", "ABG": "pH 7.22, pCO2 50, pO2 55 -- mixed acidosis (temperature corrected)", "CBC": "WBC 4.0, Hgb 16.0 (hemoconcentration), Plt 90"}
|
| 522 |
SOAP_HISTORY_DB["Severe Hypothermia"] = {"HPI": "Homeless 60M found unresponsive outdoors by police on a night with ambient temperature -5C. Unknown down time. Minimally responsive. Cold and rigid. Bystanders report he was seen drinking earlier.", "ROS": {"Neuro": "unresponsive"}, "Past_Medical_History": "Unknown -- homeless, no medical records available. Smells of alcohol.", "Medications": "Unknown", "Allergies": "Unknown", "Social_History": "Homeless, known to frequent shelters, alcohol use suspected", "Physical_Examination": "Unresponsive, GCS 5. Core temp 27.5C. Rigid musculature. Bradycardic, weak pulse. Pupils sluggish. Cold skin. No visible trauma."}
|
| 523 |
|
| 524 |
+
DISEASES_DB["Snakebite Envenomation"] = {"true_disease": "Snakebite Envenomation", "true_symptoms": ["fang marks with local swelling", "progressive edema", "ecchymosis", "metallic taste", "coagulopathy"], "correct_treatment": "CroFab antivenom 4-6 vials IV initial dose, repeat if swelling progresses, mark advancing edge of swelling, IV fluids, tetanus prophylaxis, avoid tourniquets and incision", "lethal_treatments": ["tourniquet", "incision and suction", "ice to wound"], "medical_history": "Outdoor exposure, rural area", "difficulty": "medium", "critical_labs": ["CBC", "coagulation", "BMP", "fibrinogen"]}
|
| 525 |
+
VITALS_DB["Snakebite Envenomation"] = "HR 115, BP 95/60, RR 22, SpO2 97%, Temp 37.5C -- tachycardic, mildly hypotensive"
|
| 526 |
+
|
| 527 |
+
# ---------------------------------------------------------------------------
|
| 528 |
+
# CLASS 5: ENDOCRINE / METABOLIC (5 diseases)
|
| 529 |
+
# ---------------------------------------------------------------------------
|
| 530 |
+
|
| 531 |
+
DISEASES_DB["Diabetic Ketoacidosis"] = {"true_disease": "Diabetic Ketoacidosis", "true_symptoms": ["nausea and vomiting", "abdominal pain", "fruity breath", "Kussmaul breathing", "polyuria and polydipsia"], "correct_treatment": "IV insulin drip 0.1 units/kg/hr, aggressive IV normal saline, potassium replacement, monitor glucose hourly, search for precipitant", "lethal_treatments": ["IV insulin bolus without checking potassium first", "bicarbonate unless pH below 6.9"], "medical_history": "Type 1 DM, insulin non-compliance", "difficulty": "medium", "critical_labs": ["BMP", "ABG", "CBC", "urinalysis"]}
|
| 532 |
+
VITALS_DB["Diabetic Ketoacidosis"] = "HR 120, BP 95/55, RR 32 deep Kussmaul, SpO2 98%, Temp 37.8C -- tachycardic, hypotensive, Kussmaul respirations"
|
| 533 |
+
LAB_RESULTS_DB["Diabetic Ketoacidosis"] = {"BMP": "Na 128, K 5.8 (HIGH but total body K depleted), Cl 95, CO2 8 (LOW), Glucose 520, Cr 1.8, Anion gap 25", "ABG": "pH 7.12, pCO2 18, pO2 98, HCO3 6 -- severe metabolic acidosis with anion gap", "CBC": "WBC 18.0 (stress response), Hgb 16.0 (hemoconcentration), Plt 250", "urinalysis": "Urinalysis: glucose 4+, ketones 4+, specific gravity 1.035 -- consistent with DKA"}
|
| 534 |
+
SOAP_HISTORY_DB["Diabetic Ketoacidosis"] = {"HPI": "22F with Type 1 DM presents with 2 days of nausea, vomiting, diffuse abdominal pain, and increasing confusion. Roommate reports she ran out of insulin 3 days ago. Fruity odor on breath noted.", "ROS": {"GI": "nausea, vomiting, abdominal pain", "Resp": "deep rapid breathing", "Neuro": "confusion, lethargy", "GU": "polyuria, polydipsia x 3 days"}, "Past_Medical_History": "Type 1 DM diagnosed age 14, prior DKA admission 2 years ago, depression", "Medications": "Insulin glargine 20u nightly, insulin lispro sliding scale (ran out 3 days ago), sertraline 50mg", "Allergies": "NKDA", "Social_History": "College student, non-smoker, social drinker, lives in dorm", "Physical_Examination": "Lethargic, dry mucous membranes, poor skin turgor. Fruity breath. Kussmaul respirations. Abdomen diffusely tender without peritoneal signs. Tachycardic."}
|
| 535 |
+
|
| 536 |
+
DISEASES_DB["Thyroid Storm"] = {"true_disease": "Thyroid Storm", "true_symptoms": ["high fever", "tachycardia out of proportion", "agitation and delirium", "tremor", "diarrhea"], "correct_treatment": "propylthiouracil or methimazole, propranolol for rate control, hydrocortisone 100mg IV, cooling measures, ICU admission", "lethal_treatments": ["radioactive iodine acutely", "iodine before thionamide"], "medical_history": "Graves disease, hyperthyroidism", "difficulty": "hard", "critical_labs": ["TSH", "BMP", "CBC", "ECG"]}
|
| 537 |
+
VITALS_DB["Thyroid Storm"] = "HR 165, BP 160/60 (wide pulse pressure), RR 28, SpO2 97%, Temp 40.2C -- extreme tachycardia, hyperthermia"
|
| 538 |
+
LAB_RESULTS_DB["Thyroid Storm"] = {"TSH": "TSH: <0.01 mIU/L (undetectable), Free T4: 7.8 ng/dL (CRITICAL HIGH), Free T3: 22 pg/mL (CRITICAL HIGH)", "BMP": "Na 135, K 3.2 (LOW), Glucose 250, Ca 11.5 (HIGH), Cr 1.0", "CBC": "WBC 12.0, Hgb 12.5, Plt 180", "ECG": "Sinus tachycardia rate 165, no ST changes, possible atrial fibrillation"}
|
| 539 |
+
SOAP_HISTORY_DB["Thyroid Storm"] = {"HPI": "35F presents with 2 days of worsening agitation, tremor, palpitations, and diarrhea. Temperature 40.2C at home. Husband reports she has been increasingly confused and combative. Recently stopped her thyroid medication.", "ROS": {"Neuro": "agitation, tremor, confusion", "CV": "palpitations, chest discomfort", "GI": "diarrhea x 5 episodes", "Derm": "diaphoresis, warm flushed skin"}, "Past_Medical_History": "Graves disease diagnosed 3 years ago, stopped methimazole 2 weeks ago due to side effects", "Medications": "Methimazole 10mg TID (discontinued 2 weeks ago)", "Allergies": "NKDA", "Social_History": "Marketing executive, non-smoker, no alcohol", "Physical_Examination": "Agitated, diaphoretic, tremulous. Exophthalmos bilateral. Thyroid diffusely enlarged with bruit. Tachycardic, wide pulse pressure. Hyperreflexia. Fever 40.2C."}
|
| 540 |
+
|
| 541 |
+
DISEASES_DB["Adrenal Crisis"] = {"true_disease": "Adrenal Crisis", "true_symptoms": ["severe hypotension refractory to fluids", "abdominal pain", "weakness and fatigue", "confusion", "nausea and vomiting"], "correct_treatment": "IV hydrocortisone 100mg stat then 50mg every 8 hours, aggressive IV normal saline, dextrose if hypoglycemic, treat precipitating cause", "lethal_treatments": ["vasopressors without steroids"], "medical_history": "Chronic steroid use, Addison disease", "difficulty": "hard", "critical_labs": ["cortisol", "BMP", "CBC"]}
|
| 542 |
+
VITALS_DB["Adrenal Crisis"] = "HR 130, BP 65/40 (refractory to fluids), RR 24, SpO2 96%, Temp 38.5C -- profound hypotension"
|
| 543 |
+
LAB_RESULTS_DB["Adrenal Crisis"] = {"cortisol": "Random cortisol: 1.2 mcg/dL (CRITICAL LOW -- should be >18 in stress)", "BMP": "Na 118 (CRITICAL LOW), K 6.2 (HIGH), Glucose 45 (LOW), Cr 1.5", "CBC": "WBC 3.5 (LOW), Hgb 11.0, Plt 150, eosinophilia 12%", "ACTH": "ACTH: 450 pg/mL (elevated -- primary adrenal insufficiency)"}
|
| 544 |
+
SOAP_HISTORY_DB["Adrenal Crisis"] = {"HPI": "52M presents with progressive weakness, nausea, and abdominal pain over 24 hours. Became confused and near-syncopal this morning. Has been on chronic prednisone which was abruptly stopped 5 days ago by another provider.", "ROS": {"CV": "lightheadedness, near-syncope", "GI": "nausea, vomiting, abdominal pain", "Neuro": "confusion, weakness", "Derm": "skin hyperpigmentation noted"}, "Past_Medical_History": "Rheumatoid arthritis on chronic prednisone 20mg daily x 3 years, abruptly discontinued 5 days ago", "Medications": "Prednisone 20mg daily (STOPPED 5 days ago), methotrexate 15mg weekly", "Allergies": "NKDA", "Social_History": "Retired, non-smoker, no alcohol", "Physical_Examination": "Obtunded, hyperpigmented skin creases and buccal mucosa. Profoundly hypotensive despite 2L NS. Abdomen tender diffusely. Weak pulses."}
|
| 545 |
+
|
| 546 |
+
DISEASES_DB["Severe Hypoglycemia"] = {"true_disease": "Severe Hypoglycemia", "true_symptoms": ["confusion", "diaphoresis", "tremor", "seizure", "loss of consciousness"], "correct_treatment": "IV dextrose D50 25g (50mL) stat, glucagon 1mg IM if no IV access, recheck glucose in 15 minutes, determine and treat cause", "lethal_treatments": ["insulin administration"], "medical_history": "Diabetes on insulin or sulfonylureas", "difficulty": "easy", "critical_labs": ["glucose", "BMP", "CBC"]}
|
| 547 |
+
VITALS_DB["Severe Hypoglycemia"] = "HR 110, BP 150/90, RR 20, SpO2 98%, Temp 36.5C -- tachycardic, hypertensive (catecholamine surge)"
|
| 548 |
+
LAB_RESULTS_DB["Severe Hypoglycemia"] = {"glucose": "Bedside glucose: 28 mg/dL (CRITICAL LOW)", "BMP": "Na 140, K 4.0, Glucose 28 (CRITICAL LOW), Cr 1.8 (CKD -- reduced insulin clearance)", "CBC": "WBC 8.0, Hgb 10.5, Plt 200 -- mild anemia of CKD"}
|
| 549 |
+
SOAP_HISTORY_DB["Severe Hypoglycemia"] = {"HPI": "75M found by daughter unresponsive at home. Diaphoretic and tremulous. Daughter reports he took his insulin this morning but did not eat breakfast. History of similar episodes.", "ROS": {"Neuro": "unresponsive, diaphoresis, tremor"}, "Past_Medical_History": "Type 2 DM on insulin, CKD stage 3 (reduced insulin clearance), prior hypoglycemic episodes", "Medications": "Insulin glargine 30u nightly, glipizide 10mg BID, metformin 500mg BID (should be held for CKD)", "Allergies": "NKDA", "Social_History": "Retired, lives alone, daughter visits daily, poor appetite recently", "Physical_Examination": "Unresponsive, GCS 6. Diaphoretic, cool clammy skin. Tremor. No focal neurological deficits. Pupils equal and reactive."}
|
| 550 |
+
|
| 551 |
+
DISEASES_DB["Hyperkalemia"] = {"true_disease": "Hyperkalemia", "true_symptoms": ["muscle weakness", "palpitations", "chest pain", "paresthesias", "nausea"], "correct_treatment": "IV calcium gluconate 10mL for cardiac stabilization, IV insulin 10 units with D50, albuterol nebulizer, kayexalate or patiromer, emergent dialysis if refractory", "lethal_treatments": ["calcium chloride via peripheral IV (extravasation necrosis)"], "medical_history": "CKD, ACE inhibitor use, potassium supplements", "difficulty": "medium", "critical_labs": ["BMP", "ECG", "CBC"]}
|
| 552 |
+
VITALS_DB["Hyperkalemia"] = "HR 45 (bradycardia), BP 100/65, RR 18, SpO2 97%, Temp 36.8C -- bradycardic"
|
| 553 |
+
LAB_RESULTS_DB["Hyperkalemia"] = {"BMP": "Na 132, K 7.8 (CRITICAL HIGH), Cr 5.5 (ESRD), BUN 85, CO2 16 (metabolic acidosis)", "ECG": "Peaked T waves, widened QRS, loss of P waves -- CRITICAL: approaching sine wave pattern", "CBC": "WBC 7.0, Hgb 9.0 (anemia of CKD), Plt 180"}
|
| 554 |
+
SOAP_HISTORY_DB["Hyperkalemia"] = {"HPI": "62M with ESRD on dialysis presents with 1 day of progressive weakness, palpitations, and nausea. Missed his last 2 dialysis sessions. Reports eating bananas and oranges heavily this week.", "ROS": {"CV": "palpitations, chest discomfort", "MSK": "generalized weakness", "GI": "nausea", "Neuro": "tingling in fingers"}, "Past_Medical_History": "ESRD on hemodialysis MWF, missed last 2 sessions, HTN, Type 2 DM", "Medications": "Lisinopril 40mg daily, sevelamer, EPO injections, potassium supplement (should have been stopped)", "Allergies": "NKDA", "Social_History": "Retired, lives with wife, transportation issues to dialysis center", "Physical_Examination": "Lethargic, bradycardic. Generalized muscle weakness 3/5 throughout. AV fistula left arm with good thrill. Mild peripheral edema."}
|
| 555 |
+
|
| 556 |
+
# ---------------------------------------------------------------------------
|
| 557 |
+
# CLASS 6: TOXICOLOGY (5 diseases)
|
| 558 |
+
# ---------------------------------------------------------------------------
|
| 559 |
+
|
| 560 |
+
DISEASES_DB["Opioid Overdose"] = {"true_disease": "Opioid Overdose", "true_symptoms": ["pinpoint pupils", "respiratory depression", "altered consciousness", "cyanosis", "bradycardia"], "correct_treatment": "naloxone 0.4mg IV repeat every 2-3 minutes, bag-valve mask ventilation, intubation if no response, monitor for re-sedation", "lethal_treatments": ["sedatives", "benzodiazepines"], "medical_history": "Opioid use disorder, chronic pain", "difficulty": "easy", "critical_labs": ["urine_tox", "ABG", "CBC"]}
|
| 561 |
+
VITALS_DB["Opioid Overdose"] = "HR 50, BP 85/50, RR 4, SpO2 72%, Temp 35.8C -- bradycardic, severe respiratory depression, hypothermic"
|
| 562 |
+
LAB_RESULTS_DB["Opioid Overdose"] = {"urine_tox": "Urine tox screen: positive for opioids, negative for benzos/amphetamines/cocaine", "ABG": "pH 7.18, pCO2 75, pO2 42, HCO3 24 -- respiratory acidosis from hypoventilation", "CBC": "WBC 7.0, Hgb 13.0, Plt 200 -- normal"}
|
| 563 |
+
SOAP_HISTORY_DB["Opioid Overdose"] = {"HPI": "28M found unresponsive by friends at home. Needle and drug paraphernalia nearby. Agonal respirations. Friends report heroin use.", "ROS": {"Neuro": "unresponsive", "Resp": "agonal breathing"}, "Past_Medical_History": "Opioid use disorder, prior overdose x2, hepatitis C", "Medications": "None prescribed", "Allergies": "NKDA", "Social_History": "Unemployed, IV heroin user x 5 years, lives in shelter", "Physical_Examination": "Unresponsive, GCS 3. Pinpoint pupils. RR 4, cyanotic. Track marks bilateral arms. No trauma."}
|
| 564 |
+
|
| 565 |
+
DISEASES_DB["Acetaminophen Toxicity"] = {"true_disease": "Acetaminophen Toxicity", "true_symptoms": ["nausea and vomiting", "right upper quadrant pain", "jaundice", "confusion", "malaise"], "correct_treatment": "N-acetylcysteine IV protocol 150mg/kg loading then 50mg/kg over 4h then 100mg/kg over 16h, acetaminophen level, LFTs serial, poison control consult", "lethal_treatments": ["delaying NAC beyond 8 hours post ingestion"], "medical_history": "Depression, intentional ingestion", "difficulty": "medium", "critical_labs": ["acetaminophen_level", "LFTs", "BMP", "coagulation"]}
|
| 566 |
+
VITALS_DB["Acetaminophen Toxicity"] = "HR 95, BP 110/70, RR 18, SpO2 99%, Temp 37.0C -- initially stable (deceptive)"
|
| 567 |
+
LAB_RESULTS_DB["Acetaminophen Toxicity"] = {"acetaminophen_level": "Acetaminophen level: 180 mcg/mL at 4 hours post ingestion (ABOVE Rumack-Matthew treatment line)", "LFTs": "AST 85, ALT 92, Alk Phos 120 -- early elevation, expect massive rise", "BMP": "Na 140, K 4.0, Cr 1.0, Glucose 95 -- normal early", "coagulation": "PT 14, INR 1.3 -- early coagulopathy developing"}
|
| 568 |
+
SOAP_HISTORY_DB["Acetaminophen Toxicity"] = {"HPI": "19F brought in by parents after admitting to ingesting approximately 50 tablets of extra-strength Tylenol (500mg each = ~25g) approximately 6 hours ago after argument with boyfriend. Currently nauseous with RUQ discomfort.", "ROS": {"GI": "nausea, vomiting, RUQ pain", "Psych": "suicidal ideation, regretful", "Neuro": "mild malaise"}, "Past_Medical_History": "Depression, anxiety, no prior suicide attempts", "Medications": "Sertraline 100mg daily", "Allergies": "NKDA", "Social_History": "College student, lives with parents, recently broken up with boyfriend", "Physical_Examination": "Tearful, cooperative. RUQ mildly tender. No jaundice yet. Alert and oriented. No focal deficits."}
|
| 569 |
+
|
| 570 |
+
DISEASES_DB["Carbon Monoxide Poisoning"] = {"true_disease": "Carbon Monoxide Poisoning", "true_symptoms": ["headache", "confusion", "cherry red skin", "nausea", "dizziness"], "correct_treatment": "100% oxygen via non-rebreather mask, consider hyperbaric oxygen if COHb above 25% or neurologic symptoms or pregnancy, serial COHb levels", "lethal_treatments": ["relying on pulse oximetry alone (falsely normal in CO poisoning)"], "medical_history": "Faulty heater, house fire, enclosed space", "difficulty": "medium", "critical_labs": ["COHb", "ABG", "ECG", "BMP"]}
|
| 571 |
+
VITALS_DB["Carbon Monoxide Poisoning"] = "HR 105, BP 130/80, RR 22, SpO2 98% (FALSELY NORMAL), Temp 37.0C -- SpO2 unreliable in CO poisoning!"
|
| 572 |
+
LAB_RESULTS_DB["Carbon Monoxide Poisoning"] = {"COHb": "Carboxyhemoglobin: 32% (CRITICAL -- severe CO poisoning, >25% requires hyperbaric)", "ABG": "pH 7.30, pCO2 32, pO2 85 (misleading), HCO3 18 -- metabolic acidosis, lactate 5.2", "ECG": "Sinus tachycardia, diffuse ST depression -- myocardial ischemia from CO", "BMP": "Na 140, K 4.5, Cr 1.0, Glucose 160"}
|
| 573 |
+
SOAP_HISTORY_DB["Carbon Monoxide Poisoning"] = {"HPI": "Family of 4 (father 45M presenting) brought in by fire department after found confused in home with headaches. Gas heater was running in closed room overnight. All family members symptomatic.", "ROS": {"Neuro": "headache, confusion, dizziness", "CV": "chest tightness", "GI": "nausea"}, "Past_Medical_History": "Healthy, no chronic conditions", "Medications": "None", "Allergies": "NKDA", "Social_History": "Factory worker, lives in older home with gas heating, wife and 2 children also symptomatic", "Physical_Examination": "Confused, cherry-red discoloration of lips. SpO2 reads 98% (unreliable). Tachycardic. Mild ataxia on gait testing."}
|
| 574 |
+
|
| 575 |
+
DISEASES_DB["Alcohol Withdrawal"] = {"true_disease": "Alcohol Withdrawal", "true_symptoms": ["tremor", "agitation", "hallucinations", "tachycardia", "diaphoresis"], "correct_treatment": "IV diazepam or lorazepam using CIWA protocol, thiamine 500mg IV before glucose, folate, magnesium replacement, monitor for seizures and delirium tremens", "lethal_treatments": ["IV glucose before thiamine (precipitates Wernicke)"], "medical_history": "Heavy alcohol use, prior withdrawal seizures", "difficulty": "medium", "critical_labs": ["BMP", "CBC", "LFTs", "ethanol_level"]}
|
| 576 |
+
VITALS_DB["Alcohol Withdrawal"] = "HR 125, BP 170/100, RR 22, SpO2 97%, Temp 38.3C -- tachycardic, hypertensive, low-grade fever"
|
| 577 |
+
LAB_RESULTS_DB["Alcohol Withdrawal"] = {"BMP": "Na 130, K 2.8 (LOW), Mg 1.0 (LOW), Glucose 65 (low), Cr 1.2", "CBC": "WBC 12.0, Hgb 10.5 (macrocytic), Plt 95 (low -- liver disease), MCV 108 (macrocytic)", "LFTs": "AST 220, ALT 95 (AST:ALT >2:1 -- alcoholic pattern), GGT 450, T.Bili 2.8", "ethanol_level": "Blood alcohol: 0 mg/dL (withdrawal occurring as alcohol cleared)"}
|
| 578 |
+
SOAP_HISTORY_DB["Alcohol Withdrawal"] = {"HPI": "52M presents with tremor, agitation, and visual hallucinations starting 48 hours after his last drink. Reports seeing spiders on walls. Last drink was 2 days ago when he ran out of money. History of heavy drinking 1 pint vodka daily x 20 years.", "ROS": {"Neuro": "tremor, agitation, visual hallucinations, insomnia", "CV": "palpitations", "GI": "nausea, anorexia"}, "Past_Medical_History": "Alcohol use disorder, alcoholic hepatitis, prior withdrawal seizure 1 year ago, malnutrition", "Medications": "None -- non-compliant with recommended medications", "Allergies": "NKDA", "Social_History": "Homeless, drinks 1 pint vodka daily x 20 years, smokes, no IV drug use", "Physical_Examination": "Agitated, tremulous, diaphoretic. Visual hallucinations (picking at sheets). Coarse hand tremor. Hepatomegaly. Spider angiomata. CIWA score 28 (severe)."}
|
| 579 |
+
|
| 580 |
+
DISEASES_DB["Serotonin Syndrome"] = {"true_disease": "Serotonin Syndrome", "true_symptoms": ["agitation", "hyperthermia", "clonus", "muscle rigidity", "diaphoresis"], "correct_treatment": "discontinue all serotonergic agents, cyproheptadine 12mg initial then 4mg every 2 hours, active cooling, benzodiazepines for agitation, ICU admission", "lethal_treatments": ["dantrolene (wrong diagnosis -- not NMS)", "additional serotonergic agents"], "medical_history": "Multiple serotonergic medications, recent dose change", "difficulty": "hard", "critical_labs": ["BMP", "CK", "CBC"]}
|
| 581 |
+
VITALS_DB["Serotonin Syndrome"] = "HR 135, BP 165/95, RR 26, SpO2 95%, Temp 39.8C -- hyperthermic, tachycardic, hypertensive"
|
| 582 |
+
LAB_RESULTS_DB["Serotonin Syndrome"] = {"BMP": "Na 138, K 4.8, Cr 1.5, Glucose 145", "CK": "CK: 3200 U/L (elevated -- muscle rigidity causing rhabdomyolysis)", "CBC": "WBC 14.0, Hgb 15.0 (hemoconcentration), Plt 200"}
|
| 583 |
+
SOAP_HISTORY_DB["Serotonin Syndrome"] = {"HPI": "34M presents with acute onset agitation, muscle rigidity, and fever starting 6 hours after his psychiatrist added tramadol to his existing SSRI regimen. Wife reports he became increasingly confused and developed jerking movements in his legs.", "ROS": {"Neuro": "agitation, confusion, jerking limb movements, muscle rigidity", "Derm": "profuse sweating", "GI": "diarrhea x3 episodes"}, "Past_Medical_History": "Major depressive disorder, chronic back pain, started tramadol today", "Medications": "Sertraline 200mg daily, trazodone 100mg nightly, tramadol 50mg TID (STARTED TODAY)", "Allergies": "NKDA", "Social_History": "Accountant, non-smoker, no alcohol", "Physical_Examination": "Agitated, diaphoretic, hyperthermic 39.8C. Bilateral lower extremity clonus (>10 beats). Muscle rigidity in legs. Hyperreflexia throughout. Dilated pupils. Tremor."}
|
| 584 |
+
|
| 585 |
+
# ---------------------------------------------------------------------------
|
| 586 |
+
# CLASS 7: TRAUMA (5 diseases)
|
| 587 |
+
# ---------------------------------------------------------------------------
|
| 588 |
+
|
| 589 |
+
DISEASES_DB["Traumatic Brain Injury"] = {"true_disease": "Traumatic Brain Injury", "true_symptoms": ["loss of consciousness", "confusion", "vomiting", "unequal pupils", "worsening headache"], "correct_treatment": "CT head emergent, neurosurgery consult, elevate head of bed 30 degrees, mannitol 1g/kg or hypertonic saline if herniating, intubation if GCS 8 or below", "lethal_treatments": ["lumbar puncture with elevated ICP", "anticoagulation acutely"], "medical_history": "Fall, assault, MVA", "difficulty": "medium", "critical_labs": ["CT_head", "CBC", "BMP", "coagulation"]}
|
| 590 |
+
VITALS_DB["Traumatic Brain Injury"] = "HR 55 (Cushing), BP 195/100 (Cushing), RR 10 irregular, SpO2 94%, Temp 37.0C -- Cushing triad concerning for herniation"
|
| 591 |
+
LAB_RESULTS_DB["Traumatic Brain Injury"] = {"CT_head": "CT Head: large right-sided epidural hematoma with 8mm midline shift, uncal herniation -- EMERGENT SURGICAL EVACUATION NEEDED", "CBC": "WBC 12.0, Hgb 12.5, Plt 220", "BMP": "Na 140, K 4.0, Cr 0.9 -- normal", "coagulation": "PT 12, INR 1.0, aPTT 28 -- normal"}
|
| 592 |
+
SOAP_HISTORY_DB["Traumatic Brain Injury"] = {"HPI": "35M brought by EMS after falling from 10-foot ladder at construction site. Witnessed brief LOC followed by lucid interval, now becoming progressively more confused and combative. Vomited x2 in ambulance.", "ROS": {"Neuro": "LOC, confusion, vomiting, combative"}, "Past_Medical_History": "Healthy, no bleeding disorders, no anticoagulant use", "Medications": "None", "Allergies": "NKDA", "Social_History": "Construction worker, non-smoker, social drinker, no helmet worn", "Physical_Examination": "GCS 9 (E2V3M4). Right temporal scalp hematoma. Right pupil 6mm fixed, left 3mm reactive. Left hemiparesis. Cushing triad present."}
|
| 593 |
+
|
| 594 |
+
DISEASES_DB["Open Femur Fracture"] = {"true_disease": "Open Femur Fracture", "true_symptoms": ["severe thigh pain", "visible bone through skin", "limb deformity", "significant bleeding", "inability to bear weight"], "correct_treatment": "tourniquet if active hemorrhage, IV fluid resuscitation, tetanus prophylaxis, IV cefazolin, emergent orthopedic consult, traction splint, pain management with IV fentanyl", "lethal_treatments": ["reducing open fracture in ED without OR"], "medical_history": "Trauma, MVA", "difficulty": "medium", "critical_labs": ["CBC", "BMP", "type_and_screen", "coagulation"]}
|
| 595 |
+
VITALS_DB["Open Femur Fracture"] = "HR 130, BP 80/50, RR 24, SpO2 97%, Temp 36.5C -- tachycardic, hypotensive from blood loss (up to 1500mL from femur)"
|
| 596 |
+
LAB_RESULTS_DB["Open Femur Fracture"] = {"CBC": "WBC 14.0, Hgb 8.5 (acute blood loss), Plt 200", "BMP": "Na 138, K 4.5, Cr 1.2, Lactate 3.8 -- lactic acidosis from hemorrhage", "type_and_screen": "Type A positive, crossmatch 4 units pRBC", "coagulation": "PT 13, INR 1.1, aPTT 30 -- normal"}
|
| 597 |
+
SOAP_HISTORY_DB["Open Femur Fracture"] = {"HPI": "25M motorcycle accident at high speed. Right thigh deformity with bone protruding through skin. Significant blood at scene. Screaming in pain. No LOC, no head injury.", "ROS": {"MSK": "right thigh pain, deformity, open wound", "CV": "lightheadedness"}, "Past_Medical_History": "Previously healthy", "Medications": "None", "Allergies": "NKDA", "Social_History": "College student, motorcycle rider, no helmet", "Physical_Examination": "Pale, diaphoretic, tachycardic. Right thigh: open fracture Gustilo type IIIA, bone visible, active bleeding. Right leg shortened and externally rotated. Distal pulses faint but present. Left leg normal. FAST scan negative."}
|
| 598 |
+
|
| 599 |
+
DISEASES_DB["Severe Burn Injury"] = {"true_disease": "Severe Burn Injury", "true_symptoms": ["burns over large body surface area", "pain or painless areas", "singed nasal hair", "hoarse voice", "soot in airway"], "correct_treatment": "secure airway early if inhalation injury suspected, Parkland formula IV fluids 4mL/kg per percent TBSA, wound care, tetanus, pain management, transfer to burn center", "lethal_treatments": ["delayed intubation with progressive airway edema"], "medical_history": "House fire, chemical exposure", "difficulty": "hard", "critical_labs": ["CBC", "BMP", "ABG", "COHb"]}
|
| 600 |
+
VITALS_DB["Severe Burn Injury"] = "HR 135, BP 90/55, RR 28, SpO2 93%, Temp 35.5C -- tachycardic, hypotensive from massive fluid loss, hypothermic"
|
| 601 |
+
LAB_RESULTS_DB["Severe Burn Injury"] = {"CBC": "WBC 18.0, Hgb 18.0 (hemoconcentration from plasma loss), Plt 300", "BMP": "Na 145 (high -- free water loss), K 5.5 (HIGH -- cell destruction), Cr 1.5, Glucose 200", "ABG": "pH 7.30, pCO2 35, pO2 70, HCO3 18, Lactate 5.0 -- metabolic acidosis", "COHb": "Carboxyhemoglobin: 15% (moderate -- inhalation injury likely)"}
|
| 602 |
+
SOAP_HISTORY_DB["Severe Burn Injury"] = {"HPI": "40M rescued from house fire by firefighters. Found in smoke-filled room. Burns to face, chest, bilateral arms. Hoarse voice and coughing soot. Burns estimated 35% TBSA mix of 2nd and 3rd degree.", "ROS": {"Resp": "hoarse voice, cough, soot in sputum", "Derm": "extensive burns face/chest/arms", "Neuro": "alert, severe pain in some areas, painless in others"}, "Past_Medical_History": "Healthy, no chronic conditions", "Medications": "None", "Allergies": "NKDA", "Social_History": "Electrician, smoker, fell asleep with cigarette at home", "Physical_Examination": "Burns: 2nd degree to face, anterior chest, bilateral arms. 3rd degree patches on chest (painless, waxy white). Singed nasal hairs, soot in oropharynx, stridor developing. TBSA approximately 35%."}
|
| 603 |
+
|
| 604 |
+
DISEASES_DB["Pelvic Fracture"] = {"true_disease": "Pelvic Fracture", "true_symptoms": ["pelvic pain", "inability to walk", "hemodynamic instability", "blood at urethral meatus", "lower abdominal pain"], "correct_treatment": "pelvic binder application, massive transfusion protocol, IR angiography for embolization if hemodynamically unstable, avoid Foley if blood at meatus, trauma surgery consult", "lethal_treatments": ["pelvic exam with rocking (worsens hemorrhage)", "Foley catheter if urethral injury suspected"], "medical_history": "High energy trauma, MVA, fall", "difficulty": "hard", "critical_labs": ["CBC", "type_and_screen", "CT_pelvis", "FAST"]}
|
| 605 |
+
VITALS_DB["Pelvic Fracture"] = "HR 140, BP 70/40, RR 28, SpO2 95%, Temp 35.8C -- hemorrhagic shock, hypothermic"
|
| 606 |
+
LAB_RESULTS_DB["Pelvic Fracture"] = {"CBC": "WBC 16.0, Hgb 7.0 (CRITICAL -- massive blood loss), Plt 110", "type_and_screen": "Type O negative, activate massive transfusion protocol", "CT_pelvis": "CT Pelvis: open-book pelvic fracture bilateral sacroiliac disruption, active arterial extravasation right internal iliac", "FAST": "FAST: positive for free fluid in pelvis, negative in Morrison pouch and splenorenal"}
|
| 607 |
+
SOAP_HISTORY_DB["Pelvic Fracture"] = {"HPI": "55F pedestrian struck by car at 40mph. Thrown 15 feet. Severe pelvic and lower abdominal pain. Unable to move legs. Blood noted at urethral meatus.", "ROS": {"MSK": "severe pelvic pain", "GU": "blood at meatus, unable to void", "CV": "lightheaded, thirsty"}, "Past_Medical_History": "Osteoporosis, on warfarin for DVT", "Medications": "Warfarin 5mg daily, calcium/vitamin D", "Allergies": "Codeine", "Social_History": "Retired teacher, was crossing street when struck", "Physical_Examination": "Pale, cold, diaphoretic. Pelvis unstable on gentle compression (do NOT repeat). Blood at urethral meatus. Ecchymosis perineum. Bilateral lower extremity sensation intact. Distal pulses weak."}
|
| 608 |
+
|
| 609 |
+
DISEASES_DB["Splenic Rupture"] = {"true_disease": "Splenic Rupture", "true_symptoms": ["left upper quadrant pain", "left shoulder pain Kehr sign", "abdominal rigidity", "hemodynamic instability", "history of abdominal trauma"], "correct_treatment": "emergent surgical consult, massive transfusion protocol if unstable, CT abdomen if stable enough, IR embolization for grade 3, splenectomy for grade 4-5 or unstable", "lethal_treatments": ["observation only if hemodynamically unstable"], "medical_history": "Blunt abdominal trauma, mononucleosis", "difficulty": "medium", "critical_labs": ["FAST", "CBC", "type_and_screen", "CT_abdomen"]}
|
| 610 |
+
VITALS_DB["Splenic Rupture"] = "HR 125, BP 85/50, RR 24, SpO2 96%, Temp 36.8C -- tachycardic, hypotensive from intra-abdominal hemorrhage"
|
| 611 |
+
LAB_RESULTS_DB["Splenic Rupture"] = {"FAST": "FAST: large amount of free fluid in left upper quadrant (splenorenal recess) and pelvis -- positive", "CBC": "WBC 15.0, Hgb 8.0 (dropping -- active hemorrhage), Plt 180", "type_and_screen": "Type B positive, crossmatch 6 units pRBC, activate MTP", "CT_abdomen": "CT Abdomen: Grade IV splenic laceration with active contrast extravasation, large hemoperitoneum"}
|
| 612 |
+
SOAP_HISTORY_DB["Splenic Rupture"] = {"HPI": "20M brought in after being tackled hard during football game 2 hours ago. Developed progressive LUQ abdominal pain radiating to left shoulder. Became lightheaded on the sideline then nearly passed out.", "ROS": {"GI": "LUQ pain, left shoulder pain", "CV": "lightheaded, near syncope"}, "Past_Medical_History": "Mononucleosis 3 weeks ago (splenomegaly noted on prior visit), cleared for sports by outside provider", "Medications": "None", "Allergies": "NKDA", "Social_History": "College football player, non-smoker, social drinker", "Physical_Examination": "Pale, diaphoretic, guarding LUQ. Kehr sign positive (left shoulder pain with LUQ palpation). Abdomen rigid LUQ. Rebound tenderness. Orthostatic hypotension."}
|
| 613 |
+
|
| 614 |
+
# ---------------------------------------------------------------------------
|
| 615 |
+
# CLASS 8: INFECTIOUS (5 diseases)
|
| 616 |
+
# ---------------------------------------------------------------------------
|
| 617 |
+
|
| 618 |
+
DISEASES_DB["Septic Shock"] = {"true_disease": "Septic Shock", "true_symptoms": ["high fever", "hypotension refractory to fluids", "tachycardia", "altered mental status", "warm flushed skin early then cold"], "correct_treatment": "IV broad spectrum antibiotics within 1 hour, 30mL/kg IV crystalloid bolus, norepinephrine if MAP below 65 after fluids, lactate monitoring, blood cultures before antibiotics, source control", "lethal_treatments": ["delaying antibiotics for cultures", "dopamine as first-line vasopressor"], "medical_history": "UTI, pneumonia, immunocompromised", "difficulty": "medium", "critical_labs": ["blood_culture", "lactate", "CBC", "BMP"]}
|
| 619 |
+
VITALS_DB["Septic Shock"] = "HR 130, BP 72/38 (MAP 49), RR 28, SpO2 93%, Temp 39.5C -- septic shock, MAP below 65"
|
| 620 |
+
LAB_RESULTS_DB["Septic Shock"] = {"blood_culture": "Blood cultures: Gram-negative rods growing at 6 hours -- E. coli", "lactate": "Lactate: 6.8 mmol/L (CRITICAL -- severe tissue hypoperfusion)", "CBC": "WBC 28.0 (critical, bandemia 20%), Hgb 11.0, Plt 65 (LOW -- DIC developing)", "BMP": "Na 132, K 5.0, Cr 2.5 (AKI), Glucose 180, CO2 14 (acidosis)"}
|
| 621 |
+
SOAP_HISTORY_DB["Septic Shock"] = {"HPI": "72F nursing home resident brought in with fever, confusion, and low blood pressure. Staff reports foul-smelling urine and decreased oral intake x 3 days.", "ROS": {"GU": "foul-smelling urine, frequency", "Neuro": "confusion, lethargy", "CV": "hypotension"}, "Past_Medical_History": "Type 2 DM, recurrent UTIs, Foley catheter, dementia", "Medications": "Metformin 500mg BID, donepezil 10mg daily", "Allergies": "Sulfa drugs", "Social_History": "Nursing home resident, non-ambulatory, Foley catheter", "Physical_Examination": "Obtunded, warm and flushed. Tachycardic. Hypotensive despite 1L NS. Suprapubic tenderness. Foley with cloudy malodorous urine. Mottled extremities."}
|
| 622 |
+
|
| 623 |
+
DISEASES_DB["Necrotizing Fasciitis"] = {"true_disease": "Necrotizing Fasciitis", "true_symptoms": ["pain out of proportion to exam", "rapidly spreading erythema", "crepitus", "bullae", "systemic toxicity"], "correct_treatment": "emergent surgical debridement, IV vancomycin plus piperacillin-tazobactam plus clindamycin, aggressive fluid resuscitation, ICU admission", "lethal_treatments": ["antibiotics alone without surgery", "observation"], "medical_history": "Diabetes, IV drug use, recent surgery", "difficulty": "hard", "critical_labs": ["CBC", "BMP", "CK", "lactate"]}
|
| 624 |
+
VITALS_DB["Necrotizing Fasciitis"] = "HR 135, BP 80/45, RR 26, SpO2 94%, Temp 39.8C -- septic, tachycardic"
|
| 625 |
+
LAB_RESULTS_DB["Necrotizing Fasciitis"] = {"CBC": "WBC 32.0 (CRITICAL), Hgb 12.0, Plt 80 (DIC)", "BMP": "Na 128, K 5.2, Cr 2.8 (AKI), Glucose 380", "CK": "CK: 12000 U/L (muscle destruction)", "lactate": "Lactate: 8.5 mmol/L (severe)"}
|
| 626 |
+
SOAP_HISTORY_DB["Necrotizing Fasciitis"] = {"HPI": "58M diabetic presents with 36 hours of rapidly worsening right lower leg pain, redness, and swelling. Pain is severe and out of proportion to visible findings. Small cut on shin 4 days ago. Developed dark blisters this morning.", "ROS": {"Derm": "severe leg pain, spreading redness, blisters", "Neuro": "confusion", "GI": "nausea"}, "Past_Medical_History": "Uncontrolled Type 2 DM A1c 11.2%, peripheral vascular disease, obesity", "Medications": "Metformin 1000mg BID, glipizide 10mg BID", "Allergies": "NKDA", "Social_History": "Retired, sedentary, non-smoker", "Physical_Examination": "Toxic-appearing. Right lower leg: tense edema, erythema extending rapidly (marked border advancing), hemorrhagic bullae, crepitus on palpation, pain out of proportion. Skin dusky/necrotic centrally."}
|
| 627 |
+
|
| 628 |
+
DISEASES_DB["Malaria"] = {"true_disease": "Malaria", "true_symptoms": ["cyclical high fevers", "rigors", "headache", "splenomegaly", "jaundice"], "correct_treatment": "IV artesunate for severe malaria, if uncomplicated then artemether-lumefantrine oral, monitor parasitemia every 12 hours, exchange transfusion if parasitemia above 10%", "lethal_treatments": ["chloroquine alone if P. falciparum resistant area"], "medical_history": "Travel to endemic area, no prophylaxis", "difficulty": "hard", "critical_labs": ["blood_smear", "CBC", "BMP", "LFTs"]}
|
| 629 |
+
VITALS_DB["Malaria"] = "HR 115, BP 95/60, RR 24, SpO2 95%, Temp 40.5C -- high fever with rigors"
|
| 630 |
+
LAB_RESULTS_DB["Malaria"] = {"blood_smear": "Thick and thin smear: Plasmodium falciparum, parasitemia 8%, ring forms and banana-shaped gametocytes", "CBC": "WBC 3.5 (LOW), Hgb 8.0 (severe anemia from hemolysis), Plt 35 (CRITICAL LOW)", "BMP": "Na 130, K 4.8, Cr 2.2 (AKI), Glucose 55 (LOW), T.Bili 5.5 (hemolysis)", "LFTs": "AST 180, ALT 120, LDH 850 (hemolysis)"}
|
| 631 |
+
SOAP_HISTORY_DB["Malaria"] = {"HPI": "30M presents with 5 days of cyclical high fevers with rigors every 48 hours, drenching sweats, headache, and progressive weakness. Returned from 3-week trip to sub-Saharan Africa 10 days ago. Did not take malaria prophylaxis.", "ROS": {"Neuro": "headache, confusion", "GI": "nausea, abdominal pain", "Derm": "jaundice"}, "Past_Medical_History": "Previously healthy, no prior malaria", "Medications": "Did not take prophylaxis -- was not prescribed", "Allergies": "NKDA", "Social_History": "NGO worker, traveled to rural Kenya/Tanzania, slept without bed nets", "Physical_Examination": "Jaundiced, febrile 40.5C with rigors. Splenomegaly 4cm below costal margin. Hepatomegaly. Pallor. Mildly confused. Petechiae on lower extremities."}
|
| 632 |
+
|
| 633 |
+
DISEASES_DB["Peritonsillar Abscess"] = {"true_disease": "Peritonsillar Abscess", "true_symptoms": ["severe sore throat unilateral", "trismus", "muffled hot potato voice", "drooling", "uvula deviation"], "correct_treatment": "needle aspiration or incision and drainage, IV clindamycin or ampicillin-sulbactam, dexamethasone, pain control, ENT consult", "lethal_treatments": ["blind intubation if airway compromise (risk of rupture)"], "medical_history": "Recent tonsillitis, incomplete antibiotic course", "difficulty": "easy", "critical_labs": ["CBC", "CT_neck"]}
|
| 634 |
+
VITALS_DB["Peritonsillar Abscess"] = "HR 100, BP 130/80, RR 18, SpO2 98%, Temp 38.8C -- febrile, mild tachycardia"
|
| 635 |
+
LAB_RESULTS_DB["Peritonsillar Abscess"] = {"CBC": "WBC 17.0 (left shift), Hgb 14.0, Plt 250", "CT_neck": "CT Neck with contrast: 3cm left peritonsillar abscess with rim enhancement, no extension to parapharyngeal space"}
|
| 636 |
+
SOAP_HISTORY_DB["Peritonsillar Abscess"] = {"HPI": "22M presents with 5 days of worsening left-sided sore throat, now unable to swallow. Progressive trismus -- cannot open mouth fully. Muffled voice. Drooling. Was treated for strep throat 1 week ago with 3 days of amoxicillin (did not finish course).", "ROS": {"ENT": "severe left throat pain, trismus, drooling, muffled voice", "Neuro": "no neck stiffness"}, "Past_Medical_History": "Recurrent tonsillitis x 3 episodes this year, incomplete antibiotic courses", "Medications": "Amoxicillin (stopped after 3 days of 10-day course)", "Allergies": "NKDA", "Social_History": "College student, smoker, social drinker", "Physical_Examination": "Drooling, muffled voice. Trismus (limited mouth opening). Left peritonsillar bulge with uvula deviated to right. Left tonsil displaced medially. No stridor. Neck supple, tender left submandibular lymphadenopathy."}
|
| 637 |
+
|
| 638 |
+
DISEASES_DB["Spontaneous Bacterial Peritonitis"] = {"true_disease": "Spontaneous Bacterial Peritonitis", "true_symptoms": ["abdominal pain and tenderness", "fever", "worsening ascites", "altered mental status", "diarrhea"], "correct_treatment": "IV cefotaxime 2g every 8 hours, IV albumin 1.5g/kg on day 1 and 1g/kg on day 3, diagnostic paracentesis, hepatology consult", "lethal_treatments": ["aminoglycosides in cirrhosis (nephrotoxicity)"], "medical_history": "Cirrhosis with ascites", "difficulty": "medium", "critical_labs": ["paracentesis", "CBC", "BMP", "blood_culture"]}
|
| 639 |
+
VITALS_DB["Spontaneous Bacterial Peritonitis"] = "HR 105, BP 90/55, RR 20, SpO2 96%, Temp 38.5C -- febrile, hypotensive"
|
| 640 |
+
LAB_RESULTS_DB["Spontaneous Bacterial Peritonitis"] = {"paracentesis": "Ascitic fluid: WBC 850 (PMN 680 -- above 250 threshold), protein 1.2, glucose 40, culture pending -- diagnostic of SBP", "CBC": "WBC 14.0, Hgb 9.0, Plt 55 (thrombocytopenia from liver disease)", "BMP": "Na 125 (dilutional), K 3.5, Cr 2.0 (hepatorenal), BUN 45", "blood_culture": "Blood cultures pending"}
|
| 641 |
+
SOAP_HISTORY_DB["Spontaneous Bacterial Peritonitis"] = {"HPI": "60M with decompensated cirrhosis presents with 2 days of worsening abdominal pain, distension, and fever. Reports increasing confusion per family. Ascites has been worsening despite diuretics.", "ROS": {"GI": "abdominal pain, distension, diarrhea", "Neuro": "confusion, worsening encephalopathy", "CV": "lightheadedness"}, "Past_Medical_History": "Alcoholic cirrhosis Child-Pugh C, recurrent ascites, prior SBP episode 6 months ago, esophageal varices", "Medications": "Spironolactone 100mg, furosemide 40mg, lactulose, rifaximin, nadolol", "Allergies": "NKDA", "Social_History": "Former heavy drinker (quit 1 year ago), retired, lives with adult daughter", "Physical_Examination": "Jaundiced, cachectic. Distended abdomen with tense ascites, diffusely tender with mild rebound. Shifting dullness positive. Spider angiomata. Asterixis present. Mild confusion."}
|
| 642 |
+
|
| 643 |
+
# ---------------------------------------------------------------------------
|
| 644 |
+
# CLASS 9: GENITOURINARY / RENAL (5 diseases)
|
| 645 |
+
# ---------------------------------------------------------------------------
|
| 646 |
+
|
| 647 |
+
DISEASES_DB["Acute Kidney Injury"] = {"true_disease": "Acute Kidney Injury", "true_symptoms": ["decreased urine output", "swelling", "nausea", "confusion", "shortness of breath"], "correct_treatment": "IV fluid resuscitation if prerenal, hold nephrotoxins, correct electrolytes, emergent dialysis if refractory hyperkalemia or pulmonary edema or uremia, nephrology consult", "lethal_treatments": ["NSAIDs", "IV contrast without indication", "potassium-containing fluids"], "medical_history": "Dehydration, sepsis, nephrotoxic medications", "difficulty": "medium", "critical_labs": ["BMP", "urinalysis", "CBC", "renal_US"]}
|
| 648 |
+
VITALS_DB["Acute Kidney Injury"] = "HR 95, BP 90/55, RR 22, SpO2 94%, Temp 37.5C -- hypotensive, mildly hypoxic from fluid overload"
|
| 649 |
+
LAB_RESULTS_DB["Acute Kidney Injury"] = {"BMP": "Na 130, K 6.5 (HIGH), Cr 5.8 (baseline 1.0 -- CRITICAL rise), BUN 80, CO2 14 (acidosis)", "urinalysis": "Urinalysis: muddy brown granular casts -- ATN (acute tubular necrosis)", "CBC": "WBC 12.0, Hgb 10.0, Plt 180", "renal_US": "Renal US: normal-sized kidneys, no hydronephrosis, no obstruction"}
|
| 650 |
+
SOAP_HISTORY_DB["Acute Kidney Injury"] = {"HPI": "68M presents with 2 days of minimal urine output, progressive swelling, and shortness of breath. Was treated with IV vancomycin and gentamicin for pneumonia last week at another hospital. Now confused.", "ROS": {"Renal": "oliguria, edema", "Resp": "dyspnea, cannot lie flat", "Neuro": "confusion", "GI": "nausea"}, "Past_Medical_History": "HTN, Type 2 DM, recent pneumonia treated with nephrotoxic antibiotics", "Medications": "Vancomycin (recent course), gentamicin (recent course), lisinopril 20mg, metformin", "Allergies": "NKDA", "Social_History": "Retired, lives with wife, non-smoker", "Physical_Examination": "Confused, edematous. JVD present. Bibasilar crackles. Abdomen mildly distended. 3+ pitting edema bilateral legs. Foley placed -- 50mL dark urine over 4 hours."}
|
| 651 |
+
|
| 652 |
+
DISEASES_DB["Nephrolithiasis"] = {"true_disease": "Nephrolithiasis", "true_symptoms": ["severe colicky flank pain", "hematuria", "nausea and vomiting", "pain radiating to groin", "restlessness"], "correct_treatment": "IV ketorolac 30mg for pain, IV ondansetron for nausea, IV fluids, CT abdomen without contrast, urology consult if stone greater than 6mm or signs of infection", "lethal_treatments": ["observation if obstructing stone with infection (sepsis risk)"], "medical_history": "Prior kidney stones, dehydration", "difficulty": "easy", "critical_labs": ["CT_abdomen", "urinalysis", "BMP"]}
|
| 653 |
+
VITALS_DB["Nephrolithiasis"] = "HR 100, BP 160/95 (pain), RR 20, SpO2 99%, Temp 37.0C -- tachycardic and hypertensive from pain"
|
| 654 |
+
LAB_RESULTS_DB["Nephrolithiasis"] = {"CT_abdomen": "CT Abdomen non-contrast: 7mm obstructing stone at left ureterovesical junction with moderate hydronephrosis", "urinalysis": "Urinalysis: RBC 50+, WBC 2, no bacteria, pH 5.5", "BMP": "Na 140, K 4.0, Cr 1.1, Ca 10.8 (upper normal)"}
|
| 655 |
+
SOAP_HISTORY_DB["Nephrolithiasis"] = {"HPI": "38M presents with sudden onset severe left flank pain radiating to groin that started 3 hours ago. Pain comes in waves, rates 10/10. Unable to sit still. Nausea with vomiting x2. Noticed blood in urine.", "ROS": {"GU": "flank pain, hematuria, groin pain", "GI": "nausea, vomiting"}, "Past_Medical_History": "2 prior kidney stones (passed spontaneously), gout, inadequate fluid intake", "Medications": "Allopurinol 100mg daily (poor compliance)", "Allergies": "NKDA", "Social_History": "Software developer, drinks minimal water, high protein diet, sedentary", "Physical_Examination": "Writhing in pain, unable to find comfortable position. CVA tenderness left. Abdomen soft, mild left lower quadrant tenderness. No peritoneal signs. Tachycardic."}
|
| 656 |
+
|
| 657 |
+
DISEASES_DB["Testicular Torsion"] = {"true_disease": "Testicular Torsion", "true_symptoms": ["sudden severe testicular pain", "nausea and vomiting", "absent cremasteric reflex", "high-riding testicle", "scrotal swelling"], "correct_treatment": "emergent surgical exploration and detorsion within 6 hours, attempt manual detorsion open book technique while awaiting OR, bilateral orchiopexy, doppler US if diagnosis uncertain", "lethal_treatments": ["antibiotics for presumed epididymitis without ruling out torsion"], "medical_history": "Adolescent or young adult, bell-clapper deformity", "difficulty": "medium", "critical_labs": ["doppler_US", "urinalysis"]}
|
| 658 |
+
VITALS_DB["Testicular Torsion"] = "HR 110, BP 140/85, RR 20, SpO2 99%, Temp 37.0C -- tachycardic from pain, afebrile (distinguishes from infection)"
|
| 659 |
+
LAB_RESULTS_DB["Testicular Torsion"] = {"doppler_US": "Scrotal Doppler US: absent blood flow to left testicle, testis rotated 540 degrees, edematous -- TORSION, requires emergent surgery", "urinalysis": "Urinalysis: normal -- no infection (helps distinguish from epididymitis)"}
|
| 660 |
+
SOAP_HISTORY_DB["Testicular Torsion"] = {"HPI": "16M presents with sudden onset severe left testicular pain that woke him from sleep 3 hours ago. Pain started without trauma. Associated nausea and vomiting x3. Pain is constant and worsening. No urinary symptoms.", "ROS": {"GU": "severe left testicular pain, swelling", "GI": "nausea, vomiting x3"}, "Past_Medical_History": "Previously healthy. No prior episodes. Not sexually active.", "Medications": "None", "Allergies": "NKDA", "Social_History": "High school student, athlete", "Physical_Examination": "In severe distress. Left testicle high-riding, horizontal lie, extremely tender. Absent cremasteric reflex on left. Negative Prehn sign (pain NOT relieved with elevation). Right testicle normal. No fever."}
|
| 661 |
+
|
| 662 |
+
DISEASES_DB["Pyelonephritis"] = {"true_disease": "Pyelonephritis", "true_symptoms": ["flank pain", "high fever", "dysuria", "nausea and vomiting", "CVA tenderness"], "correct_treatment": "IV ceftriaxone 1g or fluoroquinolone, IV fluids, blood cultures if septic, urine culture, admission if unable to tolerate PO or signs of sepsis", "lethal_treatments": ["oral antibiotics only if hemodynamically unstable"], "medical_history": "Recurrent UTIs, diabetes, kidney stones", "difficulty": "easy", "critical_labs": ["urinalysis", "CBC", "BMP", "blood_culture"]}
|
| 663 |
+
VITALS_DB["Pyelonephritis"] = "HR 108, BP 105/65, RR 20, SpO2 98%, Temp 39.5C -- febrile, tachycardic"
|
| 664 |
+
LAB_RESULTS_DB["Pyelonephritis"] = {"urinalysis": "Urinalysis: WBC 80+, bacteria many, nitrite positive, leukocyte esterase positive, WBC casts present -- upper tract infection", "CBC": "WBC 18.0 (left shift), Hgb 12.5, Plt 220", "BMP": "Na 138, K 3.8, Cr 1.3, Glucose 130", "blood_culture": "Blood cultures: Gram-negative rods at 12 hours -- E. coli"}
|
| 665 |
+
SOAP_HISTORY_DB["Pyelonephritis"] = {"HPI": "32F presents with 3 days of dysuria and frequency that progressed to right flank pain, high fever, and vomiting. Unable to keep fluids down. Had UTI symptoms that she tried to treat with cranberry juice.", "ROS": {"GU": "dysuria, frequency, flank pain, foul-smelling urine", "GI": "nausea, vomiting x4", "Neuro": "no confusion"}, "Past_Medical_History": "Recurrent UTIs (3 per year), Type 2 DM", "Medications": "Metformin 500mg BID", "Allergies": "Sulfa drugs (rash)", "Social_History": "Nurse, sexually active, uses diaphragm for contraception", "Physical_Examination": "Febrile, ill-appearing but alert. Right CVA tenderness on percussion. Mild suprapubic tenderness. No peritoneal signs. Well-hydrated."}
|
| 666 |
+
|
| 667 |
+
DISEASES_DB["Urinary Retention"] = {"true_disease": "Urinary Retention", "true_symptoms": ["inability to urinate", "suprapubic pain and fullness", "overflow incontinence", "lower abdominal distension", "restlessness"], "correct_treatment": "Foley catheter insertion with slow drainage max 500mL at a time to prevent decompression hematuria, post-void residual measurement, alpha-blocker tamsulosin, urology follow-up", "lethal_treatments": ["rapid complete bladder decompression over 1000mL at once"], "medical_history": "BPH, anticholinergic medications, post-operative", "difficulty": "easy", "critical_labs": ["BMP", "urinalysis", "bladder_US"]}
|
| 668 |
+
VITALS_DB["Urinary Retention"] = "HR 90, BP 155/90, RR 18, SpO2 98%, Temp 37.0C -- hypertensive from pain and distress"
|
| 669 |
+
LAB_RESULTS_DB["Urinary Retention"] = {"BMP": "Na 140, K 4.5, Cr 1.8 (mildly elevated -- obstructive), BUN 30", "urinalysis": "Urinalysis: WBC 5, RBC 10, no bacteria -- mild inflammation from distension", "bladder_US": "Bladder US: distended bladder volume approximately 1200mL -- acute urinary retention"}
|
| 670 |
+
SOAP_HISTORY_DB["Urinary Retention"] = {"HPI": "75M presents with 18 hours of inability to urinate despite strong urge. Progressive suprapubic pain and fullness. Dribbling small amounts. Was started on new cold medication (pseudoephedrine + diphenhydramine) 2 days ago.", "ROS": {"GU": "inability to void, suprapubic pain, dribbling", "GI": "mild lower abdominal pain"}, "Past_Medical_History": "BPH on tamsulosin (ran out 1 week ago), HTN, recently started OTC cold medication", "Medications": "Tamsulosin 0.4mg (stopped 1 week ago), lisinopril 10mg, pseudoephedrine/diphenhydramine (OTC cold medicine started 2 days ago)", "Allergies": "NKDA", "Social_History": "Retired, lives with wife, non-smoker", "Physical_Examination": "Uncomfortable, palpable distended bladder to umbilicus. Suprapubic tenderness. DRE: enlarged smooth prostate, no nodules. No CVA tenderness."}
|
| 671 |
+
|
| 672 |
+
# ---------------------------------------------------------------------------
|
| 673 |
+
# CLASS 10: ENVIRONMENTAL / IMMUNOLOGIC (5 diseases)
|
| 674 |
+
# ---------------------------------------------------------------------------
|
| 675 |
+
|
| 676 |
+
DISEASES_DB["Anaphylaxis"] = {"true_disease": "Anaphylaxis", "true_symptoms": ["urticaria and angioedema", "wheezing and stridor", "hypotension", "abdominal cramping", "sense of impending doom"], "correct_treatment": "IM epinephrine 0.3mg anterolateral thigh repeat every 5-15 minutes, IV fluids wide open, albuterol for bronchospasm, IV diphenhydramine, IV methylprednisolone, monitor for biphasic reaction", "lethal_treatments": ["IV epinephrine bolus (cardiac arrest risk)", "relying on antihistamines alone"], "medical_history": "Known allergies, prior anaphylaxis, bee sting", "difficulty": "medium", "critical_labs": ["tryptase", "CBC"]}
|
| 677 |
+
VITALS_DB["Anaphylaxis"] = "HR 140, BP 65/30, RR 30, SpO2 85%, Temp 37.0C -- anaphylactic shock, severe hypotension and hypoxia"
|
| 678 |
+
LAB_RESULTS_DB["Anaphylaxis"] = {"tryptase": "Serum tryptase: 45 ng/mL (CRITICAL HIGH -- confirms mast cell degranulation/anaphylaxis)", "CBC": "WBC 8.0, Hgb 14.0, Plt 220 -- normal"}
|
| 679 |
+
SOAP_HISTORY_DB["Anaphylaxis"] = {"HPI": "28F presents with sudden onset diffuse hives, lip and tongue swelling, wheezing, and lightheadedness 15 minutes after eating shrimp at restaurant. Rapidly progressive. Has known shellfish allergy but did not know dish contained shrimp.", "ROS": {"Derm": "diffuse urticaria, facial swelling", "Resp": "wheezing, throat tightness, stridor", "CV": "lightheadedness, palpitations", "GI": "abdominal cramping"}, "Past_Medical_History": "Shellfish allergy (prior mild reaction -- hives only), asthma, carries EpiPen (expired, did not use)", "Medications": "Albuterol PRN, expired EpiPen (did not administer)", "Allergies": "Shellfish (anaphylaxis), penicillin (rash)", "Social_History": "Teacher, non-smoker", "Physical_Examination": "Diffuse urticaria. Angioedema of lips and tongue. Audible stridor and wheezing. Hypotensive. Tachycardic. Using accessory muscles. Abdomen with diffuse tenderness."}
|
| 680 |
+
|
| 681 |
+
DISEASES_DB["Heat Stroke"] = {"true_disease": "Heat Stroke", "true_symptoms": ["core temperature above 40C", "altered mental status", "hot dry skin", "tachycardia", "seizures"], "correct_treatment": "rapid cooling ice water immersion or evaporative cooling, cold IV fluids, benzodiazepines for shivering or seizures, intubation if GCS below 8, monitor for rhabdomyolysis and DIC", "lethal_treatments": ["antipyretics acetaminophen or NSAIDs (ineffective and hepatotoxic in heat stroke)", "delaying cooling for workup"], "medical_history": "Exertion in heat, elderly in hot environment", "difficulty": "medium", "critical_labs": ["BMP", "CBC", "CK", "coagulation"]}
|
| 682 |
+
VITALS_DB["Heat Stroke"] = "HR 145, BP 90/55, RR 30, SpO2 95%, Temp 42.1C -- CRITICAL hyperthermia, tachycardic, hypotensive"
|
| 683 |
+
LAB_RESULTS_DB["Heat Stroke"] = {"BMP": "Na 148 (HIGH -- dehydration), K 5.5 (HIGH), Cr 2.5, Glucose 65", "CBC": "WBC 18.0, Hgb 17.0 (hemoconcentration), Plt 80 (DIC developing)", "CK": "CK: 25000 U/L (CRITICAL -- rhabdomyolysis)", "coagulation": "PT 22, INR 2.5, aPTT 55, fibrinogen 100 -- DIC"}
|
| 684 |
+
SOAP_HISTORY_DB["Heat Stroke"] = {"HPI": "22M military recruit collapsed during 10-mile training run in 38C heat. Found confused and combative. Core temp 42.1C per rectal thermometer. Hot dry skin. Witnessed seizure in field.", "ROS": {"Neuro": "confusion, combative, seizure witnessed", "Derm": "hot dry skin, no sweating"}, "Past_Medical_History": "Previously healthy, new recruit in basic training x 2 weeks", "Medications": "None", "Allergies": "NKDA", "Social_History": "Military recruit, recently moved from cold climate, not heat-acclimatized, was not adequately hydrating", "Physical_Examination": "Combative, confused, GCS 10. Core temp 42.1C. Skin hot and dry (anhidrosis). Tachycardic. Hypotensive. No focal neurological deficits. Dark urine (myoglobinuria)."}
|
| 685 |
+
|
| 686 |
+
DISEASES_DB["Severe Hypothermia"] = {"true_disease": "Severe Hypothermia", "true_symptoms": ["core temperature below 30C", "altered consciousness", "bradycardia", "J waves on ECG", "muscle rigidity"], "correct_treatment": "active core rewarming with warm IV fluids 40-42C, warm humidified oxygen, bear hugger, avoid rough handling (risk of VFib), cardiac monitoring, ECMO if cardiac arrest", "lethal_treatments": ["rapid surface rewarming alone", "pronouncing death before rewarming -- you are not dead until warm and dead"], "medical_history": "Environmental exposure, homeless, elderly", "difficulty": "hard", "critical_labs": ["BMP", "ECG", "ABG", "CBC"]}
|
| 687 |
+
VITALS_DB["Severe Hypothermia"] = "HR 32, BP 75/45, RR 6, SpO2 88%, Temp 27.5C -- severe bradycardia, profound hypothermia"
|
| 688 |
+
LAB_RESULTS_DB["Severe Hypothermia"] = {"BMP": "Na 140, K 3.0, Glucose 50 (LOW), Cr 1.5", "ECG": "Marked sinus bradycardia rate 32, Osborn (J) waves in precordial leads, prolonged QT -- classic hypothermia", "ABG": "pH 7.22, pCO2 50, pO2 55 -- mixed acidosis (temperature corrected)", "CBC": "WBC 4.0, Hgb 16.0 (hemoconcentration), Plt 90"}
|
| 689 |
+
SOAP_HISTORY_DB["Severe Hypothermia"] = {"HPI": "Homeless 60M found unresponsive outdoors by police on a night with ambient temperature -5C. Unknown down time. Minimally responsive. Cold and rigid. Bystanders report he was seen drinking earlier.", "ROS": {"Neuro": "unresponsive"}, "Past_Medical_History": "Unknown -- homeless, no medical records available. Smells of alcohol.", "Medications": "Unknown", "Allergies": "Unknown", "Social_History": "Homeless, known to frequent shelters, alcohol use suspected", "Physical_Examination": "Unresponsive, GCS 5. Core temp 27.5C. Rigid musculature. Bradycardic, weak pulse. Pupils sluggish. Cold skin. No visible trauma."}
|
| 690 |
+
|
| 691 |
DISEASES_DB["Snakebite Envenomation"] = {"true_disease": "Snakebite Envenomation", "true_symptoms": ["fang marks with local swelling", "progressive edema", "ecchymosis", "metallic taste", "coagulopathy"], "correct_treatment": "CroFab antivenom 4-6 vials IV initial dose, repeat if swelling progresses, mark advancing edge of swelling, IV fluids, tetanus prophylaxis, avoid tourniquets and incision", "lethal_treatments": ["tourniquet", "incision and suction", "ice to wound"], "medical_history": "Outdoor exposure, rural area", "difficulty": "medium", "critical_labs": ["CBC", "coagulation", "BMP", "fibrinogen"]}
|
| 692 |
VITALS_DB["Snakebite Envenomation"] = "HR 115, BP 95/60, RR 22, SpO2 97%, Temp 37.5C -- tachycardic, mildly hypotensive"
|
| 693 |
LAB_RESULTS_DB["Snakebite Envenomation"] = {"CBC": "WBC 15.0, Hgb 12.0, Plt 45 (CRITICAL LOW -- venom-induced thrombocytopenia)", "coagulation": "PT 35, INR 4.5 (CRITICAL), aPTT 85 -- severe coagulopathy from venom", "BMP": "Na 138, K 4.8, Cr 1.5, CK 2500 (myotoxicity)", "fibrinogen": "Fibrinogen: 50 mg/dL (CRITICAL LOW -- consumptive coagulopathy)"}
|
|
|
|
| 697 |
VITALS_DB["Angioedema"] = "HR 95, BP 150/90, RR 24, SpO2 93%, Temp 37.0C -- hypertensive (on ACE inhibitor), hypoxic from airway compromise"
|
| 698 |
LAB_RESULTS_DB["Angioedema"] = {"CBC": "WBC 8.0, Hgb 14.0, Plt 220 -- normal", "C4_level": "C4: 8 mg/dL (LOW -- suggests bradykinin-mediated, not histamine)", "tryptase": "Serum tryptase: 5 ng/mL (normal -- NOT allergic/mast cell mediated, confirms ACE inhibitor cause)"}
|
| 699 |
SOAP_HISTORY_DB["Angioedema"] = {"HPI": "65M presents with 4 hours of progressive swelling of tongue and lips. Now having difficulty speaking and swallowing. Mild stridor noted. He has been on lisinopril for 8 years without prior issues. No urticaria. No known allergen exposure.", "ROS": {"ENT": "tongue and lip swelling, difficulty swallowing, voice change", "Resp": "mild stridor, dyspnea", "Derm": "NO urticaria (important -- suggests bradykinin not histamine)"}, "Past_Medical_History": "HTN on lisinopril x 8 years, Type 2 DM", "Medications": "Lisinopril 20mg daily, metformin 1000mg BID", "Allergies": "NKDA", "Social_History": "Retired engineer, African American (higher risk for ACE inhibitor angioedema), non-smoker", "Physical_Examination": "Significant tongue and lip edema. Voice muffled. Mild inspiratory stridor. No urticaria anywhere. Oropharynx: tongue filling oral cavity, uvula edematous. Lungs clear. Airway assessment: concerning for progression."}
|
| 700 |
+
|
| 701 |
+
# Add emergency flag to time-critical diseases
|
| 702 |
+
_emergency_diseases = [
|
| 703 |
+
"Aortic Dissection", "Cardiac Tamponade", "Tension Pneumothorax",
|
| 704 |
+
"Acute Respiratory Distress Syndrome", "Subarachnoid Hemorrhage",
|
| 705 |
+
"Status Epilepticus", "Upper GI Bleed", "Open Femur Fracture",
|
| 706 |
+
"Pelvic Fracture", "Splenic Rupture", "Septic Shock",
|
| 707 |
+
"Necrotizing Fasciitis", "Anaphylaxis", "Heat Stroke",
|
| 708 |
+
"Severe Hypothermia", "Opioid Overdose"
|
| 709 |
+
]
|
| 710 |
+
|
| 711 |
+
for _d in _emergency_diseases:
|
| 712 |
+
if _d in DISEASES_DB:
|
| 713 |
+
DISEASES_DB[_d]["is_emergency"] = True
|
ER_MAP/envs/empathy_engine.py
CHANGED
|
@@ -296,8 +296,9 @@ class MilestoneTracker:
|
|
| 296 |
"DISCHARGE",
|
| 297 |
]
|
| 298 |
|
| 299 |
-
def __init__(self, phase: int = 1):
|
| 300 |
self.phase = phase
|
|
|
|
| 301 |
self.achieved: Dict[str, bool] = {m: False for m in self.MILESTONES}
|
| 302 |
self.order: list = [] # Track achievement order
|
| 303 |
|
|
@@ -321,6 +322,9 @@ class MilestoneTracker:
|
|
| 321 |
expected_idx = self.MILESTONES.index(milestone)
|
| 322 |
actual_idx = len(self.order) - 1
|
| 323 |
|
|
|
|
|
|
|
|
|
|
| 324 |
if self.phase == 1:
|
| 325 |
# Phase 1: Strict ordering enforcement
|
| 326 |
if actual_idx == expected_idx:
|
|
|
|
| 296 |
"DISCHARGE",
|
| 297 |
]
|
| 298 |
|
| 299 |
+
def __init__(self, phase: int = 1, is_emergency: bool = False):
|
| 300 |
self.phase = phase
|
| 301 |
+
self.is_emergency = is_emergency
|
| 302 |
self.achieved: Dict[str, bool] = {m: False for m in self.MILESTONES}
|
| 303 |
self.order: list = [] # Track achievement order
|
| 304 |
|
|
|
|
| 322 |
expected_idx = self.MILESTONES.index(milestone)
|
| 323 |
actual_idx = len(self.order) - 1
|
| 324 |
|
| 325 |
+
if self.is_emergency:
|
| 326 |
+
return 0.05 # Emergencies: reward action immediately, no ordering penalty
|
| 327 |
+
|
| 328 |
if self.phase == 1:
|
| 329 |
# Phase 1: Strict ordering enforcement
|
| 330 |
if actual_idx == expected_idx:
|
ER_MAP/envs/randomizer.py
CHANGED
|
@@ -137,7 +137,7 @@ PHASE_PERSONA_CONSTRAINTS = {
|
|
| 137 |
# Phase 3: Heavy noise -- behavioral data injected, unreliable history
|
| 138 |
# ---------------------------------------------------------------------------
|
| 139 |
|
| 140 |
-
def _apply_soap_noise(soap: Dict[str, Any], phase: int) -> Dict[str, Any]:
|
| 141 |
"""Apply phase-dependent noise to SOAP history. Returns a copy."""
|
| 142 |
soap = copy.deepcopy(soap)
|
| 143 |
|
|
@@ -159,8 +159,8 @@ def _apply_soap_noise(soap: Dict[str, Any], phase: int) -> Dict[str, Any]:
|
|
| 159 |
soap["Medications"] = soap.get("Medications", "") + " (patient unsure of doses, no medication list available)"
|
| 160 |
if random.random() < 0.35:
|
| 161 |
soap["Past_Medical_History"] = soap.get("Past_Medical_History", "") + ". NOTE: limited records, patient provides inconsistent timeline."
|
| 162 |
-
if random.random() < 0.3:
|
| 163 |
-
# Vague ROS
|
| 164 |
ros = soap.get("ROS", {})
|
| 165 |
if isinstance(ros, dict) and ros:
|
| 166 |
key = random.choice(list(ros.keys()))
|
|
@@ -169,29 +169,46 @@ def _apply_soap_noise(soap: Dict[str, Any], phase: int) -> Dict[str, Any]:
|
|
| 169 |
|
| 170 |
if phase >= 3:
|
| 171 |
# Phase 3: Behavioral and socio-economic noise
|
| 172 |
-
|
| 173 |
-
|
| 174 |
-
|
| 175 |
-
|
| 176 |
-
|
| 177 |
-
|
| 178 |
-
|
| 179 |
-
|
| 180 |
-
|
| 181 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 182 |
soap["Social_History"] = soap.get("Social_History", "") + ". " + random.choice(behavioral_notes)
|
| 183 |
|
| 184 |
# Inject conflicting/misleading physical exam findings
|
| 185 |
-
if random.random() < 0.3:
|
| 186 |
soap["Physical_Examination"] = soap.get("Physical_Examination", "") + " Patient is uncooperative with portions of exam."
|
| 187 |
|
| 188 |
# Degrade HPI reliability
|
| 189 |
if random.random() < 0.4:
|
| 190 |
-
|
| 191 |
-
|
| 192 |
-
|
| 193 |
-
|
| 194 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 195 |
soap["HPI"] = soap.get("HPI", "") + hpi_noise
|
| 196 |
|
| 197 |
return soap
|
|
@@ -257,6 +274,7 @@ def generate_ground_truth(
|
|
| 257 |
"lethal_treatments": disease.get("lethal_treatments", []),
|
| 258 |
"critical_labs": disease.get("critical_labs", []),
|
| 259 |
"difficulty": disease.get("difficulty", "medium"),
|
|
|
|
| 260 |
},
|
| 261 |
"difficulty": difficulty,
|
| 262 |
"phase": phase,
|
|
@@ -284,6 +302,7 @@ def generate_ground_truth(
|
|
| 284 |
"lethal_treatments": disease.get("lethal_treatments", []),
|
| 285 |
"critical_labs": disease.get("critical_labs", []),
|
| 286 |
"difficulty": disease.get("difficulty", "medium"),
|
|
|
|
| 287 |
},
|
| 288 |
"difficulty": "random",
|
| 289 |
"phase": phase,
|
|
@@ -291,7 +310,8 @@ def generate_ground_truth(
|
|
| 291 |
|
| 292 |
# Attach SOAP history with phase-appropriate noise
|
| 293 |
raw_soap = _SOAP_HISTORY_DB.get(disease_name, {})
|
| 294 |
-
|
|
|
|
| 295 |
|
| 296 |
# Attach vitals and labs
|
| 297 |
ground_truth["vitals"] = _VITALS_DB.get(disease_name, "")
|
|
|
|
| 137 |
# Phase 3: Heavy noise -- behavioral data injected, unreliable history
|
| 138 |
# ---------------------------------------------------------------------------
|
| 139 |
|
| 140 |
+
def _apply_soap_noise(soap: Dict[str, Any], phase: int, is_emergency: bool = False) -> Dict[str, Any]:
|
| 141 |
"""Apply phase-dependent noise to SOAP history. Returns a copy."""
|
| 142 |
soap = copy.deepcopy(soap)
|
| 143 |
|
|
|
|
| 159 |
soap["Medications"] = soap.get("Medications", "") + " (patient unsure of doses, no medication list available)"
|
| 160 |
if random.random() < 0.35:
|
| 161 |
soap["Past_Medical_History"] = soap.get("Past_Medical_History", "") + ". NOTE: limited records, patient provides inconsistent timeline."
|
| 162 |
+
if random.random() < 0.3 and not is_emergency:
|
| 163 |
+
# Vague ROS (skip for emergencies where ROS is critical)
|
| 164 |
ros = soap.get("ROS", {})
|
| 165 |
if isinstance(ros, dict) and ros:
|
| 166 |
key = random.choice(list(ros.keys()))
|
|
|
|
| 169 |
|
| 170 |
if phase >= 3:
|
| 171 |
# Phase 3: Behavioral and socio-economic noise
|
| 172 |
+
if is_emergency:
|
| 173 |
+
behavioral_notes = [
|
| 174 |
+
"EMS handoff was chaotic and rushed, some details missing.",
|
| 175 |
+
"Family members are screaming in the hallway, making it hard to obtain history.",
|
| 176 |
+
"Patient is too unstable to provide complete history; relying on bystander accounts.",
|
| 177 |
+
"Police brought patient in, no ID or medical records available.",
|
| 178 |
+
"Multiple traumas arriving simultaneously, triage is overwhelmed.",
|
| 179 |
+
"Patient unresponsive, unable to obtain comprehensive review of systems.",
|
| 180 |
+
]
|
| 181 |
+
else:
|
| 182 |
+
behavioral_notes = [
|
| 183 |
+
"Patient appears anxious about cost of treatment, asks repeatedly about billing.",
|
| 184 |
+
"Patient's family member is hostile, demanding immediate answers.",
|
| 185 |
+
"Patient is tearful, expressing fear of losing job if hospitalized.",
|
| 186 |
+
"Patient requests to leave AMA, states cannot afford to miss work.",
|
| 187 |
+
"Language barrier noted -- communicating through teenage child as interpreter.",
|
| 188 |
+
"Patient appears intoxicated, history unreliable per triage nurse.",
|
| 189 |
+
"Patient is homeless, uncertain of medication history.",
|
| 190 |
+
"Patient brought by police from shelter, no ID or insurance card.",
|
| 191 |
+
]
|
| 192 |
soap["Social_History"] = soap.get("Social_History", "") + ". " + random.choice(behavioral_notes)
|
| 193 |
|
| 194 |
# Inject conflicting/misleading physical exam findings
|
| 195 |
+
if random.random() < 0.3 and not is_emergency:
|
| 196 |
soap["Physical_Examination"] = soap.get("Physical_Examination", "") + " Patient is uncooperative with portions of exam."
|
| 197 |
|
| 198 |
# Degrade HPI reliability
|
| 199 |
if random.random() < 0.4:
|
| 200 |
+
if is_emergency:
|
| 201 |
+
hpi_noise = random.choice([
|
| 202 |
+
" Historian is an unknown bystander, limited knowledge of medical history.",
|
| 203 |
+
" HPI obtained rapidly during active resuscitation.",
|
| 204 |
+
" Pre-hospital intervention details unclear from EMS."
|
| 205 |
+
])
|
| 206 |
+
else:
|
| 207 |
+
hpi_noise = random.choice([
|
| 208 |
+
" Historian is patient's neighbor, limited knowledge of medical history.",
|
| 209 |
+
" Patient gives contradictory timeline, unclear onset.",
|
| 210 |
+
" History obtained through interpreter, possible miscommunication.",
|
| 211 |
+
])
|
| 212 |
soap["HPI"] = soap.get("HPI", "") + hpi_noise
|
| 213 |
|
| 214 |
return soap
|
|
|
|
| 274 |
"lethal_treatments": disease.get("lethal_treatments", []),
|
| 275 |
"critical_labs": disease.get("critical_labs", []),
|
| 276 |
"difficulty": disease.get("difficulty", "medium"),
|
| 277 |
+
"is_emergency": disease.get("is_emergency", False),
|
| 278 |
},
|
| 279 |
"difficulty": difficulty,
|
| 280 |
"phase": phase,
|
|
|
|
| 302 |
"lethal_treatments": disease.get("lethal_treatments", []),
|
| 303 |
"critical_labs": disease.get("critical_labs", []),
|
| 304 |
"difficulty": disease.get("difficulty", "medium"),
|
| 305 |
+
"is_emergency": disease.get("is_emergency", False),
|
| 306 |
},
|
| 307 |
"difficulty": "random",
|
| 308 |
"phase": phase,
|
|
|
|
| 310 |
|
| 311 |
# Attach SOAP history with phase-appropriate noise
|
| 312 |
raw_soap = _SOAP_HISTORY_DB.get(disease_name, {})
|
| 313 |
+
is_emergency = disease.get("is_emergency", False)
|
| 314 |
+
ground_truth["soap_history"] = _apply_soap_noise(raw_soap, phase, is_emergency)
|
| 315 |
|
| 316 |
# Attach vitals and labs
|
| 317 |
ground_truth["vitals"] = _VITALS_DB.get(disease_name, "")
|
ER_MAP/envs/triage_env.py
CHANGED
|
@@ -64,7 +64,7 @@ class TriageEnv(gym.Env):
|
|
| 64 |
groq_api_key: Optional[str] = None,
|
| 65 |
nurse_api_key: Optional[str] = None,
|
| 66 |
patient_api_key: Optional[str] = None,
|
| 67 |
-
model: str = "llama-3.
|
| 68 |
render_mode: Optional[str] = None,
|
| 69 |
):
|
| 70 |
super().__init__()
|
|
@@ -146,7 +146,8 @@ class TriageEnv(gym.Env):
|
|
| 146 |
|
| 147 |
# 5. Initialize phase-based systems
|
| 148 |
self.patient_state = PatientState(self.ground_truth["patient"])
|
| 149 |
-
|
|
|
|
| 150 |
|
| 151 |
# 5. Build initial observation for Doctor
|
| 152 |
# Doctor sees nurse experience level + the pre-populated SOAP note.
|
|
@@ -158,6 +159,7 @@ class TriageEnv(gym.Env):
|
|
| 158 |
"A nurse is available to assist. A patient has just arrived. "
|
| 159 |
"Use your tools to diagnose and treat the patient.\n"
|
| 160 |
"TOOLS: speak_to, order_lab, read_soap, update_soap, terminal_discharge.\n"
|
|
|
|
| 161 |
"The patient's prior medical history and initial presentation "
|
| 162 |
"have been recorded in the SOAP note. Use 'read_soap' to review it. "
|
| 163 |
"Update the Assessment and Plan sections before discharging."
|
|
@@ -183,7 +185,11 @@ class TriageEnv(gym.Env):
|
|
| 183 |
info: Dict[str, Any] = {}
|
| 184 |
|
| 185 |
# --- Turn penalty ---
|
| 186 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 187 |
|
| 188 |
# --- Parse Doctor's JSON action ---
|
| 189 |
doctor_action = self._parse_doctor_action(action)
|
|
@@ -504,8 +510,24 @@ class TriageEnv(gym.Env):
|
|
| 504 |
"""Handle Doctor using 'terminal_discharge' tool. This ends the episode."""
|
| 505 |
reward = 0.0
|
| 506 |
treatment = doctor_action.get("treatment", "").strip().lower()
|
|
|
|
|
|
|
| 507 |
self.done = True
|
| 508 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 509 |
# --- SOAP reward shaping: penalize empty Assessment, reward filled ---
|
| 510 |
assessment = self.emr.get("Assessment", "").strip()
|
| 511 |
if assessment:
|
|
@@ -516,22 +538,24 @@ class TriageEnv(gym.Env):
|
|
| 516 |
logger.info("SOAP penalty: No Assessment documented before discharge.")
|
| 517 |
|
| 518 |
# --- SOAP reward shaping: penalize ignoring patient history ---
|
| 519 |
-
|
|
|
|
| 520 |
reward += -0.50 # Heavy penalty for practicing medicine blind
|
| 521 |
logger.info("SOAP penalty: Discharged without reading patient history (read_soap).")
|
| 522 |
|
| 523 |
# --- Early discharge penalty: phase-aware ---
|
| 524 |
# Phase 1: min 4 steps, Phase 2: min 6 steps, Phase 3: min 8 steps
|
| 525 |
-
|
| 526 |
-
|
| 527 |
-
|
| 528 |
-
|
| 529 |
-
|
| 530 |
-
|
| 531 |
-
|
| 532 |
-
|
| 533 |
-
|
| 534 |
-
|
|
|
|
| 535 |
|
| 536 |
# Auto-record the treatment in SOAP Plan
|
| 537 |
self.emr["Plan"] = treatment
|
|
@@ -540,77 +564,136 @@ class TriageEnv(gym.Env):
|
|
| 540 |
lethal_treatments = [
|
| 541 |
t.lower() for t in self.ground_truth["disease"]["lethal_treatments"]
|
| 542 |
]
|
|
|
|
| 543 |
|
| 544 |
-
# ---
|
| 545 |
-
# Use
|
| 546 |
-
|
| 547 |
-
|
| 548 |
-
|
| 549 |
-
|
| 550 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 551 |
|
| 552 |
-
|
| 553 |
-
|
| 554 |
-
|
| 555 |
-
|
| 556 |
-
|
| 557 |
-
|
| 558 |
-
break
|
| 559 |
|
| 560 |
-
|
| 561 |
|
| 562 |
-
|
| 563 |
-
is_lethal = any(
|
| 564 |
-
lethal_kw in treatment
|
| 565 |
-
for lethal_kw in lethal_treatments
|
| 566 |
-
)
|
| 567 |
|
| 568 |
-
|
| 569 |
-
|
| 570 |
-
|
| 571 |
-
|
| 572 |
-
|
| 573 |
-
|
| 574 |
-
|
| 575 |
-
|
| 576 |
-
|
| 577 |
-
|
| 578 |
-
|
| 579 |
-
|
| 580 |
-
|
| 581 |
-
|
| 582 |
-
|
| 583 |
-
|
| 584 |
-
|
| 585 |
-
|
| 586 |
-
|
| 587 |
-
|
| 588 |
-
|
| 589 |
-
|
| 590 |
-
|
| 591 |
-
|
| 592 |
-
|
| 593 |
-
|
| 594 |
-
|
| 595 |
-
|
| 596 |
-
|
| 597 |
-
|
| 598 |
-
|
| 599 |
-
|
| 600 |
-
|
| 601 |
-
|
| 602 |
-
|
| 603 |
-
|
| 604 |
-
|
| 605 |
-
|
| 606 |
-
|
| 607 |
-
|
| 608 |
-
|
| 609 |
-
|
| 610 |
-
|
| 611 |
-
"match_ratio": round(overlap_ratio, 2),
|
| 612 |
-
"soap_note": self.emr,
|
| 613 |
-
})
|
| 614 |
|
| 615 |
return obs, reward
|
| 616 |
|
|
|
|
| 64 |
groq_api_key: Optional[str] = None,
|
| 65 |
nurse_api_key: Optional[str] = None,
|
| 66 |
patient_api_key: Optional[str] = None,
|
| 67 |
+
model: str = "llama-3.3-70b-versatile",
|
| 68 |
render_mode: Optional[str] = None,
|
| 69 |
):
|
| 70 |
super().__init__()
|
|
|
|
| 146 |
|
| 147 |
# 5. Initialize phase-based systems
|
| 148 |
self.patient_state = PatientState(self.ground_truth["patient"])
|
| 149 |
+
is_emergency = self.ground_truth.get("disease", {}).get("is_emergency", False)
|
| 150 |
+
self.milestone_tracker = MilestoneTracker(phase=self.phase, is_emergency=is_emergency)
|
| 151 |
|
| 152 |
# 5. Build initial observation for Doctor
|
| 153 |
# Doctor sees nurse experience level + the pre-populated SOAP note.
|
|
|
|
| 159 |
"A nurse is available to assist. A patient has just arrived. "
|
| 160 |
"Use your tools to diagnose and treat the patient.\n"
|
| 161 |
"TOOLS: speak_to, order_lab, read_soap, update_soap, terminal_discharge.\n"
|
| 162 |
+
"When using 'terminal_discharge', you MUST include an 'is_emergency' boolean field (true/false) to indicate if this is a time-critical emergency.\n"
|
| 163 |
"The patient's prior medical history and initial presentation "
|
| 164 |
"have been recorded in the SOAP note. Use 'read_soap' to review it. "
|
| 165 |
"Update the Assessment and Plan sections before discharging."
|
|
|
|
| 185 |
info: Dict[str, Any] = {}
|
| 186 |
|
| 187 |
# --- Turn penalty ---
|
| 188 |
+
is_emergency = self.ground_truth.get("disease", {}).get("is_emergency", False)
|
| 189 |
+
if is_emergency:
|
| 190 |
+
reward += -0.15 # Heavy penalty for wasting time in emergencies
|
| 191 |
+
else:
|
| 192 |
+
reward += -0.01
|
| 193 |
|
| 194 |
# --- Parse Doctor's JSON action ---
|
| 195 |
doctor_action = self._parse_doctor_action(action)
|
|
|
|
| 510 |
"""Handle Doctor using 'terminal_discharge' tool. This ends the episode."""
|
| 511 |
reward = 0.0
|
| 512 |
treatment = doctor_action.get("treatment", "").strip().lower()
|
| 513 |
+
declared_emergency = bool(doctor_action.get("is_emergency", False))
|
| 514 |
+
is_actual_emergency = self.ground_truth.get("disease", {}).get("is_emergency", False)
|
| 515 |
self.done = True
|
| 516 |
|
| 517 |
+
# --- Emergency Identification Reward ---
|
| 518 |
+
if declared_emergency and is_actual_emergency:
|
| 519 |
+
reward += 0.50
|
| 520 |
+
logger.info("Emergency correctly identified (+0.50).")
|
| 521 |
+
elif not declared_emergency and not is_actual_emergency:
|
| 522 |
+
reward += 0.10
|
| 523 |
+
logger.info("Non-emergency correctly identified (+0.10).")
|
| 524 |
+
elif declared_emergency and not is_actual_emergency:
|
| 525 |
+
reward += -0.30
|
| 526 |
+
logger.info("False positive emergency identification (-0.30).")
|
| 527 |
+
elif not declared_emergency and is_actual_emergency:
|
| 528 |
+
reward += -0.50
|
| 529 |
+
logger.info("Failed to identify true emergency (-0.50).")
|
| 530 |
+
|
| 531 |
# --- SOAP reward shaping: penalize empty Assessment, reward filled ---
|
| 532 |
assessment = self.emr.get("Assessment", "").strip()
|
| 533 |
if assessment:
|
|
|
|
| 538 |
logger.info("SOAP penalty: No Assessment documented before discharge.")
|
| 539 |
|
| 540 |
# --- SOAP reward shaping: penalize ignoring patient history ---
|
| 541 |
+
is_emergency = self.ground_truth.get("disease", {}).get("is_emergency", False)
|
| 542 |
+
if self.milestone_tracker and not self.milestone_tracker.achieved.get("READ_SOAP", False) and not is_emergency:
|
| 543 |
reward += -0.50 # Heavy penalty for practicing medicine blind
|
| 544 |
logger.info("SOAP penalty: Discharged without reading patient history (read_soap).")
|
| 545 |
|
| 546 |
# --- Early discharge penalty: phase-aware ---
|
| 547 |
# Phase 1: min 4 steps, Phase 2: min 6 steps, Phase 3: min 8 steps
|
| 548 |
+
if not is_emergency:
|
| 549 |
+
min_steps_map = {1: 4, 2: 6, 3: 8}
|
| 550 |
+
min_steps = min_steps_map.get(self.phase, 5)
|
| 551 |
+
if self.step_count < min_steps:
|
| 552 |
+
shortfall = min_steps - self.step_count
|
| 553 |
+
penalty = -0.15 * shortfall # -0.15 per missing step
|
| 554 |
+
reward += penalty
|
| 555 |
+
logger.info(
|
| 556 |
+
f"Early discharge penalty: step {self.step_count} < min {min_steps} "
|
| 557 |
+
f"(phase {self.phase}), penalty={penalty:.2f}"
|
| 558 |
+
)
|
| 559 |
|
| 560 |
# Auto-record the treatment in SOAP Plan
|
| 561 |
self.emr["Plan"] = treatment
|
|
|
|
| 564 |
lethal_treatments = [
|
| 565 |
t.lower() for t in self.ground_truth["disease"]["lethal_treatments"]
|
| 566 |
]
|
| 567 |
+
disease_name = self.ground_truth["disease"]["true_disease"]
|
| 568 |
|
| 569 |
+
# --- LLM-as-a-Judge Evaluation ---
|
| 570 |
+
# Use a 70B model to semantically grade the treatment plan
|
| 571 |
+
judge_result = None
|
| 572 |
+
if self.router:
|
| 573 |
+
try:
|
| 574 |
+
judge_result = self.router.evaluate_treatment(
|
| 575 |
+
prescribed_treatment=treatment,
|
| 576 |
+
correct_treatment=self.ground_truth["disease"]["correct_treatment"],
|
| 577 |
+
lethal_treatments=self.ground_truth["disease"]["lethal_treatments"],
|
| 578 |
+
disease_name=disease_name,
|
| 579 |
+
)
|
| 580 |
+
logger.info(f"LLM Judge result: {judge_result}")
|
| 581 |
+
except Exception as e:
|
| 582 |
+
logger.error(f"LLM Judge failed, falling back to keyword matching: {e}")
|
| 583 |
+
|
| 584 |
+
if judge_result and judge_result.get("score") is not None:
|
| 585 |
+
# --- LLM Judge path ---
|
| 586 |
+
score = judge_result["score"]
|
| 587 |
+
is_lethal = judge_result.get("is_lethal", False)
|
| 588 |
+
reasoning = judge_result.get("reasoning", "")
|
| 589 |
+
|
| 590 |
+
if is_lethal:
|
| 591 |
+
reward += -1.50
|
| 592 |
+
obs = json.dumps({
|
| 593 |
+
"event": "terminal_fatal",
|
| 594 |
+
"message": f"CRITICAL ERROR: Lethal treatment administered. {reasoning}",
|
| 595 |
+
"ground_truth": disease_name,
|
| 596 |
+
"prescribed_treatment": treatment,
|
| 597 |
+
"judge_score": score,
|
| 598 |
+
"judge_reasoning": reasoning,
|
| 599 |
+
"soap_note": self.emr,
|
| 600 |
+
})
|
| 601 |
+
elif score >= 0.75:
|
| 602 |
+
reward += 1.00
|
| 603 |
+
obs = json.dumps({
|
| 604 |
+
"event": "terminal_win",
|
| 605 |
+
"message": f"Correct diagnosis and treatment! {reasoning}",
|
| 606 |
+
"ground_truth": disease_name,
|
| 607 |
+
"prescribed_treatment": treatment,
|
| 608 |
+
"judge_score": score,
|
| 609 |
+
"judge_reasoning": reasoning,
|
| 610 |
+
"soap_note": self.emr,
|
| 611 |
+
})
|
| 612 |
+
elif score >= 0.30:
|
| 613 |
+
partial_reward = -0.40 + (score * 1.4) # scales from 0.02 to 0.65
|
| 614 |
+
reward += partial_reward
|
| 615 |
+
obs = json.dumps({
|
| 616 |
+
"event": "terminal_partial",
|
| 617 |
+
"message": f"Partially correct treatment (Judge: {score:.0%}). {reasoning}",
|
| 618 |
+
"ground_truth": disease_name,
|
| 619 |
+
"correct_treatment": self.ground_truth["disease"]["correct_treatment"],
|
| 620 |
+
"prescribed_treatment": treatment,
|
| 621 |
+
"judge_score": score,
|
| 622 |
+
"judge_reasoning": reasoning,
|
| 623 |
+
"soap_note": self.emr,
|
| 624 |
+
})
|
| 625 |
+
else:
|
| 626 |
+
reward += -1.00
|
| 627 |
+
obs = json.dumps({
|
| 628 |
+
"event": "terminal_incorrect",
|
| 629 |
+
"message": f"Incorrect treatment (Judge: {score:.0%}). {reasoning}",
|
| 630 |
+
"ground_truth": disease_name,
|
| 631 |
+
"correct_treatment": self.ground_truth["disease"]["correct_treatment"],
|
| 632 |
+
"prescribed_treatment": treatment,
|
| 633 |
+
"judge_score": score,
|
| 634 |
+
"judge_reasoning": reasoning,
|
| 635 |
+
"soap_note": self.emr,
|
| 636 |
+
})
|
| 637 |
+
else:
|
| 638 |
+
# --- Fallback: Keyword matching (if LLM Judge unavailable) ---
|
| 639 |
+
stop_words = {"for", "if", "or", "and", "with", "to", "of", "the", "a", "an", "in", "on", "at", "by", "from", "unable", "po", "signs", "is", "are", "then", "above", "below"}
|
| 640 |
+
correct_keywords = set(re.findall(r"\w+", correct_treatment)) - stop_words
|
| 641 |
+
treatment_keywords = set(re.findall(r"\w+", treatment)) - stop_words
|
| 642 |
|
| 643 |
+
overlap = correct_keywords & treatment_keywords
|
| 644 |
+
for c_kw in correct_keywords - overlap:
|
| 645 |
+
for t_kw in treatment_keywords:
|
| 646 |
+
if len(c_kw) >= 4 and (c_kw in t_kw or t_kw in c_kw):
|
| 647 |
+
overlap.add(c_kw)
|
| 648 |
+
break
|
|
|
|
| 649 |
|
| 650 |
+
overlap_ratio = len(overlap) / max(len(correct_keywords), 1)
|
| 651 |
|
| 652 |
+
is_lethal = any(lethal_kw in treatment for lethal_kw in lethal_treatments)
|
|
|
|
|
|
|
|
|
|
|
|
|
| 653 |
|
| 654 |
+
if is_lethal:
|
| 655 |
+
reward += -1.50
|
| 656 |
+
obs = json.dumps({
|
| 657 |
+
"event": "terminal_fatal",
|
| 658 |
+
"message": "CRITICAL ERROR: Lethal treatment administered. Patient death.",
|
| 659 |
+
"ground_truth": disease_name,
|
| 660 |
+
"prescribed_treatment": treatment,
|
| 661 |
+
"soap_note": self.emr,
|
| 662 |
+
})
|
| 663 |
+
elif overlap_ratio >= 0.70:
|
| 664 |
+
reward += 1.00
|
| 665 |
+
obs = json.dumps({
|
| 666 |
+
"event": "terminal_win",
|
| 667 |
+
"message": "Correct diagnosis and treatment! Patient stabilized.",
|
| 668 |
+
"ground_truth": disease_name,
|
| 669 |
+
"prescribed_treatment": treatment,
|
| 670 |
+
"match_ratio": round(overlap_ratio, 2),
|
| 671 |
+
"soap_note": self.emr,
|
| 672 |
+
})
|
| 673 |
+
elif overlap_ratio >= 0.20:
|
| 674 |
+
partial_reward = -0.40 + (overlap_ratio * 1.2)
|
| 675 |
+
reward += partial_reward
|
| 676 |
+
obs = json.dumps({
|
| 677 |
+
"event": "terminal_partial",
|
| 678 |
+
"message": f"Partially correct treatment ({overlap_ratio:.0%} match). Key interventions missing.",
|
| 679 |
+
"ground_truth": disease_name,
|
| 680 |
+
"correct_treatment": self.ground_truth["disease"]["correct_treatment"],
|
| 681 |
+
"prescribed_treatment": treatment,
|
| 682 |
+
"match_ratio": round(overlap_ratio, 2),
|
| 683 |
+
"matched_keywords": sorted(overlap),
|
| 684 |
+
"soap_note": self.emr,
|
| 685 |
+
})
|
| 686 |
+
else:
|
| 687 |
+
reward += -1.00
|
| 688 |
+
obs = json.dumps({
|
| 689 |
+
"event": "terminal_incorrect",
|
| 690 |
+
"message": "Incorrect treatment. Patient outcome: adverse.",
|
| 691 |
+
"ground_truth": disease_name,
|
| 692 |
+
"correct_treatment": self.ground_truth["disease"]["correct_treatment"],
|
| 693 |
+
"prescribed_treatment": treatment,
|
| 694 |
+
"match_ratio": round(overlap_ratio, 2),
|
| 695 |
+
"soap_note": self.emr,
|
| 696 |
+
})
|
|
|
|
|
|
|
|
|
|
| 697 |
|
| 698 |
return obs, reward
|
| 699 |
|
ER_MAP/evaluate.py
CHANGED
|
@@ -46,7 +46,7 @@ RESPOND ONLY WITH VALID JSON."""
|
|
| 46 |
|
| 47 |
|
| 48 |
class DoctorBrain:
|
| 49 |
-
def __init__(self, api_key: str, model: str = "llama-3.
|
| 50 |
from groq import Groq
|
| 51 |
self.client = Groq(api_key=api_key)
|
| 52 |
self.model = model
|
|
|
|
| 46 |
|
| 47 |
|
| 48 |
class DoctorBrain:
|
| 49 |
+
def __init__(self, api_key: str, model: str = "llama-3.3-70b-versatile"):
|
| 50 |
from groq import Groq
|
| 51 |
self.client = Groq(api_key=api_key)
|
| 52 |
self.model = model
|
ER_MAP/openenv.yaml
CHANGED
|
@@ -19,7 +19,7 @@ env:
|
|
| 19 |
|
| 20 |
env_kwargs:
|
| 21 |
groq_api_key: "${GROQ_API_KEY}"
|
| 22 |
-
model: "llama-3.
|
| 23 |
render_mode: "human"
|
| 24 |
|
| 25 |
dependencies:
|
|
|
|
| 19 |
|
| 20 |
env_kwargs:
|
| 21 |
groq_api_key: "${GROQ_API_KEY}"
|
| 22 |
+
model: "llama-3.3-70b-versatile"
|
| 23 |
render_mode: "human"
|
| 24 |
|
| 25 |
dependencies:
|