Spaces:
Runtime error
Runtime error
| """ | |
| 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(""" | |
| <div class="header"> | |
| <h1>🎓 UPB Careers Assistant</h1> | |
| <p>Asistente Virtual para Exploración de Carreras - Universidad Pontificia Bolivariana</p> | |
| </div> | |
| """) | |
| # Disclaimer | |
| gr.HTML(""" | |
| <div class="disclaimer"> | |
| <strong>⚠️ Nota importante:</strong> 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. | |
| </div> | |
| """) | |
| # 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(""" | |
| <div class="footer"> | |
| <p> | |
| 💻 Desarrollado con LangChain, FAISS, y Gradio | | |
| 🚀 Desplegado en HuggingFace Spaces | | |
| 📧 Universidad Pontificia Bolivariana | |
| </p> | |
| <p style="font-size: 0.8em; color: #9ca3af;"> | |
| Versión 1.0 | Octubre 2025 | |
| </p> | |
| </div> | |
| """) | |
| # 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, | |
| ) | |