Spaces:
Sleeping
Sleeping
| #!/usr/bin/env python3 | |
| """ | |
| Enhanced MedeX AI Engine - Hybrid System | |
| Real medical AI with fallback to advanced local capabilities | |
| """ | |
| import asyncio | |
| import json | |
| import uuid | |
| import re | |
| from datetime import datetime | |
| from typing import Dict, List, Any, Optional, Union, Tuple | |
| from dataclasses import dataclass, asdict | |
| from pathlib import Path | |
| import numpy as np | |
| from sentence_transformers import SentenceTransformer | |
| # Import our real Kimi client | |
| from .real_kimi_client import RealKimiClient, KimiRequest | |
| class EnhancedMedicalQuery: | |
| """Enhanced medical query with full context analysis""" | |
| query_id: str | |
| original_text: str | |
| user_type: str # 'patient' or 'professional' | |
| query_type: str # 'consultation', 'emergency', 'education', etc. | |
| urgency_level: str # 'routine', 'urgent', 'emergency' | |
| context: Dict[str, Any] | |
| confidence: float | |
| medical_specialty: Optional[str] | |
| timestamp: datetime | |
| class EnhancedMedicalResponse: | |
| """Enhanced medical response with rich metadata""" | |
| response_id: str | |
| query_id: str | |
| user_type: str | |
| response_text: str | |
| confidence: float | |
| medical_sources: List[Dict[str, Any]] | |
| recommendations: List[str] | |
| warnings: List[str] | |
| follow_up: List[str] | |
| emergency_level: str | |
| ai_source: str # 'kimi_real', 'local_advanced', 'hybrid' | |
| processing_time: float | |
| timestamp: datetime | |
| class AdvancedMedicalReasoningEngine: | |
| """Advanced medical reasoning with sophisticated logic""" | |
| def __init__(self): | |
| self.medical_patterns = self._load_medical_patterns() | |
| self.diagnostic_algorithms = self._load_diagnostic_algorithms() | |
| self.emergency_protocols = self._load_emergency_protocols() | |
| def _load_medical_patterns(self) -> Dict[str, Any]: | |
| """Load sophisticated medical pattern recognition""" | |
| return { | |
| "cardiovascular_patterns": { | |
| "acute_coronary_syndrome": { | |
| "primary_indicators": [ | |
| "dolor torácico", "dolor precordial", "opresión torácica", | |
| "dolor subesternal", "malestar torácico" | |
| ], | |
| "secondary_indicators": [ | |
| "disnea", "diaforesis", "náuseas", "dolor irradiado", | |
| "brazo izquierdo", "mandíbula", "espalda" | |
| ], | |
| "risk_factors": [ | |
| "diabetes", "hipertensión", "tabaquismo", "edad", | |
| "antecedentes familiares", "dislipidemia" | |
| ], | |
| "urgency_weight": 0.95, | |
| "confidence_threshold": 0.7 | |
| }, | |
| "hypertensive_emergency": { | |
| "primary_indicators": [ | |
| "presión arterial alta", "hipertensión severa", | |
| "cefalea intensa", "visión borrosa" | |
| ], | |
| "secondary_indicators": [ | |
| "confusión", "convulsiones", "dolor torácico", | |
| "dificultad respiratoria" | |
| ], | |
| "urgency_weight": 0.85, | |
| "confidence_threshold": 0.6 | |
| } | |
| }, | |
| "neurological_patterns": { | |
| "stroke": { | |
| "primary_indicators": [ | |
| "debilidad súbita", "parálisis", "dificultad hablar", | |
| "confusión súbita", "pérdida visión" | |
| ], | |
| "secondary_indicators": [ | |
| "cefalea súbita", "mareos", "pérdida equilibrio", | |
| "entumecimiento facial" | |
| ], | |
| "urgency_weight": 0.98, | |
| "confidence_threshold": 0.8 | |
| } | |
| }, | |
| "respiratory_patterns": { | |
| "acute_respiratory_distress": { | |
| "primary_indicators": [ | |
| "dificultad respiratoria", "disnea severa", | |
| "falta aire", "respiración laboriosa" | |
| ], | |
| "secondary_indicators": [ | |
| "cianosis", "uso músculos accesorios", | |
| "taquipnea", "ortopnea" | |
| ], | |
| "urgency_weight": 0.90, | |
| "confidence_threshold": 0.7 | |
| } | |
| } | |
| } | |
| def _load_diagnostic_algorithms(self) -> Dict[str, Any]: | |
| """Load diagnostic reasoning algorithms""" | |
| return { | |
| "bayesian_inference": { | |
| "prior_probabilities": { | |
| "chest_pain_cardiac": 0.15, # 15% of chest pain is cardiac | |
| "chest_pain_musculoskeletal": 0.35, | |
| "chest_pain_gastrointestinal": 0.25, | |
| "chest_pain_pulmonary": 0.15, | |
| "chest_pain_other": 0.10 | |
| }, | |
| "likelihood_ratios": { | |
| "diabetes_present": 2.5, # Increases cardiac probability | |
| "age_over_50": 2.0, | |
| "male_gender": 1.5, | |
| "smoking": 2.0, | |
| "family_history": 1.8 | |
| } | |
| }, | |
| "clinical_decision_rules": { | |
| "chest_pain_risk_stratification": { | |
| "very_high_risk": { | |
| "criteria": ["ST_elevation", "hemodynamic_instability", "ongoing_chest_pain"], | |
| "action": "immediate_catheterization" | |
| }, | |
| "high_risk": { | |
| "criteria": ["troponin_elevation", "ECG_changes", "diabetes_with_symptoms"], | |
| "action": "urgent_cardiology_consultation" | |
| }, | |
| "intermediate_risk": { | |
| "criteria": ["risk_factors_present", "atypical_symptoms"], | |
| "action": "stress_testing_or_imaging" | |
| } | |
| } | |
| } | |
| } | |
| def _load_emergency_protocols(self) -> Dict[str, Any]: | |
| """Load emergency medical protocols""" | |
| return { | |
| "stemi_protocol": { | |
| "recognition_criteria": [ | |
| "ST elevation > 1mm in ≥2 contiguous leads", | |
| "new LBBB", | |
| "posterior MI equivalent" | |
| ], | |
| "time_targets": { | |
| "door_to_ECG": "< 10 minutes", | |
| "door_to_balloon": "< 90 minutes", | |
| "door_to_needle": "< 30 minutes" | |
| }, | |
| "medications": { | |
| "aspirin": "300mg chewed", | |
| "clopidogrel": "600mg loading dose", | |
| "atorvastatin": "80mg", | |
| "metoprolol": "25mg BID if no contraindications" | |
| } | |
| }, | |
| "stroke_protocol": { | |
| "recognition_criteria": [ | |
| "FAST positive", | |
| "NIHSS > 0", | |
| "symptom onset < 4.5 hours" | |
| ], | |
| "time_targets": { | |
| "door_to_CT": "< 25 minutes", | |
| "door_to_needle": "< 60 minutes" | |
| }, | |
| "exclusion_criteria": [ | |
| "hemorrhage on CT", | |
| "recent surgery", | |
| "anticoagulation" | |
| ] | |
| } | |
| } | |
| def analyze_medical_condition(self, text: str, context: Dict[str, Any]) -> Dict[str, Any]: | |
| """Advanced medical condition analysis""" | |
| analysis_results = { | |
| "conditions_detected": [], | |
| "urgency_score": 0.0, | |
| "confidence": 0.0, | |
| "recommended_actions": [], | |
| "risk_factors": [], | |
| "differential_diagnosis": [] | |
| } | |
| text_lower = text.lower() | |
| # Analyze all medical patterns | |
| for category, patterns in self.medical_patterns.items(): | |
| for condition, pattern_data in patterns.items(): | |
| condition_score = 0.0 | |
| indicators_found = [] | |
| # Check primary indicators | |
| primary_matches = sum(1 for indicator in pattern_data["primary_indicators"] | |
| if indicator in text_lower) | |
| primary_score = primary_matches / len(pattern_data["primary_indicators"]) | |
| # Check secondary indicators | |
| secondary_matches = sum(1 for indicator in pattern_data["secondary_indicators"] | |
| if indicator in text_lower) | |
| secondary_score = secondary_matches / len(pattern_data["secondary_indicators"]) * 0.6 | |
| # Check risk factors from context | |
| risk_factor_score = 0.0 | |
| if "medical_history" in context: | |
| risk_matches = sum(1 for rf in pattern_data.get("risk_factors", []) | |
| if any(rf in hist for hist in context["medical_history"])) | |
| risk_factor_score = risk_matches / max(1, len(pattern_data.get("risk_factors", []))) * 0.4 | |
| # Calculate total condition score | |
| condition_score = primary_score + secondary_score + risk_factor_score | |
| # If above threshold, add to detected conditions | |
| if condition_score >= pattern_data["confidence_threshold"]: | |
| analysis_results["conditions_detected"].append({ | |
| "condition": condition, | |
| "category": category, | |
| "confidence": min(0.95, condition_score), | |
| "urgency_weight": pattern_data["urgency_weight"], | |
| "indicators_found": indicators_found | |
| }) | |
| # Update overall urgency score | |
| weighted_urgency = condition_score * pattern_data["urgency_weight"] | |
| analysis_results["urgency_score"] = max(analysis_results["urgency_score"], weighted_urgency) | |
| # Apply Bayesian inference for differential diagnosis | |
| if analysis_results["conditions_detected"]: | |
| analysis_results["differential_diagnosis"] = self._generate_differential_diagnosis( | |
| analysis_results["conditions_detected"], context | |
| ) | |
| # Generate recommendations based on findings | |
| analysis_results["recommended_actions"] = self._generate_clinical_recommendations( | |
| analysis_results["conditions_detected"], analysis_results["urgency_score"] | |
| ) | |
| # Calculate overall confidence | |
| if analysis_results["conditions_detected"]: | |
| analysis_results["confidence"] = np.mean([ | |
| cond["confidence"] for cond in analysis_results["conditions_detected"] | |
| ]) | |
| return analysis_results | |
| def _generate_differential_diagnosis(self, conditions: List[Dict], context: Dict) -> List[Dict]: | |
| """Generate differential diagnosis using Bayesian reasoning""" | |
| differential = [] | |
| for condition in conditions: | |
| # Apply Bayesian inference | |
| prior_prob = 0.1 # Base probability | |
| likelihood_ratio = 1.0 | |
| # Adjust based on risk factors | |
| if context.get("demographics", {}).get("age", 0) > 50: | |
| likelihood_ratio *= 1.5 | |
| if "diabetes" in context.get("medical_history", []): | |
| likelihood_ratio *= 2.0 | |
| # Calculate posterior probability | |
| posterior_odds = (prior_prob / (1 - prior_prob)) * likelihood_ratio | |
| posterior_prob = posterior_odds / (1 + posterior_odds) | |
| differential.append({ | |
| "condition": condition["condition"], | |
| "probability": min(0.95, posterior_prob * condition["confidence"]), | |
| "evidence_strength": "strong" if condition["confidence"] > 0.8 else "moderate" | |
| }) | |
| # Sort by probability | |
| differential.sort(key=lambda x: x["probability"], reverse=True) | |
| return differential[:5] # Top 5 differential diagnoses | |
| def _generate_clinical_recommendations(self, conditions: List[Dict], urgency_score: float) -> List[str]: | |
| """Generate evidence-based clinical recommendations""" | |
| recommendations = [] | |
| if urgency_score > 0.8: # High urgency | |
| recommendations.extend([ | |
| "Evaluación médica inmediata requerida", | |
| "Considerar activación de protocolo de emergencia", | |
| "Monitoreo continuo de signos vitales", | |
| "Acceso venoso y preparación para intervención" | |
| ]) | |
| elif urgency_score > 0.5: # Moderate urgency | |
| recommendations.extend([ | |
| "Evaluación médica urgente dentro de 2-4 horas", | |
| "Estudios diagnósticos inmediatos", | |
| "Observación clínica estrecha" | |
| ]) | |
| else: # Routine | |
| recommendations.extend([ | |
| "Evaluación médica programada", | |
| "Seguimiento clínico apropiado", | |
| "Educación al paciente sobre signos de alarma" | |
| ]) | |
| # Add condition-specific recommendations | |
| for condition in conditions: | |
| if condition["condition"] == "acute_coronary_syndrome": | |
| recommendations.extend([ | |
| "ECG de 12 derivaciones inmediato", | |
| "Troponinas seriadas", | |
| "Aspirina 300mg si no contraindicada", | |
| "Evaluación cardiológica urgente" | |
| ]) | |
| elif condition["condition"] == "stroke": | |
| recommendations.extend([ | |
| "TC craneal urgente", | |
| "Evaluación neurológica (NIHSS)", | |
| "Considerar trombolisis si indicada", | |
| "Neuroprotección" | |
| ]) | |
| return list(set(recommendations)) # Remove duplicates | |
| class EnhancedMedeXAIEngine: | |
| """Enhanced MedeX AI Engine with hybrid capabilities""" | |
| def __init__(self, kimi_api_key: Optional[str] = None): | |
| self.kimi_client = None | |
| self.kimi_available = False | |
| # Initialize Kimi if API key provided | |
| if kimi_api_key: | |
| self.kimi_client = RealKimiClient(kimi_api_key) | |
| # Test availability will be done on first use | |
| # Initialize advanced local components | |
| self.reasoning_engine = AdvancedMedicalReasoningEngine() | |
| self.embedding_model = SentenceTransformer('all-MiniLM-L6-v2') | |
| # Load enhanced medical knowledge | |
| self.medical_knowledge = self._load_enhanced_medical_knowledge() | |
| # Session tracking | |
| self.session_history = [] | |
| self.performance_metrics = { | |
| "kimi_requests": 0, | |
| "local_requests": 0, | |
| "hybrid_requests": 0, | |
| "average_confidence": 0.0, | |
| "average_response_time": 0.0 | |
| } | |
| def _load_enhanced_medical_knowledge(self) -> Dict[str, Any]: | |
| """Load comprehensive medical knowledge base""" | |
| return { | |
| "conditions": { | |
| "acute_coronary_syndrome": { | |
| "name": "Síndrome Coronario Agudo", | |
| "icd10": "I20-I25", | |
| "category": "Cardiovascular", | |
| "urgency": "critical", | |
| "description": "Espectro de condiciones causadas por isquemia miocárdica aguda, incluyendo angina inestable, NSTEMI y STEMI", | |
| "pathophysiology": "Ruptura o erosión de placa aterosclerótica con trombosis coronaria resultante", | |
| "clinical_presentation": { | |
| "typical": [ | |
| "Dolor torácico subesternal opresivo", | |
| "Duración > 20 minutos", | |
| "Irradiación a brazo izquierdo, cuello, mandíbula", | |
| "Asociado a diaforesis, náuseas, disnea" | |
| ], | |
| "atypical": [ | |
| "Dolor epigástrico", | |
| "Disnea aislada", | |
| "Síncope", | |
| "Presentación silente (especialmente diabéticos)" | |
| ] | |
| }, | |
| "risk_factors": { | |
| "non_modifiable": ["edad", "género masculino", "antecedentes familiares"], | |
| "modifiable": ["diabetes", "hipertensión", "dislipidemia", "tabaquismo", "obesidad"] | |
| }, | |
| "diagnostic_criteria": { | |
| "stemi": [ | |
| "Elevación ST ≥ 1mm en ≥2 derivaciones contiguas", | |
| "Nuevo bloqueo rama izquierda", | |
| "Ondas Q patológicas de nueva aparición" | |
| ], | |
| "nstemi": [ | |
| "Elevación troponinas sin elevación ST", | |
| "Depresión ST o inversión T", | |
| "Síntomas compatibles" | |
| ] | |
| }, | |
| "professional_management": { | |
| "immediate_actions": [ | |
| "Evaluación primaria ABCDE", | |
| "ECG < 10 minutos", | |
| "Acceso venoso bilateral", | |
| "Oxígeno si SatO2 < 90%", | |
| "Monitoreo cardíaco continuo" | |
| ], | |
| "pharmacotherapy": { | |
| "antiplatelet": { | |
| "aspirin": "300mg VO dosis carga, luego 75-100mg/día", | |
| "clopidogrel": "600mg VO dosis carga, luego 75mg/día", | |
| "ticagrelor": "180mg VO dosis carga, luego 90mg BID" | |
| }, | |
| "anticoagulation": { | |
| "heparina": "60 UI/kg IV bolo, luego 12 UI/kg/h", | |
| "enoxaparina": "1mg/kg SC BID" | |
| }, | |
| "additional": { | |
| "atorvastatina": "80mg VO", | |
| "metoprolol": "25mg BID si no contraindicado" | |
| } | |
| }, | |
| "reperfusion": { | |
| "primary_pci": "< 90 minutos door-to-balloon", | |
| "thrombolysis": "< 30 minutos door-to-needle si PCI no disponible" | |
| } | |
| }, | |
| "patient_guidance": { | |
| "emergency_signs": [ | |
| "Dolor de pecho intenso > 5 minutos", | |
| "Dolor que se extiende al brazo, cuello o mandíbula", | |
| "Sudoración fría profusa", | |
| "Náuseas y vómitos con dolor pecho", | |
| "Sensación de muerte inminente" | |
| ], | |
| "immediate_actions": [ | |
| "Llamar al 911 inmediatamente", | |
| "Tomar aspirina 300mg si disponible", | |
| "Sentarse o recostarse", | |
| "No conducir al hospital", | |
| "Aflojar ropa ajustada" | |
| ] | |
| }, | |
| "prognosis": { | |
| "stemi": "Mortalidad hospitalaria 4-6%, mejor pronóstico con reperfusión oportuna", | |
| "nstemi": "Mortalidad hospitalaria 2-4%, pronóstico depende de estratificación de riesgo" | |
| }, | |
| "evidence_level": "A", | |
| "guidelines": ["ESC 2020", "AHA/ACC 2021", "STEMI Task Force 2017"] | |
| }, | |
| "diabetes_mellitus_type_2": { | |
| "name": "Diabetes Mellitus Tipo 2", | |
| "icd10": "E11", | |
| "category": "Endocrinología", | |
| "urgency": "routine", | |
| "description": "Trastorno metabólico crónico caracterizado por hiperglucemia debido a resistencia insulínica y deficiencia relativa de insulina", | |
| "pathophysiology": "Combinación de resistencia periférica a la insulina y disfunción de células beta pancreáticas", | |
| "clinical_presentation": { | |
| "classic_triad": ["poliuria", "polidipsia", "polifagia"], | |
| "additional_symptoms": [ | |
| "pérdida de peso inexplicada", | |
| "fatiga crónica", | |
| "visión borrosa", | |
| "cicatrización lenta", | |
| "infecciones recurrentes" | |
| ], | |
| "asymptomatic": "Hasta 50% de casos pueden ser asintomáticos al diagnóstico" | |
| }, | |
| "diagnostic_criteria": { | |
| "hba1c": "≥ 6.5% (48 mmol/mol)", | |
| "fasting_glucose": "≥ 126 mg/dL (7.0 mmol/L)", | |
| "ogtt_2h": "≥ 200 mg/dL (11.1 mmol/L)", | |
| "random_glucose": "≥ 200 mg/dL con síntomas clásicos" | |
| }, | |
| "complications": { | |
| "microvascular": [ | |
| "Retinopatía diabética", | |
| "Nefropatía diabética", | |
| "Neuropatía diabética" | |
| ], | |
| "macrovascular": [ | |
| "Enfermedad coronaria", | |
| "Enfermedad cerebrovascular", | |
| "Enfermedad arterial periférica" | |
| ], | |
| "acute": [ | |
| "Cetoacidosis diabética (rara en T2DM)", | |
| "Estado hiperosmolar hiperglucémico", | |
| "Hipoglucemia (iatrogénica)" | |
| ] | |
| }, | |
| "professional_management": { | |
| "lifestyle_intervention": [ | |
| "Reducción peso 5-10% si sobrepeso", | |
| "Actividad física 150 min/semana moderada", | |
| "Dieta mediterránea o baja en carbohidratos", | |
| "Cesación tabáquica" | |
| ], | |
| "pharmacotherapy": { | |
| "first_line": { | |
| "metformin": "500-2000mg/día dividido en 2-3 tomas" | |
| }, | |
| "second_line": { | |
| "sulfonylureas": "glimepiride 1-4mg/día", | |
| "dpp4_inhibitors": "sitagliptin 100mg/día", | |
| "sglt2_inhibitors": "empagliflozin 10-25mg/día", | |
| "glp1_agonists": "liraglutide 0.6-1.8mg SC/día" | |
| }, | |
| "insulin": "Si HbA1c > 9% o síntomas severos" | |
| }, | |
| "targets": { | |
| "hba1c": "< 7% (< 53 mmol/mol) en mayoría", | |
| "blood_pressure": "< 130/80 mmHg", | |
| "ldl_cholesterol": "< 100 mg/dL (< 70 si alto riesgo)" | |
| }, | |
| "monitoring": { | |
| "hba1c": "Cada 3-6 meses", | |
| "lipid_profile": "Anualmente", | |
| "renal_function": "Anualmente", | |
| "eye_exam": "Anualmente", | |
| "foot_exam": "Cada visita" | |
| } | |
| }, | |
| "patient_guidance": { | |
| "daily_management": [ | |
| "Monitoreo glucémico según indicación médica", | |
| "Tomar medicación según prescrito", | |
| "Mantener horarios regulares de comida", | |
| "Inspección diaria de pies", | |
| "Hidratación adecuada" | |
| ], | |
| "warning_signs": [ | |
| "Glucosa > 300 mg/dL persistente", | |
| "Síntomas de cetoacidosis", | |
| "Hipoglucemia severa", | |
| "Úlceras en pies que no cicatrizan", | |
| "Cambios visuales súbitos" | |
| ] | |
| }, | |
| "prognosis": "Con manejo adecuado, expectativa de vida normal. Complicaciones evitables con control glucémico estricto", | |
| "evidence_level": "A", | |
| "guidelines": ["ADA Standards 2023", "EASD/ESC 2019", "AACE 2022"] | |
| } | |
| }, | |
| "medications": { | |
| "aspirin": { | |
| "name": "Aspirina", | |
| "generic": "Ácido acetilsalicílico", | |
| "drug_class": "Antiagregante plaquetario, AINE", | |
| "mechanism": "Inhibición irreversible de COX-1 plaquetaria, reduciendo síntesis de tromboxano A2", | |
| "pharmacokinetics": { | |
| "absorption": "Rápida, pico plasmático 1-2 horas", | |
| "metabolism": "Hepático a ácido salicílico", | |
| "elimination": "Renal, vida media 15-20 minutos", | |
| "effect_duration": "Duración antiagregante 7-10 días" | |
| }, | |
| "indications": { | |
| "cardiovascular": [ | |
| "Prevención primaria en riesgo alto (>10% a 10 años)", | |
| "Prevención secundaria post-infarto", | |
| "Síndrome coronario agudo", | |
| "Post-revascularización coronaria" | |
| ], | |
| "cerebrovascular": [ | |
| "Prevención secundaria post-ACV isquémico", | |
| "Ataque isquémico transitorio" | |
| ], | |
| "other": [ | |
| "Antiinflamatorio (dosis altas)", | |
| "Antipirético" | |
| ] | |
| }, | |
| "dosing": { | |
| "cardiovascular_prevention": "75-100mg/día", | |
| "acute_coronary_syndrome": "300mg dosis carga, luego 75-100mg/día", | |
| "stroke_prevention": "75-100mg/día", | |
| "anti_inflammatory": "500-1000mg cada 6 horas PRN" | |
| }, | |
| "contraindications": { | |
| "absolute": [ | |
| "Alergia a salicilatos", | |
| "Hemorragia activa", | |
| "Hemofilia u otros trastornos hemorrágicos", | |
| "Úlcera péptica activa" | |
| ], | |
| "relative": [ | |
| "Historia de sangrado GI", | |
| "Asma inducida por aspirina", | |
| "Insuficiencia renal severa", | |
| "Cirugía programada" | |
| ] | |
| }, | |
| "side_effects": { | |
| "common": [ | |
| "Dispepsia (10-40%)", | |
| "Náuseas", | |
| "Dolor abdominal" | |
| ], | |
| "serious": [ | |
| "Hemorragia gastrointestinal (0.3-0.5%/año)", | |
| "Hemorragia intracraneal (0.1-0.2%/año)", | |
| "Broncoespasmo (pacientes susceptibles)", | |
| "Síndrome de Reye (niños con infecciones virales)" | |
| ] | |
| }, | |
| "drug_interactions": { | |
| "major": [ | |
| "Warfarina: ↑↑ riesgo hemorrágico", | |
| "Metotrexato: ↑ toxicidad por ↓ eliminación", | |
| "IECA: ↓ efecto antihipertensivo" | |
| ], | |
| "moderate": [ | |
| "Diuréticos: ↓ efecto diurético", | |
| "Corticosteroides: ↑ riesgo úlcera GI", | |
| "Alcohol: ↑ riesgo sangrado GI" | |
| ] | |
| }, | |
| "monitoring": [ | |
| "Síntomas de sangrado GI", | |
| "Función renal (uso crónico)", | |
| "Signos de toxicidad salicílica (dosis altas)" | |
| ], | |
| "patient_counseling": { | |
| "administration": [ | |
| "Tomar con alimentos para ↓ irritación GI", | |
| "No masticar tabletas entéricas", | |
| "Mantener hidratación adecuada" | |
| ], | |
| "monitoring": [ | |
| "Reportar sangrado inusual", | |
| "Dolor abdominal persistente", | |
| "Heces negras o con sangre", | |
| "Mareos o confusión" | |
| ] | |
| }, | |
| "evidence_level": "A", | |
| "guidelines": ["ESC 2019", "AHA/ACC 2019", "USPSTF 2022"] | |
| } | |
| } | |
| } | |
| async def process_enhanced_medical_query(self, | |
| text: str, | |
| image_data: Optional[bytes] = None) -> EnhancedMedicalResponse: | |
| """Process medical query with enhanced AI capabilities""" | |
| start_time = datetime.now() | |
| # Enhanced context analysis | |
| enhanced_query = await self._analyze_enhanced_context(text, bool(image_data)) | |
| # Try Kimi first if available | |
| ai_source = "local_advanced" | |
| response_text = "" | |
| if self.kimi_client and await self._test_kimi_availability(): | |
| try: | |
| response_text = await self._process_with_kimi(enhanced_query, image_data) | |
| ai_source = "kimi_real" | |
| self.performance_metrics["kimi_requests"] += 1 | |
| except Exception as e: | |
| print(f"⚠️ Kimi unavailable, using advanced local: {e}") | |
| response_text = await self._process_with_advanced_local(enhanced_query) | |
| ai_source = "local_advanced" | |
| self.performance_metrics["local_requests"] += 1 | |
| else: | |
| # Use advanced local processing | |
| response_text = await self._process_with_advanced_local(enhanced_query) | |
| self.performance_metrics["local_requests"] += 1 | |
| # Get medical knowledge sources | |
| medical_sources = self._search_enhanced_knowledge(text) | |
| # Generate comprehensive recommendations | |
| recommendations = self._generate_enhanced_recommendations(enhanced_query, medical_sources) | |
| # Generate warnings and follow-up | |
| warnings = self._generate_enhanced_warnings(enhanced_query) | |
| follow_up = self._generate_enhanced_followup(enhanced_query) | |
| # Calculate processing time | |
| processing_time = (datetime.now() - start_time).total_seconds() | |
| # Create enhanced response | |
| response = EnhancedMedicalResponse( | |
| response_id=str(uuid.uuid4()), | |
| query_id=enhanced_query.query_id, | |
| user_type=enhanced_query.user_type, | |
| response_text=response_text, | |
| confidence=enhanced_query.confidence, | |
| medical_sources=medical_sources, | |
| recommendations=recommendations, | |
| warnings=warnings, | |
| follow_up=follow_up, | |
| emergency_level=enhanced_query.urgency_level, | |
| ai_source=ai_source, | |
| processing_time=processing_time, | |
| timestamp=datetime.now() | |
| ) | |
| # Update performance metrics | |
| self._update_performance_metrics(response) | |
| # Store in history | |
| self.session_history.append((enhanced_query, response)) | |
| return response | |
| async def _analyze_enhanced_context(self, text: str, has_image: bool) -> EnhancedMedicalQuery: | |
| """Enhanced context analysis with medical reasoning""" | |
| query_id = str(uuid.uuid4()) | |
| text_lower = text.lower() | |
| # Advanced user type detection | |
| professional_score = self._calculate_professional_score(text) | |
| patient_score = self._calculate_patient_score(text) | |
| user_type = "professional" if professional_score > patient_score else "patient" | |
| confidence = max(professional_score, patient_score) | |
| # Enhanced query type detection | |
| query_type = self._detect_enhanced_query_type(text, has_image) | |
| # Advanced urgency detection | |
| urgency_level = self._detect_enhanced_urgency(text) | |
| # Medical specialty detection | |
| medical_specialty = self._detect_medical_specialty(text) | |
| # Comprehensive context extraction | |
| context = self._extract_comprehensive_context(text) | |
| # Apply medical reasoning | |
| medical_analysis = self.reasoning_engine.analyze_medical_condition(text, context) | |
| context["medical_analysis"] = medical_analysis | |
| # Adjust urgency based on medical analysis | |
| if medical_analysis["urgency_score"] > 0.8: | |
| urgency_level = "emergency" | |
| elif medical_analysis["urgency_score"] > 0.5: | |
| urgency_level = "urgent" | |
| return EnhancedMedicalQuery( | |
| query_id=query_id, | |
| original_text=text, | |
| user_type=user_type, | |
| query_type=query_type, | |
| urgency_level=urgency_level, | |
| context=context, | |
| confidence=confidence, | |
| medical_specialty=medical_specialty, | |
| timestamp=datetime.now() | |
| ) | |
| def _calculate_professional_score(self, text: str) -> float: | |
| """Calculate professional medical language score""" | |
| professional_indicators = [ | |
| "paciente", "dx", "diagnóstico diferencial", "protocolo", "manejo", | |
| "tratamiento de elección", "indicaciones", "contraindicaciones", | |
| "dosis", "posología", "seguimiento", "derivación", "interconsulta", | |
| "historia clínica", "examen físico", "estudios complementarios", | |
| "pronóstico", "epidemiología", "fisiopatología", "farmacoterapia", | |
| "biomarkers", "troponinas", "electrocardiograma", "ecocardiograma" | |
| ] | |
| text_lower = text.lower() | |
| matches = sum(2 if indicator in text_lower else 0 for indicator in professional_indicators) | |
| # Weight by text length and complexity | |
| length_factor = min(1.5, len(text.split()) / 10) | |
| complexity_factor = 1.2 if any(term in text_lower for term in ["fisiopatología", "farmacoterapia"]) else 1.0 | |
| score = (matches / len(professional_indicators)) * length_factor * complexity_factor | |
| return min(0.95, score) | |
| def _calculate_patient_score(self, text: str) -> float: | |
| """Calculate patient language score""" | |
| patient_indicators = [ | |
| "me duele", "tengo", "siento", "estoy", "mi", "me preocupa", | |
| "qué debo hacer", "es normal", "debo ir al doctor", "es grave", | |
| "me pasa", "me molesta", "mi familia", "mi hijo", "mi esposa", | |
| "desde hace", "me siento", "estoy asustado", "no sé qué hacer" | |
| ] | |
| text_lower = text.lower() | |
| matches = sum(1 if indicator in text_lower else 0 for indicator in patient_indicators) | |
| # Personal pronouns increase patient score | |
| personal_pronouns = ["me", "mi", "yo", "estoy", "tengo", "siento"] | |
| pronoun_count = sum(text_lower.count(pronoun) for pronoun in personal_pronouns) | |
| base_score = matches / len(patient_indicators) | |
| pronoun_bonus = min(0.3, pronoun_count * 0.05) | |
| return min(0.9, base_score + pronoun_bonus) | |
| def _detect_enhanced_query_type(self, text: str, has_image: bool) -> str: | |
| """Enhanced query type detection""" | |
| text_lower = text.lower() | |
| if has_image: | |
| return "image_analysis" | |
| # Medical education queries | |
| education_patterns = ["qué es", "explicar", "información sobre", "cómo funciona"] | |
| if any(pattern in text_lower for pattern in education_patterns): | |
| return "medical_education" | |
| # Medication queries | |
| medication_patterns = ["dosis", "medicamento", "fármaco", "efectos secundarios", "interacciones"] | |
| if any(pattern in text_lower for pattern in medication_patterns): | |
| return "medication_inquiry" | |
| # Diagnostic queries | |
| diagnostic_patterns = ["diagnóstico", "síntomas", "dolor", "molestia", "presenta"] | |
| if any(pattern in text_lower for pattern in diagnostic_patterns): | |
| return "diagnostic_consultation" | |
| # Protocol queries | |
| protocol_patterns = ["protocolo", "manejo", "tratamiento", "guía", "algoritmo"] | |
| if any(pattern in text_lower for pattern in protocol_patterns): | |
| return "clinical_protocol" | |
| # Lab interpretation | |
| lab_patterns = ["análisis", "laboratorio", "resultado", "valores", "examen"] | |
| if any(pattern in text_lower for pattern in lab_patterns): | |
| return "lab_interpretation" | |
| return "general_consultation" | |
| def _detect_enhanced_urgency(self, text: str) -> str: | |
| """Enhanced urgency detection with medical reasoning""" | |
| text_lower = text.lower() | |
| # Critical emergencies | |
| critical_patterns = [ | |
| "dolor torácico", "dolor precordial", "opresión torácica", | |
| "dificultad respiratoria severa", "disnea súbita", | |
| "pérdida de conciencia", "convulsiones", "estado confusional agudo", | |
| "sangrado masivo", "hematemesis", "melena", | |
| "dolor abdominal severo", "abdomen agudo" | |
| ] | |
| for pattern in critical_patterns: | |
| if pattern in text_lower: | |
| return "emergency" | |
| # Urgent conditions | |
| urgent_patterns = [ | |
| "fiebre alta", "temperatura > 39", "dolor intenso", | |
| "vómitos persistentes", "deshidratación", | |
| "cambios neurológicos", "alteración conciencia" | |
| ] | |
| for pattern in urgent_patterns: | |
| if pattern in text_lower: | |
| return "urgent" | |
| # Time-based urgency | |
| time_patterns = ["súbito", "repentino", "desde hace minutos", "desde hace horas"] | |
| if any(pattern in text_lower for pattern in time_patterns): | |
| return "urgent" | |
| return "routine" | |
| def _detect_medical_specialty(self, text: str) -> Optional[str]: | |
| """Detect relevant medical specialty""" | |
| text_lower = text.lower() | |
| specialty_keywords = { | |
| "cardiology": ["corazón", "cardíaco", "precordial", "angina", "infarto", "arritmia"], | |
| "neurology": ["cerebro", "neurológico", "convulsión", "parálisis", "cefalea", "mareos"], | |
| "pulmonology": ["pulmón", "respiratorio", "tos", "disnea", "asma", "neumonía"], | |
| "gastroenterology": ["estómago", "digestivo", "náusea", "diarrea", "hígado", "abdomen"], | |
| "endocrinology": ["diabetes", "tiroides", "hormona", "glucosa", "metabolismo"], | |
| "emergency": ["emergencia", "urgencia", "trauma", "accidente", "lesión"] | |
| } | |
| for specialty, keywords in specialty_keywords.items(): | |
| if any(keyword in text_lower for keyword in keywords): | |
| return specialty | |
| return None | |
| def _extract_comprehensive_context(self, text: str) -> Dict[str, Any]: | |
| """Extract comprehensive medical context""" | |
| context = { | |
| "demographics": {}, | |
| "symptoms": [], | |
| "medical_history": [], | |
| "medications": [], | |
| "allergies": [], | |
| "time_course": {}, | |
| "severity_indicators": [], | |
| "risk_factors": [], | |
| "social_history": {} | |
| } | |
| # Demographics | |
| age_match = re.search(r'(\d+)\s*años?', text, re.IGNORECASE) | |
| if age_match: | |
| context["demographics"]["age"] = int(age_match.group(1)) | |
| gender_patterns = { | |
| "masculino": r'\b(masculino|hombre|varón|macho|paciente.*él)\b', | |
| "femenino": r'\b(femenino|mujer|paciente.*ella)\b' | |
| } | |
| for gender, pattern in gender_patterns.items(): | |
| if re.search(pattern, text, re.IGNORECASE): | |
| context["demographics"]["gender"] = gender | |
| break | |
| # Enhanced symptom extraction | |
| symptom_patterns = [ | |
| r'dolor\s+(?:en\s+)?(?:el\s+|la\s+)?(\w+)', | |
| r'(\w+)\s+dolor', | |
| r'molestia\s+(?:en\s+)?(\w+)', | |
| r'síntomas?\s+de\s+(\w+)', | |
| r'presenta\s+(\w+)', | |
| r'sensación\s+de\s+(\w+)', | |
| r'dificultad\s+para\s+(\w+)' | |
| ] | |
| for pattern in symptom_patterns: | |
| matches = re.findall(pattern, text, re.IGNORECASE) | |
| context["symptoms"].extend(matches) | |
| # Time course extraction | |
| time_patterns = { | |
| "duration": r'(?:desde\s+hace|hace|durante)\s+(\d+)\s*(minutos?|horas?|días?|semanas?|meses?|años?)', | |
| "onset": r'\b(súbito|gradual|progresivo|agudo|crónico|insidioso)\b', | |
| "frequency": r'(\d+)\s*veces?\s*(?:por|al)\s*(día|semana|mes)', | |
| "pattern": r'\b(continuo|intermitente|episódico|nocturno|matutino)\b' | |
| } | |
| for key, pattern in time_patterns.items(): | |
| matches = re.findall(pattern, text, re.IGNORECASE) | |
| if matches: | |
| context["time_course"][key] = matches | |
| # Medical history | |
| history_patterns = [ | |
| r'diabético|diabetes', | |
| r'hipertenso|hipertensión', | |
| r'cardíaco|cardiaco|coronario', | |
| r'asmático|asma', | |
| r'alérgico|alergia', | |
| r'cirugía|operado|intervenido', | |
| r'cáncer|tumor|neoplasia', | |
| r'renal|riñón', | |
| r'hepático|hígado' | |
| ] | |
| for pattern in history_patterns: | |
| if re.search(pattern, text, re.IGNORECASE): | |
| condition = pattern.split('|')[0] | |
| context["medical_history"].append(condition) | |
| # Severity indicators | |
| severity_patterns = [ | |
| r'\b(intenso|severo|fuerte|insoportable)\b', | |
| r'\b(leve|ligero|moderado)\b', | |
| r'\b(empeora|mejora|estable)\b', | |
| r'escala.*(\d+).*10' | |
| ] | |
| for pattern in severity_patterns: | |
| matches = re.findall(pattern, text, re.IGNORECASE) | |
| context["severity_indicators"].extend(matches) | |
| return context | |
| async def _test_kimi_availability(self) -> bool: | |
| """Test if Kimi is available""" | |
| if not hasattr(self, '_kimi_last_test'): | |
| self._kimi_last_test = datetime.min | |
| # Test every 5 minutes | |
| if (datetime.now() - self._kimi_last_test).seconds < 300: | |
| return self.kimi_available | |
| try: | |
| async with self.kimi_client as kimi: | |
| self.kimi_available = await kimi.test_connection() | |
| self._kimi_last_test = datetime.now() | |
| return self.kimi_available | |
| except: | |
| self.kimi_available = False | |
| return False | |
| async def _process_with_kimi(self, query: EnhancedMedicalQuery, image_data: Optional[bytes] = None) -> str: | |
| """Process query with real Kimi API""" | |
| # Prepare system prompt based on user type | |
| if query.user_type == "professional": | |
| system_prompt = f"""Eres un sistema de IA médica avanzada para profesionales de la salud. | |
| PROTOCOLO DE RESPUESTA PROFESIONAL: | |
| - Proporciona análisis clínico detallado con evidencia científica | |
| - Incluye diagnósticos diferenciales con probabilidades estimadas | |
| - Especifica protocolos de manejo y dosis farmacológicas exactas | |
| - Cita guías clínicas y evidencia actual cuando sea relevante | |
| - Indica criterios de derivación y seguimiento | |
| URGENCIA: {query.urgency_level.upper()} | |
| ESPECIALIDAD: {query.medical_specialty or 'General'} | |
| CONTEXTO MÉDICO: | |
| {json.dumps(query.context, indent=2)} | |
| Responde con precisión técnica apropiada para profesionales médicos.""" | |
| else: | |
| system_prompt = f"""Eres un sistema de IA médica para educar y orientar a pacientes. | |
| PROTOCOLO DE RESPUESTA PARA PACIENTES: | |
| - Usa lenguaje claro y comprensible | |
| - Proporciona información educativa sin alarmar | |
| - Enfatiza cuándo es importante buscar atención médica | |
| - Incluye medidas de autocuidado apropiadas | |
| - Mantén un tono empático y tranquilizador | |
| URGENCIA: {query.urgency_level.upper()} | |
| CONTEXTO DEL PACIENTE: | |
| {json.dumps(query.context, indent=2)} | |
| Responde de manera comprensible y útil para el paciente.""" | |
| # Add emergency protocol if needed | |
| if query.urgency_level == "emergency": | |
| system_prompt += "\n\n🚨 PROTOCOLO DE EMERGENCIA ACTIVADO 🚨\nProporciona instrucciones claras de acción inmediata." | |
| async with self.kimi_client as kimi: | |
| if image_data: | |
| response = await kimi.analyze_medical_image( | |
| image_data, | |
| query.original_text, | |
| query.user_type | |
| ) | |
| else: | |
| response = await kimi.generate_medical_consultation( | |
| query.original_text, | |
| query.context, | |
| query.user_type, | |
| query.urgency_level | |
| ) | |
| return response | |
| async def _process_with_advanced_local(self, query: EnhancedMedicalQuery) -> str: | |
| """Process with advanced local AI capabilities""" | |
| response_parts = [] | |
| # Emergency protocol activation | |
| if query.urgency_level == "emergency": | |
| response_parts.append(self._generate_emergency_protocol(query)) | |
| # Medical analysis from reasoning engine | |
| medical_analysis = query.context.get("medical_analysis", {}) | |
| if medical_analysis.get("conditions_detected"): | |
| response_parts.append(self._format_medical_analysis(medical_analysis, query.user_type)) | |
| # Knowledge-based response | |
| knowledge_response = self._generate_knowledge_based_response(query) | |
| if knowledge_response: | |
| response_parts.append(knowledge_response) | |
| # Specialty-specific guidance | |
| if query.medical_specialty: | |
| specialty_guidance = self._generate_specialty_guidance(query) | |
| if specialty_guidance: | |
| response_parts.append(specialty_guidance) | |
| # Default comprehensive response if no specific content | |
| if not response_parts: | |
| response_parts.append(self._generate_default_comprehensive_response(query)) | |
| return "\n\n".join(response_parts) | |
| def _generate_emergency_protocol(self, query: EnhancedMedicalQuery) -> str: | |
| """Generate emergency protocol response""" | |
| if query.user_type == "professional": | |
| return """🚨 PROTOCOLO DE EMERGENCIA MÉDICA ACTIVADO | |
| 📋 EVALUACIÓN PRIMARIA: | |
| • Evaluación ABCDE inmediata | |
| • Signos vitales completos | |
| • Acceso venoso y oxigenoterapia | |
| • Monitoreo cardíaco continuo | |
| • Preparación para soporte vital avanzado | |
| ⏱️ CRONOMETRAJE CRÍTICO: | |
| • ECG < 10 minutos | |
| • Laboratorio urgente < 15 minutos | |
| • Evaluación especialista < 30 minutos | |
| 🚨 CRITERIOS DE ACTIVACIÓN DE CÓDIGO: | |
| • Evaluar necesidad de código azul/rojo | |
| • Notificar a equipo de respuesta rápida | |
| • Documentación continua de intervenciones""" | |
| else: | |
| return """🚨 EMERGENCIA MÉDICA DETECTADA | |
| ⚠️ ACCIÓN INMEDIATA REQUERIDA: | |
| • 📞 Llame al 911 AHORA MISMO | |
| • 🚗 NO conduzca usted mismo al hospital | |
| • 🏠 Manténgase en lugar seguro y cómodo | |
| • 👥 Avise a alguien de confianza | |
| • 📱 Mantenga el teléfono cerca | |
| 💊 SI TIENE EN CASA: | |
| • Aspirina: tome 300mg si no es alérgico | |
| • Nitroglicerina: use según prescripción previa | |
| ⏰ EL TIEMPO ES CRÍTICO EN EMERGENCIAS MÉDICAS | |
| 🏥 Un profesional médico debe evaluarlo INMEDIATAMENTE""" | |
| def _format_medical_analysis(self, analysis: Dict[str, Any], user_type: str) -> str: | |
| """Format medical analysis results""" | |
| if user_type == "professional": | |
| output = ["🧠 ANÁLISIS MÉDICO AVANZADO:"] | |
| if analysis.get("conditions_detected"): | |
| output.append("\n📊 CONDICIONES IDENTIFICADAS:") | |
| for condition in analysis["conditions_detected"]: | |
| output.append(f"• {condition['condition']}: {condition['confidence']:.1%} confianza") | |
| output.append(f" Categoría: {condition['category']}") | |
| output.append(f" Peso de urgencia: {condition['urgency_weight']:.1%}") | |
| if analysis.get("differential_diagnosis"): | |
| output.append("\n🎯 DIAGNÓSTICO DIFERENCIAL:") | |
| for dx in analysis["differential_diagnosis"]: | |
| output.append(f"• {dx['condition']}: {dx['probability']:.1%} probabilidad") | |
| output.append(f" Fuerza de evidencia: {dx['evidence_strength']}") | |
| if analysis.get("recommended_actions"): | |
| output.append("\n💡 ACCIONES RECOMENDADAS:") | |
| for action in analysis["recommended_actions"]: | |
| output.append(f"• {action}") | |
| return "\n".join(output) | |
| else: | |
| # Simplified for patients | |
| if analysis.get("urgency_score", 0) > 0.7: | |
| return """🚨 ANÁLISIS MÉDICO: | |
| Se han detectado síntomas que requieren atención médica inmediata. | |
| Por favor, busque ayuda médica profesional sin demora.""" | |
| else: | |
| return """📋 ANÁLISIS MÉDICO: | |
| Sus síntomas han sido analizados. Se recomienda consulta médica para evaluación completa.""" | |
| def _generate_knowledge_based_response(self, query: EnhancedMedicalQuery) -> str: | |
| """Generate response based on medical knowledge""" | |
| relevant_knowledge = self._search_enhanced_knowledge(query.original_text) | |
| if not relevant_knowledge: | |
| return "" | |
| response_parts = ["📚 INFORMACIÓN MÉDICA RELEVANTE:"] | |
| for item in relevant_knowledge[:2]: # Top 2 most relevant | |
| if item["type"] == "condition": | |
| response_parts.append(f"\n🏥 {item['name']} ({item.get('icd10', 'N/A')})") | |
| if query.user_type == "professional": | |
| response_parts.append(f"📖 {item.get('description', '')}") | |
| if item.get('professional_management'): | |
| mgmt = item['professional_management'] | |
| if 'immediate_actions' in mgmt: | |
| response_parts.append("⚡ Acciones inmediatas:") | |
| for action in mgmt['immediate_actions'][:3]: | |
| response_parts.append(f" • {action}") | |
| else: | |
| # Patient-friendly explanation | |
| desc = item.get('description', '').replace('isquemia miocárdica', 'falta de oxígeno al corazón') | |
| response_parts.append(f"📖 {desc}") | |
| if item.get('patient_guidance'): | |
| guidance = item['patient_guidance'] | |
| if 'emergency_signs' in guidance: | |
| response_parts.append("⚠️ Signos de alarma:") | |
| for sign in guidance['emergency_signs'][:3]: | |
| response_parts.append(f" • {sign}") | |
| return "\n".join(response_parts) | |
| def _search_enhanced_knowledge(self, query: str) -> List[Dict[str, Any]]: | |
| """Enhanced medical knowledge search""" | |
| query_embedding = self.embedding_model.encode(query.lower()) | |
| results = [] | |
| # Search conditions | |
| for condition_key, condition in self.medical_knowledge["conditions"].items(): | |
| searchable_text = f"{condition['name']} {condition['description']}" | |
| if 'clinical_presentation' in condition: | |
| if 'typical' in condition['clinical_presentation']: | |
| searchable_text += " " + " ".join(condition['clinical_presentation']['typical']) | |
| condition_embedding = self.embedding_model.encode(searchable_text.lower()) | |
| similarity = np.dot(query_embedding, condition_embedding) / ( | |
| np.linalg.norm(query_embedding) * np.linalg.norm(condition_embedding) | |
| ) | |
| if similarity > 0.3: | |
| result = { | |
| "type": "condition", | |
| "similarity": similarity, | |
| **condition | |
| } | |
| results.append(result) | |
| # Search medications | |
| for med_key, medication in self.medical_knowledge["medications"].items(): | |
| searchable_text = f"{medication['name']} {medication['generic']}" | |
| if 'indications' in medication: | |
| searchable_text += " " + " ".join(medication['indications']['cardiovascular']) | |
| med_embedding = self.embedding_model.encode(searchable_text.lower()) | |
| similarity = np.dot(query_embedding, med_embedding) / ( | |
| np.linalg.norm(query_embedding) * np.linalg.norm(med_embedding) | |
| ) | |
| if similarity > 0.3: | |
| result = { | |
| "type": "medication", | |
| "similarity": similarity, | |
| **medication | |
| } | |
| results.append(result) | |
| results.sort(key=lambda x: x["similarity"], reverse=True) | |
| return results[:5] | |
| def _generate_specialty_guidance(self, query: EnhancedMedicalQuery) -> str: | |
| """Generate specialty-specific guidance""" | |
| specialty_guidance = { | |
| "cardiology": { | |
| "professional": """🫀 ORIENTACIÓN CARDIOLÓGICA: | |
| • Evaluación de factores de riesgo cardiovascular | |
| • Estratificación según scores validados (GRACE, TIMI) | |
| • Considerar biomarcadores cardíacos | |
| • Evaluación de función ventricular | |
| • Plan de revascularización si indicado""", | |
| "patient": """❤️ CUIDADO CARDÍACO: | |
| • Los problemas del corazón requieren atención especializada | |
| • Mantenga un registro de sus síntomas | |
| • Siga las indicaciones médicas estrictamente | |
| • Evite esfuerzos hasta evaluación completa | |
| • Busque ayuda inmediata si empeoran los síntomas""" | |
| }, | |
| "emergency": { | |
| "professional": """🚨 MEDICINA DE EMERGENCIA: | |
| • Aplicación de protocolos de triage | |
| • Estabilización hemodinámica prioritaria | |
| • Evaluación sistemática ABCDE | |
| • Coordinación con especialistas | |
| • Documentación continua de intervenciones""", | |
| "patient": """🆘 SITUACIÓN DE EMERGENCIA: | |
| • Su condición requiere atención médica inmediata | |
| • Siga todas las instrucciones del personal médico | |
| • Mantenga la calma y coopere con el tratamiento | |
| • Informe cualquier cambio en sus síntomas | |
| • Un ser querido debe estar informado de su situación""" | |
| } | |
| } | |
| if query.medical_specialty in specialty_guidance: | |
| return specialty_guidance[query.medical_specialty][query.user_type] | |
| return "" | |
| def _generate_default_comprehensive_response(self, query: EnhancedMedicalQuery) -> str: | |
| """Generate default comprehensive response""" | |
| if query.user_type == "professional": | |
| return f"""📋 EVALUACIÓN MÉDICA SISTEMÁTICA: | |
| 🎯 ANÁLISIS DE CONSULTA: | |
| • Tipo de consulta: {query.query_type.replace('_', ' ').title()} | |
| • Nivel de urgencia: {query.urgency_level.upper()} | |
| • Especialidad relevante: {query.medical_specialty or 'Medicina General'} | |
| 📊 CONTEXTO CLÍNICO IDENTIFICADO: | |
| {json.dumps(query.context, indent=2)} | |
| 💡 RECOMENDACIONES GENERALES: | |
| • Evaluación clínica integral con historia clínica completa | |
| • Examen físico dirigido según sintomatología | |
| • Estudios complementarios según indicación clínica | |
| • Seguimiento programado según evolución | |
| • Interconsulta especializada si corresponde | |
| ⚠️ CONSIDERACIONES: | |
| • Correlacionar siempre con hallazgos clínicos | |
| • Individualizar manejo según comorbilidades | |
| • Documentar adecuadamente en historia clínica""" | |
| else: | |
| return f"""🏥 ORIENTACIÓN MÉDICA GENERAL: | |
| 📋 SU CONSULTA: | |
| Hemos analizado su consulta médica y entendemos su preocupación. | |
| 💡 RECOMENDACIONES: | |
| • Consulte con su médico de cabecera para evaluación completa | |
| • Mantenga un registro detallado de sus síntomas | |
| • No se automedique sin supervisión médica | |
| • Siga las indicaciones médicas que reciba | |
| • Busque atención médica si los síntomas empeoran | |
| ⚠️ CUÁNDO BUSCAR AYUDA INMEDIATA: | |
| • Si los síntomas empeoran rápidamente | |
| • Si aparecen nuevos síntomas preocupantes | |
| • Si tiene dudas sobre su condición | |
| • Si siente que algo no está bien | |
| 🏥 RECUERDE: | |
| • Esta información es educativa únicamente | |
| • Un médico debe evaluarlo personalmente | |
| • Cada caso es único y requiere atención individualizada""" | |
| def _generate_enhanced_recommendations(self, query: EnhancedMedicalQuery, | |
| medical_sources: List[Dict[str, Any]]) -> List[str]: | |
| """Generate enhanced recommendations""" | |
| recommendations = [] | |
| # Urgency-based recommendations | |
| if query.urgency_level == "emergency": | |
| if query.user_type == "professional": | |
| recommendations.extend([ | |
| "Activación inmediata de protocolo de emergencia", | |
| "Evaluación ABCDE y estabilización hemodinámica", | |
| "Monitoreo continuo de signos vitales", | |
| "Preparación para soporte vital avanzado", | |
| "Coordinación con equipo multidisciplinario" | |
| ]) | |
| else: | |
| recommendations.extend([ | |
| "Llamar al 911 inmediatamente", | |
| "No demorar la búsqueda de atención médica", | |
| "Seguir instrucciones del personal de emergencias", | |
| "Mantener la calma y cooperar con el tratamiento" | |
| ]) | |
| elif query.urgency_level == "urgent": | |
| recommendations.extend([ | |
| "Evaluación médica dentro de 2-4 horas", | |
| "Estudios diagnósticos prioritarios", | |
| "Observación clínica estrecha", | |
| "Reevaluación según evolución" | |
| ]) | |
| else: # routine | |
| recommendations.extend([ | |
| "Consulta médica programada", | |
| "Seguimiento clínico apropiado", | |
| "Educación sobre signos de alarma" | |
| ]) | |
| # Specialty-specific recommendations | |
| if query.medical_specialty == "cardiology": | |
| recommendations.extend([ | |
| "ECG de 12 derivaciones", | |
| "Evaluación de factores de riesgo cardiovascular", | |
| "Considerar biomarcadores cardíacos" | |
| ]) | |
| elif query.medical_specialty == "neurology": | |
| recommendations.extend([ | |
| "Evaluación neurológica detallada", | |
| "Considerar neuroimagen si indicada", | |
| "Escalas de valoración neurológica" | |
| ]) | |
| # Knowledge-based recommendations | |
| for source in medical_sources[:2]: | |
| if source["type"] == "condition" and query.user_type == "professional": | |
| mgmt = source.get("professional_management", {}) | |
| if "immediate_actions" in mgmt: | |
| recommendations.extend(mgmt["immediate_actions"][:2]) | |
| return list(set(recommendations)) # Remove duplicates | |
| def _generate_enhanced_warnings(self, query: EnhancedMedicalQuery) -> List[str]: | |
| """Generate enhanced medical warnings""" | |
| warnings = [ | |
| "Esta información es educativa y de apoyo únicamente", | |
| "NO reemplaza la evaluación médica profesional directa", | |
| "Cada caso requiere evaluación médica individualizada" | |
| ] | |
| if query.urgency_level == "emergency": | |
| warnings.insert(0, "EMERGENCIA MÉDICA - Contacte servicios de urgencia inmediatamente") | |
| if query.user_type == "patient": | |
| warnings.extend([ | |
| "No se automedique basándose en esta información", | |
| "Consulte siempre con un profesional de la salud", | |
| "En caso de duda, busque atención médica" | |
| ]) | |
| return warnings | |
| def _generate_enhanced_followup(self, query: EnhancedMedicalQuery) -> List[str]: | |
| """Generate enhanced follow-up actions""" | |
| followup = [] | |
| if query.user_type == "professional": | |
| followup.extend([ | |
| "Documentación completa en historia clínica", | |
| "Plan de seguimiento protocolizado", | |
| "Reevaluación según evolución clínica", | |
| "Consideración de interconsulta si no hay mejoría" | |
| ]) | |
| else: | |
| followup.extend([ | |
| "Seguimiento médico según indicaciones", | |
| "Control de síntomas y respuesta", | |
| "Consulta de control en fechas programadas", | |
| "Buscar atención si empeoran los síntomas" | |
| ]) | |
| # Urgency-specific follow-up | |
| if query.urgency_level == "emergency": | |
| followup.insert(0, "Seguimiento hospitalario hasta estabilización completa") | |
| elif query.urgency_level == "urgent": | |
| followup.insert(0, "Reevaluación en 24-48 horas") | |
| return followup | |
| def _update_performance_metrics(self, response: EnhancedMedicalResponse): | |
| """Update system performance metrics""" | |
| # Update confidence average | |
| total_responses = sum([ | |
| self.performance_metrics["kimi_requests"], | |
| self.performance_metrics["local_requests"], | |
| self.performance_metrics["hybrid_requests"] | |
| ]) | |
| if total_responses > 0: | |
| current_avg = self.performance_metrics["average_confidence"] | |
| new_avg = ((current_avg * (total_responses - 1)) + response.confidence) / total_responses | |
| self.performance_metrics["average_confidence"] = new_avg | |
| # Update response time average | |
| if total_responses > 0: | |
| current_avg = self.performance_metrics["average_response_time"] | |
| new_avg = ((current_avg * (total_responses - 1)) + response.processing_time) / total_responses | |
| self.performance_metrics["average_response_time"] = new_avg | |
| def get_enhanced_session_summary(self) -> Dict[str, Any]: | |
| """Get comprehensive session summary""" | |
| if not self.session_history: | |
| return {"message": "No hay consultas en la sesión actual"} | |
| total_queries = len(self.session_history) | |
| emergency_queries = sum(1 for q, r in self.session_history if q.urgency_level == "emergency") | |
| professional_queries = sum(1 for q, r in self.session_history if q.user_type == "professional") | |
| kimi_responses = sum(1 for q, r in self.session_history if r.ai_source == "kimi_real") | |
| specialties = [q.medical_specialty for q, r in self.session_history if q.medical_specialty] | |
| most_common_specialty = max(set(specialties), key=specialties.count) if specialties else None | |
| return { | |
| "session_overview": { | |
| "total_consultations": total_queries, | |
| "emergency_consultations": emergency_queries, | |
| "professional_consultations": professional_queries, | |
| "patient_consultations": total_queries - professional_queries, | |
| "kimi_powered_responses": kimi_responses, | |
| "local_ai_responses": total_queries - kimi_responses | |
| }, | |
| "performance_metrics": self.performance_metrics, | |
| "medical_insights": { | |
| "most_common_specialty": most_common_specialty, | |
| "emergency_rate": f"{(emergency_queries/total_queries)*100:.1f}%" if total_queries > 0 else "0%", | |
| "professional_usage": f"{(professional_queries/total_queries)*100:.1f}%" if total_queries > 0 else "0%" | |
| }, | |
| "system_status": { | |
| "kimi_available": self.kimi_available, | |
| "advanced_local_active": True, | |
| "medical_reasoning_active": True, | |
| "knowledge_base_loaded": True | |
| }, | |
| "session_duration": str(datetime.now() - self.session_history[0][0].timestamp) if self.session_history else "0:00:00", | |
| "last_consultation": self.session_history[-1][1].timestamp.isoformat() if self.session_history else None | |
| } | |
| # Export main class | |
| __all__ = ['EnhancedMedeXAIEngine', 'EnhancedMedicalQuery', 'EnhancedMedicalResponse'] |