Spaces:
Sleeping
Sleeping
| 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"<div class='sentiment-display {color_class}'>{emoji} {label.upper()} ({score:.2f})</div>" + \ | |
| f"<p style='color: #FFFFFF; font-size: 0.7em; margin-top: 3px; margin-bottom: 0; line-height: 1.2; white-space: normal; word-wrap: break-word;'>{description}</p>" | |
| # --- Sentiment Analysis Function --- | |
| def analyze_sentiment(text): | |
| if not model_loaded_successfully: | |
| return ( | |
| "<div class='sentiment-display'>⚠️ Model Error ⚠️</div><p style='color: #FFFFFF; font-size: 0.7em;'>Model not loaded.</p>", | |
| {}, | |
| {"error": "Model loading failed."} | |
| ) | |
| if not text.strip(): | |
| return ( | |
| "<div class='sentiment-display'>✍️ Enter text ✍️</div><p style='color: #FFFFFF; font-size: 0.7em;'>Type text to analyze.</p>", | |
| {}, | |
| {"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"<div class='sentiment-display'>❌ Error ❌</div><p style='color: #FFFFFF; font-size: 0.7em;'>Analysis failed.</p>", | |
| {}, | |
| {"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("<h1 style='color: #00BFFF;'>🌌 Sentiment Analyzer 🌌</h1>") | |
| gr.Markdown("<p style='color: #AAB7C4;'>Analyze the emotional tone of your English text.</p>") | |
| 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("<h3 style='color: #00BFFF; margin-top: 5px; margin-bottom: 5px;'>Try examples:</h3>") | |
| # Contenedor para los botones de ejemplo para manejar el "wrap" vía CSS | |
| with gr.Row(elem_classes="example-buttons-container"): | |
| # Generar 3 botones de ejemplo aleatorios | |
| for example_text in random.sample(ALL_EXAMPLES, 3): | |
| gr.Button( | |
| example_text, | |
| elem_classes="example-button" | |
| ).click( | |
| fn=lambda x: x, # Función simple para pasar el texto del botón al textbox | |
| inputs=[gr.State(example_text)], # Usamos gr.State para pasar el texto del botón | |
| outputs=text_input, | |
| ).then( | |
| fn=analyze_sentiment, # Luego de cargar el texto, ejecuta el análisis | |
| inputs=text_input, | |
| outputs=[overall_sentiment_output, confidence_scores_output, raw_output] | |
| ) | |
| gr.Markdown("<hr>") # Separador | |
| gr.Markdown("<h2 style='color: #00BFFF;'>📊 Results</h2>") | |
| # Las salidas ya están definidas arriba, ahora solo las colocamos en el layout | |
| # (overall_sentiment_output ya se definió, y su visibilidad y escala se controlan arriba) | |
| # No es necesario re-definirlas aquí, solo asegurarnos de que estén en el flujo de la interfaz | |
| # (Gradio las "conoce" porque ya han sido creadas). | |
| pass # No es necesario poner nada aquí, ya las variables fueron declaradas arriba | |
| # --- Event Listeners --- | |
| analyze_btn.click( | |
| fn=analyze_sentiment, | |
| inputs=text_input, | |
| outputs=[overall_sentiment_output, confidence_scores_output, raw_output] | |
| ) | |
| text_input.change( | |
| fn=analyze_sentiment, | |
| inputs=text_input, | |
| outputs=[overall_sentiment_output, confidence_scores_output, raw_output], | |
| # live=True | |
| ) | |
| # Launch the Gradio application | |
| demo.launch() |