JairoCesar commited on
Commit
5a2ebee
路
verified 路
1 Parent(s): 3e2de58

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +99 -35
app.py CHANGED
@@ -16,7 +16,7 @@ import altair as alt
16
  from datetime import datetime
17
  from io import BytesIO
18
  import docx
19
- import difflib
20
 
21
  # --- CONFIGURACI脫N DE P脕GINA Y LOGGING ---
22
  st.set_page_config(page_title="El Detective de Alimentos", page_icon="馃崕", layout="wide")
@@ -464,6 +464,7 @@ def extract_keywords(text):
464
  words = re.findall(r'\b\w+\b', sanitize_text(text))
465
  return set(w for w in words if w not in STOPWORDS_ES and len(w) > 2)
466
 
 
467
  def get_compounds_for_food(food_name, food_map):
468
  if not food_name: return []
469
  food_clean = food_name.lower().strip()
@@ -473,19 +474,50 @@ def get_compounds_for_food(food_name, food_map):
473
  return food_map[food_clean]
474
 
475
  # 2. Limpieza de Plurales
476
- if food_clean.endswith("s") and food_clean[:-1] in food_map:
477
- return food_map[food_clean[:-1]]
478
- if food_clean.endswith("es") and food_clean[:-2] in food_map:
479
- return food_map[food_clean[:-2]]
480
-
481
- # 3. B煤squeda por subcadena
 
 
 
 
 
 
 
482
  matched_compounds = set()
483
  for key, compounds in food_map.items():
484
  if key in food_clean or food_clean in key:
485
  matched_compounds.update(compounds)
486
-
487
- return list(matched_compounds)
488
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
489
  def validar_seguro_logico(entry, user_foods, user_query, food_compound_map):
490
  if not user_foods:
491
  return True
@@ -664,39 +696,60 @@ def build_local_hypothesis(entities, food_compound_map, neuro_map):
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"]):
@@ -716,9 +769,9 @@ def get_compounds_for_food(food_name, food_map):
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", [])
@@ -727,26 +780,39 @@ def build_local_hypothesis(entities, food_compound_map, neuro_map):
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
@@ -757,11 +823,11 @@ def build_local_hypothesis(entities, food_compound_map, neuro_map):
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:
@@ -779,8 +845,6 @@ def build_local_hypothesis(entities, food_compound_map, neuro_map):
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:
 
16
  from datetime import datetime
17
  from io import BytesIO
18
  import docx
19
+ import difflib
20
 
21
  # --- CONFIGURACI脫N DE P脕GINA Y LOGGING ---
22
  st.set_page_config(page_title="El Detective de Alimentos", page_icon="馃崕", layout="wide")
 
464
  words = re.findall(r'\b\w+\b', sanitize_text(text))
465
  return set(w for w in words if w not in STOPWORDS_ES and len(w) > 2)
466
 
467
+ # --- BUSCADOR UNIVERSAL CON CORRECTOR DIFUSO DE ERRATAS (difflib) ---
468
  def get_compounds_for_food(food_name, food_map):
469
  if not food_name: return []
470
  food_clean = food_name.lower().strip()
 
474
  return food_map[food_clean]
475
 
476
  # 2. Limpieza de Plurales
477
+ for suffix in ["es", "s"]:
478
+ if food_clean.endswith(suffix):
479
+ norm = food_clean[:-len(suffix)]
480
+ if norm in food_map:
481
+ return food_map[norm]
482
+
483
+ # 3. CORRECTOR DIFUSO DE ERRATAS (Corrige "vino tino" -> "vino tinto", "chotanduro" -> "chontaduro")
484
+ fuzzy_matches = difflib.get_close_matches(food_clean, list(food_map.keys()), n=1, cutoff=0.6)
485
+ if fuzzy_matches:
486
+ logger.info(f"馃挕 Errata corregida autom谩ticamente: '{food_clean}' -> '{fuzzy_matches[0]}'")
487
+ return food_map[fuzzy_matches[0]]
488
+
489
+ # 4. B煤squeda por subcadena en el diccionario
490
  matched_compounds = set()
491
  for key, compounds in food_map.items():
492
  if key in food_clean or food_clean in key:
493
  matched_compounds.update(compounds)
494
+ if matched_compounds:
495
+ return list(matched_compounds)
496
 
497
+ # 5. Correcci贸n difusa por palabras individuales dentro de frases largas
498
+ inferred = set()
499
+ words = food_clean.split()
500
+ for word in words:
501
+ if len(word) > 3:
502
+ sub_fuzzy = difflib.get_close_matches(word, list(food_map.keys()), n=1, cutoff=0.7)
503
+ if sub_fuzzy:
504
+ inferred.update(food_map[sub_fuzzy[0]])
505
+
506
+ if inferred:
507
+ return list(inferred)
508
+
509
+ # 6. INFERENCIA UNIVERSAL DE CLASES PARA ALIMENTOS NO REGISTRADOS
510
+ if any(w in food_clean for w in ["vino", "cerveza", "licor", "trago", "bebida"]):
511
+ return ["histamina", "tiramina", "sulfitos", "alcohol", "taninos"]
512
+ elif any(w in food_clean for w in ["carne", "cerdo", "res", "pollo", "pavo", "cordero"]):
513
+ return ["histamina", "purinas", "prote铆nas"]
514
+ elif any(w in food_clean for w in ["jugo", "zumo", "batido", "fruta", "licuado"]):
515
+ return ["fructosa", "salicilatos", "histamina", "fenoles"]
516
+ elif any(w in food_clean for w in ["queso", "leche", "crema", "yogur"]):
517
+ return ["case铆na", "lactosa", "histamina", "tiramina"]
518
+
519
+ return []
520
+
521
  def validar_seguro_logico(entry, user_foods, user_query, food_compound_map):
522
  if not user_foods:
523
  return True
 
696
  "sintomas_clave": user_symptoms,
697
  "es_hipotesis_local": True
698
  }
699
+
700
+
701
  def get_compounds_for_food(food_name, food_map):
702
  """
703
+ Buscador e Inferidor Universal de Compuestos (Definitivo):
704
  1. Coincidencia exacta.
705
+ 2. Normalizaci贸n de plurales.
706
+ 3. Corrector difuso de errores de tipeo (difflib).
707
+ 4. B煤squeda por subcadena en el diccionario.
708
+ 5. Inferencia universal por familia biom茅dica para alimentos no registrados.
709
  """
710
+ if not food_name:
711
+ return []
712
+
713
  food_clean = food_name.lower().strip()
714
 
715
  # 1. Coincidencia exacta
716
  if food_clean in food_map:
717
  return food_map[food_clean]
718
 
719
+ # 2. Normalizaci贸n de Plurales
720
  for suffix in ["es", "s"]:
721
  if food_clean.endswith(suffix):
722
  norm = food_clean[:-len(suffix)]
723
  if norm in food_map:
724
  return food_map[norm]
725
 
726
+ # 3. Correcci贸n difusa de erratas de tipeo ("vino tino" -> "vino tinto", "chotanduro" -> "chontaduro")
727
+ fuzzy_matches = difflib.get_close_matches(food_clean, list(food_map.keys()), n=1, cutoff=0.6)
728
+ if fuzzy_matches:
729
+ return food_map[fuzzy_matches[0]]
730
+
731
+ # 4. B煤squeda por subcadena dentro del diccionario
732
  matched_compounds = set()
733
  for key, compounds in food_map.items():
734
  if key in food_clean or food_clean in key:
735
  matched_compounds.update(compounds)
736
+
737
  if matched_compounds:
738
  return list(matched_compounds)
739
 
740
+ # 5. Inferencia difusa por palabras individuales en nombres compuestos
741
  inferred = set()
742
+ words = food_clean.split()
743
+ for word in words:
744
+ if len(word) > 3:
745
+ sub_fuzzy = difflib.get_close_matches(word, list(food_map.keys()), n=1, cutoff=0.7)
746
+ if sub_fuzzy:
747
+ inferred.update(food_map[sub_fuzzy[0]])
748
+
749
+ if inferred:
750
+ return list(inferred)
751
+
752
+ # 6. INFERENCIA UNIVERSAL POR FAMILIA BIOL脫GICA (Alimentos raros o no registrados)
753
  if any(w in food_clean for w in ["carne", "cerdo", "res", "pollo", "pavo", "cordero", "pato", "chicharr贸n", "tocino"]):
754
  inferred.update(["histamina", "purinas", "prote铆nas"])
755
  elif any(w in food_clean for w in ["pescado", "at煤n", "marisco", "camar贸n", "sardina", "langostino", "pulpo"]):
 
769
 
770
  def build_local_hypothesis(entities, food_compound_map, neuro_map):
771
  """
772
+ Generador Local Definitivo de Hip贸tesis:
773
+ Construye respuestas cl铆nicas estructuradas clasificando los s铆ntomas en 4 grandes
774
+ sistemas org谩nicos (Neurolog铆a, Dermatolog铆a, Insomnio, Gastroenterolog铆a).
775
  """
776
  user_foods = entities.get("alimentos", [])
777
  user_symptoms = entities.get("sintomas", [])
 
780
 
781
  symptom_text = " ".join(user_symptoms).lower()
782
 
783
+ # Categorizaci贸n cl铆nica por sistemas de s铆ntomas
784
+ es_dermatologico = any(k in symptom_text for k in ["piel", "rasca", "picaz贸n", "picazon", "manchas", "urticaria", "erupci贸n", "erupcion", "alergia", "ronchas", "granitos"])
785
+ es_cefalea = any(k in symptom_text for k in ["dolor de cabeza", "migra帽a", "cefalea", "jaqueca", "dolor", "cabeza"])
786
+ es_insomnio = any(k in symptom_text for k in ["insomnio", "no puedo dormir", "sue帽o", "desvelado", "desvelo", "despierto"])
787
+ es_gastro = 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"])
788
 
789
  candidates = []
790
  for food in user_foods:
791
  comps = get_compounds_for_food(food, food_compound_map)
792
  for compound in comps:
793
  info = neuro_map.get(compound, {})
794
+ effect = info.get('efecto_neuropsicologico', f'Sustancia bioactiva ({compound}) con impacto inmunol贸gico o metabolico en {food}')
795
  candidates.append({"alimento": food, "compuesto": compound, "efecto": effect})
796
 
797
+ # Si no hay candidatos bioqu铆micos espec铆ficos, genera la hip贸tesis con la familia inferida
798
  if not candidates:
799
  alimentos_str = ", ".join(user_foods).capitalize()
800
  sintomas_str = ", ".join(user_symptoms)
801
+
802
+ if es_cefalea:
803
+ condicion_fallback = f"Cefalea o Migra帽a Alimentaria relacionada con {alimentos_str}"
804
+ elif es_insomnio:
805
+ condicion_fallback = f"Alteraci贸n del Sue帽o o Insomnio relacionado con {alimentos_str}"
806
+ elif es_gastro:
807
+ condicion_fallback = f"Distensi贸n o Malestar Gastrointestinal relacionado con {alimentos_str}"
808
+ elif es_dermatologico:
809
+ condicion_fallback = f"Reacci贸n Dermatol贸gica o Urticaria relacionada con {alimentos_str}"
810
+ else:
811
+ condicion_fallback = f"Sensibilidad o Intolerancia Alimentaria a {alimentos_str}"
812
+
813
  return {
814
+ "condicion_asociada": condicion_fallback,
815
+ "mecanismo_fisiologico": f"Respuesta inmunol贸gica, enzim谩tica o metab贸lica inducida por los componentes bioactivos de {alimentos_str}, manifest谩ndose con {sintomas_str}.",
816
  "compuesto_alimento": f"{alimentos_str} (Compuestos bioactivos)",
817
  "sintomas_clave": user_symptoms,
818
  "es_hipotesis_local": True
 
823
  compuestos_str = ", ".join(compuestos_unicos)
824
  alimentos_str = ", ".join(alimentos_unicos).capitalize()
825
 
826
+ # Construcci贸n sint谩ctica por sistema afectado
827
+ if es_cefalea:
828
+ condicion = f"Cefalea o Migra帽a Vasoactiva asociada a {compuestos_str} en {alimentos_str}"
829
+ elif es_dermatologico:
830
  condicion = f"Reacci贸n Histamin茅rgica / Dermatol贸gica por {compuestos_str} en {alimentos_str}"
 
 
831
  elif es_insomnio:
832
  condicion = f"Alteraci贸n del Sue帽o e Insomnio por respuesta a {compuestos_str} en {alimentos_str}"
833
  elif es_gastro:
 
845
  "es_hipotesis_local": True
846
  }
847
 
 
 
848
  def generate_ai_hypothesis_on_the_fly(query, entities):
849
  """
850
  Agente Rescatador Universal por IA: