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) */ | |
.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 */ | |
} | |
.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 */ | |
} | |
/* BOTONES PRINCIPALES */ | |
.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 */ | |
} | |
.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; | |
} | |
.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) */ | |
.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.75em; /* Tamaño de fuente más pequeño para los ejemplos */ | |
white-space: nowrap; /* Evitar que el texto se rompa en varias líneas */ | |
flex-shrink: 0; /* No permitir que se encojan si hay poco espacio */ | |
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 */ | |
} | |
.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 que no podemos usar en gr.Row! */ | |
justify-content: center; /* Centrar los botones si no llenan la línea */ | |
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 */ | |
} | |
""" | |
# --- 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 = "" | |
return f"<div class='sentiment-display {color_class}'>{emoji} {label.upper()} ({score:.2f})</div>" + \ | |
f"<p style='color: #FFFFFF; font-size: 0.75em; margin-top: 3px; margin-bottom: 0;'>{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.75em;'>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.75em;'>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.75em;'>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() |