JairoCesar commited on
Commit
3e2de58
·
verified ·
1 Parent(s): 78c2128

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +143 -20
app.py CHANGED
@@ -664,39 +664,162 @@ def build_local_hypothesis(entities, food_compound_map, neuro_map):
664
  "sintomas_clave": user_symptoms,
665
  "es_hipotesis_local": True
666
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
667
 
668
  def generate_ai_hypothesis_on_the_fly(query, entities):
 
 
 
 
669
  gemini_client = get_gemini_client()
670
  if not gemini_client: return None
671
 
672
- prompt = f"""
673
- Rol: Experto en Nutrición Funcional e Inmunología de Alimentos.
674
- Caso del paciente: "{query}"
675
- Alimentos detectados: {entities.get('alimentos')}
676
- Síntomas detectados: {entities.get('sintomas')}
677
-
678
- Genera un JSON estricto con la hipótesis clínica para este caso:
679
- {{
680
- "condicion_asociada": "Nombre conciso de la condición (ej: Distensión Abdominal e Intolerancia a FODMAPs por Frijoles en Bandeja Paisa)",
681
- "mecanismo_fisiologico": "Explicación concisa del exceso de galactanos/FODMAPs y fermentación intestinal",
682
- "compuesto_alimento": "Alimentos y compuestos responsables principales",
683
- "sintomas_clave": {entities.get('sintomas')}
684
- }}
685
- Devuelve SOLO el JSON estricto.
686
- """
 
 
 
687
  try:
688
  response = gemini_client.models.generate_content(
689
  model='gemini-2.5-flash-lite',
690
  contents=prompt
691
  )
692
- text = response.text
693
- if "```json" in text: text = text.split("```json")[1].split("```")[0]
694
- elif "```" in text: text = text.split("```")[1].split("```")[0]
695
- data = json.loads(text)
696
  data["es_hipotesis_local"] = True
697
  return data
698
  except Exception as e:
699
- logger.error(f"Error generando hipótesis por IA: {e}")
700
  return None
701
 
702
  def find_best_foodb_matches(user_foods_es, _foodb_index, _foodb_lower_map, food_name_map, limit=3):
 
664
  "sintomas_clave": user_symptoms,
665
  "es_hipotesis_local": True
666
  }
667
+ def get_compounds_for_food(food_name, food_map):
668
+ """
669
+ Buscador e Inferidor Universal de Compuestos:
670
+ 1. Coincidencia exacta.
671
+ 2. Limpieza de plurales.
672
+ 3. Búsqueda por subcadena.
673
+ 4. INFERENCIA UNIVERSAL DE CLASES: Si el alimento es totalmente nuevo,
674
+ deduce sus compuestos por su familia biológica (carnes, mariscos, frutas, lácteos, etc.).
675
+ """
676
+ if not food_name: return []
677
+ food_clean = food_name.lower().strip()
678
+
679
+ # 1. Coincidencia exacta
680
+ if food_clean in food_map:
681
+ return food_map[food_clean]
682
+
683
+ # 2. Limpieza de Plurales
684
+ for suffix in ["es", "s"]:
685
+ if food_clean.endswith(suffix):
686
+ norm = food_clean[:-len(suffix)]
687
+ if norm in food_map:
688
+ return food_map[norm]
689
+
690
+ # 3. Búsqueda por subcadena en el diccionario
691
+ matched_compounds = set()
692
+ for key, compounds in food_map.items():
693
+ if key in food_clean or food_clean in key:
694
+ matched_compounds.update(compounds)
695
+ if matched_compounds:
696
+ return list(matched_compounds)
697
+
698
+ # 4. INFERENCIA UNIVERSAL DE CLASES PARA ALIMENTOS NO REGISTRADOS
699
+ inferred = set()
700
+ if any(w in food_clean for w in ["carne", "cerdo", "res", "pollo", "pavo", "cordero", "pato", "chicharrón", "tocino"]):
701
+ inferred.update(["histamina", "purinas", "proteínas"])
702
+ elif any(w in food_clean for w in ["pescado", "atún", "marisco", "camarón", "sardina", "langostino", "pulpo"]):
703
+ inferred.update(["histamina", "purinas", "albúmina"])
704
+ elif any(w in food_clean for w in ["jugo", "zumo", "batido", "fruta", "licuado"]):
705
+ inferred.update(["fructosa", "salicilatos", "histamina", "fenoles"])
706
+ elif any(w in food_clean for w in ["queso", "leche", "crema", "yogur", "mantequilla"]):
707
+ inferred.update(["caseína", "lactosa", "histamina", "tiramina"])
708
+ elif any(w in food_clean for w in ["pan", "galleta", "pasta", "harina", "bizcocho", "tortilla"]):
709
+ inferred.update(["gluten", "fodmaps"])
710
+ elif any(w in food_clean for w in ["frijol", "lenteja", "garbanzo", "legumbre", "soja"]):
711
+ inferred.update(["lectinas", "fitatos", "fodmaps", "saponinas"])
712
+ elif any(w in food_clean for w in ["salsa", "sazonador", "aderezo", "caldo", "adobo"]):
713
+ inferred.update(["glutamato", "histamina", "sulfitos"])
714
+
715
+ return list(inferred)
716
+
717
+ def build_local_hypothesis(entities, food_compound_map, neuro_map):
718
+ """
719
+ Generador Local Universal de Hipótesis:
720
+ Construye diagnósticos para dermatología (piel/alergias), neurología,
721
+ gastroenterología, insomnio y síntomas generales.
722
+ """
723
+ user_foods = entities.get("alimentos", [])
724
+ user_symptoms = entities.get("sintomas", [])
725
+ if not user_foods or not user_symptoms:
726
+ return None
727
+
728
+ symptom_text = " ".join(user_symptoms).lower()
729
+
730
+ # Categorización clínica del síntoma predominante
731
+ es_dermatologico = any(k in symptom_text for k in ["piel", "rasca", "picazón", "picazon", "manchas", "urticaria", "erupción", "alergia", "ronchas", "granitos"])
732
+ es_cefalea = any(k in symptom_text for k in ["dolor de cabeza", "migraña", "cefalea", "jaqueca"])
733
+ es_insomnio = any(k in symptom_text for k in ["insomnio", "no puedo dormir", "sueño", "desvelado"])
734
+ es_gastro = any(k in symptom_text for k in ["hinchazón", "hinchazon", "gases", "diarrea", "dolor abdominal", "retortijón", "cólico"])
735
+
736
+ candidates = []
737
+ for food in user_foods:
738
+ comps = get_compounds_for_food(food, food_compound_map)
739
+ for compound in comps:
740
+ info = neuro_map.get(compound, {})
741
+ effect = info.get('efecto_neuropsicologico', f'Sustancia bioactiva ({compound}) presente en {food}')
742
+ candidates.append({"alimento": food, "compuesto": compound, "efecto": effect})
743
+
744
+ if not candidates:
745
+ alimentos_str = ", ".join(user_foods).capitalize()
746
+ sintomas_str = ", ".join(user_symptoms)
747
+ return {
748
+ "condicion_asociada": f"Sensibilidad o Intolerancia Alimentaria a {alimentos_str}",
749
+ "mecanismo_fisiologico": f"Respuesta inmunológica o enzimática a los componentes de {alimentos_str}, manifestándose con {sintomas_str}.",
750
+ "compuesto_alimento": f"{alimentos_str} (Compuestos bioactivos)",
751
+ "sintomas_clave": user_symptoms,
752
+ "es_hipotesis_local": True
753
+ }
754
+
755
+ compuestos_unicos = sorted(set(c['compuesto'] for c in candidates))
756
+ alimentos_unicos = sorted(set(c['alimento'] for c in candidates))
757
+ compuestos_str = ", ".join(compuestos_unicos)
758
+ alimentos_str = ", ".join(alimentos_unicos).capitalize()
759
+
760
+ # Construcción sintáctica dinámica por sistema orgánico
761
+ if es_dermatologico:
762
+ condicion = f"Reacción Histaminérgica / Dermatológica por {compuestos_str} en {alimentos_str}"
763
+ elif es_cefalea:
764
+ condicion = f"Cefalea o Migraña por Aminas Vasoactivas / Compuestos ({compuestos_str}) en {alimentos_str}"
765
+ elif es_insomnio:
766
+ condicion = f"Alteración del Sueño e Insomnio por respuesta a {compuestos_str} en {alimentos_str}"
767
+ elif es_gastro:
768
+ condicion = f"Distensión Abdominal e Intolerancia Digestiva a {compuestos_str} en {alimentos_str}"
769
+ else:
770
+ condicion = f"Hipersensibilidad o Intolerancia a {compuestos_str} en {alimentos_str}"
771
+
772
+ mecanismo = " | ".join(sorted(set(c['efecto'] for c in candidates)))
773
+
774
+ return {
775
+ "condicion_asociada": condicion,
776
+ "mecanismo_fisiologico": mecanismo,
777
+ "compuesto_alimento": f"{alimentos_str} ({compuestos_str})",
778
+ "sintomas_clave": user_symptoms,
779
+ "es_hipotesis_local": True
780
+ }
781
+
782
+
783
 
784
  def generate_ai_hypothesis_on_the_fly(query, entities):
785
+ """
786
+ Agente Rescatador Universal por IA:
787
+ Sintetiza una hipótesis clínica para CUALQUIER caso, alimento o síntoma no registrado localmente.
788
+ """
789
  gemini_client = get_gemini_client()
790
  if not gemini_client: return None
791
 
792
+ foods = entities.get('alimentos', [])
793
+ symptoms = entities.get('sintomas', [])
794
+
795
+ foods_text = ", ".join(foods) if foods else "alimentos mencionados en la consulta"
796
+ symptoms_text = ", ".join(symptoms) if symptoms else "síntomas expresados por el paciente"
797
+
798
+ prompt = (
799
+ "Rol: Médico Especialista en Nutrición Funcional e Inmunología de Alimentos.\n"
800
+ f'Consulta del paciente: "{query}"\n'
801
+ f"Alimentos clave: {foods_text}\n"
802
+ f"Síntomas clave: {symptoms_text}\n\n"
803
+ "Analiza la fisiología de este caso y genera un JSON estricto con las siguientes 4 claves:\n"
804
+ '1. "condicion_asociada": Título médico conciso de la hipersensibilidad o intolerancia.\n'
805
+ '2. "mecanismo_fisiologico": Explicación concisa del mecanismo celular o enzimático subyacente.\n'
806
+ '3. "compuesto_alimento": Nombre de los alimentos y las moléculas responsables.\n'
807
+ f'4. "sintomas_clave": {json.dumps(symptoms if symptoms else ["malestar"])}\n\n'
808
+ "Devuelve ÚNICAMENTE el código JSON válido, sin bloques de código markdown ni texto adicional."
809
+ )
810
  try:
811
  response = gemini_client.models.generate_content(
812
  model='gemini-2.5-flash-lite',
813
  contents=prompt
814
  )
815
+ raw = response.text.strip()
816
+ if "```json" in raw: raw = raw.split("```json")[1].split("```")[0]
817
+ elif "```" in raw: raw = raw.split("```")[1].split("```")[0]
818
+ data = json.loads(raw.strip())
819
  data["es_hipotesis_local"] = True
820
  return data
821
  except Exception as e:
822
+ logger.error(f"Error en Agente Rescatador Universal de IA: {e}")
823
  return None
824
 
825
  def find_best_foodb_matches(user_foods_es, _foodb_index, _foodb_lower_map, food_name_map, limit=3):