JairoCesar commited on
Commit
70eb63c
路
verified 路
1 Parent(s): 65b7f99

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +69 -22
app.py CHANGED
@@ -46,7 +46,7 @@ try:
46
 
47
  client = genai.Client(
48
  api_key=APIkeydetective,
49
- http_options=types.HttpOptions(timeout=30000) # 30 segundos
50
  )
51
 
52
  except Exception as e:
@@ -110,6 +110,47 @@ FOOD_TO_COMPOUND_MAP = {
110
  "cholado": ["fructosa", "lactosa", "histamina", "az煤car refinado"]
111
  }
112
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
113
  COMPOUND_SYNONYM_MAP = {
114
  "gluten": ["gluten", "gliadin"],
115
  "case铆na": ["casein", "casomorphin"],
@@ -120,7 +161,7 @@ COMPOUND_SYNONYM_MAP = {
120
  }
121
 
122
  KNOWN_TRIGGERS_MAP = {
123
- "Histamine": ["dolor de cabeza", "migra帽a", "inflamaci贸n", "congesti贸n nasal", "picaz贸n", "erupci贸n", "ansiedad", "diarrea"],
124
  "Tyramine": ["dolor de cabeza", "migra帽a"],
125
  "Phenylethylamine": ["dolor de cabeza", "migra帽a"],
126
  "Gluten": ["niebla mental", "dolor abdominal", "diarrea", "inflamaci贸n", "dolor articular", "ataxia"],
@@ -144,7 +185,7 @@ MASTER_SYMPTOM_MAP = {
144
  "keywords_en": ["anxiety", "nervousness"]
145
  },
146
  "insomnio": {
147
- "frases_es": ["insomnio", "no puedo dormir", "dificultad para dormir", "desvelado"],
148
  "keywords_en": ["insomnia", "sleep trouble"]
149
  }
150
  }
@@ -197,8 +238,8 @@ INTEGRATED_NEURO_FOOD_MAP = {
197
  "fuentes_comunes": ["quesos madurados", "vino tinto", "embutidos", "alimentos fermentados"]
198
  },
199
  "histamina": {
200
- "efecto_neuropsicologico": "Neurotransmisor central. Su acumulaci贸n por bajo DAO provoca insomnio, ansiedad, migra帽as, irritabilidad y niebla mental.",
201
- "fuentes_comunes": ["queso", "vino", "tomate", "pescado azul", "alimentos conservados"]
202
  },
203
  "cafe铆na": {
204
  "efecto_neuropsicologico": "Bloquea los receptores de adenosina en el cerebro. En dosis altas o personas sensibles provoca hiperactivaci贸n del SNC, ansiedad e insomnio.",
@@ -213,7 +254,7 @@ INTEGRATED_NEURO_FOOD_MAP = {
213
  "fuentes_comunes": ["leche", "queso fresco", "crema de leche"]
214
  },
215
  "fructosa": {
216
- "efecto_neuropsicologico": "La malabsorci贸n de fructosa disminuye la disponibilidad de tript贸fano, interfiriendo con la s铆ntesis de serotonina y favoreciendo la irritabilidad.",
217
  "fuentes_comunes": ["jugo de naranja", "frutas muy dulces", "miel", "jarabe de ma铆z"]
218
  },
219
  "sulfitos": {
@@ -221,7 +262,7 @@ INTEGRATED_NEURO_FOOD_MAP = {
221
  "fuentes_comunes": ["vino", "cerveza", "frutos secos desecados"]
222
  },
223
  "saponinas": {
224
- "efecto_neuropsicologico": "Aumentan la permeabilidad intestinal ('intestino permeable'), facilitando la migraci贸n de lipopolisac谩ridos al torrente sangu铆neo e induciendo neuroinflamaci贸n.",
225
  "fuentes_comunes": ["quinoa", "legumbres", "soja"]
226
  },
227
  "oxalatos": {
@@ -234,7 +275,7 @@ INTEGRATED_NEURO_FOOD_MAP = {
234
  }
235
  }
236
 
237
- # --- FUNCIONES DE L脫GICA Y PROCESAMIENTO ---
238
  @st.cache_data(show_spinner=False)
239
  def generate_word_report_cached(report_text):
240
  try:
@@ -284,8 +325,8 @@ def extract_entities_with_gemini(query):
284
  system_prompt = f"""
285
  Rol: Extractor M茅dico JSON experto en nutrici贸n y s铆ntomas.
286
  Tarea: Analiza el texto del usuario y extrae un JSON estricto con dos claves:
287
- - "alimentos": lista de alimentos o bebidas mencionados en espa帽ol, forma simple (ej: "queso", "pan", "vino").
288
- - "sintomas": lista de s铆ntomas mencionados en espa帽ol, forma cl铆nica simple (ej: "dolor de cabeza", "hinchaz贸n").
289
 
290
  Input: "{query}"
291
  Output (JSON estricto):
@@ -354,6 +395,10 @@ def find_best_matches_hybrid(entities, data, food_compound_map=None):
354
 
355
  results = []
356
  for entry in data:
 
 
 
 
357
  db_food_keywords = extract_keywords(entry.get("compuesto_alimento", ""))
358
  db_symptom_keywords = extract_keywords(" ".join(entry.get("sintomas_clave", [])))
359
 
@@ -380,11 +425,14 @@ def build_local_hypothesis(entities, food_compound_map, neuro_map):
380
 
381
  symptom_text = " ".join(user_symptoms).lower()
382
  headache_keywords = ["dolor de cabeza", "migra帽a", "cefalea", "jaqueca"]
 
 
383
  es_dolor_cabeza = any(k in symptom_text for k in headache_keywords)
 
384
 
385
  candidates = []
386
  for food in user_foods:
387
- for compound in food_compound_map.get(food, []):
388
  info = neuro_map.get(compound)
389
  if not info:
390
  continue
@@ -393,12 +441,17 @@ def build_local_hypothesis(entities, food_compound_map, neuro_map):
393
  if not candidates:
394
  return None
395
 
396
- compuestos_str = ", ".join(sorted(set(c['compuesto'] for c in candidates)))
397
- alimentos_str = ", ".join(sorted(set(c['alimento'] for c in candidates)))
 
 
398
 
399
- condicion = f"Sensibilidad o intolerancia a {compuestos_str} presente en {alimentos_str}"
400
  if es_dolor_cabeza:
401
  condicion = f"Posible migra帽a o cefalea alimentaria por sensibilidad a aminas vasoactivas ({compuestos_str})"
 
 
 
 
402
 
403
  mecanismo = " | ".join(sorted(set(c['efecto'] for c in candidates)))
404
 
@@ -410,7 +463,6 @@ def build_local_hypothesis(entities, food_compound_map, neuro_map):
410
  "es_hipotesis_local": True
411
  }
412
 
413
- # --- B脷SQUEDA EN FOODB INSTANT脕NEA ---
414
  def find_best_foodb_matches(user_foods_es, _foodb_index, _foodb_lower_map, food_name_map, limit=3):
415
  if not user_foods_es or not _foodb_index: return []
416
 
@@ -426,17 +478,14 @@ def find_best_foodb_matches(user_foods_es, _foodb_index, _foodb_lower_map, food_
426
  found_matches = []
427
  for term in search_terms_en:
428
  term_lower = term.lower().strip()
429
- # Coincidencia exacta O(1)
430
  if term_lower in _foodb_lower_map:
431
  found_matches.append(_foodb_lower_map[term_lower])
432
  continue
433
- # B煤squeda r谩pida por subcadena acotada
434
  for k_lower, orig_key in _foodb_lower_map.items():
435
  if term_lower in k_lower:
436
  found_matches.append(orig_key)
437
  if len(found_matches) >= limit: break
438
 
439
- # Deduplicaci贸n
440
  seen = set()
441
  unique = []
442
  for item in found_matches:
@@ -471,7 +520,7 @@ def generate_detailed_analysis(query, match, entities=None, food_compound_map=No
471
  Tarea: Escribe un an谩lisis en Markdown estructurado:
472
  1. Saludo breve y emp谩tico.
473
  2. Frase inicial clara con la hip贸tesis probable.
474
- 3. Explicaci贸n de las mol茅culas responsables (ej: tiramina, histamina, cafe铆na, gluten).
475
  4. Mecanismo fisiol贸gico en t茅rminos sencillos.
476
  5. Alimentos a evitar o moderar.
477
  6. Alternativas o reemplazos sugeridos.
@@ -518,7 +567,6 @@ def generate_molecular_report_text(best_match, entities, _foodb_index, _foodb_lo
518
 
519
  found_any = False
520
 
521
- # 1. Base bioqu铆mica curada interna
522
  for food in user_foods:
523
  compounds = food_compound_map.get(food, [])
524
  if compounds:
@@ -530,7 +578,6 @@ def generate_molecular_report_text(best_match, entities, _foodb_index, _foodb_lo
530
  else:
531
  report_lines.append(f"- {compound.capitalize()}")
532
 
533
- # 2. Base de datos externa FooDB
534
  if _foodb_index:
535
  best_food_matches = find_best_foodb_matches(user_foods, _foodb_index, _foodb_lower_map, food_name_map)
536
  for food_key in best_food_matches:
@@ -573,7 +620,7 @@ if 'entities' not in st.session_state: st.session_state.entities = None
573
  if 'analysis_cache' not in st.session_state: st.session_state.analysis_cache = {}
574
 
575
  with st.form(key="search_form"):
576
- query = st.text_area("Tu Caso:", height=150, placeholder="Ej: Cuando tomo jugo de naranja o queso me da dolor de cabeza y ansiedad.")
577
  submitted = st.form_submit_button("Analizar Caso", type="primary")
578
 
579
  if submitted and query:
 
46
 
47
  client = genai.Client(
48
  api_key=APIkeydetective,
49
+ http_options=types.HttpOptions(timeout=30000)
50
  )
51
 
52
  except Exception as e:
 
110
  "cholado": ["fructosa", "lactosa", "histamina", "az煤car refinado"]
111
  }
112
 
113
+ # --- SEGURO L脫GICO: REGLAS ESTRICTAS DE EXCLUSI脫N BIOQU脥MICA ---
114
+ STRICT_COMPOUND_TRIGGERS = {
115
+ "cafe铆na": ["cafe铆na", "caffeine", "caf茅"],
116
+ "gluten": ["gluten", "trigo", "cebada", "centeno"],
117
+ "lactosa": ["lactosa", "lactose"],
118
+ "case铆na": ["case铆na", "casein"],
119
+ "tiramina": ["tiramina", "tyramine"],
120
+ "sulfitos": ["sulfito", "sulfitos", "sulfite"],
121
+ "solanina": ["solanina", "solanine"]
122
+ }
123
+
124
+ def validar_seguro_logico(entry, user_foods, food_compound_map):
125
+ """
126
+ SEGURO L脫GICO: Si una condici贸n atribuida en la BD o hip贸tesis requiere un
127
+ compuesto exclusivo (ej: cafe铆na, gluten, lactosa, tiramina), verifica que AL MENOS
128
+ UNO de los alimentos ingresados por el usuario contenga efectivamente dicho compuesto.
129
+ Evita falsos positivos como asociar cafe铆na al jugo de naranja.
130
+ """
131
+ if not user_foods:
132
+ return True
133
+
134
+ user_compounds = set()
135
+ for food in user_foods:
136
+ food_clean = food.lower().strip()
137
+ user_compounds.update(food_compound_map.get(food_clean, []))
138
+ user_compounds.add(food_clean)
139
+
140
+ texto_condicion = (entry.get("condicion_asociada", "") + " " + entry.get("compuesto_alimento", "")).lower()
141
+
142
+ for compound_key, synonyms in STRICT_COMPOUND_TRIGGERS.items():
143
+ if any(syn in texto_condicion for syn in synonyms):
144
+ tiene_compuesto = any(
145
+ syn in user_compounds or any(syn in c.lower() for c in user_compounds)
146
+ for syn in synonyms
147
+ )
148
+ if not tiene_compuesto:
149
+ logger.info(f"馃毄 SEGURO L脫GICO ACTIVADO: Se descart贸 '{entry.get('condicion_asociada')}' porque '{user_foods}' NO contiene {compound_key}.")
150
+ return False
151
+
152
+ return True
153
+
154
  COMPOUND_SYNONYM_MAP = {
155
  "gluten": ["gluten", "gliadin"],
156
  "case铆na": ["casein", "casomorphin"],
 
161
  }
162
 
163
  KNOWN_TRIGGERS_MAP = {
164
+ "Histamine": ["dolor de cabeza", "migra帽a", "inflamaci贸n", "congesti贸n nasal", "picaz贸n", "erupci贸n", "ansiedad", "diarrea", "insomnio"],
165
  "Tyramine": ["dolor de cabeza", "migra帽a"],
166
  "Phenylethylamine": ["dolor de cabeza", "migra帽a"],
167
  "Gluten": ["niebla mental", "dolor abdominal", "diarrea", "inflamaci贸n", "dolor articular", "ataxia"],
 
185
  "keywords_en": ["anxiety", "nervousness"]
186
  },
187
  "insomnio": {
188
+ "frases_es": ["insomnio", "no puedo dormir", "dificultad para dormir", "desvelado", "no duermo"],
189
  "keywords_en": ["insomnia", "sleep trouble"]
190
  }
191
  }
 
238
  "fuentes_comunes": ["quesos madurados", "vino tinto", "embutidos", "alimentos fermentados"]
239
  },
240
  "histamina": {
241
+ "efecto_neuropsicologico": "Neurotransmisor estimulante central. Su exceso por baja degradaci贸n causa insomnio, fragmentaci贸n del sue帽o, ansiedad y migra帽as.",
242
+ "fuentes_comunes": ["queso", "vino", "tomate", "pescado azul", "jugo de naranja", "alimentos conservados"]
243
  },
244
  "cafe铆na": {
245
  "efecto_neuropsicologico": "Bloquea los receptores de adenosina en el cerebro. En dosis altas o personas sensibles provoca hiperactivaci贸n del SNC, ansiedad e insomnio.",
 
254
  "fuentes_comunes": ["leche", "queso fresco", "crema de leche"]
255
  },
256
  "fructosa": {
257
+ "efecto_neuropsicologico": "La sobrecarga o malabsorci贸n de fructosa disminuye la biodisponibilidad de tript贸fano en el intestino, interfiriendo en la s铆ntesis de serotonina y melatonina (desregulando el sue帽o).",
258
  "fuentes_comunes": ["jugo de naranja", "frutas muy dulces", "miel", "jarabe de ma铆z"]
259
  },
260
  "sulfitos": {
 
262
  "fuentes_comunes": ["vino", "cerveza", "frutos secos desecados"]
263
  },
264
  "saponinas": {
265
+ "efecto_neuropsicologico": "Aumentan la permeabilidad intestinal ('intestino permeable'), facilitando la migraci贸n de lipopolisac谩ridos e induciendo neuroinflamaci贸n.",
266
  "fuentes_comunes": ["quinoa", "legumbres", "soja"]
267
  },
268
  "oxalatos": {
 
275
  }
276
  }
277
 
278
+ # --- FUNCIONES DE AUXILIO ---
279
  @st.cache_data(show_spinner=False)
280
  def generate_word_report_cached(report_text):
281
  try:
 
325
  system_prompt = f"""
326
  Rol: Extractor M茅dico JSON experto en nutrici贸n y s铆ntomas.
327
  Tarea: Analiza el texto del usuario y extrae un JSON estricto con dos claves:
328
+ - "alimentos": lista de alimentos o bebidas mencionados en espa帽ol, forma simple (ej: "jugo de naranja", "queso", "vino").
329
+ - "sintomas": lista de s铆ntomas mencionados en espa帽ol, forma cl铆nica simple (ej: "insomnio", "dolor de cabeza", "hinchaz贸n").
330
 
331
  Input: "{query}"
332
  Output (JSON estricto):
 
395
 
396
  results = []
397
  for entry in data:
398
+ # APLICACI脫N DEL SEGURO L脫GICO
399
+ if food_compound_map and not validar_seguro_logico(entry, user_foods_list, food_compound_map):
400
+ continue
401
+
402
  db_food_keywords = extract_keywords(entry.get("compuesto_alimento", ""))
403
  db_symptom_keywords = extract_keywords(" ".join(entry.get("sintomas_clave", [])))
404
 
 
425
 
426
  symptom_text = " ".join(user_symptoms).lower()
427
  headache_keywords = ["dolor de cabeza", "migra帽a", "cefalea", "jaqueca"]
428
+ insomnia_keywords = ["insomnio", "no puedo dormir", "sue帽o", "desvelado"]
429
+
430
  es_dolor_cabeza = any(k in symptom_text for k in headache_keywords)
431
+ es_insomnio = any(k in symptom_text for k in insomnia_keywords)
432
 
433
  candidates = []
434
  for food in user_foods:
435
+ for compound in food_compound_map.get(food.lower().strip(), []):
436
  info = neuro_map.get(compound)
437
  if not info:
438
  continue
 
441
  if not candidates:
442
  return None
443
 
444
+ compuestos_unicos = sorted(set(c['compuesto'] for c in candidates))
445
+ alimentos_unicos = sorted(set(c['alimento'] for c in candidates))
446
+ compuestos_str = ", ".join(compuestos_unicos)
447
+ alimentos_str = ", ".join(alimentos_unicos)
448
 
 
449
  if es_dolor_cabeza:
450
  condicion = f"Posible migra帽a o cefalea alimentaria por sensibilidad a aminas vasoactivas ({compuestos_str})"
451
+ elif es_insomnio:
452
+ condicion = f"Alteraci贸n del sue帽o / insomnio por respuesta a {compuestos_str} presente en {alimentos_str}"
453
+ else:
454
+ condicion = f"Sensibilidad o intolerancia a {compuestos_str} presente en {alimentos_str}"
455
 
456
  mecanismo = " | ".join(sorted(set(c['efecto'] for c in candidates)))
457
 
 
463
  "es_hipotesis_local": True
464
  }
465
 
 
466
  def find_best_foodb_matches(user_foods_es, _foodb_index, _foodb_lower_map, food_name_map, limit=3):
467
  if not user_foods_es or not _foodb_index: return []
468
 
 
478
  found_matches = []
479
  for term in search_terms_en:
480
  term_lower = term.lower().strip()
 
481
  if term_lower in _foodb_lower_map:
482
  found_matches.append(_foodb_lower_map[term_lower])
483
  continue
 
484
  for k_lower, orig_key in _foodb_lower_map.items():
485
  if term_lower in k_lower:
486
  found_matches.append(orig_key)
487
  if len(found_matches) >= limit: break
488
 
 
489
  seen = set()
490
  unique = []
491
  for item in found_matches:
 
520
  Tarea: Escribe un an谩lisis en Markdown estructurado:
521
  1. Saludo breve y emp谩tico.
522
  2. Frase inicial clara con la hip贸tesis probable.
523
+ 3. Explicaci贸n de las mol茅culas responsables (ej: histamina, fructosa, tiramina, gluten).
524
  4. Mecanismo fisiol贸gico en t茅rminos sencillos.
525
  5. Alimentos a evitar o moderar.
526
  6. Alternativas o reemplazos sugeridos.
 
567
 
568
  found_any = False
569
 
 
570
  for food in user_foods:
571
  compounds = food_compound_map.get(food, [])
572
  if compounds:
 
578
  else:
579
  report_lines.append(f"- {compound.capitalize()}")
580
 
 
581
  if _foodb_index:
582
  best_food_matches = find_best_foodb_matches(user_foods, _foodb_index, _foodb_lower_map, food_name_map)
583
  for food_key in best_food_matches:
 
620
  if 'analysis_cache' not in st.session_state: st.session_state.analysis_cache = {}
621
 
622
  with st.form(key="search_form"):
623
+ query = st.text_area("Tu Caso:", height=150, placeholder="Ej: Cuando tomo mucho jugo de naranja no puedo dormir.")
624
  submitted = st.form_submit_button("Analizar Caso", type="primary")
625
 
626
  if submitted and query: