import gradio as gr from transformers import pipeline import os import random # Añade esto para verificar la versión de Gradio en tiempo de ejecución print(f"Gradio version at runtime: {gr.__version__}") # --- Model Loading --- MODEL_ID = "Light-Dav/sentiment-analysis-full-project" try: sentiment_analyzer = pipeline("sentiment-analysis", model=MODEL_ID, top_k=None) model_loaded_successfully = True print("Sentiment analysis model loaded successfully.") except Exception as e: print(f"Error loading model: {e}") sentiment_analyzer = None model_loaded_successfully = False print("Sentiment analysis model failed to load. Please check MODEL_ID and network connection.") # --- Custom CSS with the NEW COLOR PALETTE and MAXIMUM COMPACTNESS --- custom_css = """ /* RESETEO BÁSICO Y FONDOS GENERALES */ body { background-color: #1E2B38; /* Fondo General Oscuro */ color: #FFFFFF; /* Blanco para texto principal */ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; padding: 0; margin: 0; overflow: hidden; /* Ocultar scrollbar si hay un pequeño desbordamiento */ height: 100vh; /* Asegurar que el body ocupe toda la altura del viewport */ display: flex; flex-direction: column; } /* CONTENEDOR PRINCIPAL DE GRADIO */ .gradio-container { box-shadow: 0 2px 4px rgba(0, 122, 204, 0.1); /* Sombra más sutil */ border-radius: 6px; /* Borde más pequeño */ overflow: hidden; background-color: #1E2B38; /* Fondo de la tarjeta, coincide con el body */ padding: 10px; /* Reducir padding general del contenedor */ margin-bottom: 5px; /* Reducir margen inferior */ border: 1px solid #007ACC; /* Borde sutil con Azul Oscuro */ flex-grow: 1; /* Permite que el contenedor ocupe el espacio disponible */ display: flex; flex-direction: column; } /* AJUSTES DE TÍTULOS Y PÁRRAFOS */ h1, h2, h3 { color: #00BFFF; /* Azul Brillante para títulos */ text-align: center; font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; animation: fadeIn 1s ease-in-out; margin-top: 5px; /* Margen superior muy pequeño */ margin-bottom: 8px; /* Margen inferior reducido */ font-size: 1.4em; /* h1 más pequeño */ } h2 { font-size: 1.1em; } /* h2 más pequeño */ h3 { font-size: 0.95em; } /* h3 más pequeño, casi texto normal */ p { color: #AAB7C4; /* Gris medio para texto secundario */ text-align: center; margin-bottom: 10px; /* Margen debajo de los párrafos reducido */ font-size: 0.8em; /* Tamaño de fuente más pequeño para párrafos */ line-height: 1.3; /* Espaciado entre líneas para legibilidad */ } /* COMPONENTES DE ENTRADA (TEXTBOX) */ /* Selector para el label del textbox */ .gr-textbox label, .gradio-output .label { color: #AAB7C4 !important; /* Gris medio para las etiquetas */ font-weight: bold; font-size: 0.85em; /* Etiqueta más pequeña */ margin-bottom: 3px; /* Espacio mínimo entre etiqueta y caja */ } /* Selector para el textarea del textbox */ .gr-textbox textarea { background-color: rgba(0, 122, 204, 0.1); /* Azul Oscuro muy transparente */ border: 1px solid #007ACC; /* Borde con Azul Oscuro */ color: #FFFFFF; /* Texto blanco en el textarea */ border-radius: 5px; /* Bordes más pequeños */ padding: 6px; /* Reducir padding del textarea */ font-size: 0.9em; /* Fuente más pequeña en el textarea */ resize: vertical; /* Permite redimensionar verticalmente */ min-height: 50px; /* Altura mínima */ white-space: pre-wrap; /* Permite saltos de línea y respeta espacios */ word-wrap: break-word; /* Rompe palabras largas */ overflow-wrap: break-word; /* Estándar más moderno */ } /* BOTONES PRINCIPALES */ /* Cambiado el color del botón primario */ .gr-button.primary { background-color: #00BFFF !important; /* ¡Azul Brillante para el botón primario (Acento)! */ color: #1E2B38 !important; /* Texto oscuro para el botón primario */ border-radius: 5px; transition: background-color 0.3s ease; padding: 7px 12px; /* Padding más pequeño del botón */ font-size: 0.9em; /* Un poco más pequeño para el botón */ font-weight: bold; margin-top: 8px; /* Reducir margen superior */ margin-bottom: 5px; /* Añadir un pequeño margen inferior */ } .gr-button.primary:hover { background-color: #007ACC !important; /* Azul Oscuro al pasar el ratón */ color: #FFFFFF !important; /* Texto blanco al pasar el ratón */ } /* COMPONENTES DE SALIDA (HTML, LABEL, JSON) */ .gradio-output { border: 1px solid #4A5B6C; /* Borde sutil con Gris Claro */ border-radius: 5px; /* Borde más pequeño */ padding: 8px; /* Reducir padding de la salida */ margin-top: 8px; /* Reducir margen superior */ background-color: rgba(0, 122, 204, 0.08); /* Fondo más sutil para la salida */ color: #FFFFFF; /* Texto blanco en la salida */ flex-grow: 1; /* Permitir que ocupe el espacio restante */ display: flex; flex-direction: column; justify-content: center; /* Centrar verticalmente el contenido */ min-height: 80px; /* Altura mínima para asegurar visibilidad */ width: 100%; /* Asegurar que ocupe todo el ancho disponible */ box-sizing: border-box; /* Incluir padding y borde en el ancho */ } .gradio-output .label-text { color: #00BFFF !important; /* Color de acento para el texto del label (LABEL_1, LABEL_0, etc.) */ font-weight: bold; } .gradio-output .label-score { color: #FFFFFF !important; /* Blanco para el score del label */ } .gradio-output .label-container { padding-bottom: 5px !important; /* Reducir padding entre elementos del label */ } /* DISPLAY DE SENTIMIENTO (DENTRO DE HTML OUTPUT) */ .sentiment-display { text-align: center; padding: 6px; /* Reducir padding */ border-radius: 4px; /* Borde más pequeño */ margin-top: 3px; /* Margen muy pequeño */ font-size: 1em; /* Un poco más pequeño */ font-weight: bold; color: #FFFFFF; /* Texto blanco para todos los sentimientos */ /* Asegurar que el texto se envuelva */ white-space: normal; word-wrap: break-word; overflow-wrap: break-word; } .sentiment-display p { /* Estilo específico para el párrafo de descripción */ font-size: 0.75em; /* Tamaño más pequeño para la descripción */ margin-top: 3px; margin-bottom: 0; line-height: 1.2; white-space: normal; /* Asegurar que el párrafo se envuelva */ word-wrap: break-word; overflow-wrap: break-word; } .sentiment-positive { background-color: #28a745; } /* Mantener estos colores por claridad de sentimiento */ .sentiment-negative { background-color: #dc3545; } .sentiment-neutral { background-color: #007BFF; } /* NUEVOS BOTONES DE EJEMPLO DIRECTOS (NO gr.Examples) */ /* Ajustes para el texto de los botones de ejemplo */ .example-button { background-color: #4A5B6C !important; /* Gris Claro para los botones de ejemplo */ color: #FFFFFF !important; border: 1px solid #4A5B6C; border-radius: 3px; /* Borde más pequeño */ padding: 5px 8px; /* Padding más pequeño */ margin: 2px; /* Margen mínimo entre botones */ transition: background-color 0.3s ease; font-size: 0.7em; /* ¡Tamaño de fuente más pequeño para los ejemplos! */ white-space: normal; /* ¡Permitir que el texto se rompa en varias líneas! */ word-wrap: break-word; /* Rompe palabras largas */ overflow-wrap: break-word; /* Estándar más moderno */ flex-shrink: 1; /* Permitir que se encojan si es necesario */ cursor: pointer; /* Indicar que son clickeables */ flex-grow: 1; /* Permitir que los botones de ejemplo crezcan para llenar el espacio */ min-width: 80px; /* Asegurar un ancho mínimo para cada botón */ text-align: center; /* Centrar el texto dentro del botón */ } .example-button:hover { background-color: #007ACC !important; /* Azul Oscuro al pasar el ratón por los ejemplos */ border-color: #00BFFF !important; } /* Contenedor de los botones de ejemplo para forzar el "wrap" con flexbox */ .example-buttons-container { display: flex; flex-wrap: wrap; /* ¡Esto simula el wrap! */ justify-content: center; /* Centrar los botones si no llenan la línea */ align-items: stretch; /* Asegura que los botones tengan la misma altura si el texto envuelve */ margin-top: 5px; /* Pequeño margen superior */ margin-bottom: 10px; /* Pequeño margen inferior */ } /* LÍNEAS DIVISORIAS */ hr { border-top: 1px solid #4A5B6C; /* Línea divisoria con Gris Claro */ margin-top: 10px; /* Reducir margen superior */ margin-bottom: 10px; /* Reducir margen inferior */ } /* ANIMACIÓN */ @keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } } /* OCULTAR EL PIE DE PÁGINA DE GRADIO (CRUCIAL PARA IFRAMES PEQUEÑOS) */ footer { opacity: 0; /* Hacerlo invisible */ height: 0 !important; /* Eliminar su altura */ margin: 0 !important; /* Eliminar sus márgenes */ padding: 0 !important; /* Eliminar su padding */ pointer-events: none; /* No interactuable */ visibility: hidden; /* Asegurar que no ocupe espacio visual */ } .gradio-app > div > :last-child:not(.gradio-container) { /* Apuntar al elemento del pie de página */ display: none !important; /* Otra forma de ocultar completamente si lo anterior no funciona */ } /* Ajustes para el contenedor de la salida de Label (para que el texto interno envuelva) */ .gradio-output > .label { white-space: normal !important; /* Permitir que el texto envuelva */ word-wrap: break-word !important; /* Romper palabras largas */ overflow-wrap: break-word !important; /* Estándar moderno */ } /* Asegurarse de que el texto dentro de los resultados de Gradio (si no es HTML) también envuelva */ .gradio-output > div { white-space: normal !important; word-wrap: break-word !important; overflow-wrap: break-word !important; } /* Ajustes para el texto dentro del gr.Label (Confidence Scores) si se hace visible */ .gr-label-container { font-size: 0.8em; /* Reducir la fuente de los scores de confianza */ } .gr-label-container .label-text { font-size: 1em !important; /* Mantener la etiqueta del score legible */ } .gr-label-container .label-score { font-size: 0.9em !important; /* Un poco más pequeño que la etiqueta */ } """ # --- Helper Function for Sentiment Interpretation --- def interpret_sentiment(label, score): emoji = "" description = "" color_class = "" if label.lower() == "positive" or label.lower() == "label_2": emoji = "😊" description = "Positive sentiment detected." color_class = "sentiment-positive" elif label.lower() == "negative" or label.lower() == "label_0": emoji = "😠" description = "Negative sentiment detected." color_class = "sentiment-negative" elif label.lower() == "neutral" or label.lower() == "label_1": emoji = "😐" description = "Neutral sentiment detected." color_class = "sentiment-neutral" else: emoji = "❓" description = "Sentiment not determined." color_class = "" # El HTML de salida también debe permitir el salto de línea y reducir la fuente del párrafo return f"
{description}
" # --- Sentiment Analysis Function --- def analyze_sentiment(text): if not model_loaded_successfully: return ( "Model not loaded.
", {}, {"error": "Model loading failed."} ) if not text.strip(): return ( "Type text to analyze.
", {}, {"info": "No text entered."} ) try: results = sentiment_analyzer(text)[0] results_sorted = sorted(results, key=lambda x: x['score'], reverse=True) top_sentiment = results_sorted[0] label = top_sentiment['label'] score = top_sentiment['score'] confidence_scores_output = {item['label']: item['score'] for item in results} overall_sentiment_display = interpret_sentiment(label, score) return (overall_sentiment_display, confidence_scores_output, results) except Exception as e: return ( f"Analysis failed.
", {}, {"error_message": str(e)} ) # --- Lista completa de ejemplos para selección aleatoria --- ALL_EXAMPLES = [ "The product quality is absolutely outstanding and worth every penny!", "I found the customer support unhelpful and quite rude, a terrible experience.", "The new software update introduced several bugs, making it very unstable.", "This book is a captivating read, I couldn't put it down!", "The delivery was late, and the package arrived damaged.", "Despite the bad reviews, I thoroughly enjoyed the film and its unique plot.", "The instructions were unclear, leading to a lot of confusion during assembly.", "What a delicious meal! Every dish was prepared to perfection.", "I'm very disappointed with the recent policy changes; they are unfair.", "The new art exhibition is thought-provoking and visually stunning.", "Traffic was unexpectedly heavy, causing significant delays.", "Overall, a solid performance, though there's room for improvement." ] # --- Gradio Interface --- with gr.Blocks(css=custom_css, theme=None) as demo: gr.Markdown("Analyze the emotional tone of your English text.
") with gr.Column(elem_classes="gradio-container"): text_input = gr.Textbox( lines=2, placeholder="Type your English text here...", label="Your Text", interactive=True, value=random.choice(ALL_EXAMPLES) # Establece un ejemplo aleatorio inicial ) analyze_btn = gr.Button("Analyze Sentiment", variant="primary") # --- Definir las salidas ANTES de usarlas en los eventos de los botones --- overall_sentiment_output = gr.HTML(label="Overall Sentiment") confidence_scores_output = gr.Label(num_top_classes=3, label="Confidence Scores", visible=False) raw_output = gr.JSON(label="Raw Model Output", visible=False) # --- Título para los ejemplos --- gr.Markdown("