Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -46,7 +46,7 @@ try:
|
|
| 46 |
|
| 47 |
client = genai.Client(
|
| 48 |
api_key=APIkeydetective,
|
| 49 |
-
http_options=types.HttpOptions(timeout=30000)
|
| 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
|
| 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
|
| 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
|
| 225 |
"fuentes_comunes": ["quinoa", "legumbres", "soja"]
|
| 226 |
},
|
| 227 |
"oxalatos": {
|
|
@@ -234,7 +275,7 @@ INTEGRATED_NEURO_FOOD_MAP = {
|
|
| 234 |
}
|
| 235 |
}
|
| 236 |
|
| 237 |
-
# --- FUNCIONES DE
|
| 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: "
|
| 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 |
-
|
| 397 |
-
|
|
|
|
|
|
|
| 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:
|
| 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
|
| 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:
|