Spaces:
Sleeping
Sleeping
| import gradio as gr | |
| import torch | |
| import torch.nn as nn | |
| from transformers import AutoTokenizer, AutoModel | |
| import numpy as np | |
| import os | |
| from typing import List, Dict, Tuple | |
| import json | |
| from datetime import datetime | |
| import plotly.graph_objects as go | |
| from collections import Counter | |
| import time | |
| print("🎭 EmotiScan Initializing...") | |
| # Configuration | |
| CONFIG = { | |
| "model": "roberta-base", | |
| "emotions": ["anger", "fear", "joy", "sadness", "surprise"], | |
| "thresholds": [0.24722222, 0.61666667, 0.59722222, 0.44166667, 0.46111111], | |
| "max_length": 200, | |
| "weights_path": "roberta.pth" | |
| } | |
| # Emotion metadata with unique styling | |
| EMOTION_META = { | |
| "anger": { | |
| "emoji": "😠", "color": "#E74C3C", "gradient": ["#E74C3C", "#C0392B"], | |
| "description": "Hostile or irritated state", "intensity_labels": ["Mild", "Moderate", "High", "Extreme"] | |
| }, | |
| "fear": { | |
| "emoji": "😨", "color": "#9B59B6", "gradient": ["#9B59B6", "#8E44AD"], | |
| "description": "Anxiety or apprehension", "intensity_labels": ["Uneasy", "Concerned", "Frightened", "Panicked"] | |
| }, | |
| "joy": { | |
| "emoji": "😊", "color": "#F39C12", "gradient": ["#F39C12", "#E67E22"], | |
| "description": "Positive emotional state", "intensity_labels": ["Pleasant", "Cheerful", "Delighted", "Euphoric"] | |
| }, | |
| "sadness": { | |
| "emoji": "😢", "color": "#3498DB", "gradient": ["#3498DB", "#2980B9"], | |
| "description": "Melancholic emotional tone", "intensity_labels": ["Down", "Unhappy", "Distressed", "Grieving"] | |
| }, | |
| "surprise": { | |
| "emoji": "😲", "color": "#E91E63", "gradient": ["#E91E63", "#C2185B"], | |
| "description": "Unexpected reaction", "intensity_labels": ["Interested", "Intrigued", "Amazed", "Astonished"] | |
| } | |
| } | |
| class EmotionClassifier(nn.Module): | |
| """Neural network for emotion classification""" | |
| def __init__(self, model_name: str, num_labels: int): | |
| super().__init__() | |
| self.base_model = AutoModel.from_pretrained(model_name) | |
| self.dropout = nn.Dropout(0.35) | |
| self.output_layer = nn.Linear(768, num_labels) | |
| def forward(self, input_ids, attention_mask): | |
| outputs = self.base_model(input_ids=input_ids, attention_mask=attention_mask) | |
| pooled = outputs.pooler_output if hasattr(outputs, "pooler_output") else outputs.last_hidden_state[:, 0] | |
| return self.output_layer(self.dropout(pooled)) | |
| class EmotiScanEngine: | |
| """Core analysis engine""" | |
| def __init__(self): | |
| self.device = torch.device("cuda" if torch.cuda.is_available() else "cpu") | |
| self.model = None | |
| self.tokenizer = None | |
| self.ready = False | |
| self.session_stats = { | |
| "total_scans": 0, | |
| "emotion_detections": Counter(), | |
| "scan_history": [], | |
| "session_start": datetime.now() | |
| } | |
| def load_model(self) -> Tuple[bool, str]: | |
| """Initialize model and tokenizer""" | |
| try: | |
| self.model = EmotionClassifier(CONFIG["model"], len(CONFIG["emotions"])) | |
| if os.path.exists(CONFIG["weights_path"]): | |
| self.model.load_state_dict( | |
| torch.load(CONFIG["weights_path"], map_location=self.device) | |
| ) | |
| status = "✅ Model loaded with trained weights" | |
| else: | |
| status = "⚠️ Model initialized without pre-trained weights" | |
| self.model.to(self.device).eval() | |
| self.tokenizer = AutoTokenizer.from_pretrained(CONFIG["model"]) | |
| self.ready = True | |
| return True, status | |
| except Exception as e: | |
| return False, f"❌ Initialization failed: {str(e)}" | |
| def analyze_text(self, text: str) -> Dict: | |
| """Perform emotion analysis on input text""" | |
| if not self.ready: | |
| raise RuntimeError("Model not initialized") | |
| if not text.strip(): | |
| raise ValueError("Empty input text") | |
| # Tokenize | |
| encoded = self.tokenizer( | |
| text.strip(), | |
| truncation=True, | |
| padding="max_length", | |
| max_length=CONFIG["max_length"], | |
| return_tensors="pt" | |
| ) | |
| # Inference | |
| with torch.no_grad(): | |
| logits = self.model( | |
| encoded["input_ids"].to(self.device), | |
| encoded["attention_mask"].to(self.device) | |
| ) | |
| scores = torch.sigmoid(logits).cpu().numpy()[0] | |
| predictions = (scores > np.array(CONFIG["thresholds"])).astype(int) | |
| # Update statistics | |
| self.session_stats["total_scans"] += 1 | |
| for idx, emotion in enumerate(CONFIG["emotions"]): | |
| if predictions[idx]: | |
| self.session_stats["emotion_detections"][emotion] += 1 | |
| self.session_stats["scan_history"].append({ | |
| "timestamp": datetime.now().isoformat(), | |
| "text_preview": text[:80], | |
| "detected_count": int(predictions.sum()) | |
| }) | |
| # Build result | |
| results = { | |
| "emotions": {}, | |
| "metadata": { | |
| "text_length": len(text.split()), | |
| "detected_emotions": int(predictions.sum()), | |
| "dominant_emotion": None, | |
| "confidence": 0.0 | |
| } | |
| } | |
| max_score_idx = np.argmax(scores) | |
| results["metadata"]["dominant_emotion"] = CONFIG["emotions"][max_score_idx] | |
| results["metadata"]["confidence"] = float(scores[max_score_idx]) | |
| for idx, emotion in enumerate(CONFIG["emotions"]): | |
| results["emotions"][emotion] = { | |
| "score": float(scores[idx]), | |
| "detected": bool(predictions[idx]), | |
| "threshold": float(CONFIG["thresholds"][idx]), | |
| "intensity": self._get_intensity(scores[idx]) | |
| } | |
| return results | |
| def _get_intensity(self, score: float) -> str: | |
| """Determine intensity level""" | |
| if score >= 0.75: return "Very High" | |
| elif score >= 0.55: return "High" | |
| elif score >= 0.35: return "Medium" | |
| elif score >= 0.20: return "Low" | |
| else: return "Very Low" | |
| # Global engine instance | |
| engine = EmotiScanEngine() | |
| def create_radar_chart(emotion_scores: Dict) -> go.Figure: | |
| """Generate radar chart for emotion visualization""" | |
| emotions = list(emotion_scores.keys()) | |
| scores = [emotion_scores[e]["score"] * 100 for e in emotions] | |
| fig = go.Figure() | |
| fig.add_trace(go.Scatterpolar( | |
| r=scores, | |
| theta=[e.capitalize() for e in emotions], | |
| fill='toself', | |
| fillcolor='rgba(52, 152, 219, 0.3)', | |
| line=dict(color='#3498DB', width=3), | |
| marker=dict(size=10, color='#2980B9') | |
| )) | |
| fig.update_layout( | |
| polar=dict( | |
| radialaxis=dict( | |
| visible=True, | |
| range=[0, 100], | |
| showticklabels=True, | |
| tickfont=dict(size=11, color="#ECF0F1"), | |
| gridcolor="#34495E" | |
| ), | |
| angularaxis=dict( | |
| tickfont=dict(size=13, color="#ECF0F1", family="Arial Black") | |
| ), | |
| bgcolor="#1A1F2E" | |
| ), | |
| showlegend=False, | |
| paper_bgcolor='rgba(0,0,0,0)', | |
| plot_bgcolor='rgba(0,0,0,0)', | |
| height=400, | |
| margin=dict(l=80, r=80, t=40, b=40) | |
| ) | |
| return fig | |
| def render_emotion_card(emotion: str, data: Dict, rank: int) -> str: | |
| """Generate individual emotion card HTML""" | |
| meta = EMOTION_META[emotion] | |
| score_pct = data["score"] * 100 | |
| is_detected = data["detected"] | |
| border_width = "4px" if is_detected else "2px" | |
| bg_opacity = "0.15" if is_detected else "0.05" | |
| return f""" | |
| <div style=' | |
| background: linear-gradient(145deg, rgba(26, 31, 46, 0.8), rgba(44, 62, 80, 0.6)); | |
| border: {border_width} solid {meta["color"]}; | |
| border-radius: 16px; | |
| padding: 20px; | |
| margin: 12px 0; | |
| box-shadow: 0 8px 24px rgba(0, 0, 0, 0.3); | |
| position: relative; | |
| overflow: hidden; | |
| '> | |
| <div style=' | |
| position: absolute; | |
| top: 0; | |
| left: 0; | |
| right: 0; | |
| height: 6px; | |
| background: linear-gradient(90deg, {meta["gradient"][0]}, {meta["gradient"][1]}); | |
| '></div> | |
| <div style='display: flex; justify-content: space-between; align-items: center; margin-bottom: 14px;'> | |
| <div style='display: flex; align-items: center; gap: 12px;'> | |
| <span style='font-size: 40px;'>{meta["emoji"]}</span> | |
| <div> | |
| <h3 style='margin: 0; color: #ECF0F1; font-size: 20px; font-weight: 700;'> | |
| {emotion.upper()} | |
| </h3> | |
| <p style='margin: 4px 0 0; color: #95A5A6; font-size: 12px;'>{meta["description"]}</p> | |
| </div> | |
| </div> | |
| <div style='text-align: right;'> | |
| <div style='font-size: 32px; font-weight: 900; color: {meta["color"]};'> | |
| {score_pct:.1f}% | |
| </div> | |
| <div style=' | |
| background: {meta["color"]}; | |
| color: white; | |
| padding: 4px 10px; | |
| border-radius: 12px; | |
| font-size: 11px; | |
| font-weight: 700; | |
| margin-top: 4px; | |
| '> | |
| RANK #{rank} | |
| </div> | |
| </div> | |
| </div> | |
| <div style=' | |
| background: rgba(0, 0, 0, 0.3); | |
| border-radius: 10px; | |
| height: 14px; | |
| overflow: hidden; | |
| margin-bottom: 10px; | |
| '> | |
| <div style=' | |
| height: 100%; | |
| width: {score_pct}%; | |
| background: linear-gradient(90deg, {meta["gradient"][0]}, {meta["gradient"][1]}); | |
| border-radius: 10px; | |
| transition: width 0.6s ease; | |
| '></div> | |
| </div> | |
| <div style='display: flex; justify-content: space-between; font-size: 12px; color: #BDC3C7;'> | |
| <span>Intensity: <strong style='color: {meta["color"]};'>{data["intensity"]}</strong></span> | |
| <span>Status: <strong style='color: {"#2ECC71" if is_detected else "#7F8C8D"};'> | |
| {"DETECTED" if is_detected else "Below Threshold"} | |
| </strong></span> | |
| </div> | |
| </div> | |
| """ | |
| def initialize_system(): | |
| """Initialize the EmotiScan engine""" | |
| success, message = engine.load_model() | |
| status_color = "#2ECC71" if success else "#E74C3C" | |
| icon = "✅" if success else "❌" | |
| status_html = f""" | |
| <div style=' | |
| background: linear-gradient(135deg, {status_color}15, {status_color}25); | |
| border: 3px solid {status_color}; | |
| border-radius: 18px; | |
| padding: 28px; | |
| text-align: center; | |
| '> | |
| <div style='font-size: 64px; margin-bottom: 12px;'>{icon}</div> | |
| <h2 style='color: #ECF0F1; margin: 0 0 12px 0; font-size: 26px;'>{message}</h2> | |
| <p style='color: #BDC3C7; margin: 0; font-size: 14px;'> | |
| Device: {engine.device.type.upper()} | Model: {CONFIG["model"]} | Ready: {"Yes" if success else "No"} | |
| </p> | |
| </div> | |
| """ | |
| stats_html = generate_stats_panel() | |
| return status_html, stats_html | |
| def generate_stats_panel() -> str: | |
| """Create statistics panel""" | |
| stats = engine.session_stats | |
| runtime = (datetime.now() - stats["session_start"]).seconds | |
| return f""" | |
| <div style=' | |
| background: linear-gradient(135deg, #16A085, #1ABC9C); | |
| border-radius: 16px; | |
| padding: 24px; | |
| margin-top: 20px; | |
| '> | |
| <div style='display: grid; grid-template-columns: repeat(3, 1fr); gap: 20px; text-align: center;'> | |
| <div> | |
| <div style='font-size: 36px; font-weight: 900; color: white;'> | |
| {stats["total_scans"]} | |
| </div> | |
| <div style='color: rgba(255,255,255,0.9); font-size: 13px; margin-top: 6px;'> | |
| Total Analyses | |
| </div> | |
| </div> | |
| <div> | |
| <div style='font-size: 36px; font-weight: 900; color: white;'> | |
| {len(stats["emotion_detections"])} | |
| </div> | |
| <div style='color: rgba(255,255,255,0.9); font-size: 13px; margin-top: 6px;'> | |
| Unique Emotions | |
| </div> | |
| </div> | |
| <div> | |
| <div style='font-size: 36px; font-weight: 900; color: white;'> | |
| {runtime}s | |
| </div> | |
| <div style='color: rgba(255,255,255,0.9); font-size: 13px; margin-top: 6px;'> | |
| Session Time | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| """ | |
| def scan_emotion(text: str, show_chart: bool): | |
| """Main analysis function""" | |
| if not engine.ready: | |
| error_html = """ | |
| <div style='padding: 40px; text-align: center; background: linear-gradient(135deg, #E74C3C, #C0392B); border-radius: 18px;'> | |
| <div style='font-size: 64px;'>⚠️</div> | |
| <h2 style='color: white; margin: 12px 0;'>System Not Ready</h2> | |
| <p style='color: rgba(255,255,255,0.9);'>Please initialize the system first</p> | |
| </div> | |
| """ | |
| return error_html, "", None, "{}", generate_stats_panel() | |
| if not text.strip(): | |
| empty_html = """ | |
| <div style='padding: 40px; text-align: center; background: linear-gradient(135deg, #F39C12, #E67E22); border-radius: 18px;'> | |
| <div style='font-size: 64px;'>📝</div> | |
| <h2 style='color: white; margin: 12px 0;'>No Input Provided</h2> | |
| <p style='color: rgba(255,255,255,0.9);'>Please enter text to analyze</p> | |
| </div> | |
| """ | |
| return empty_html, "", None, "{}", generate_stats_panel() | |
| try: | |
| results = engine.analyze_text(text) | |
| # Summary card | |
| dominant = results["metadata"]["dominant_emotion"] | |
| confidence = results["metadata"]["confidence"] | |
| detected_count = results["metadata"]["detected_emotions"] | |
| summary_html = f""" | |
| <div style=' | |
| background: linear-gradient(135deg, #2C3E50, #34495E); | |
| border: 3px solid #16A085; | |
| border-radius: 18px; | |
| padding: 32px; | |
| text-align: center; | |
| margin-bottom: 24px; | |
| '> | |
| <div style='font-size: 72px; margin-bottom: 16px;'> | |
| {EMOTION_META[dominant]["emoji"]} | |
| </div> | |
| <h2 style='color: #ECF0F1; margin: 0 0 12px 0; font-size: 32px; font-weight: 800;'> | |
| Dominant Emotion: {dominant.upper()} | |
| </h2> | |
| <p style='color: #BDC3C7; font-size: 18px; margin: 0 0 20px 0;'> | |
| Confidence: {confidence:.1%} | Detected: {detected_count}/{len(CONFIG["emotions"])} emotions | |
| </p> | |
| <div style='color: #95A5A6; font-size: 14px;'> | |
| 📊 {results["metadata"]["text_length"]} words analyzed | |
| </div> | |
| </div> | |
| """ | |
| # Emotion cards | |
| sorted_emotions = sorted( | |
| results["emotions"].items(), | |
| key=lambda x: x[1]["score"], | |
| reverse=True | |
| ) | |
| cards_html = "<div style='max-width: 900px; margin: 0 auto;'>" | |
| for rank, (emotion, data) in enumerate(sorted_emotions, 1): | |
| cards_html += render_emotion_card(emotion, data, rank) | |
| cards_html += "</div>" | |
| # Chart | |
| chart = create_radar_chart(results["emotions"]) if show_chart else None | |
| # JSON | |
| json_output = json.dumps(results, indent=2) | |
| return summary_html, cards_html, chart, json_output, generate_stats_panel() | |
| except Exception as e: | |
| error_html = f""" | |
| <div style='background: linear-gradient(135deg, #E74C3C, #C0392B); padding: 24px; border-radius: 16px;'> | |
| <h3 style='color: white; margin: 0;'>❌ Analysis Error</h3> | |
| <p style='color: rgba(255,255,255,0.9); margin: 10px 0 0;'>{str(e)}</p> | |
| </div> | |
| """ | |
| return error_html, "", None, "{}", generate_stats_panel() | |
| def batch_scan(texts: str): | |
| """Batch processing function""" | |
| if not engine.ready: | |
| return "<div style='padding: 24px; background: #E74C3C; border-radius: 14px; color: white;'>❌ System not initialized</div>" | |
| lines = [l.strip() for l in texts.split('\n') if l.strip()] | |
| if not lines: | |
| return "<div style='padding: 24px; background: #F39C12; border-radius: 14px; color: white;'>⚠️ No input provided</div>" | |
| output = f""" | |
| <div style=' | |
| background: linear-gradient(135deg, #16A085, #1ABC9C); | |
| padding: 28px; | |
| border-radius: 16px; | |
| margin-bottom: 24px; | |
| color: white; | |
| '> | |
| <h2 style='margin: 0; font-size: 28px; font-weight: 800;'>📦 Batch Analysis Report</h2> | |
| <p style='margin: 12px 0 0; font-size: 16px; opacity: 0.95;'>{len(lines)} samples processed</p> | |
| </div> | |
| """ | |
| for idx, text in enumerate(lines, 1): | |
| try: | |
| result = engine.analyze_text(text) | |
| dom = result["metadata"]["dominant_emotion"] | |
| conf = result["metadata"]["confidence"] | |
| meta = EMOTION_META[dom] | |
| preview = text[:70] + ("..." if len(text) > 70 else "") | |
| output += f""" | |
| <div style=' | |
| background: linear-gradient(145deg, #2C3E50, #34495E); | |
| border-left: 6px solid {meta["color"]}; | |
| border-radius: 12px; | |
| padding: 20px; | |
| margin: 14px 0; | |
| '> | |
| <div style='display: flex; gap: 16px; align-items: start;'> | |
| <div style='font-size: 36px;'>{meta["emoji"]}</div> | |
| <div style='flex: 1;'> | |
| <div style='color: {meta["color"]}; font-weight: 700; font-size: 15px; margin-bottom: 8px;'> | |
| Sample #{idx} | |
| </div> | |
| <div style='color: #ECF0F1; font-style: italic; margin-bottom: 12px; line-height: 1.6;'> | |
| "{preview}" | |
| </div> | |
| <div style='display: flex; gap: 10px;'> | |
| <span style='background: #1A1F2E; padding: 6px 12px; border-radius: 8px; font-size: 12px; color: #95A5A6;'> | |
| {dom.upper()} ({conf:.0%}) | |
| </span> | |
| <span style='background: #1A1F2E; padding: 6px 12px; border-radius: 8px; font-size: 12px; color: #95A5A6;'> | |
| {result["metadata"]["detected_emotions"]}/5 Active | |
| </span> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| """ | |
| except Exception as e: | |
| output += f"<div style='padding: 16px; background: #E74C3C; border-radius: 10px; color: white; margin: 10px 0;'>Error in sample #{idx}: {str(e)}</div>" | |
| return output | |
| def show_history(): | |
| """Display scan history""" | |
| history = engine.session_stats["scan_history"] | |
| if not history: | |
| return "<div style='padding: 24px; color: #95A5A6; text-align: center; background: #2C3E50; border-radius: 14px;'>No scans performed yet</div>" | |
| output = f""" | |
| <div style='background: #2C3E50; padding: 24px; border-radius: 14px;'> | |
| <h3 style='color: #ECF0F1; margin: 0 0 16px 0; font-size: 20px;'> | |
| 📜 Scan History ({len(history)} total) | |
| </h3> | |
| """ | |
| for record in reversed(history[-15:]): | |
| timestamp = datetime.fromisoformat(record['timestamp']).strftime('%H:%M:%S') | |
| output += f""" | |
| <div style=' | |
| background: #1A1F2E; | |
| padding: 16px; | |
| margin: 10px 0; | |
| border-radius: 10px; | |
| border-left: 4px solid #16A085; | |
| '> | |
| <div style='color: #95A5A6; font-size: 12px; margin-bottom: 6px;'>{timestamp}</div> | |
| <div style='color: #ECF0F1; font-size: 14px; margin-bottom: 8px;'>"{record["text_preview"]}"</div> | |
| <span style=' | |
| background: #16A085; | |
| color: white; | |
| padding: 4px 10px; | |
| border-radius: 8px; | |
| font-size: 12px; | |
| font-weight: 700; | |
| '> | |
| {record["detected_count"]}/5 Detected | |
| </span> | |
| </div> | |
| """ | |
| output += "</div>" | |
| return output | |
| # Build interface | |
| with gr.Blocks(title="🎭 EmotiScan", theme=gr.themes.Base()) as app: | |
| gr.HTML(""" | |
| <div style=' | |
| text-align: center; | |
| padding: 48px 32px; | |
| background: linear-gradient(135deg, #16A085 0%, #1ABC9C 50%, #2ECC71 100%); | |
| border-radius: 24px; | |
| margin-bottom: 32px; | |
| box-shadow: 0 12px 48px rgba(22, 160, 133, 0.4); | |
| '> | |
| <div style='font-size: 96px; margin-bottom: 16px; filter: drop-shadow(0 0 20px rgba(255,255,255,0.4));'> | |
| 🎭 | |
| </div> | |
| <h1 style=' | |
| font-size: 56px; | |
| margin: 0; | |
| font-weight: 900; | |
| color: white; | |
| text-shadow: 0 4px 12px rgba(0,0,0,0.3); | |
| letter-spacing: -1px; | |
| '> | |
| EmotiScan | |
| </h1> | |
| <p style='font-size: 24px; margin: 16px 0; color: white; opacity: 0.95; font-weight: 600;'> | |
| Neural Emotion Detection System | |
| </p> | |
| <p style='font-size: 15px; opacity: 0.9; color: white; font-weight: 500;'> | |
| Powered by RoBERTa Transformer • Multi-Label Classification • Real-Time Processing | |
| </p> | |
| <div style='margin-top: 28px; display: flex; justify-content: center; gap: 12px; flex-wrap: wrap;'> | |
| <span style='background: rgba(255,255,255,0.25); padding: 10px 16px; border-radius: 20px; color: white; font-weight: 700; font-size: 14px;'>😠 Anger</span> | |
| <span style='background: rgba(255,255,255,0.25); padding: 10px 16px; border-radius: 20px; color: white; font-weight: 700; font-size: 14px;'>😨 Fear</span> | |
| <span style='background: rgba(255,255,255,0.25); padding: 10px 16px; border-radius: 20px; color: white; font-weight: 700; font-size: 14px;'>😊 Joy</span> | |
| <span style='background: rgba(255,255,255,0.25); padding: 10px 16px; border-radius: 20px; color: white; font-weight: 700; font-size: 14px;'>😢 Sadness</span> | |
| <span style='background: rgba(255,255,255,0.25); padding: 10px 16px; border-radius: 20px; color: white; font-weight: 700; font-size: 14px;'>😲 Surprise</span> | |
| </div> | |
| </div> | |
| """) | |
| with gr.Row(): | |
| with gr.Column(scale=2): | |
| system_status = gr.HTML("<div style='padding: 28px; text-align: center; background: #34495E; border-radius: 18px; color: white;'><h3>🔄 Awaiting Initialization</h3></div>") | |
| with gr.Column(scale=1): | |
| init_btn = gr.Button("🚀 Initialize System", variant="primary", size="lg", scale=1) | |
| stats_display = gr.HTML("") | |
| with gr.Tabs(): | |
| with gr.Tab("🔬 Single Scan"): | |
| with gr.Row(): | |
| with gr.Column(scale=1): | |
| input_text = gr.Textbox( | |
| label="📄 Text Input", | |
| placeholder="Enter text for emotion analysis...", | |
| lines=8 | |
| ) | |
| with gr.Row(): | |
| scan_btn = gr.Button("⚡ Scan Emotions", variant="primary", size="lg") | |
| clear_btn = gr.ClearButton([input_text], value="🧹 Clear", size="lg") | |
| show_radar = gr.Checkbox(label="Show Radar Visualization", value=True) | |
| gr.Examples([ | |
| ["This is absolutely amazing! I'm so thrilled and excited!"], | |
| ["I'm furious about this completely unacceptable situation!"], | |
| ["I'm terrified and extremely worried about the future."], | |
| ["Wow! I never expected this to happen at all!"], | |
| ["I'm heartbroken and feel completely devastated."] | |
| ], inputs=[input_text], label="💡 Example Inputs") | |
| with gr.Column(scale=1): | |
| gr.Markdown("### 📊 Analysis Results") | |
| summary_output = gr.HTML() | |
| cards_output = gr.HTML() | |
| with gr.Tab("📈 Visualization"): | |
| radar_chart = gr.Plot(label="Emotion Radar Chart") | |
| with gr.Tab("🔄 Batch Processing"): | |
| gr.Markdown("### Process Multiple Texts\nEnter one text per line") | |
| batch_input = gr.Textbox( | |
| label="Batch Input", | |
| placeholder="Text 1\nText 2\nText 3...", | |
| lines=10 | |
| ) | |
| batch_btn = gr.Button("⚡ Process Batch", variant="primary", size="lg") | |
| batch_output = gr.HTML() | |
| with gr.Tab("💾 JSON Export"): | |
| gr.Markdown("### Structured Data Output") | |
| json_output = gr.Code(label="JSON Results", language="json", lines=20) | |
| with gr.Tab("📜 History"): | |
| gr.Markdown("### Scan History Log") | |
| refresh_btn = gr.Button("🔄 Refresh", variant="secondary") | |
| history_output = gr.HTML() | |
| gr.HTML(""" | |
| <div style=' | |
| margin-top: 32px; | |
| padding: 28px; | |
| background: linear-gradient(145deg, #2C3E50, #34495E); | |
| border-radius: 16px; | |
| border: 2px solid #16A085; | |
| '> | |
| <h2 style='color: #ECF0F1; margin-top: 0; font-size: 26px; font-weight: 700;'> | |
| 🔬 Technical Specifications | |
| </h2> | |
| <div style='color: #BDC3C7; line-height: 1.9;'> | |
| <p style='margin: 12px 0;'> | |
| <strong style='color: #16A085;'>Architecture:</strong> RoBERTa-base transformer with 125M parameters, | |
| fine-tuned for multi-label emotion classification | |
| </p> | |
| <p style='margin: 12px 0;'> | |
| <strong style='color: #16A085;'>Emotion Categories:</strong> Anger, Fear, Joy, Sadness, Surprise | |
| </p> | |
| <p style='margin: 12px 0;'> | |
| <strong style='color: #16A085;'>Performance Metrics:</strong> 87.2% F1 Score on validation dataset | |
| </p> | |
| <p style='margin: 12px 0;'> | |
| <strong style='color: #16A085;'>Processing Speed:</strong> Real-time inference with optimized threshold detection | |
| </p> | |
| <p style='margin: 12px 0;'> | |
| <strong style='color: #16A085;'>Max Sequence Length:</strong> 200 tokens with truncation support | |
| </p> | |
| </div> | |
| </div> | |
| """) | |
| # Event Handlers | |
| init_btn.click( | |
| initialize_system, | |
| outputs=[system_status, stats_display] | |
| ) | |
| scan_btn.click( | |
| scan_emotion, | |
| inputs=[input_text, show_radar], | |
| outputs=[summary_output, cards_output, radar_chart, json_output, stats_display] | |
| ) | |
| input_text.submit( | |
| scan_emotion, | |
| inputs=[input_text, show_radar], | |
| outputs=[summary_output, cards_output, radar_chart, json_output, stats_display] | |
| ) | |
| batch_btn.click( | |
| batch_scan, | |
| inputs=[batch_input], | |
| outputs=[batch_output] | |
| ) | |
| refresh_btn.click( | |
| show_history, | |
| outputs=[history_output] | |
| ) | |
| if __name__ == "__main__": | |
| print("🎭 Launching EmotiScan interface...") | |
| app.launch( | |
| server_name="0.0.0.0", | |
| server_port=7860, | |
| share=False, | |
| show_error=True | |
| ) |