""" Interfaz Gradio para la práctica final (RA3): asistente hospitalario con RAG. Autor: Maxime Pol Marcet. Soy **Maxime Pol Marcet**, y la autoría de **todo** este archivo —y de la práctica en su conjunto— es **exclusivamente mía**: he diseñado e implementado cada parte de la interfaz y de la lógica que aquí se expone. He organizado la ventana con **cabecera** «Sistema de preguntas con RAG», **fila superior** (pregunta a la izquierda, panel con sliders a la derecha), **salidas** apiladas y botón **Enviar** a ancho completo. He aplicado un **tema oscuro** con acentos morados y **CSS** propio porque así reproducía la referencia visual que buscaba; dejo claro que eso **no sustituye** los requisitos del **MOPT DAW2**: los controles obligatorios (textbox de consulta con *placeholder*, sliders de *top_k* y *umbral*, textos de respuesta y de documentos, botón **Enviar**, función `ask` y `demo.launch()`) están cumplidos; el aspecto solo los presenta de forma ordenada. He enlazado los sliders a **`config.py`** para que los valores por defecto (5 y 0,55) coincidan con el enunciado y con los tests. He expuesto la API con `api_name="ask"` (ruta típica **`/ask`**) y he fijado **`server_port=7860`** para un arranque local predecible; en **Gradio 6** paso `theme` y `css` a **`launch()`**, que es donde corresponde. En **`ask`** he unido la interfaz y la API: llamo a `recuperar_documentos` y `generar_respuesta` de **mi** `rag_engine.py`, formateo los documentos separando los pasajes con líneas en blanco y el delimitador `---`, tal como pide la práctica, y, si la consulta llega vacía, devuelvo un aviso sin invocar al modelo, porque así evito trabajo inútil y mensajes confusos. """ import gradio as gr from config import DEFAULT_TOP_K, DEFAULT_UMBRAL from rag_engine import generar_respuesta, recuperar_documentos def ask(query, top_k, umbral): """ Punto único de entrada para la UI y para la API HTTP: yo lo definí así para no duplicar la lógica RAG entre el botón y `gradio_client`. """ q = (query or "").strip() if not q: return ( "Escribe tu pregunta en inglés en el cuadro de arriba y pulsa Enviar.", "", ) top_k = int(top_k) umbral = float(umbral) docs = recuperar_documentos(q, top_k=top_k, umbral=umbral) docs_formateados = ( "\n\n---\n\n".join(docs) if docs else "No se recuperaron documentos con el umbral actual." ) respuesta = generar_respuesta(q, docs) return respuesta, docs_formateados # Tema Soft con matiz morado: lo elegí para alinear botones, sliders y tipografía con mi diseño. _RAG_THEME = gr.themes.Soft( primary_hue=gr.themes.colors.purple, neutral_hue=gr.themes.colors.slate, font=[gr.themes.GoogleFont("Inter"), "ui-sans-serif", "system-ui", "sans-serif"], ).set( body_background_fill_dark="#0b0f14", block_background_fill_dark="#121820", block_border_width="1px", block_label_background_fill_dark="#5b21b6", block_label_text_color_dark="#f8fafc", block_title_text_color_dark="#e2e8f0", body_text_color_dark="#e2e8f0", body_text_color_subdued_dark="#94a3b8", border_color_primary_dark="#334155", input_background_fill_dark="#1a2230", input_border_color_dark="#334155", button_primary_background_fill_dark="#5b21b6", button_primary_background_fill_hover_dark="#6d28d9", button_primary_text_color_dark="#ffffff", slider_color_dark="#7c3aed", ) # Estilos que añadí para el modo oscuro y las etiquetas tipo “badge”; no cambian el comportamiento de Gradio. _RAG_CSS = """ .rag-capture-wrap { max-width: 1100px; margin: 0 auto; } .rag-capture-wrap .gradio-container { background: #0b0f14 !important; color: #e2e8f0 !important; } .rag-capture-wrap .gr-markdown h1, .rag-capture-wrap .prose h1, .rag-capture-wrap .markdown.prose h1 { color: #f8fafc !important; font-weight: 700 !important; font-size: 1.75rem !important; margin-bottom: 0.35rem !important; letter-spacing: -0.02em; } .rag-capture-wrap .gr-markdown p, .rag-capture-wrap .prose p, .rag-capture-wrap .markdown.prose p { color: #cbd5e1 !important; font-size: 0.95rem !important; margin: 0 !important; } .rag-capture-wrap .label-wrap label, .rag-capture-wrap .label-wrap span { background: #5b21b6 !important; color: #f8fafc !important; border-radius: 6px !important; padding: 0.2rem 0.65rem !important; font-size: 0.78rem !important; font-weight: 600 !important; letter-spacing: 0.02em; } .rag-capture-wrap textarea, .rag-capture-wrap input[type="text"] { background: #1a2230 !important; color: #e2e8f0 !important; border-color: #334155 !important; } .rag-capture-wrap .rag-params-panel { border: 1px solid #334155 !important; border-radius: 10px !important; padding: 0.85rem 1rem 1rem !important; background: #121820 !important; } .rag-capture-wrap .rag-send button { width: 100% !important; min-height: 48px !important; font-weight: 700 !important; font-size: 1rem !important; border-radius: 8px !important; } /* `elem_classes` en Markdown aplica al contenedor del bloque (div), no a