from collections import Counter import json def predict_category(new_incident, k=3, supabase=None, embed_text=None): """ Predice la categoría de un nuevo incidente basándose en similitudes con embeddings almacenados en Supabase. Retorna los niveles de clasificación, el id_case más similar y el tareas_json completo asociado. """ if supabase is None or embed_text is None: raise ValueError("Debes proporcionar los parámetros 'supabase' y 'embed_text'.") # Obtener embedding del nuevo incidente query_emb = embed_text(new_incident) # Llamada al procedimiento remoto en Supabase result = supabase.rpc("match_case_email", { "query_embedding": query_emb, "match_count": k }).execute() matches = result.data if not matches: return { "niveles": None, "id_case": None, "tareas_json": None } # Mostrar matches y validar tareas_json for i, m in enumerate(matches): print(f"\n🔍 Match #{i+1} - ID_CASE: {m['id_case']} - Similaridad: {m['similarity']:.2f}") if m.get("tareas_json"): print("🧠 tareas_json detectado ✅") print(str(m["tareas_json"])[:500]) # solo muestra primeros 500 caracteres else: print("⚠️ tareas_json está vacío o es None ❌") # Clasificación por mayoría (niveles) niveles = {} for lvl in ["case_level_0", "case_level_1", "case_level_2", "case_level_3"]: valores = [m[lvl] for m in matches if m.get(lvl)] niveles[lvl.replace("case_", "nivel_")] = Counter(valores).most_common(1)[0][0] if valores else None print("\n📥 Input del cliente:") print(new_incident) print("\n🔎 Casos similares:") for m in matches: print( f" ({m['similarity']:.2f}) [{m['id_case']}] {m['description_context']} → " f"{m['case_level_0']} / {m['case_level_1']} / {m['case_level_2']} / {m['case_level_3']}" ) print("\n📊 Clasificación:") for key, val in niveles.items(): print(f" {key.capitalize()}: {val}") # Obtener el match con mayor similarity (siempre, tenga o no tareas_json) match_mas_similar = matches[0] if matches else None # Obtener el match con tareas_json válido y mejor similarity match_con_tareas = next( (m for m in sorted(matches, key=lambda x: x["similarity"], reverse=True) if m.get("tareas_json")), None ) if not match_con_tareas: print("❌ Ningún match tiene tareas_json válido.") resumen = ( f"📥 **Correo contextualizado:**\n{new_incident}\n\n" "🔎 **Casos similares encontrados (sin tareas_json):**\n" + "\n".join([ f"({m['similarity']:.2f}) [ID_CASE {m['id_case']}] {m['description_context']} → " f"{m['case_level_0']} / {m['case_level_1']} / {m['case_level_2']} / {m['case_level_3']}" for m in matches ]) ) return { "niveles": niveles, "id_case": None, "tareas_json": None, "resumen": resumen } # Extraer el tareas_json completo (sin cortar) tareas_json = match_con_tareas["tareas_json"] id_case_con_tareas = match_con_tareas["id_case"] id_case_mas_similar = match_mas_similar["id_case"] # Extraer la clasificación del caso más similar (real) nivel_0 = match_mas_similar.get("case_level_0") nivel_1 = match_mas_similar.get("case_level_1") nivel_2 = match_mas_similar.get("case_level_2") nivel_3 = match_mas_similar.get("case_level_3") print(f"\n✅ Retornando tareas_json del caso más similar (ID_CASE: {id_case_mas_similar})") # 🔹 Construir resumen legible para interfaz resumen = f"📥 Interpretación del correo:\n{new_incident}\n\n" resumen += "🔎 Casos similares de Product Issues en Vibia encontrados desde Julio 2025:\n" for m in matches[:3]: resumen += ( f" 🎯 Caso Nº {m['id_case']} (score: {m['similarity']:.2f}) : {m['description_context'][:400]}...\n" f" Clasificación → {m['case_level_0']} / {m['case_level_1']} / {m['case_level_2']} / {m['case_level_3']}\n\n" ) resumen += ( f"🧠 Clasificación recomendada (basada en el caso más similar Nº {id_case_mas_similar})\n" f" Level 0: {nivel_0}\n" f" Level 1: {nivel_1}\n" f" Level 2: {nivel_2}\n" f" Level 3: {nivel_3}\n\n" ) # ✅ Retornar ambos: resumen legible + datos técnicos return { "resumen": resumen, "niveles": niveles, "id_case": id_case_con_tareas, "tareas_json": tareas_json }