import { generateResponse } from './llmClient.js'; import Warning, { WarningType, WarningSeverity } from '../models/Warning.js'; import Patient from '../models/Patient.js'; import dotenv from 'dotenv'; dotenv.config(); // Re-export enums for backward compatibility export { WarningType, WarningSeverity }; class WarningService { /** * Get all warnings for a patient */ async getPatientWarnings(patientId, includeAcknowledged = false) { const warnings = await Warning.findByPatient(patientId, includeAcknowledged); return warnings.map(w => w.toResponse()); } /** * Get all unacknowledged warnings across all patients */ async getAllUnacknowledgedWarnings(limit = 50) { const warnings = await Warning.findUnacknowledged(limit); return warnings.map(w => w.toResponse()); } /** * Acknowledge a clinical warning */ async acknowledgeWarning(warningId, userId, notes = null) { try { const warning = await Warning.findById(warningId); if (!warning) { return null; } await warning.acknowledge(userId, notes); return warning.toResponse(); } catch { return null; } } /** * Create a new clinical warning */ async createWarning(warningData) { const warning = new Warning(warningData); await warning.save(); return warning.toResponse(); } /** * Delete all warnings for a patient */ async deletePatientWarnings(patientId) { const result = await Warning.deleteMany({ patient_id: patientId }); return result.deletedCount; } /** * Use AI to analyze patient data and generate clinical warnings */ async analyzePatientForWarnings(patientId, newMedication = null, newCondition = null) { const patient = await Patient.findById(patientId); if (!patient) { return []; } // Build the analysis prompt const medications = patient.medications || []; const medNames = medications .filter(m => typeof m === 'object') .map(m => m.name || ''); let conditions = patient.medical_history || []; if (newMedication) { medNames.push(`${newMedication} (NEW)`); } if (newCondition) { conditions = [...conditions, `${newCondition} (NEW)`]; } const conditionsText = conditions.map(c => { if (typeof c === 'string') return c; return c.condition || 'Unknown'; }).join(', '); const prompt = `You are a clinical decision support AI. Analyze this patient's data for potential clinical warnings. PATIENT INFORMATION: - Age: ${patient.age || 'Unknown'} - Gender: ${patient.gender || 'Unknown'} - Medical Conditions: ${conditionsText || 'None recorded'} - Current Medications: ${medNames.join(', ') || 'None recorded'} Analyze for: 1. DRUG INTERACTIONS - Check if any medications interact negatively with each other 2. CONTRAINDICATIONS - Check if any medications are contraindicated given the patient's conditions 3. ALLERGY RISKS - Common allergy cross-reactions 4. DOSAGE CONCERNS - Age-related dosage considerations 5. DUPLICATE THERAPY - Multiple drugs for the same purpose Return a JSON array of warnings. Each warning should have: - warning_type: one of "drug_interaction", "allergy", "contraindication", "abnormal_pattern", "dosage_alert", "duplicate_therapy" - severity: one of "low", "medium", "high", "critical" - title: short title (max 50 chars) - description: detailed explanation - related_medications: array of medication names involved - related_conditions: array of conditions involved - recommendation: suggested action If there are no warnings, return an empty array []. Return ONLY valid JSON, no markdown or other text.`; try { let responseText = await generateResponse(prompt); responseText = responseText.trim(); // Remove markdown code blocks if present if (responseText.startsWith('```')) { responseText = responseText.split('```')[1]; if (responseText.startsWith('json')) { responseText = responseText.substring(4); } } responseText = responseText.trim(); const warningsData = JSON.parse(responseText); if (!Array.isArray(warningsData)) { return []; } // Create warnings in database const createdWarnings = []; for (const warningData of warningsData) { try { const warning = new Warning({ patient_id: patientId, warning_type: warningData.warning_type || 'general', severity: warningData.severity || 'medium', title: (warningData.title || 'Clinical Warning').substring(0, 100), description: warningData.description || '', related_medications: warningData.related_medications || [], related_conditions: warningData.related_conditions || [], recommendations: warningData.recommendation ? [warningData.recommendation] : [], source: 'ai_analysis' }); await warning.save(); createdWarnings.push(warning.toResponse()); } catch (error) { console.log(`Error creating warning: ${error.message}`); continue; } } return createdWarnings; } catch (error) { if (error instanceof SyntaxError) { console.log(`JSON parse error: ${error.message}`); } else { console.log(`Error analyzing patient: ${error.message}`); } return []; } } /** * Get warning statistics */ async getWarningStats() { return await Warning.getStats(); } } // Singleton instance export const warningService = new WarningService();