Spaces:
Running
Running
Update app.py
Browse files
app.py
CHANGED
|
@@ -270,14 +270,21 @@ def sanitize_text(text):
|
|
| 270 |
def extract_and_infer_with_gemini(query, condiciones):
|
| 271 |
if not model: return None
|
| 272 |
condiciones_str = "\n".join([f"- {c}" for c in condiciones])
|
|
|
|
|
|
|
| 273 |
system_prompt = f"""
|
| 274 |
Eres un asistente de triaje cl铆nico experto. Tu tarea es analizar la consulta de un usuario y extraer tres tipos de informaci贸n:
|
| 275 |
1. `alimentos`: Lista de alimentos consumidos.
|
| 276 |
-
2. `sintomas`: Lista de s铆ntomas descritos
|
| 277 |
-
3. `condicion_probable`: Tu mejor inferencia sobre cu谩l de las siguientes condiciones podr铆a explicar los s铆ntomas
|
|
|
|
| 278 |
LISTA DE CONDICIONES POSIBLES:
|
| 279 |
{condiciones_str}
|
| 280 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 281 |
Consulta: "{query}"
|
| 282 |
"""
|
| 283 |
try:
|
|
@@ -292,46 +299,43 @@ def extract_and_infer_with_gemini(query, condiciones):
|
|
| 292 |
logger.error(f"Error en la extracci贸n/inferencia con Gemini: {e}")
|
| 293 |
st.error(f"Hubo un problema al interpretar tu consulta con la IA.")
|
| 294 |
return None
|
|
|
|
| 295 |
def find_best_matches_hybrid(entities, data):
|
| 296 |
if not entities or not data: return []
|
| 297 |
|
| 298 |
-
# 1. Extracci贸n de entidades del usuario (sin cambios)
|
| 299 |
user_symptoms = set(sanitize_text(s) for s in entities.get("sintomas", []))
|
| 300 |
user_foods = set(sanitize_text(f) for f in entities.get("alimentos", []))
|
| 301 |
inferred_condition_raw = sanitize_text(entities.get("condicion_probable", ""))
|
| 302 |
|
| 303 |
-
# 2. Obtenci贸n de t茅rminos de b煤squeda (sin cambios)
|
| 304 |
candidate_terms = set(user_foods)
|
| 305 |
for food in user_foods:
|
| 306 |
food_sanitized = sanitize_text(food)
|
| 307 |
if food_sanitized in FOOD_TO_COMPOUND_MAP:
|
| 308 |
candidate_terms.update(c.lower() for c in FOOD_TO_COMPOUND_MAP[food_sanitized])
|
|
|
|
|
|
|
|
|
|
| 309 |
|
| 310 |
-
# 3. L贸gica de puntuaci贸n CORREGIDA Y ROBUSTA
|
| 311 |
results = []
|
| 312 |
for entry in data:
|
| 313 |
-
# --- PASO CLAVE: FILTRO OBLIGATORIO POR ALIMENTO ---
|
| 314 |
entry_compounds_text = sanitize_text(entry.get("compuesto_alimento", ""))
|
| 315 |
food_match = any(term in entry_compounds_text for term in candidate_terms)
|
| 316 |
|
| 317 |
-
# Si NO hay conexi贸n entre la comida del usuario y la condici贸n, se ignora por completo.
|
| 318 |
if not food_match:
|
| 319 |
continue
|
| 320 |
|
| 321 |
-
#
|
| 322 |
-
score_details = {'condition': 0, 'food': 15, 'symptoms': 0
|
| 323 |
|
| 324 |
-
# Bono de Condici贸n: Si la IA la sugiri贸, recibe un gran bono.
|
| 325 |
if inferred_condition_raw:
|
| 326 |
entry_condition_sanitized = sanitize_text(entry.get("condicion_asociada", ""))
|
| 327 |
is_match = (entry_condition_sanitized == inferred_condition_raw)
|
| 328 |
if not is_match and entry_condition_sanitized in CONDITION_SYNONYMS:
|
| 329 |
-
if inferred_condition_raw in [sanitize_text(s) for s in CONDITION_SYNONYMS
|
| 330 |
is_match = True
|
| 331 |
if is_match:
|
| 332 |
score_details['condition'] = 100
|
| 333 |
|
| 334 |
-
# Puntuaci贸n por S铆ntomas
|
| 335 |
entry_symptoms_keys = set(sanitize_text(s) for s in entry.get("sintomas_clave", []))
|
| 336 |
symptom_score = 0
|
| 337 |
matched_symptoms = []
|
|
@@ -343,8 +347,9 @@ def find_best_matches_hybrid(entities, data):
|
|
| 343 |
break
|
| 344 |
score_details['symptoms'] = symptom_score
|
| 345 |
|
| 346 |
-
#
|
| 347 |
-
|
|
|
|
| 348 |
|
| 349 |
results.append({
|
| 350 |
'entry': entry,
|
|
@@ -354,6 +359,10 @@ def find_best_matches_hybrid(entities, data):
|
|
| 354 |
|
| 355 |
if not results: return []
|
| 356 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 357 |
# 4. Ordenar y devolver (sin cambios)
|
| 358 |
sorted_results = sorted(results, key=lambda x: x['score']['total'], reverse=True)
|
| 359 |
return sorted_results
|
|
|
|
| 270 |
def extract_and_infer_with_gemini(query, condiciones):
|
| 271 |
if not model: return None
|
| 272 |
condiciones_str = "\n".join([f"- {c}" for c in condiciones])
|
| 273 |
+
|
| 274 |
+
# Prompt mejorado para ser m谩s estricto
|
| 275 |
system_prompt = f"""
|
| 276 |
Eres un asistente de triaje cl铆nico experto. Tu tarea es analizar la consulta de un usuario y extraer tres tipos de informaci贸n:
|
| 277 |
1. `alimentos`: Lista de alimentos consumidos.
|
| 278 |
+
2. `sintomas`: Lista de s铆ntomas descritos.
|
| 279 |
+
3. `condicion_probable`: Tu mejor inferencia sobre cu谩l de las siguientes condiciones podr铆a explicar la **conexi贸n directa entre los alimentos y los s铆ntomas mencionados**. Debes elegir la opci贸n m谩s l贸gica de la lista. Si ninguna encaja bien, puedes dejarlo en blanco.
|
| 280 |
+
|
| 281 |
LISTA DE CONDICIONES POSIBLES:
|
| 282 |
{condiciones_str}
|
| 283 |
+
|
| 284 |
+
Devuelve la respuesta 脷NICAMENTE en formato JSON estricto.
|
| 285 |
+
|
| 286 |
+
Ejemplo: si el usuario dice "vino tinto y dolor de cabeza", la condici贸n probable es "Histaminosis / Intolerancia a la Histamina." o "Migra帽a.", no "Intolerancia a la Lactosa.".
|
| 287 |
+
|
| 288 |
Consulta: "{query}"
|
| 289 |
"""
|
| 290 |
try:
|
|
|
|
| 299 |
logger.error(f"Error en la extracci贸n/inferencia con Gemini: {e}")
|
| 300 |
st.error(f"Hubo un problema al interpretar tu consulta con la IA.")
|
| 301 |
return None
|
| 302 |
+
|
| 303 |
def find_best_matches_hybrid(entities, data):
|
| 304 |
if not entities or not data: return []
|
| 305 |
|
|
|
|
| 306 |
user_symptoms = set(sanitize_text(s) for s in entities.get("sintomas", []))
|
| 307 |
user_foods = set(sanitize_text(f) for f in entities.get("alimentos", []))
|
| 308 |
inferred_condition_raw = sanitize_text(entities.get("condicion_probable", ""))
|
| 309 |
|
|
|
|
| 310 |
candidate_terms = set(user_foods)
|
| 311 |
for food in user_foods:
|
| 312 |
food_sanitized = sanitize_text(food)
|
| 313 |
if food_sanitized in FOOD_TO_COMPOUND_MAP:
|
| 314 |
candidate_terms.update(c.lower() for c in FOOD_TO_COMPOUND_MAP[food_sanitized])
|
| 315 |
+
# A帽adir tambi茅n los componentes del vino directamente para ser m谩s robusto
|
| 316 |
+
if food_sanitized in ["vino", "vino tinto", "vino rojo"]:
|
| 317 |
+
candidate_terms.update(["histamina", "tiramina", "sulfitos"])
|
| 318 |
|
|
|
|
| 319 |
results = []
|
| 320 |
for entry in data:
|
|
|
|
| 321 |
entry_compounds_text = sanitize_text(entry.get("compuesto_alimento", ""))
|
| 322 |
food_match = any(term in entry_compounds_text for term in candidate_terms)
|
| 323 |
|
|
|
|
| 324 |
if not food_match:
|
| 325 |
continue
|
| 326 |
|
| 327 |
+
# Inicializamos el diccionario SIN el total
|
| 328 |
+
score_details = {'condition': 0, 'food': 15, 'symptoms': 0}
|
| 329 |
|
|
|
|
| 330 |
if inferred_condition_raw:
|
| 331 |
entry_condition_sanitized = sanitize_text(entry.get("condicion_asociada", ""))
|
| 332 |
is_match = (entry_condition_sanitized == inferred_condition_raw)
|
| 333 |
if not is_match and entry_condition_sanitized in CONDITION_SYNONYMS:
|
| 334 |
+
if inferred_condition_raw in [sanitize_text(s) for s in CONDITION_SYNONYMS.get(entry_condition_sanitized, [])]:
|
| 335 |
is_match = True
|
| 336 |
if is_match:
|
| 337 |
score_details['condition'] = 100
|
| 338 |
|
|
|
|
| 339 |
entry_symptoms_keys = set(sanitize_text(s) for s in entry.get("sintomas_clave", []))
|
| 340 |
symptom_score = 0
|
| 341 |
matched_symptoms = []
|
|
|
|
| 347 |
break
|
| 348 |
score_details['symptoms'] = symptom_score
|
| 349 |
|
| 350 |
+
# --- L脥NEA CORREGIDA Y CR脥TICA ---
|
| 351 |
+
# Calculamos la puntuaci贸n total y la a帽adimos al diccionario
|
| 352 |
+
score_details['total'] = score_details['condition'] + score_details['food'] + score_details['symptoms']
|
| 353 |
|
| 354 |
results.append({
|
| 355 |
'entry': entry,
|
|
|
|
| 359 |
|
| 360 |
if not results: return []
|
| 361 |
|
| 362 |
+
# Ahora el ordenamiento funcionar谩 correctamente porque 'total' tiene el valor correcto
|
| 363 |
+
sorted_results = sorted(results, key=lambda x: x['score']['total'], reverse=True)
|
| 364 |
+
return sorted_results
|
| 365 |
+
|
| 366 |
# 4. Ordenar y devolver (sin cambios)
|
| 367 |
sorted_results = sorted(results, key=lambda x: x['score']['total'], reverse=True)
|
| 368 |
return sorted_results
|