JairoCesar commited on
Commit
65b38db
·
verified ·
1 Parent(s): c0d765f

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +38 -26
app.py CHANGED
@@ -1,5 +1,5 @@
1
  # ==============================================================================
2
- # EL DETECTIVE DE ALIMENTOS - EDICIÓN HÍBRIDA MULTISISTÉMICA (10 SISTEMAS ORGÁNICOS)
3
  # Desarrollado por: Jairo Alexander & Diana Milena Soler Martinez
4
  # Motor: Gemini 2.5 Flash Lite + FooDB.ca Graph + Fast-Path Local Engine
5
  # ==============================================================================
@@ -37,7 +37,7 @@ Este análisis es una herramienta informativa de IA y **NO es un diagnóstico m
37
  **Si tus síntomas son severos, busca ayuda médica urgente.**
38
  """
39
 
40
- # --- CONFIGURACIÓN DE CLIENTE GEMINI CON VARIABLE GEMINI_API_KEY ---
41
  def get_gemini_client():
42
  api_key = os.getenv("GEMINI_API_KEY")
43
 
@@ -66,7 +66,7 @@ def get_gemini_client():
66
  logger.warning(f"❌ Error instanciando cliente Gemini: {e}")
67
  return None
68
 
69
- # --- CARGA ULTRA-RÁPIDA DE DATOS Y FOODB (CERO COPIA EN MEMORIA) ---
70
  @st.cache_resource
71
  def load_data():
72
  try:
@@ -95,7 +95,6 @@ def get_foodb_lower_map(_foodb_index):
95
 
96
  foodb_lower_map = get_foodb_lower_map(foodb_index)
97
 
98
- # --- CACHÉ GLOBAL DE APRENDIZAJE EN RAM ---
99
  @st.cache_resource
100
  def get_global_learned_cache():
101
  return {}
@@ -446,7 +445,7 @@ COMPOUND_SYNONYM_MAP = {
446
  "proteínas": ["protein", "amino acid"]
447
  }
448
 
449
- # 10 CATEGORÍAS SINTOMÁTICAS MULTISISTÉMICAS
450
  MASTER_SYMPTOM_MAP = {
451
  "dolor abdominal": {
452
  "frases_es": ["dolor de estómago", "dolor de panza", "dolor abdominal", "retortijones", "cólicos", "calambres abdominales", "gastritis", "acidez", "reflujo", "pesadez"],
@@ -456,6 +455,14 @@ MASTER_SYMPTOM_MAP = {
456
  "frases_es": ["hinchazón abdominal", "vientre hinchado", "distensión abdominal", "hinchazón", "distensión", "estomago se infla", "inflamado", "inflado", "infla", "hincha", "gases", "flatulencia"],
457
  "keywords_en": ["bloating", "distension", "flatulence", "swelling"]
458
  },
 
 
 
 
 
 
 
 
459
  "dolor de cabeza": {
460
  "frases_es": ["dolor de cabeza", "migraña", "cefalea", "jaqueca", "me duele la cabeza", "duele la cabeza", "dolor de sien", "dolor en la cabeza", "pesadez de cabeza"],
461
  "keywords_en": ["headache", "migraine"]
@@ -473,7 +480,7 @@ MASTER_SYMPTOM_MAP = {
473
  "keywords_en": ["rash", "itching", "urticaria", "eczema"]
474
  },
475
  "reactividad respiratoria": {
476
- "frases_es": ["asma", "tos", "respirar", "pecho", "sibilancia", "ahogo", "falta de aire", "congestión nasal", "stornudos", "rinitis"],
477
  "keywords_en": ["asthma", "cough", "wheezing", "rhinitis"]
478
  },
479
  "respuesta cardiovascular": {
@@ -623,8 +630,8 @@ def extract_entities_with_gemini(query):
623
  system_prompt = f"""
624
  Rol: Extractor Médico JSON experto en nutrición y síntomas.
625
  Tarea: Analiza el texto del usuario y extrae un JSON estricto con dos claves:
626
- - "alimentos": lista de alimentos o bebidas mencionados en español, forma simple (ej: "jugo de naranja", "chontaduro", "bandeja paisa", "queso", "vino tinto").
627
- - "sintomas": lista de síntomas mencionados en español, forma clínica simple (ej: "insomnio", "dolor de cabeza", "hinchazón").
628
 
629
  Input: "{query}"
630
  Output (JSON estricto):
@@ -659,7 +666,7 @@ def reinforce_entities_with_keywords(entities, query, food_map, master_symptom_m
659
  if match_jugo:
660
  current_foods.add(match_jugo.group(0))
661
  else:
662
- match_tomo = re.search(r'\b(como|tomo|consumo|ingiero)\s+([a-zA-ZáéíóúñÁÉÍÓÚÑ\s]{3,20})\b', query_sanitized)
663
  if match_tomo:
664
  food_extracted = match_tomo.group(2).strip()
665
  if food_extracted not in STOPWORDS_ES:
@@ -673,6 +680,18 @@ def reinforce_entities_with_keywords(entities, query, food_map, master_symptom_m
673
  if phrase in query_sanitized:
674
  current_symptoms.add(main_symptom)
675
  break
 
 
 
 
 
 
 
 
 
 
 
 
676
  entities["sintomas"] = list(current_symptoms)
677
  return entities
678
 
@@ -747,7 +766,7 @@ def build_local_hypothesis(entities, food_compound_map, neuro_map):
747
  es_neurologico = any(k in symptom_text for k in ["dolor de cabeza", "migraña", "cefalea", "jaqueca", "dolor de sien", "mareo", "vértigo", "vertigo", "neblina mental", "cabeza"])
748
  es_psicologico = any(k in symptom_text for k in ["ansiedad", "angustia", "nerviosismo", "depresión", "depresion", "irritabilidad", "desrealización", "desrealizacion", "agitación", "agitacion"])
749
  es_insomnio = any(k in symptom_text for k in ["insomnio", "no puedo dormir", "sueño", "desvelado", "desvelo", "despierto", "dormir"])
750
- es_gastrointestinal = any(k in symptom_text for k in ["hinchazón", "hinchazon", "gases", "diarrea", "dolor abdominal", "retortijón", "retortijon", "cólico", "colico", "inflamado", "inflama", "pesadez", "estómago", "estomago", "náuseas", "nausea", "acidez", "reflujo"])
751
  es_dermatologico = any(k in symptom_text for k in ["piel", "rasca", "picazón", "picazon", "manchas", "urticaria", "erupción", "erupcion", "alergia", "ronchas", "granitos", "eccema"])
752
  es_respiratorio = any(k in symptom_text for k in ["asma", "tos", "respirar", "pecho", "sibilancia", "ahogo", "falta de aire", "congestión nasal", "congestion nasal", "estornudo", "rinitis"])
753
  es_cardiovascular = any(k in symptom_text for k in ["palpitaciones", "taquicardia", "ritmo cardíaco", "presión", "presion", "rubor", "sofoco", "frío"])
@@ -841,22 +860,14 @@ def generate_ai_hypothesis_on_the_fly(query, entities):
841
  if not gemini_client:
842
  return None
843
 
844
- foods = entities.get('alimentos', [])
845
- symptoms = entities.get('sintomas', [])
846
-
847
- foods_text = ", ".join(foods) if foods else "alimentos mencionados en la consulta"
848
- symptoms_text = ", ".join(symptoms) if symptoms else "síntomas expresados por el paciente"
849
-
850
  prompt = (
851
  "Rol: Médico Especialista en Nutrición Funcional e Inmunología de Alimentos.\n"
852
- f'Consulta del paciente: "{query}"\n'
853
- f"Alimentos clave: {foods_text}\n"
854
- f"Síntomas clave: {symptoms_text}\n\n"
855
  "Analiza la fisiología de este caso y genera un JSON estricto con las siguientes 4 claves:\n"
856
- '1. "condicion_asociada": Título médico conciso de la hipersensibilidad o intolerancia.\n'
857
- '2. "mecanismo_fisiologico": Explicación concisa del mecanismo celular o enzimático subyacente.\n'
858
- '3. "compuesto_alimento": Nombre de los alimentos y las moléculas responsables.\n'
859
- f'4. "sintomas_clave": {json.dumps(symptoms if symptoms else ["malestar"])}\n\n'
860
  "Devuelve ÚNICAMENTE el código JSON válido, sin bloques de código markdown ni texto adicional."
861
  )
862
  try:
@@ -1005,7 +1016,7 @@ def generate_detailed_analysis(query, match, entities=None, food_compound_map=No
1005
  Tarea: Escribe un análisis en Markdown estructurado:
1006
  1. Saludo breve y empático.
1007
  2. Frase inicial clara con la hipótesis probable.
1008
- 3. Explicación de las moléculas responsables (ej: histamina, tiramina, sulfitos, fructosa, gluten, salicilatos, fenoles, escopoletina).
1009
  4. Mecanismo fisiológico en términos sencillos.
1010
  5. Alimentos a evitar o moderar.
1011
  6. Alternativas o reemplazos sugeridos.
@@ -1017,7 +1028,7 @@ def generate_detailed_analysis(query, match, entities=None, food_compound_map=No
1017
  model='gemini-2.5-flash-lite',
1018
  contents=prompt
1019
  )
1020
- ai_report = response.text + TEXTO_BANDERAS_ROJAS
1021
  global_learned_cache[cache_lookup_key] = ai_report
1022
  return ai_report
1023
  except Exception as e:
@@ -1160,7 +1171,7 @@ with st.form(key="search_form", clear_on_submit=False):
1160
  query_input = st.text_area(
1161
  "Tu Caso:",
1162
  height=150,
1163
- placeholder="Ej: Si tomo mucho jugo de chontaduro no puedo dormir.",
1164
  key="input_query_text"
1165
  )
1166
  submitted = st.form_submit_button("Analizar Caso", type="primary")
@@ -1188,6 +1199,7 @@ if submitted:
1188
  if local_hypothesis:
1189
  results = [{'entry': local_hypothesis, 'score': {'total': 50}, 'matched_keywords': []}]
1190
 
 
1191
  if not results:
1192
  ai_hypothesis = generate_ai_hypothesis_on_the_fly(current_query, final_entities)
1193
  if ai_hypothesis:
 
1
  # ==============================================================================
2
+ # EL DETECTIVE DE ALIMENTOS - EDICIÓN HÍBRIDA DE ALTA DISPONIBILIDAD
3
  # Desarrollado por: Jairo Alexander & Diana Milena Soler Martinez
4
  # Motor: Gemini 2.5 Flash Lite + FooDB.ca Graph + Fast-Path Local Engine
5
  # ==============================================================================
 
37
  **Si tus síntomas son severos, busca ayuda médica urgente.**
38
  """
39
 
40
+ # --- CONFIGURACIÓN GEMINI CON LA VARIABLE GEMINI_API_KEY ---
41
  def get_gemini_client():
42
  api_key = os.getenv("GEMINI_API_KEY")
43
 
 
66
  logger.warning(f"❌ Error instanciando cliente Gemini: {e}")
67
  return None
68
 
69
+ # --- CARGA ULTRA-RÁPIDA DE DATOS Y FOODB ---
70
  @st.cache_resource
71
  def load_data():
72
  try:
 
95
 
96
  foodb_lower_map = get_foodb_lower_map(foodb_index)
97
 
 
98
  @st.cache_resource
99
  def get_global_learned_cache():
100
  return {}
 
445
  "proteínas": ["protein", "amino acid"]
446
  }
447
 
448
+ # MAPA COMPLETO DE SÍNTOMAS (NUNCA DEJA VACÍA LA EXTRACCIÓN LOCAL)
449
  MASTER_SYMPTOM_MAP = {
450
  "dolor abdominal": {
451
  "frases_es": ["dolor de estómago", "dolor de panza", "dolor abdominal", "retortijones", "cólicos", "calambres abdominales", "gastritis", "acidez", "reflujo", "pesadez"],
 
455
  "frases_es": ["hinchazón abdominal", "vientre hinchado", "distensión abdominal", "hinchazón", "distensión", "estomago se infla", "inflamado", "inflado", "infla", "hincha", "gases", "flatulencia"],
456
  "keywords_en": ["bloating", "distension", "flatulence", "swelling"]
457
  },
458
+ "diarrea": {
459
+ "frases_es": ["diarrea", "diarreas", "soltura", "estómago suelto", "deposiciones líquidas", "deposiciones liquidas", "diarrea acuosa"],
460
+ "keywords_en": ["diarrhea", "loose stools"]
461
+ },
462
+ "náuseas": {
463
+ "frases_es": ["náuseas", "nauseas", "vómito", "vomito", "ganas de vomitar", "mareo digestivo"],
464
+ "keywords_en": ["nausea", "vomiting"]
465
+ },
466
  "dolor de cabeza": {
467
  "frases_es": ["dolor de cabeza", "migraña", "cefalea", "jaqueca", "me duele la cabeza", "duele la cabeza", "dolor de sien", "dolor en la cabeza", "pesadez de cabeza"],
468
  "keywords_en": ["headache", "migraine"]
 
480
  "keywords_en": ["rash", "itching", "urticaria", "eczema"]
481
  },
482
  "reactividad respiratoria": {
483
+ "frases_es": ["asma", "tos", "respirar", "pecho", "sibilancia", "ahogo", "falta de aire", "congestión nasal", "estornudos", "rinitis"],
484
  "keywords_en": ["asthma", "cough", "wheezing", "rhinitis"]
485
  },
486
  "respuesta cardiovascular": {
 
630
  system_prompt = f"""
631
  Rol: Extractor Médico JSON experto en nutrición y síntomas.
632
  Tarea: Analiza el texto del usuario y extrae un JSON estricto con dos claves:
633
+ - "alimentos": lista de alimentos o bebidas mencionados en español, forma simple (ej: "jugo de naranja", "chontaduro", "bandeja paisa", "queso", "leche", "vino tinto").
634
+ - "sintomas": lista de síntomas mencionados en español, forma clínica simple (ej: "insomnio", "dolor de cabeza", "hinchazón", "diarrea").
635
 
636
  Input: "{query}"
637
  Output (JSON estricto):
 
666
  if match_jugo:
667
  current_foods.add(match_jugo.group(0))
668
  else:
669
+ match_tomo = re.search(r'\b(como|tomo|consumo|ingiero|da)\s+([a-zA-ZáéíóúñÁÉÍÓÚÑ\s]{3,20})\b', query_sanitized)
670
  if match_tomo:
671
  food_extracted = match_tomo.group(2).strip()
672
  if food_extracted not in STOPWORDS_ES:
 
680
  if phrase in query_sanitized:
681
  current_symptoms.add(main_symptom)
682
  break
683
+
684
+ # EXTRAER SÍNTOMAS ADICIONALES POR PALABRAS CLAVE DIRECTAS
685
+ if not current_symptoms:
686
+ if any(w in query_sanitized for w in ["diarrea", "diarreas", "soltura", "líquida"]):
687
+ current_symptoms.add("diarrea")
688
+ elif any(w in query_sanitized for w in ["hinchazón", "hinchazon", "gases", "inflamado"]):
689
+ current_symptoms.add("hinchazón")
690
+ elif any(w in query_sanitized for w in ["cabeza", "migraña", "cefalea"]):
691
+ current_symptoms.add("dolor de cabeza")
692
+ elif any(w in query_sanitized for w in ["dormir", "insomnio", "desvelo"]):
693
+ current_symptoms.add("insomnio")
694
+
695
  entities["sintomas"] = list(current_symptoms)
696
  return entities
697
 
 
766
  es_neurologico = any(k in symptom_text for k in ["dolor de cabeza", "migraña", "cefalea", "jaqueca", "dolor de sien", "mareo", "vértigo", "vertigo", "neblina mental", "cabeza"])
767
  es_psicologico = any(k in symptom_text for k in ["ansiedad", "angustia", "nerviosismo", "depresión", "depresion", "irritabilidad", "desrealización", "desrealizacion", "agitación", "agitacion"])
768
  es_insomnio = any(k in symptom_text for k in ["insomnio", "no puedo dormir", "sueño", "desvelado", "desvelo", "despierto", "dormir"])
769
+ es_gastrointestinal = any(k in symptom_text for k in ["diarrea", "diarreas", "hinchazón", "hinchazon", "gases", "dolor abdominal", "retortijón", "retortijon", "cólico", "colico", "inflamado", "inflama", "pesadez", "estómago", "estomago", "náuseas", "nausea", "acidez", "reflujo"])
770
  es_dermatologico = any(k in symptom_text for k in ["piel", "rasca", "picazón", "picazon", "manchas", "urticaria", "erupción", "erupcion", "alergia", "ronchas", "granitos", "eccema"])
771
  es_respiratorio = any(k in symptom_text for k in ["asma", "tos", "respirar", "pecho", "sibilancia", "ahogo", "falta de aire", "congestión nasal", "congestion nasal", "estornudo", "rinitis"])
772
  es_cardiovascular = any(k in symptom_text for k in ["palpitaciones", "taquicardia", "ritmo cardíaco", "presión", "presion", "rubor", "sofoco", "frío"])
 
860
  if not gemini_client:
861
  return None
862
 
 
 
 
 
 
 
863
  prompt = (
864
  "Rol: Médico Especialista en Nutrición Funcional e Inmunología de Alimentos.\n"
865
+ f'Consulta directa del paciente: "{query}"\n\n'
 
 
866
  "Analiza la fisiología de este caso y genera un JSON estricto con las siguientes 4 claves:\n"
867
+ '1. "condicion_asociada": Título médico conciso de la hipersensibilidad o intolerancia (ej: Intolerancia a la Lactosa y Malabsorción Digestiva por Leche).\n'
868
+ '2. "mecanismo_fisiologico": Explicación concisa de la deficiencia enzimática de lactasa e hiperfermentación colónica.\n'
869
+ '3. "compuesto_alimento": Nombre de los alimentos y las moléculas responsables (ej: Leche (Lactosa, Caseína)).\n'
870
+ '4. "sintomas_clave": Lista de los síntomas expresados por el paciente.\n\n'
871
  "Devuelve ÚNICAMENTE el código JSON válido, sin bloques de código markdown ni texto adicional."
872
  )
873
  try:
 
1016
  Tarea: Escribe un análisis en Markdown estructurado:
1017
  1. Saludo breve y empático.
1018
  2. Frase inicial clara con la hipótesis probable.
1019
+ 3. Explicación de las moléculas responsables (ej: histamina, tiramina, sulfitos, fructosa, gluten, salicilatos, fenoles, escopoletina, lactosa, caseína).
1020
  4. Mecanismo fisiológico en términos sencillos.
1021
  5. Alimentos a evitar o moderar.
1022
  6. Alternativas o reemplazos sugeridos.
 
1028
  model='gemini-2.5-flash-lite',
1029
  contents=prompt
1030
  )
1031
+ ai_report = "🤖 **Análisis Redactado por IA Gemini 2.5 Flash Lite:**\n\n" + response.text + TEXTO_BANDERAS_ROJAS
1032
  global_learned_cache[cache_lookup_key] = ai_report
1033
  return ai_report
1034
  except Exception as e:
 
1171
  query_input = st.text_area(
1172
  "Tu Caso:",
1173
  height=150,
1174
+ placeholder="Ej: Me da diarrea si tomo leche.",
1175
  key="input_query_text"
1176
  )
1177
  submitted = st.form_submit_button("Analizar Caso", type="primary")
 
1199
  if local_hypothesis:
1200
  results = [{'entry': local_hypothesis, 'score': {'total': 50}, 'matched_keywords': []}]
1201
 
1202
+ # RESCATE UNIVERSAL POR IA CON TEXTO CRUDO
1203
  if not results:
1204
  ai_hypothesis = generate_ai_hypothesis_on_the_fly(current_query, final_entities)
1205
  if ai_hypothesis: