|
import gradio as gr |
|
from transformers import pipeline |
|
import os |
|
import random |
|
|
|
|
|
print(f"Gradio version at runtime: {gr.__version__}") |
|
|
|
|
|
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 = """ |
|
/* 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 */ |
|
} |
|
|
|
""" |
|
|
|
|
|
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.7em; margin-top: 3px; margin-bottom: 0; line-height: 1.2; white-space: normal; word-wrap: break-word;'>{description}</p>" |
|
|
|
|
|
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)} |
|
) |
|
|
|
|
|
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." |
|
] |
|
|
|
|
|
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) |
|
) |
|
analyze_btn = gr.Button("Analyze Sentiment", variant="primary") |
|
|
|
|
|
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) |
|
|
|
|
|
gr.Markdown("<h3 style='color: #00BFFF; margin-top: 5px; margin-bottom: 5px;'>Try examples:</h3>") |
|
|
|
|
|
with gr.Row(elem_classes="example-buttons-container"): |
|
|
|
for example_text in random.sample(ALL_EXAMPLES, 3): |
|
gr.Button( |
|
example_text, |
|
elem_classes="example-button" |
|
).click( |
|
fn=lambda x: x, |
|
inputs=[gr.State(example_text)], |
|
outputs=text_input, |
|
).then( |
|
fn=analyze_sentiment, |
|
inputs=text_input, |
|
outputs=[overall_sentiment_output, confidence_scores_output, raw_output] |
|
) |
|
|
|
gr.Markdown("<hr>") |
|
gr.Markdown("<h2 style='color: #00BFFF;'>📊 Results</h2>") |
|
|
|
|
|
|
|
|
|
|
|
pass |
|
|
|
|
|
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], |
|
|
|
) |
|
|
|
|
|
demo.launch() |