rpabbi/TriageFlow
0
1import os2 3# Set to True when you have an API key4USE_CLAUDE_API = bool(os.environ.get("ANTHROPIC_API_KEY"))5 6 7def get_clinical_assessment(chief_complaint: str, vitals: dict, triage_result: dict,8 patient_history: dict | None = None) -> str:9 """Generate clinical assessment using Claude API or mock response."""10 if USE_CLAUDE_API:11 return _claude_assessment(chief_complaint, vitals, triage_result, patient_history)12 return _mock_assessment(chief_complaint, vitals, triage_result, patient_history)13 14 15def get_interaction_explanation(interactions: list[dict]) -> str:16 """Generate plain-language explanation of drug interactions."""17 if USE_CLAUDE_API:18 return _claude_interaction_explanation(interactions)19 return _mock_interaction_explanation(interactions)20 21 22def get_patient_brief(summary: dict) -> str:23 """Generate an SBAR-style patient brief."""24 if USE_CLAUDE_API:25 return _claude_patient_brief(summary)26 return _mock_patient_brief(summary)27 28 29# --- Claude API implementations ---30 31def _claude_assessment(complaint, vitals, triage, history):32 from anthropic import Anthropic33 client = Anthropic()34 35 history_ctx = ""36 if history:37 meds = ", ".join([m["drug_name"] for m in history.get("active_medications", [])])38 dx = ", ".join(history.get("diagnoses", [])[:5])39 history_ctx = f"\nPatient history: {history['demographics']['age']}yo {history['demographics']['sex']}, known conditions: {dx}, active medications: {meds}"40 41 prompt = f"""You are an emergency medicine clinical decision support system. Provide a concise clinical assessment.42 43Patient presents with: {complaint}44Vitals: HR {vitals.get('heart_rate', 'N/A')}, BP {vitals.get('systolic_bp', 'N/A')}/{vitals.get('diastolic_bp', 'N/A')}, Temp {vitals.get('temperature', 'N/A')}C, RR {vitals.get('respiratory_rate', 'N/A')}, O2 Sat {vitals.get('o2_saturation', 'N/A')}%, Pain {vitals.get('pain_scale', 'N/A')}/1045AI Triage Level: CTAS {triage['predicted_level']} ({triage['level_name']})46Clinical Flags: {', '.join(triage.get('clinical_flags', []))}47{history_ctx}48 49Provide:501. Assessment: 2-3 sentence clinical impression512. Differential diagnoses: Top 3 most likely523. Recommended workup: Initial tests/imaging to order534. Red flags to watch for54 55Keep it concise and actionable. This is a decision SUPPORT tool - all decisions are made by the physician."""56 57 response = client.messages.create(58 model="claude-sonnet-4-20250514",59 max_tokens=500,60 messages=[{"role": "user", "content": prompt}],61 )62 return response.content[0].text63 64 65def _claude_interaction_explanation(interactions):66 from anthropic import Anthropic67 client = Anthropic()68 69 interaction_text = "\n".join([70 f"- {i['drug_1']} + {i['drug_2']} ({i['severity']}): {i['risk']}"71 for i in interactions72 ])73 74 prompt = f"""You are a clinical pharmacist. Explain these drug interactions in clear, actionable language for a physician.75 76{interaction_text}77 78For each interaction:791. What could happen to the patient802. What to do about it (monitor, adjust dose, substitute)81Keep it concise - 2-3 sentences per interaction."""82 83 response = client.messages.create(84 model="claude-sonnet-4-20250514",85 max_tokens=400,86 messages=[{"role": "user", "content": prompt}],87 )88 return response.content[0].text89 90 91def _claude_patient_brief(summary):92 from anthropic import Anthropic93 client = Anthropic()94 95 demo = summary.get("demographics", {})96 meds = ", ".join([m["drug_name"] for m in summary.get("active_medications", [])])97 dx = ", ".join(summary.get("diagnoses", [])[:5])98 last_enc = summary.get("last_encounter", {})99 100 prompt = f"""Generate a concise SBAR clinical handoff brief for this patient.101 102Patient: {demo.get('name', 'Unknown')}, {demo.get('age', 'N/A')}yo {demo.get('sex', 'N/A')}103Conditions: {dx}104Active medications: {meds}105Encounter count: {summary.get('encounter_count', 0)}106Last visit: {last_enc.get('encounter_date', 'N/A')} - {last_enc.get('chief_complaint', 'N/A')} ({last_enc.get('diagnosis_description', 'N/A')})107Abnormal labs: {summary.get('abnormal_lab_count', 0)}108Risk flags: Polypharmacy={summary['risk_factors']['polypharmacy']}, Multiple conditions={summary['risk_factors']['multiple_conditions']}, Frequent ED={summary['risk_factors']['frequent_ed_visits']}109 110Format as SBAR (Situation, Background, Assessment, Recommendation). Keep it to 4-6 sentences total."""111 112 response = client.messages.create(113 model="claude-sonnet-4-20250514",114 max_tokens=400,115 messages=[{"role": "user", "content": prompt}],116 )117 return response.content[0].text118 119 120# --- Mock implementations ---121 122def _mock_assessment(complaint, vitals, triage, history):123 flags = triage.get("clinical_flags", [])124 flag_text = ", ".join(flags) if flags else "No critical flags"125 126 level = triage["predicted_level"]127 level_name = triage["level_name"]128 129 history_note = ""130 if history:131 dx = history.get("diagnoses", [])132 if dx:133 history_note = f"\n\n**Relevant History:** Known conditions include {', '.join(dx[:3])}. "134 if history["risk_factors"]["polypharmacy"]:135 history_note += "Patient is on 5+ medications (polypharmacy risk). "136 if history["risk_factors"]["frequent_ed_visits"]:137 history_note += "Frequent ED utilizer - consider care coordination."138 139 assessment = f"""**Clinical Assessment (AI-Generated)**140 141**Impression:** Patient presents with {complaint}. Vital signs {'show concerning findings' if level <= 2 else 'are within acceptable parameters'} for CTAS Level {level} ({level_name}). {flag_text}.142 143**Differential Diagnoses:**1441. Most likely based on presenting complaint and vital signs1452. Consider secondary causes related to patient demographics1463. Rule out emergent conditions if red flags present147 148**Recommended Workup:**149- {'Immediate ECG, troponin, CBC, BMP' if 'Cardiac' in flag_text else 'CBC, BMP, urinalysis as indicated'}150- {'Chest X-ray' if any(f in complaint.lower() for f in ['chest', 'breath', 'cough']) else 'Imaging as clinically indicated'}151- Reassess in {'15 minutes' if level <= 2 else '30-60 minutes'}152 153**Red Flags:** {'Hemodynamic instability requiring immediate intervention' if level == 1 else 'Monitor for clinical deterioration. Reassess if symptoms worsen.'}154{history_note}155 156*This is a decision support tool. All clinical decisions must be made by the treating physician.*"""157 return assessment158 159 160def _mock_interaction_explanation(interactions):161 if not interactions:162 return "No significant drug interactions detected."163 164 explanations = []165 for i in interactions:166 severity_icon = {"HIGH": "!!!", "MODERATE": "!!", "LOW": "!"}[i["severity"]]167 explanations.append(168 f"**{severity_icon} {i['drug_1'].title()} + {i['drug_2'].title()} [{i['severity']}]**\n{i['risk']}"169 )170 return "\n\n".join(explanations)171 172 173def _mock_patient_brief(summary):174 demo = summary.get("demographics", {})175 meds = [m["drug_name"] for m in summary.get("active_medications", [])]176 dx = summary.get("diagnoses", [])177 last_enc = summary.get("last_encounter", {})178 risks = summary.get("risk_factors", {})179 180 risk_items = []181 if risks.get("polypharmacy"):182 risk_items.append("polypharmacy")183 if risks.get("multiple_conditions"):184 risk_items.append("multiple comorbidities")185 if risks.get("frequent_ed_visits"):186 risk_items.append("frequent ED utilizer")187 if risks.get("abnormal_labs"):188 risk_items.append("abnormal lab values")189 190 last_date = "N/A"191 if last_enc and last_enc.get("encounter_date"):192 last_date = str(last_enc["encounter_date"])[:10]193 194 return f"""**SBAR Patient Brief**195 196**Situation:** {demo.get('name', 'Unknown')}, {demo.get('age', 'N/A')}yo {demo.get('sex', 'N/A')}, presenting with {last_enc.get('chief_complaint', 'N/A') if last_enc else 'N/A'}. Last seen {last_date} at {last_enc.get('facility', 'N/A') if last_enc else 'N/A'}.197 198**Background:** {len(dx)} known conditions ({', '.join(dx[:3]) if dx else 'none documented'}). Currently on {len(meds)} active medication{'s' if len(meds) != 1 else ''} ({', '.join(meds[:4]) if meds else 'none'}). Total {summary.get('encounter_count', 0)} encounters on record.199 200**Assessment:** {f"Risk factors identified: {', '.join(risk_items)}." if risk_items else "No significant risk factors identified."} {summary.get('abnormal_lab_count', 0)} abnormal lab result(s) on file.201 202**Recommendation:** {'High-risk patient — review medication list for interactions, consider care plan coordination.' if len(risk_items) >= 2 else 'Standard care pathway. Follow up on any outstanding lab results.'}"""203 