Spaces:
Sleeping
Sleeping
| 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(); | |