""" Gradio UI for UPB RAG Career Exploration Assistant Deployed on HuggingFace Spaces """ import os import gradio as gr from pathlib import Path import sys from dotenv import load_dotenv # Load environment variables from .env file (for local development) # HuggingFace Spaces will use Secrets instead load_dotenv() # Add src to path sys.path.insert(0, str(Path(__file__).parent / "src")) from setup_retrieval import setup_retrieval_system from rag.chain import UPBRAGChain # Initialize RAG system print("🚀 Initializing UPB RAG System...") retriever, vectorstore_manager, chunks = setup_retrieval_system() rag_chain = UPBRAGChain(retriever, retrieval_method="hybrid") print("✅ System ready!") # Custom CSS for better UI custom_css = """ .header { text-align: center; padding: 20px; background: linear-gradient(135deg, #1e3a8a 0%, #3b82f6 100%); color: white; border-radius: 10px; margin-bottom: 20px; } .header h1 { margin: 0; font-size: 2.5em; font-weight: bold; } .header p { margin: 10px 0 0 0; font-size: 1.2em; opacity: 0.9; } .disclaimer { background-color: #fef3c7; border-left: 4px solid #f59e0b; padding: 15px; margin: 15px 0; border-radius: 5px; font-size: 0.9em; } .footer { text-align: center; padding: 20px; color: #6b7280; font-size: 0.9em; } .chat-message { padding: 10px; border-radius: 8px; margin: 5px 0; } .source-box { background-color: #f3f4f6; border-left: 3px solid #3b82f6; padding: 10px; margin: 10px 0; border-radius: 5px; font-size: 0.85em; } """ def format_sources(sources): """Format source citations nicely""" if not sources: return "" sources_text = "\n\n---\n\n### 📚 Fuentes consultadas:\n\n" seen = set() for source in sources: source_key = (source.get('category', ''), source.get('source', '')) if source_key not in seen: seen.add(source_key) category = source.get('category', 'general').title() source_name = source.get('source', 'N/A') sources_text += f"- **{category}**: `{source_name}`\n" return sources_text def chat_with_upb(message, history, include_sources): """ Main chat function for Gradio interface Args: message: User's message history: Chat history (list of [user_msg, bot_msg] pairs) include_sources: Boolean to show source citations Returns: Updated chat history """ if not message or not message.strip(): return history try: # Get response from RAG chain response = rag_chain.invoke(message, include_sources=include_sources) # Format answer answer = response['answer'] # Add sources if requested if include_sources and response.get('sources'): answer += format_sources(response['sources']) # Update history history.append([message, answer]) return history except Exception as e: error_msg = f"⚠️ Lo siento, ocurrió un error: {str(e)}\n\nPor favor, intenta reformular tu pregunta." history.append([message, error_msg]) return history def clear_conversation(): """Clear chat history""" rag_chain.clear_history() return [] def get_example_questions(): """Return example questions for quick start""" return [ ["¿Qué ingenierías ofrece la UPB?"], ["¿Cuáles programas tienen acreditación ABET?"], ["Cuéntame sobre Ingeniería de Sistemas"], ["¿Qué becas están disponibles?"], ["¿Cómo puedo contactar a la UPB?"], ["¿Qué es la Ingeniería en Ciencia de Datos?"], ] # Create Gradio interface with gr.Blocks(css=custom_css, theme=gr.themes.Soft()) as demo: # Header gr.HTML("""

🎓 UPB Careers Assistant

Asistente Virtual para Exploración de Carreras - Universidad Pontificia Bolivariana

""") # Disclaimer gr.HTML("""
⚠️ Nota importante: Este asistente proporciona información general sobre los programas de ingeniería de la UPB. Para información específica sobre matrículas, fechas límite, o detalles particulares del plan de estudios, por favor contacta directamente con la oficina de admisiones de la UPB.
""") # Main chat interface with gr.Row(): with gr.Column(scale=4): chatbot = gr.Chatbot( label="💬 Conversación", height=500, show_label=True, avatar_images=(None, "🎓"), bubble_full_width=False, ) with gr.Row(): msg = gr.Textbox( label="Tu pregunta", placeholder="Escribe tu pregunta aquí... (ejemplo: ¿Qué ingenierías ofrece la UPB?)", scale=4, lines=2, ) submit_btn = gr.Button("Enviar 📤", variant="primary", scale=1) with gr.Row(): clear_btn = gr.Button("🗑️ Limpiar conversación", variant="secondary") sources_checkbox = gr.Checkbox( label="📚 Mostrar fuentes", value=True, info="Incluir referencias a documentos fuente" ) with gr.Column(scale=1): gr.Markdown("### 💡 Preguntas de ejemplo") gr.Markdown("Haz clic en cualquier pregunta para probarla:") example_btns = [] for example in get_example_questions(): btn = gr.Button(example[0], size="sm") example_btns.append((btn, example[0])) # Information section with gr.Accordion("ℹ️ Información del Sistema", open=False): gr.Markdown(f""" ### Acerca de este asistente Este asistente utiliza **Retrieval-Augmented Generation (RAG)** para proporcionar información precisa sobre los programas de ingeniería de la Universidad Pontificia Bolivariana. **Características:** - 🤖 **LLM**: Azure GPT-4o-mini (temperatura: 0.0 para máxima precisión) - 📊 **Base de conocimiento**: {len(chunks)} fragmentos de {len(set(doc.metadata.get('source') for doc in chunks if hasattr(doc, 'metadata')))} documentos - 🔍 **Método de búsqueda**: Híbrido (BM25 + Vector con RRF) - 📚 **Programas incluidos**: 12 ingenierías - ✅ **Información de acreditaciones**: ABET, Alta Calidad **Temas cubiertos:** - Información general sobre la UPB - 12 programas de ingeniería (descripción, plan de estudios, perfil profesional) - Procesos de inscripción y admisión - Becas y financiación - Información de contacto **Limitaciones:** - La información está basada en documentos curados manualmente - Para detalles específicos de fechas, costos exactos, o cambios recientes, consulta directamente con la UPB - El sistema puede no tener información sobre cursos muy específicos del plan de estudios **Consejos de uso:** - Haz preguntas específicas y claras - Puedes hacer preguntas de seguimiento (el sistema mantiene el contexto) - Usa el botón "Limpiar conversación" para empezar un tema nuevo """) # Statistics with gr.Accordion("📊 Estadísticas del Sistema", open=False): gr.Markdown(f""" - **Total de documentos cargados**: {len(set(doc.metadata.get('source') for doc in chunks if hasattr(doc, 'metadata')))} - **Total de fragmentos procesados**: {len(chunks)} - **Promedio de caracteres por fragmento**: {sum(len(doc.page_content) for doc in chunks) // len(chunks) if chunks else 0} - **Categorías disponibles**: Ingenierías, Información general, Becas, Inscripciones, Contacto - **Modelo de embeddings**: Azure text-embedding-3-small - **Vector store**: FAISS (CPU) """) # Footer gr.HTML(""" """) # Event handlers def submit_message(message, history, sources): return "", chat_with_upb(message, history, sources) # Submit button submit_btn.click( submit_message, inputs=[msg, chatbot, sources_checkbox], outputs=[msg, chatbot], ) # Enter key msg.submit( submit_message, inputs=[msg, chatbot, sources_checkbox], outputs=[msg, chatbot], ) # Clear button clear_btn.click( clear_conversation, outputs=chatbot, ) # Example buttons for btn, text in example_btns: btn.click( lambda t=text: (t, ""), outputs=[msg, msg], ).then( submit_message, inputs=[msg, chatbot, sources_checkbox], outputs=[msg, chatbot], ) # Launch configuration if __name__ == "__main__": # For local development demo.launch( server_name="0.0.0.0", server_port=7860, share=False, show_error=True, )