JonusNattapong's picture
Update app.py
5fc012b verified
import gradio as gr
from transformers import pipeline
import re
from functools import lru_cache
import logging
from typing import List, Dict, Tuple
import json
# Set up logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
# Enhanced model list with descriptions
MODEL_LIST = [
("ZombitX64/MultiSent-E5-Pro", "🏆 MultiSent E5 Pro - แนะนำ (ความแม่นยำสูงสุด)"),
("ZombitX64/Thai-sentiment-e5", "🎯 Thai Sentiment E5 - เฉพาะภาษาไทย"),
("poom-sci/WangchanBERTa-finetuned-sentiment", "🔥 WangchanBERTa - โมเดลไทยยอดนิยม"),
("SandboxBhh/sentiment-thai-text-model", "✨ Sandbox Thai - เร็วและแม่นยำ"),
("ZombitX64/MultiSent-E5", "⚡ MultiSent E5 - รวดเร็ว"),
("Thaweewat/wangchanberta-hyperopt-sentiment-01", "🧠 WangchanBERTa Hyperopt"),
("cardiffnlp/twitter-xlm-roberta-base-sentiment", "🌐 XLM-RoBERTa - หลายภาษา"),
("phoner45/wangchan-sentiment-thai-text-model", "📱 Wangchan Mobile"),
("ZombitX64/Sentiment-01", "🔬 Sentiment v1"),
("ZombitX64/Sentiment-02", "🔬 Sentiment v2"),
("ZombitX64/Sentiment-03", "🔬 Sentiment v3"),
("ZombitX64/sentiment-103", "🔬 Sentiment 103"),
("ZombitX64/sentimentSumdata-v1", "🔬 sentimentSumdata-v1"),
("ZombitX64/wangchanberta-att-spm-uncased-sentiment", "wangchanberta-att-spm-uncased-sentiment"),
]
# Cache for model loading
@lru_cache(maxsize=3)
def get_nlp(model_name: str):
try:
return pipeline("sentiment-analysis", model=model_name)
except Exception as e:
logger.error(f"Error loading model {model_name}: {e}")
raise gr.Error(f"ไม่สามารถโหลดโมเดล {model_name} ได้: {str(e)}")
# Model-specific label mappings
MODEL_LABEL_MAPPINGS = {
# wangchanberta-att-spm-uncased-sentiment - 3 classes ( negative, neutral, positive)
"ZombitX64/wangchanberta-att-spm-uncased-sentiment": {
"LABEL_0": {"code": 0, "name": "negative", "emoji": "😢", "color": "#f87171", "bg": "rgba(248, 113, 113, 0.2)", "description": "เชิงลบ"},
"LABEL_1": {"code": 1, "name": "neutral", "emoji": "😐", "color": "#facc15", "bg": "rgba(250, 204, 21, 0.2)", "description": "เป็นกลาง"},
"LABEL_2": {"code": 2, "name": "positive", "emoji": "😊", "color": "#34d399", "bg": "rgba(52, 211, 153, 0.2)", "description": "เชิงบวก"},
},
# MultiSent E5 Pro - 4 classes (question, negative, neutral, positive)
"ZombitX64/MultiSent-E5-Pro": {
"LABEL_0": {"code": 0, "name": "question", "emoji": "🤔", "color": "#60a5fa", "bg": "rgba(96, 165, 250, 0.2)", "description": "คำถาม"},
"LABEL_1": {"code": 1, "name": "negative", "emoji": "😢", "color": "#f87171", "bg": "rgba(248, 113, 113, 0.2)", "description": "เชิงลบ"},
"LABEL_2": {"code": 2, "name": "neutral", "emoji": "😐", "color": "#facc15", "bg": "rgba(250, 204, 21, 0.2)", "description": "เป็นกลาง"},
"LABEL_3": {"code": 3, "name": "positive", "emoji": "😊", "color": "#34d399", "bg": "rgba(52, 211, 153, 0.2)", "description": "เชิงบวก"},
},
# Thai Sentiment E5 - 3 classes (negative, neutral, positive)
"ZombitX64/Thai-sentiment-e5": {
"LABEL_0": {"code": 0, "name": "negative", "emoji": "😢", "color": "#f87171", "bg": "rgba(248, 113, 113, 0.2)", "description": "เชิงลบ"},
"LABEL_1": {"code": 1, "name": "neutral", "emoji": "😐", "color": "#facc15", "bg": "rgba(250, 204, 21, 0.2)", "description": "เป็นกลาง"},
"LABEL_2": {"code": 2, "name": "positive", "emoji": "😊", "color": "#34d399", "bg": "rgba(52, 211, 153, 0.2)", "description": "เชิงบวก"},
},
# WangchanBERTa - usually neg/neu/pos
"poom-sci/WangchanBERTa-finetuned-sentiment": {
"neg": {"code": 0, "name": "negative", "emoji": "😢", "color": "#f87171", "bg": "rgba(248, 113, 113, 0.2)", "description": "เชิงลบ"},
"neu": {"code": 1, "name": "neutral", "emoji": "😐", "color": "#facc15", "bg": "rgba(250, 204, 21, 0.2)", "description": "เป็นกลาง"},
"pos": {"code": 2, "name": "positive", "emoji": "😊", "color": "#34d399", "bg": "rgba(52, 211, 153, 0.2)", "description": "เชิงบวก"},
},
# Sandbox Thai - 3 classes
"SandboxBhh/sentiment-thai-text-model": {
"LABEL_0": {"code": 0, "name": "negative", "emoji": "😢", "color": "#f87171", "bg": "rgba(248, 113, 113, 0.2)", "description": "เชิงลบ"},
"LABEL_1": {"code": 1, "name": "neutral", "emoji": "😐", "color": "#facc15", "bg": "rgba(250, 204, 21, 0.2)", "description": "เป็นกลาง"},
"LABEL_2": {"code": 2, "name": "positive", "emoji": "😊", "color": "#34d399", "bg": "rgba(52, 211, 153, 0.2)", "description": "เชิงบวก"},
},
# MultiSent E5 - 3 classes
"ZombitX64/MultiSent-E5": {
"LABEL_0": {"code": 0, "name": "negative", "emoji": "😢", "color": "#f87171", "bg": "rgba(248, 113, 113, 0.2)", "description": "เชิงลบ"},
"LABEL_1": {"code": 1, "name": "neutral", "emoji": "😐", "color": "#facc15", "bg": "rgba(250, 204, 21, 0.2)", "description": "เป็นกลาง"},
"LABEL_2": {"code": 2, "name": "positive", "emoji": "😊", "color": "#34d399", "bg": "rgba(52, 211, 153, 0.2)", "description": "เชิงบวก"},
},
# WangchanBERTa Hyperopt
"Thaweewat/wangchanberta-hyperopt-sentiment-01": {
"neg": {"code": 0, "name": "negative", "emoji": "😢", "color": "#f87171", "bg": "rgba(248, 113, 113, 0.2)", "description": "เชิงลบ"},
"neu": {"code": 1, "name": "neutral", "emoji": "😐", "color": "#facc15", "bg": "rgba(250, 204, 21, 0.2)", "description": "เป็นกลาง"},
"pos": {"code": 2, "name": "positive", "emoji": "😊", "color": "#34d399", "bg": "rgba(52, 211, 153, 0.2)", "description": "เชิงบวก"},
},
# Twitter XLM-RoBERTa - NEGATIVE/NEUTRAL/POSITIVE
"cardiffnlp/twitter-xlm-roberta-base-sentiment": {
"NEGATIVE": {"code": 0, "name": "negative", "emoji": "😢", "color": "#f87171", "bg": "rgba(248, 113, 113, 0.2)", "description": "เชิงลบ"},
"NEUTRAL": {"code": 1, "name": "neutral", "emoji": "😐", "color": "#facc15", "bg": "rgba(250, 204, 21, 0.2)", "description": "เป็นกลาง"},
"POSITIVE": {"code": 2, "name": "positive", "emoji": "😊", "color": "#34d399", "bg": "rgba(52, 211, 153, 0.2)", "description": "เชิงบวก"},
},
# Wangchan Mobile
"phoner45/wangchan-sentiment-thai-text-model": {
"LABEL_0": {"code": 0, "name": "negative", "emoji": "😢", "color": "#f87171", "bg": "rgba(248, 113, 113, 0.2)", "description": "เชิงลบ"},
"LABEL_1": {"code": 1, "name": "neutral", "emoji": "😐", "color": "#facc15", "bg": "rgba(250, 204, 21, 0.2)", "description": "เป็นกลาง"},
"LABEL_2": {"code": 2, "name": "positive", "emoji": "😊", "color": "#34d399", "bg": "rgba(52, 211, 153, 0.2)", "description": "เชิงบวก"},
},
# ZombitX64 Sentiment models - 3 classes
"ZombitX64/Sentiment-01": {
"LABEL_0": {"code": 0, "name": "negative", "emoji": "😢", "color": "#f87171", "bg": "rgba(248, 113, 113, 0.2)", "description": "เชิงลบ"},
"LABEL_1": {"code": 1, "name": "neutral", "emoji": "😐", "color": "#facc15", "bg": "rgba(250, 204, 21, 0.2)", "description": "เป็นกลาง"},
"LABEL_2": {"code": 2, "name": "positive", "emoji": "😊", "color": "#34d399", "bg": "rgba(52, 211, 153, 0.2)", "description": "เชิงบวก"},
},
"ZombitX64/Sentiment-02": {
"LABEL_0": {"code": 0, "name": "negative", "emoji": "😢", "color": "#f87171", "bg": "rgba(248, 113, 113, 0.2)", "description": "เชิงลบ"},
"LABEL_1": {"code": 1, "name": "neutral", "emoji": "😐", "color": "#facc15", "bg": "rgba(250, 204, 21, 0.2)", "description": "เป็นกลาง"},
"LABEL_2": {"code": 2, "name": "positive", "emoji": "😊", "color": "#34d399", "bg": "rgba(52, 211, 153, 0.2)", "description": "เชิงบวก"},
},
"ZombitX64/Sentiment-03": {
"LABEL_0": {"code": 0, "name": "negative", "emoji": "😢", "color": "#f87171", "bg": "rgba(248, 113, 113, 0.2)", "description": "เชิงลบ"},
"LABEL_1": {"code": 1, "name": "neutral", "emoji": "😐", "color": "#facc15", "bg": "rgba(250, 204, 21, 0.2)", "description": "เป็นกลาง"},
"LABEL_2": {"code": 2, "name": "positive", "emoji": "😊", "color": "#34d399", "bg": "rgba(52, 211, 153, 0.2)", "description": "เชิงบวก"},
},
"ZombitX64/sentiment-103": {
"LABEL_0": {"code": 0, "name": "negative", "emoji": "😢", "color": "#f87171", "bg": "rgba(248, 113, 113, 0.2)", "description": "เชิงลบ"},
"LABEL_1": {"code": 1, "name": "neutral", "emoji": "😐", "color": "#facc15", "bg": "rgba(250, 204, 21, 0.2)", "description": "เป็นกลาง"},
"LABEL_2": {"code": 2, "name": "positive", "emoji": "😊", "color": "#34d399", "bg": "rgba(52, 211, 153, 0.2)", "description": "เชิงบวก"},
},
"ZombitX64/sentimentSumdata-v1": {
"LABEL_0": {"code": 0, "name": "negative", "emoji": "😢", "color": "#f87171", "bg": "rgba(248, 113, 113, 0.2)", "description": "เชิงลบ"},
"LABEL_1": {"code": 1, "name": "neutral", "emoji": "😐", "color": "#facc15", "bg": "rgba(250, 204, 21, 0.2)", "description": "เป็นกลาง"},
"LABEL_2": {"code": 2, "name": "positive", "emoji": "😊", "color": "#34d399", "bg": "rgba(52, 211, 153, 0.2)", "description": "เชิงบวก"},
},
}
def get_label_info(label: str, model_name: str) -> Dict:
"""Get label information for specific model with fallback for unknown labels"""
model_mappings = MODEL_LABEL_MAPPINGS.get(model_name, {})
if label in model_mappings:
return model_mappings[label]
# Fallback for unknown labels
return {
"code": -1,
"name": label.lower(),
"emoji": "🔍",
"color": "#64748b",
"bg": "rgba(100, 116, 139, 0.2)",
"description": f"ไม่ทราบ ({label})"
}
def split_sentences(text: str) -> List[str]:
"""Enhanced sentence splitting with better Thai support"""
sentences = re.split(r'[.!?။\n]+', text)
sentences = [s.strip() for s in sentences if s.strip() and len(s.strip()) > 2]
return sentences
def create_confidence_bar(score: float) -> str:
"""Create a modern confidence visualization"""
percentage = int(score * 100)
return f"""
<div style="display: flex; align-items: center; gap: 10px; margin: 8px 0;">
<div style="flex: 1; height: 8px; background: #334155; border-radius: 4px; overflow: hidden;">
<div style="width: {percentage}%; height: 100%; background: linear-gradient(90deg, #60a5fa, #3b82f6); transition: all 0.3s ease;"></div>
</div>
<span style="font-weight: 600; color: #cbd5e1; min-width: 50px;">{percentage}%</span>
</div>
"""
def analyze_text(text: str, model_name: str) -> str:
"""Enhanced text analysis with modern HTML formatting"""
if not text or not text.strip():
return """
<div style="padding: 20px; background: rgba(248, 113, 113, 0.2); border-radius: 12px; border-left: 4px solid #f87171;">
<div style="color: #f87171; font-weight: 600; display: flex; align-items: center; gap: 8px;">
<span style="font-size: 20px;">⚠️</span>
กรุณาใส่ข้อความที่ต้องการวิเคราะห์
</div>
</div>
"""
sentences = split_sentences(text)
if not sentences:
return """
<div style="padding: 20px; background: rgba(248, 113, 113, 0.2); border-radius: 12px; border-left: 4px solid #f87171;">
<div style="color: #f87171; font-weight: 600; display: flex; align-items: center; gap: 8px;">
<span style="font-size: 20px;">⚠️</span>
ไม่พบประโยคที่สามารถวิเคราะห์ได้ กรุณาใส่ข้อความที่ยาวกว่านี้
</div>
</div>
"""
try:
nlp = get_nlp(model_name)
except Exception as e:
return f"""
<div style="padding: 20px; background: rgba(248, 113, 113, 0.2); border-radius: 12px; border-left: 4px solid #f87171;">
<div style="color: #f87171; font-weight: 600; display: flex; align-items: center; gap: 8px;">
<span style="font-size: 20px;">❌</span>
เกิดข้อผิดพลาดในการโหลดโมเดล: {str(e)}
</div>
</div>
"""
# Header
html_parts = [f"""
<div style="background: linear-gradient(135deg, #1e3a8a 0%, #3b82f6 100%); color: #f8fafc; padding: 24px; border-radius: 16px 16px 0 0; margin-bottom: 0;">
<h2 style="margin: 0; font-size: 24px; font-weight: 700; display: flex; align-items: center; gap: 12px;">
<span style="font-size: 28px;">🧠</span>
ผลการวิเคราะห์ความรู้สึก
</h2>
<p style="margin: 8px 0 0 0; opacity: 0.9; font-size: 14px;">โมเดล: {model_name.split('/')[-1]}</p>
</div>
"""]
sentiment_counts = {"positive": 0, "negative": 0, "neutral": 0, "question": 0, "other": 0}
total_confidence = 0
sentence_results = []
# Analyze each sentence
for i, sentence in enumerate(sentences, 1):
try:
result = nlp(sentence)[0]
label = result['label']
score = result['score']
label_info = get_label_info(label, model_name)
label_name = label_info["name"]
if label_name in sentiment_counts:
sentiment_counts[label_name] += 1
else:
sentiment_counts["other"] += 1
total_confidence += score
# Store result for display
sentence_results.append({
'sentence': sentence,
'label_info': label_info,
'score': score,
'index': i,
'original_label': label
})
except Exception as e:
logger.error(f"Error analyzing sentence {i}: {e}")
sentence_results.append({
'sentence': sentence,
'error': str(e),
'index': i
})
# Results container
html_parts.append("""
<div style="background: #0f172a; padding: 0; border-radius: 0 0 16px 16px; box-shadow: 0 4px 20px rgba(0,0,0,0.3); overflow: hidden;">
""")
# Individual sentence results
for result in sentence_results:
if 'error' in result:
html_parts.append(f"""
<div style="padding: 20px; border-bottom: 1px solid #1e293b;">
<div style="color: #f87171; font-weight: 600; display: flex; align-items: center; gap: 8px;">
<span style="font-size: 18px;">❌</span>
เกิดข้อผิดพลาดในการวิเคราะห์ประโยคที่ {result['index']}
</div>
<p style="color: #94a3b8; margin: 8px 0 0 0; font-size: 14px;">{result['error']}</p>
</div>
""")
else:
label_info = result['label_info']
confidence_bar = create_confidence_bar(result['score'])
html_parts.append(f"""
<div style="padding: 20px; border-bottom: 1px solid #1e293b; transition: all 0.2s ease;" onmouseover="this.style.background='#1e293b'" onmouseout="this.style.background='#0f172a'">
<div style="display: flex; align-items: flex-start; gap: 16px;">
<div style="background: {label_info['bg']}; padding: 12px; border-radius: 50%; min-width: 48px; height: 48px; display: flex; align-items: center; justify-content: center;">
<span style="font-size: 20px;">{label_info['emoji']}</span>
</div>
<div style="flex: 1;">
<div style="display: flex; align-items: center; gap: 8px; margin-bottom: 8px;">
<span style="background: {label_info['color']}; color: #f8fafc; padding: 4px 12px; border-radius: 20px; font-size: 12px; font-weight: 600; text-transform: uppercase;">
{label_info['description']}
</span>
<span style="color: #94a3b8; font-size: 12px; background: #1e293b; padding: 2px 8px; border-radius: 12px;">
{result['original_label']}
</span>
<span style="color: #94a3b8; font-size: 14px;">ประโยคที่ {result['index']}</span>
</div>
<p style="color: #f8fafc; margin: 0 0 12px 0; font-size: 16px; line-height: 1.5;">
"{result['sentence'][:150]}{'...' if len(result['sentence']) > 150 else ''}"
</p>
<div style="color: #94a3b8; font-size: 14px; margin-bottom: 8px;">ความมั่นใจ:</div>
{confidence_bar}
</div>
</div>
</div>
""")
# Summary section
total_sentences = len(sentences)
avg_confidence = total_confidence / total_sentences if total_sentences > 0 else 0
# Create chart data for summary
chart_items = []
colors = {"positive": "#34d399", "negative": "#f87171", "neutral": "#facc15", "question": "#60a5fa", "other": "#64748b"}
emojis = {"positive": "😊", "negative": "😢", "neutral": "😐", "question": "🤔", "other": "🔍"}
for sentiment, count in sentiment_counts.items():
if count > 0:
percentage = (count / total_sentences) * 100
chart_items.append(f"""
<div style="display: flex; align-items: center; gap: 12px; padding: 12px; background: rgba(59, 130, 246, 0.1); border-radius: 8px;">
<span style="font-size: 24px;">{emojis.get(sentiment, '🔍')}</span>
<div style="flex: 1;">
<div style="font-weight: 600; color: #f8fafc; text-transform: capitalize;">{sentiment}</div>
<div style="color: #94a3b8; font-size: 14px;">{count} ประโยค ({percentage:.1f}%)</div>
</div>
<div style="width: 60px; height: 6px; background: #334155; border-radius: 3px; overflow: hidden;">
<div style="width: {percentage}%; height: 100%; background: {colors.get(sentiment, '#64748b')}; transition: all 0.3s ease;"></div>
</div>
</div>
""")
html_parts.append(f"""
<div style="padding: 24px; background: linear-gradient(135deg, #1e293b 0%, #0f172a 100%);">
<h3 style="color: #f8fafc; margin: 0 0 20px 0; font-size: 20px; font-weight: 700; display: flex; align-items: center; gap: 8px;">
<span style="font-size: 24px;">📊</span>
สรุปผลการวิเคราะห์
</h3>
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 16px; margin-bottom: 20px;">
<div style="background: #1e293b; padding: 20px; border-radius: 12px; text-align: center; box-shadow: 0 2px 8px rgba(0,0,0,0.3);">
<div style="font-size: 32px; font-weight: 700; color: #60a5fa; margin-bottom: 4px;">{total_sentences}</div>
<div style="color: #94a3b8; font-size: 14px;">ประโยคทั้งหมด</div>
</div>
<div style="background: #1e293b; padding: 20px; border-radius: 12px; text-align: center; box-shadow: 0 2px 8px rgba(0,0,0,0.3);">
<div style="font-size: 32px; font-weight: 700; color: #34d399; margin-bottom: 4px;">{avg_confidence*100:.0f}%</div>
<div style="color: #94a3b8; font-size: 14px;">ความมั่นใจเฉลี่ย</div>
</div>
</div>
<div style="display: grid; gap: 8px;">
{"".join(chart_items)}
</div>
</div>
""")
html_parts.append("</div>")
return "".join(html_parts)
# Modern CSS with dark blue theme
CUSTOM_CSS = """
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700&display=swap');
* { font-family: 'Inter', 'Noto Sans Thai', sans-serif !important; }
body, .gradio-container {
background: linear-gradient(135deg, #181f2a 0%, #232e3c 100%) !important;
min-height: 100vh;
}
.main-uxui-card {
background: #232e3c !important;
border-radius: 20px;
box-shadow: 0 6px 32px rgba(0,0,0,0.22);
border: 1.5px solid #2d3a4d;
padding: 32px 28px 28px 28px;
margin: 0 0 32px 0;
color: #e3e8ef !important;
transition: box-shadow 0.2s;
}
.main-uxui-card:hover {
box-shadow: 0 12px 36px rgba(0,0,0,0.28);
}
.main-uxui-header {
text-align: center;
margin-bottom: 32px;
}
.main-uxui-header h1 {
font-size: 2.8em;
color: #e3e8ef;
font-weight: 800;
margin-bottom: 0.2em;
letter-spacing: 0.5px;
}
.main-uxui-header p {
color: #7da2e3;
font-size: 1.25em;
margin-top: 0;
margin-bottom: 0.5em;
}
.main-uxui-section-title {
font-size: 1.18em;
color: #7da2e3;
font-weight: 700;
margin-bottom: 12px;
letter-spacing: 0.2px;
display: flex;
align-items: center;
gap: 8px;
}
.main-uxui-btn {
font-size: 1.13em;
padding: 0.9em 2.7em;
border-radius: 13px;
font-weight: 600;
background: linear-gradient(90deg, #2563eb 0%, #1e293b 100%);
color: #f8fafc !important;
border: none;
box-shadow: 0 2px 8px #1e253355;
transition: all 0.2s;
}
.main-uxui-btn:hover {
filter: brightness(1.08);
box-shadow: 0 6px 18px #1e253377;
transform: translateY(-2px) scale(1.03);
}
.main-uxui-btn.secondary {
background: #232e3c;
color: #7da2e3 !important;
border: 1.5px solid #2d3a4d;
}
.main-uxui-input, .main-uxui-dropdown {
font-size: 1.13em;
border-radius: 10px;
border: 1.5px solid #2d3a4d;
background: #1e2533;
color: #e3e8ef;
padding: 14px;
margin-bottom: 10px;
}
.main-uxui-dropdown { min-width: 220px; }
.main-uxui-output {
background: #1e2533;
border-radius: 14px;
border: 1.5px solid #2d3a4d;
color: #e3e8ef;
padding: 22px 18px;
font-size: 1.08em;
min-height: 180px;
margin-bottom: 0;
}
.main-uxui-legend {
background: #232e3c;
border-radius: 16px;
border: 1.5px solid #2d3a4d;
color: #7da2e3;
padding: 24px 18px;
margin-top: 32px;
font-size: 1.05em;
}
.main-uxui-legend .legend-row {
display: flex;
gap: 24px;
flex-wrap: wrap;
margin-top: 12px;
}
.main-uxui-legend .legend-item {
flex: 1 1 180px;
background: #1e2533;
border-radius: 10px;
padding: 16px 10px;
margin-bottom: 10px;
text-align: center;
border: 1px solid #2d3a4d;
}
.main-uxui-legend .legend-item strong {
color: #e3e8ef;
font-size: 1.08em;
}
.main-uxui-legend .legend-item small {
color: #7da2e3;
}
@media (max-width: 900px) {
.main-uxui-card { padding: 16px 6px; }
.main-uxui-header h1 { font-size: 2em; }
.main-uxui-section-title { font-size: 1em; }
}
"""
# Gradio Blocks app definition
with gr.Blocks(css=CUSTOM_CSS, theme=gr.themes.Base()) as demo:
with gr.Column(elem_classes="main-uxui-card"):
with gr.Row():
gr.HTML("""
<div class='main-uxui-header'>
<h1>Sentiment Analysis</h1>
<p>วิเคราะห์ความรู้สึกภาษาไทย และอื่นๆ etc. รองรับหลายโมเดล</p>
</div>
""")
with gr.Row():
model_dropdown = gr.Dropdown(
choices=[(desc, name) for name, desc in MODEL_LIST],
value=MODEL_LIST[0][0],
label="เลือกโมเดล (Model)",
elem_classes="main-uxui-dropdown"
)
with gr.Row():
input_box = gr.Textbox(
lines=4,
placeholder="พิมพ์ข้อความภาษาไทยหรืออังกฤษ (รองรับหลายประโยค)",
label="ข้อความที่ต้องการวิเคราะห์",
elem_classes="main-uxui-input"
)
with gr.Row():
analyze_btn = gr.Button("วิเคราะห์", elem_classes="main-uxui-btn")
clear_btn = gr.Button("ล้างข้อมูล", elem_classes="main-uxui-btn secondary")
with gr.Row():
output_html = gr.HTML(label="ผลลัพธ์", elem_classes="main-uxui-output")
with gr.Row():
gr.Examples([
["วันนี้อากาศดีมากๆ รู้สึกสดชื่นและมีความสุขมาก!"],
["เศร้ามากเลยวันนี้ งานเยอะเกินไป"],
["อาหารอร่อยดี แต่บริการช้ามาก"],
["คุณคิดอย่างไรกับเศรษฐกิจไทย?"],
["I love this product! It's amazing."],
["이 제품에는 매우 나쁩니다."],
["इस उत्पाद के लिए बहुत बुरा.."],
["This is the worst experience I've ever had."]
],
inputs=input_box,
label="ตัวอย่างข้อความ",
)
with gr.Row():
gr.HTML("""
<div class='main-uxui-legend'>
<div class='main-uxui-section-title'>
<span>🗂️</span> คำอธิบายผลลัพธ์
</div>
<div class='legend-row'>
<div class='legend-item'><strong>😊 เชิงบวก</strong><br><small>Positive</small></div>
<div class='legend-item'><strong>😢 เชิงลบ</strong><br><small>Negative</small></div>
<div class='legend-item'><strong>😐 เป็นกลาง</strong><br><small>Neutral</small></div>
<div class='legend-item'><strong>🤔 คำถาม</strong><br><small>Question</small></div>
</div>
</div>
""")
def on_analyze(text, model):
return analyze_text(text, model)
analyze_btn.click(on_analyze, [input_box, model_dropdown], output_html)
input_box.submit(on_analyze, [input_box, model_dropdown], output_html)
model_dropdown.change(on_analyze, [input_box, model_dropdown], output_html)
clear_btn.click(lambda: (""), None, output_html)
# Launch configuration
if __name__ == "__main__":
demo.queue(
max_size=50,
default_concurrency_limit=10
).launch(
server_name="0.0.0.0",
server_port=7860,
share=True,
show_error=True,
show_api=False,
quiet=False,
favicon_path=None,
ssl_verify=False,
app_kwargs={
"docs_url": None,
"redoc_url": None,
}
)