Nattapong Tapachoom commited on
Commit
41cdf64
·
1 Parent(s): cadbbaa

Implement new feature for user authentication and improve error handling

Browse files
Files changed (1) hide show
  1. app.py +560 -238
app.py CHANGED
@@ -4,29 +4,30 @@ import re
4
  from functools import lru_cache
5
  import logging
6
  from typing import List, Dict, Tuple
 
7
 
8
  # Set up logging
9
  logging.basicConfig(level=logging.INFO)
10
  logger = logging.getLogger(__name__)
11
 
12
- # รายชื่อโมเดลที่ให้เลือก
13
  MODEL_LIST = [
14
- "SandboxBhh/sentiment-thai-text-model",
15
- "poom-sci/WangchanBERTa-finetuned-sentiment",
16
- "Thaweewat/wangchanberta-hyperopt-sentiment-01",
17
- "cardiffnlp/twitter-xlm-roberta-base-sentiment",
18
- "phoner45/wangchan-sentiment-thai-text-model",
19
- "ZombitX64/Sentiment-01",
20
- "ZombitX64/Sentiment-02",
21
- "ZombitX64/Sentiment-03",
22
- "ZombitX64/MultiSent-E5-Pro",
23
- "ZombitX64/MultiSent-E5",
24
- "ZombitX64/Thai-sentiment-e5",
25
- "ZombitX64/sentiment-103",
26
- "nlptown/bert-base-multilingual-uncased-sentiment"
27
  ]
28
 
29
- # ใช้ cache เพื่อไม่ต้องโหลดโมเดลซ้ำ
30
  @lru_cache(maxsize=3)
31
  def get_nlp(model_name: str):
32
  try:
@@ -35,22 +36,19 @@ def get_nlp(model_name: str):
35
  logger.error(f"Error loading model {model_name}: {e}")
36
  raise gr.Error(f"ไม่สามารถโหลดโมเดล {model_name} ได้: {str(e)}")
37
 
38
- # Enhanced label mapping with more comprehensive support
39
  LABEL_MAPPINGS = {
40
- # Standard labels
41
- "LABEL_0": {"code": 0, "name": "question", "emoji": "", "color": "#2196F3", "bg": "#E3F2FD"},
42
- "LABEL_1": {"code": 1, "name": "negative", "emoji": "😔", "color": "#F44336", "bg": "#FFEBEE"},
43
- "LABEL_2": {"code": 2, "name": "neutral", "emoji": "😐", "color": "#FF9800", "bg": "#FFF3E0"},
44
- "LABEL_3": {"code": 3, "name": "positive", "emoji": "😊", "color": "#4CAF50", "bg": "#E8F5E8"},
45
 
46
- # Alternative label formats
47
- "POSITIVE": {"code": 3, "name": "positive", "emoji": "😊", "color": "#4CAF50", "bg": "#E8F5E8"},
48
- "NEGATIVE": {"code": 1, "name": "negative", "emoji": "😔", "color": "#F44336", "bg": "#FFEBEE"},
49
- "NEUTRAL": {"code": 2, "name": "neutral", "emoji": "😐", "color": "#FF9800", "bg": "#FFF3E0"},
50
 
51
- # Numerical labels
52
- "0": {"code": 0, "name": "negative", "emoji": "😔", "color": "#F44336", "bg": "#FFEBEE"},
53
- "1": {"code": 1, "name": "positive", "emoji": "😊", "color": "#4CAF50", "bg": "#E8F5E8"},
54
  }
55
 
56
  def get_label_info(label: str) -> Dict:
@@ -59,45 +57,81 @@ def get_label_info(label: str) -> Dict:
59
  "code": -1,
60
  "name": label.lower(),
61
  "emoji": "🔍",
62
- "color": "#666666",
63
- "bg": "#F5F5F5"
 
64
  })
65
 
66
  def split_sentences(text: str) -> List[str]:
67
  """Enhanced sentence splitting with better Thai support"""
68
- # Split by various sentence endings and normalize
69
- sentences = re.split(r'[.!?。\n]+', text)
70
- # Clean and filter empty sentences
71
  sentences = [s.strip() for s in sentences if s.strip() and len(s.strip()) > 2]
72
  return sentences
73
 
74
- def create_progress_bar(score: float, width: int = 20) -> str:
75
- """Create a visual progress bar"""
76
- filled = int(score * width)
77
- return "" * filled + "░" * (width - filled)
 
 
 
 
 
 
 
78
 
79
  def analyze_text(text: str, model_name: str) -> str:
80
- """Enhanced text analysis with better error handling and formatting"""
81
  if not text or not text.strip():
82
- return "❗ กรุณาใส่ข้อความที่ต้องการวิเคราะห์"
 
 
 
 
 
 
 
83
 
84
  sentences = split_sentences(text)
85
 
86
  if not sentences:
87
- return "❗ ไม่พบประโยคที่สามารถวิเคราะห์ได้ กรุณาใส่ข้อความที่ยาวกว่านี้"
 
 
 
 
 
 
 
88
 
89
  try:
90
  nlp = get_nlp(model_name)
91
  except Exception as e:
92
- return f"❌ เกิดข้อผิดพลาดในการโหลดโมเดล: {str(e)}"
 
 
 
 
 
 
 
93
 
94
- results = []
95
- results.append("📊 **ผลการวิเคราะห์ความรู้สึก**\n" + "="*60 + "\n")
96
- results.append(f"🤖 **โมเดล:** {model_name}\n")
 
 
 
 
 
 
 
97
 
98
  sentiment_counts = {"positive": 0, "negative": 0, "neutral": 0, "question": 0, "other": 0}
99
  total_confidence = 0
 
100
 
 
101
  for i, sentence in enumerate(sentences, 1):
102
  try:
103
  result = nlp(sentence)[0]
@@ -107,7 +141,6 @@ def analyze_text(text: str, model_name: str) -> str:
107
  label_info = get_label_info(label)
108
  label_name = label_info["name"]
109
 
110
- # Count sentiments
111
  if label_name in sentiment_counts:
112
  sentiment_counts[label_name] += 1
113
  else:
@@ -115,267 +148,556 @@ def analyze_text(text: str, model_name: str) -> str:
115
 
116
  total_confidence += score
117
 
118
- # Create formatted result
119
- progress_bar = create_progress_bar(score)
120
- confidence_percent = score * 100
121
-
122
- result_text = f"""
123
- 🔸 **ประโยคที่ {i}:** "{sentence[:100]}{'...' if len(sentence) > 100 else ''}"
124
-
125
- {label_info['emoji']} **ผลวิเคราะห์:** {label_name.upper()} (รหัส: {label_info['code']})
126
- 📈 **ความมั่นใจ:** {score:.3f} ({confidence_percent:.1f}%)
127
- {progress_bar} {score:.3f}
128
-
129
- {'─' * 70}
130
- """
131
- results.append(result_text)
132
 
133
  except Exception as e:
134
  logger.error(f"Error analyzing sentence {i}: {e}")
135
- results.append(f"\n❌ เกิดข้อผิดพลาดในการวิเคราะห์ประโยคที่ {i}: {str(e)}\n{'─' * 70}\n")
 
 
 
 
136
 
137
- # Enhanced summary
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
138
  total_sentences = len(sentences)
139
  avg_confidence = total_confidence / total_sentences if total_sentences > 0 else 0
140
 
141
- summary = f"""
142
- 📋 **สรุปผลการวิเคราะห์**
143
- ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
144
- 📊 จำนวนประโยคทั้งหมด: {total_sentences} ประโยค
145
- 📈 ความมั่นใจเฉลี่ย: {avg_confidence:.3f} ({avg_confidence*100:.1f}%)
146
-
147
- 🎯 **การกระจายของความรู้สึก:**
148
- """
149
 
150
  for sentiment, count in sentiment_counts.items():
151
  if count > 0:
152
  percentage = (count / total_sentences) * 100
153
- emoji = {"positive": "😊", "negative": "😔", "neutral": "😐", "question": "❓", "other": "🔍"}
154
- summary += f" {emoji.get(sentiment, '🔍')} {sentiment.title()}: {count} ประโยค ({percentage:.1f}%)\n"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
155
 
156
- results.append(summary)
157
 
158
- return "\n".join(results)
159
 
160
- # Modern minimal CSS
161
  CUSTOM_CSS = """
162
- body, .gradio-container {
163
- background: #f7faff !important;
 
 
164
  }
 
165
  .gradio-container {
166
- max-width: 1100px !important;
167
- margin: 32px auto 32px auto !important;
168
- border-radius: 22px !important;
169
- box-shadow: 0 8px 32px 0 #bdbdbd55;
170
- border: 1.5px solid #e3e3e3;
171
- min-height: 96vh;
172
- padding-bottom: 32px;
173
  }
174
- .main-card {
175
- background: #fff !important;
176
- border-radius: 18px;
177
- box-shadow: 0 2px 12px 0 #bdbdbd55;
178
- padding: 28px 24px 20px 24px;
179
- margin: 18px 0;
180
- border: 1.5px solid #d1d1d1;
181
- color: #222 !important;
182
- transition: box-shadow 0.2s;
183
  }
184
- .main-card:hover {
185
- box-shadow: 0 6px 24px 0 #bdbdbd77;
 
 
 
 
 
 
186
  }
187
- .output-markdown {
188
- font-family: 'Segoe UI', 'Noto Sans Thai', sans-serif !important;
189
- line-height: 1.7;
190
- font-size: 1.08em;
191
- color: #222 !important;
192
  }
 
193
  .gr-button {
194
- font-size: 1.13em;
195
- padding: 0.9em 2.7em;
196
- border-radius: 13px;
197
- font-weight: 600;
198
- transition: all 0.2s;
199
- background: linear-gradient(90deg, #4a63e7 0%, #764ba2 100%);
200
- color: #fff !important;
201
- border: none;
202
- box-shadow: 0 2px 8px #bdbdbd33;
203
- }
204
- .gr-button.secondary, .gr-button[variant="secondary"] {
205
- background: #eaeaea !important;
206
- color: #333 !important;
207
- border: 1px solid #cccccc;
208
  }
 
209
  .gr-button:hover {
210
- transform: translateY(-2px) scale(1.03);
211
- box-shadow: 0 6px 18px #bdbdbd55;
212
- filter: brightness(1.04);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
213
  }
214
- .gr-textbox textarea {
215
- font-size: 1.13em;
216
- min-height: 150px;
217
- font-family: 'Segoe UI', 'Noto Sans Thai', sans-serif;
218
- background: #fff;
219
- border-radius: 10px;
220
- border: 1.5px solid #cccccc;
221
- color: #222;
222
- padding: 14px;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
223
  }
224
- .gr-dropdown input {
225
- font-size: 1.1em;
226
- border-radius: 8px;
227
- background: #fff;
228
- border: 1.5px solid #cccccc;
229
- color: #222;
 
 
 
 
 
230
  }
231
- .gr-markdown h1, .gr-markdown h3 {
232
- color: #2a2a4d !important;
 
233
  }
234
- .gr-markdown, .gr-markdown ul, .gr-markdown p, .gr-markdown li {
235
- color: #222 !important;
 
 
 
236
  }
237
- .gr-markdown ul {
238
- margin-left: 1.2em;
 
 
 
 
 
 
239
  }
240
- @media (max-width: 900px) {
241
- .gradio-container { max-width: 99vw !important; padding: 0 2vw; }
242
- .main-card { padding: 16px 6px; }
 
243
  }
244
  """
245
 
246
- # Enhanced Gradio interface
247
  with gr.Blocks(
248
- theme=gr.themes.Soft(primary_hue="blue", secondary_hue="purple", neutral_hue="gray"),
 
 
 
 
 
249
  css=CUSTOM_CSS,
250
- title="Thai Sentiment Analyzer - AI วิเคราะห์ความรู้สึกภาษาไทย"
251
  ) as demo:
252
 
253
- gr.Markdown("""
254
- <div style="text-align: center; padding: 38px 0 18px 0;">
255
- <h1 style="font-size:2.7em; margin-bottom: 0.18em; color:#3b3b6d; letter-spacing:0.5px;">🧠 Thai Sentiment Analyzer</h1>
256
- <div style="font-size:1.18em; color:#4a4a7d; opacity:0.92;">
257
- AI วิเคราะห์ความรู้สึกในข้อความภาษาไทย รองรับหลายโมเดลและหลายประโยค
 
 
 
 
 
 
258
  </div>
259
  </div>
260
  """)
261
 
262
- with gr.Row():
 
263
  with gr.Column(scale=1):
264
- gr.Markdown("""
265
- <div class='main-card'>
266
- <h3 style='color:#3b3b6d; margin-bottom:15px; font-size:1.18em;'>🤖 เลือกโมเดลวิเคราะห์</h3>
267
- <p style='color:#666; font-size:0.97em; margin-bottom:10px;'>เลือกโมเดล AI ที่ต้องการใช้ในการวิเคราะห์ความรู้สึก</p>
268
- </div>
269
- """)
270
-
271
- model_dropdown = gr.Dropdown(
272
- choices=MODEL_LIST,
273
- value="ZombitX64/MultiSent-E5-Pro",
274
- label="โมเดลที่ต้องการใช้",
275
- info="แนะนำ: MultiSent-E5-Pro สำหรับความแม่นยำสูง"
276
- )
277
-
278
- gr.Markdown("""
279
- <div class='main-card' style='margin-top:20px;'>
280
- <h3 style='color:#3b3b6d; margin-bottom:15px; font-size:1.13em;'>📖 คำแนะนำการใช้งาน</h3>
281
- <ul style='color:#555; font-size:1em; line-height:1.7; margin-bottom:0;'>
282
- <li>พิมพ์ข้อความภาษาไทยที่ต้องการวิเคราะห์</li>
283
- <li>แยกประโยคด้วยจุด (.) หรือขึ้นบรรทัดใหม่</li>
284
- <li>รองรับการวิเคราะห์หลายประโยคพร้อมกัน</li>
285
- <li>ผลลัพธ์จะแสดงความมั่นใจและสรุปภาพรวม</li>
286
- </ul>
287
- </div>
288
- """)
289
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
290
  with gr.Column(scale=2):
291
- gr.Markdown("""
292
- <div class='main-card'>
293
- <h3 style='color:#3b3b6d; margin-bottom:15px; font-size:1.15em;'>📝 ข้อความที่ต้องการวิเคราะห์</h3>
294
- </div>
295
- """)
296
-
297
- text_input = gr.Textbox(
298
- lines=8,
299
- placeholder="ตัวอย่าง:\nวันนี้อากาศดีมาก ฉันรู้สึกมีความสุข\nแต่การจราจรติดมาก น่าเบื่อจริงๆ\nโดยรวมแล้วก็โอเคนะ",
300
- label="",
301
- show_label=False
302
- )
303
-
304
- with gr.Row():
305
- analyze_btn = gr.Button("🔍 วิเคราะห์ข้อความ", variant="primary", size="lg")
306
- clear_btn = gr.Button("🗑️ ล้างข้อความ", variant="secondary")
307
-
308
- output_box = gr.Textbox(
309
- label="📊 ผลการวิเคราะห์ความรู้สึก",
310
- lines=20,
311
- show_copy_button=True,
312
- show_label=True
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
313
  )
314
 
315
- # Enhanced examples
 
 
 
 
 
 
 
 
316
  examples = gr.Examples(
317
  examples=[
318
- ["วันนี้อากาศดีมาก ฉันรู้สึกมีความสุขมาก สีฟ้าสวยจริงๆ"],
319
- ["ฉันไม่ชอบอาหารนี้เลย รสชาติแปลกมาก เค็มเกินไป"],
320
- ["วันนี้เป็นยังไงบ้าง\nเรียนหนังสือกันไหม\nมีงานอะไรให้ช่วยไหม"],
321
- ["บริการดีมาก พนักงานใจดีและเป็นกันเอง\nแต่ของมีราคาแพงไปหน่อย\nโดยรวมแล้วพอใจครับ แนะนำให้เพื่อนมาลอง"],
322
- ["เมื่อไหร่จะได้เจอกันอีก คิดถึงมากเลย\nแต่ตอนนี้ต้องทำงานหนักก่อน เพื่ออนาคตที่ดี"]
323
  ],
324
  inputs=[text_input],
325
- label="💡 คลิกเพื่อลองใช้ตัวอย่าง"
 
326
  )
327
 
328
- # Sentiment legend with enhanced styling
329
- gr.Markdown("""
330
- <div class='main-card' style='margin-top:30px; background: #5b3b6d;'>
331
- <h3 style='color:#3b3b6d; text-align:center; margin-bottom:20px;'>🎯 คำอธิบายผลการวิเคราะห์</h3>
332
- <div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 18px; padding: 12px 0;">
333
- <div style="text-align: center; padding: 13px; background: rgba(76, 175, 80, 0.08); border-radius: 11px;">
334
- <div style="font-size: 26px; margin-bottom: 7px;">😊</div>
335
- <strong style="color: #4CAF50; font-size: 1.08em;">Positive</strong><br>
336
- <small style="color: #666;">ความรู้สึกเชิงบวก<br>ดี, สุข, ชอบ</small>
 
 
337
  </div>
338
- <div style="text-align: center; padding: 13px; background: rgba(244, 67, 54, 0.08); border-radius: 11px;">
339
- <div style="font-size: 26px; margin-bottom: 7px;">😔</div>
340
- <strong style="color: #F44336; font-size: 1.08em;">Negative</strong><br>
341
- <small style="color: #666;">ความรู้สึกเชิงลบ<br>เศร้า, โกรธ, ไม่ชอบ</small>
342
  </div>
343
- <div style="text-align: center; padding: 13px; background: rgba(255, 152, 0, 0.08); border-radius: 11px;">
344
- <div style="font-size: 26px; margin-bottom: 7px;">😐</div>
345
- <strong style="color: #FF9800; font-size: 1.08em;">Neutral</strong><br>
346
- <small style="color: #666;">ความรู้สึกเป็นกลาง<br>ปกติ, พอใช้ได้</small>
347
  </div>
348
- <div style="text-align: center; padding: 13px; background: rgba(33, 150, 243, 0.08); border-radius: 11px;">
349
- <div style="font-size: 26px; margin-bottom: 7px;">❓</div>
350
- <strong style="color: #2196F3; font-size: 1.08em;">Question</strong><br>
351
- <small style="color: #666;">ประโยคคำถาม<br>อะไร, ไหน, เมื่อไหร่</small>
352
  </div>
353
  </div>
354
  </div>
355
  """)
 
 
 
 
 
 
 
 
 
356
 
357
- # Enhanced event handlers
358
- def analyze_wrapper(text, model_name):
359
  if not text.strip():
360
- return "❗ กรุณาใส่ข้อความที่ต้องการวิเคราะห์"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
361
  return analyze_text(text, model_name)
362
 
363
  def clear_text():
364
- return ""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
365
 
366
  # Connect events
367
- analyze_btn.click(analyze_wrapper, inputs=[text_input, model_dropdown], outputs=output_box)
368
- text_input.submit(analyze_wrapper, inputs=[text_input, model_dropdown], outputs=output_box)
369
- model_dropdown.change(analyze_wrapper, inputs=[text_input, model_dropdown], outputs=output_box)
370
- clear_btn.click(clear_text, outputs=text_input)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
371
 
372
- # Launch with enhanced settings
373
  if __name__ == "__main__":
374
- demo.queue(max_size=20).launch(
 
 
 
375
  server_name="0.0.0.0",
376
  server_port=7860,
377
- share=False, # Set to True if you want to create a public link
378
  show_error=True,
379
- favicon_path=None, # Add your favicon path here
380
- ssl_verify=False
381
- )
 
 
 
 
 
 
 
4
  from functools import lru_cache
5
  import logging
6
  from typing import List, Dict, Tuple
7
+ import json
8
 
9
  # Set up logging
10
  logging.basicConfig(level=logging.INFO)
11
  logger = logging.getLogger(__name__)
12
 
13
+ # Enhanced model list with descriptions
14
  MODEL_LIST = [
15
+ ("ZombitX64/MultiSent-E5-Pro", "🏆 MultiSent E5 Pro - แนะนำ (ความแม่นยำสูงสุด)"),
16
+ ("ZombitX64/Thai-sentiment-e5", "🎯 Thai Sentiment E5 - เฉพาะภาษาไทย"),
17
+ ("poom-sci/WangchanBERTa-finetuned-sentiment", "🔥 WangchanBERTa - โมเดลไทยยอดนิยม"),
18
+ ("SandboxBhh/sentiment-thai-text-model", "✨ Sandbox Thai - เร็วและแม่นยำ"),
19
+ ("ZombitX64/MultiSent-E5", "⚡ MultiSent E5 - รวดเร็ว"),
20
+ ("Thaweewat/wangchanberta-hyperopt-sentiment-01", "🧠 WangchanBERTa Hyperopt"),
21
+ ("cardiffnlp/twitter-xlm-roberta-base-sentiment", "🌐 XLM-RoBERTa - หลายภาษา"),
22
+ ("phoner45/wangchan-sentiment-thai-text-model", "📱 Wangchan Mobile"),
23
+ ("ZombitX64/Sentiment-01", "🔬 Sentiment v1"),
24
+ ("ZombitX64/Sentiment-02", "🔬 Sentiment v2"),
25
+ ("ZombitX64/Sentiment-03", "🔬 Sentiment v3"),
26
+ ("ZombitX64/sentiment-103", "🔬 Sentiment 103"),
27
+ ("nlptown/bert-base-multilingual-uncased-sentiment", "🌍 BERT Multilingual")
28
  ]
29
 
30
+ # Cache for model loading
31
  @lru_cache(maxsize=3)
32
  def get_nlp(model_name: str):
33
  try:
 
36
  logger.error(f"Error loading model {model_name}: {e}")
37
  raise gr.Error(f"ไม่สามารถโหลดโมเดล {model_name} ได้: {str(e)}")
38
 
39
+ # Enhanced label mapping with modern styling
40
  LABEL_MAPPINGS = {
41
+ "LABEL_0": {"code": 0, "name": "question", "emoji": "🤔", "color": "#6366f1", "bg": "rgba(99, 102, 241, 0.1)", "description": "คำถาม"},
42
+ "LABEL_1": {"code": 1, "name": "negative", "emoji": "😢", "color": "#ef4444", "bg": "rgba(239, 68, 68, 0.1)", "description": "เชิงลบ"},
43
+ "LABEL_2": {"code": 2, "name": "neutral", "emoji": "😐", "color": "#f59e0b", "bg": "rgba(245, 158, 11, 0.1)", "description": "เป็นกลาง"},
44
+ "LABEL_3": {"code": 3, "name": "positive", "emoji": "😊", "color": "#10b981", "bg": "rgba(16, 185, 129, 0.1)", "description": "เชิงบวก"},
 
45
 
46
+ "POSITIVE": {"code": 3, "name": "positive", "emoji": "😊", "color": "#10b981", "bg": "rgba(16, 185, 129, 0.1)", "description": "เชิงบวก"},
47
+ "NEGATIVE": {"code": 1, "name": "negative", "emoji": "😢", "color": "#ef4444", "bg": "rgba(239, 68, 68, 0.1)", "description": "เชิงลบ"},
48
+ "NEUTRAL": {"code": 2, "name": "neutral", "emoji": "😐", "color": "#f59e0b", "bg": "rgba(245, 158, 11, 0.1)", "description": "เป็นกลาง"},
 
49
 
50
+ "0": {"code": 0, "name": "negative", "emoji": "😢", "color": "#ef4444", "bg": "rgba(239, 68, 68, 0.1)", "description": "เชิงลบ"},
51
+ "1": {"code": 1, "name": "positive", "emoji": "😊", "color": "#10b981", "bg": "rgba(16, 185, 129, 0.1)", "description": "เชิงบวก"},
 
52
  }
53
 
54
  def get_label_info(label: str) -> Dict:
 
57
  "code": -1,
58
  "name": label.lower(),
59
  "emoji": "🔍",
60
+ "color": "#6b7280",
61
+ "bg": "rgba(107, 114, 128, 0.1)",
62
+ "description": "ไม่ทราบ"
63
  })
64
 
65
  def split_sentences(text: str) -> List[str]:
66
  """Enhanced sentence splitting with better Thai support"""
67
+ sentences = re.split(r'[.!?။\n]+', text)
 
 
68
  sentences = [s.strip() for s in sentences if s.strip() and len(s.strip()) > 2]
69
  return sentences
70
 
71
+ def create_confidence_bar(score: float) -> str:
72
+ """Create a modern confidence visualization"""
73
+ percentage = int(score * 100)
74
+ return f"""
75
+ <div style="display: flex; align-items: center; gap: 10px; margin: 8px 0;">
76
+ <div style="flex: 1; height: 8px; background: #e5e7eb; border-radius: 4px; overflow: hidden;">
77
+ <div style="width: {percentage}%; height: 100%; background: linear-gradient(90deg, #10b981, #059669); transition: all 0.3s ease;"></div>
78
+ </div>
79
+ <span style="font-weight: 600; color: #374151; min-width: 50px;">{percentage}%</span>
80
+ </div>
81
+ """
82
 
83
  def analyze_text(text: str, model_name: str) -> str:
84
+ """Enhanced text analysis with modern HTML formatting"""
85
  if not text or not text.strip():
86
+ return """
87
+ <div style="padding: 20px; background: rgba(239, 68, 68, 0.1); border-radius: 12px; border-left: 4px solid #ef4444;">
88
+ <div style="color: #dc2626; font-weight: 600; display: flex; align-items: center; gap: 8px;">
89
+ <span style="font-size: 20px;">⚠️</span>
90
+ กรุณาใส่ข้อความที่ต้องการวิเคราะห์
91
+ </div>
92
+ </div>
93
+ """
94
 
95
  sentences = split_sentences(text)
96
 
97
  if not sentences:
98
+ return """
99
+ <div style="padding: 20px; background: rgba(239, 68, 68, 0.1); border-radius: 12px; border-left: 4px solid #ef4444;">
100
+ <div style="color: #dc2626; font-weight: 600; display: flex; align-items: center; gap: 8px;">
101
+ <span style="font-size: 20px;">⚠️</span>
102
+ ไม่พบประโยคที่สามารถวิเคราะห์ได้ กรุณาใส่ข้อความที่ยาวกว่านี้
103
+ </div>
104
+ </div>
105
+ """
106
 
107
  try:
108
  nlp = get_nlp(model_name)
109
  except Exception as e:
110
+ return f"""
111
+ <div style="padding: 20px; background: rgba(239, 68, 68, 0.1); border-radius: 12px; border-left: 4px solid #ef4444;">
112
+ <div style="color: #dc2626; font-weight: 600; display: flex; align-items: center; gap: 8px;">
113
+ <span style="font-size: 20px;">❌</span>
114
+ เกิดข้อผิดพลาดในการโหลดโมเดล: {str(e)}
115
+ </div>
116
+ </div>
117
+ """
118
 
119
+ # Header
120
+ html_parts = ["""
121
+ <div style="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; padding: 24px; border-radius: 16px 16px 0 0; margin-bottom: 0;">
122
+ <h2 style="margin: 0; font-size: 24px; font-weight: 700; display: flex; align-items: center; gap: 12px;">
123
+ <span style="font-size: 28px;">🧠</span>
124
+ ผลการวิเคราะห์ความรู้สึก
125
+ </h2>
126
+ <p style="margin: 8px 0 0 0; opacity: 0.9; font-size: 14px;">โมเดล: """ + model_name.split('/')[-1] + """</p>
127
+ </div>
128
+ """]
129
 
130
  sentiment_counts = {"positive": 0, "negative": 0, "neutral": 0, "question": 0, "other": 0}
131
  total_confidence = 0
132
+ sentence_results = []
133
 
134
+ # Analyze each sentence
135
  for i, sentence in enumerate(sentences, 1):
136
  try:
137
  result = nlp(sentence)[0]
 
141
  label_info = get_label_info(label)
142
  label_name = label_info["name"]
143
 
 
144
  if label_name in sentiment_counts:
145
  sentiment_counts[label_name] += 1
146
  else:
 
148
 
149
  total_confidence += score
150
 
151
+ # Store result for display
152
+ sentence_results.append({
153
+ 'sentence': sentence,
154
+ 'label_info': label_info,
155
+ 'score': score,
156
+ 'index': i
157
+ })
 
 
 
 
 
 
 
158
 
159
  except Exception as e:
160
  logger.error(f"Error analyzing sentence {i}: {e}")
161
+ sentence_results.append({
162
+ 'sentence': sentence,
163
+ 'error': str(e),
164
+ 'index': i
165
+ })
166
 
167
+ # Results container
168
+ html_parts.append("""
169
+ <div style="background: white; padding: 0; border-radius: 0 0 16px 16px; box-shadow: 0 4px 20px rgba(0,0,0,0.1); overflow: hidden;">
170
+ """)
171
+
172
+ # Individual sentence results
173
+ for result in sentence_results:
174
+ if 'error' in result:
175
+ html_parts.append(f"""
176
+ <div style="padding: 20px; border-bottom: 1px solid #f3f4f6;">
177
+ <div style="color: #dc2626; font-weight: 600; display: flex; align-items: center; gap: 8px;">
178
+ <span style="font-size: 18px;">❌</span>
179
+ เกิดข้อผิดพลาดในการวิเคราะห์ประโยคที่ {result['index']}
180
+ </div>
181
+ <p style="color: #6b7280; margin: 8px 0 0 0; font-size: 14px;">{result['error']}</p>
182
+ </div>
183
+ """)
184
+ else:
185
+ label_info = result['label_info']
186
+ confidence_bar = create_confidence_bar(result['score'])
187
+
188
+ html_parts.append(f"""
189
+ <div style="padding: 20px; border-bottom: 1px solid #f3f4f6; transition: all 0.2s ease;" onmouseover="this.style.background='#f9fafb'" onmouseout="this.style.background='white'">
190
+ <div style="display: flex; align-items: flex-start; gap: 16px;">
191
+ <div style="background: {label_info['bg']}; padding: 12px; border-radius: 50%; min-width: 48px; height: 48px; display: flex; align-items: center; justify-content: center;">
192
+ <span style="font-size: 20px;">{label_info['emoji']}</span>
193
+ </div>
194
+ <div style="flex: 1;">
195
+ <div style="display: flex; align-items: center; gap: 8px; margin-bottom: 8px;">
196
+ <span style="background: {label_info['color']}; color: white; padding: 4px 12px; border-radius: 20px; font-size: 12px; font-weight: 600; text-transform: uppercase;">
197
+ {label_info['description']}
198
+ </span>
199
+ <span style="color: #6b7280; font-size: 14px;">ประโยคที่ {result['index']}</span>
200
+ </div>
201
+ <p style="color: #374151; margin: 0 0 12px 0; font-size: 16px; line-height: 1.5;">
202
+ "{result['sentence'][:150]}{'...' if len(result['sentence']) > 150 else ''}"
203
+ </p>
204
+ <div style="color: #6b7280; font-size: 14px; margin-bottom: 8px;">ความมั่นใจ:</div>
205
+ {confidence_bar}
206
+ </div>
207
+ </div>
208
+ </div>
209
+ """)
210
+
211
+ # Summary section
212
  total_sentences = len(sentences)
213
  avg_confidence = total_confidence / total_sentences if total_sentences > 0 else 0
214
 
215
+ # Create chart data for summary
216
+ chart_items = []
217
+ colors = {"positive": "#10b981", "negative": "#ef4444", "neutral": "#f59e0b", "question": "#6366f1", "other": "#6b7280"}
218
+ emojis = {"positive": "😊", "negative": "😢", "neutral": "😐", "question": "🤔", "other": "🔍"}
 
 
 
 
219
 
220
  for sentiment, count in sentiment_counts.items():
221
  if count > 0:
222
  percentage = (count / total_sentences) * 100
223
+ chart_items.append(f"""
224
+ <div style="display: flex; align-items: center; gap: 12px; padding: 12px; background: rgba(99, 102, 241, 0.02); border-radius: 8px;">
225
+ <span style="font-size: 24px;">{emojis.get(sentiment, '🔍')}</span>
226
+ <div style="flex: 1;">
227
+ <div style="font-weight: 600; color: #374151; text-transform: capitalize;">{sentiment}</div>
228
+ <div style="color: #6b7280; font-size: 14px;">{count} ประโยค ({percentage:.1f}%)</div>
229
+ </div>
230
+ <div style="width: 60px; height: 6px; background: #e5e7eb; border-radius: 3px; overflow: hidden;">
231
+ <div style="width: {percentage}%; height: 100%; background: {colors.get(sentiment, '#6b7280')}; transition: all 0.3s ease;"></div>
232
+ </div>
233
+ </div>
234
+ """)
235
+
236
+ html_parts.append(f"""
237
+ <div style="padding: 24px; background: linear-gradient(135deg, #f8fafc 0%, #f1f5f9 100%);">
238
+ <h3 style="color: #1e293b; margin: 0 0 20px 0; font-size: 20px; font-weight: 700; display: flex; align-items: center; gap: 8px;">
239
+ <span style="font-size: 24px;">📊</span>
240
+ สรุปผลการวิเคราะห์
241
+ </h3>
242
+
243
+ <div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 16px; margin-bottom: 20px;">
244
+ <div style="background: white; padding: 20px; border-radius: 12px; text-align: center; box-shadow: 0 2px 8px rgba(0,0,0,0.05);">
245
+ <div style="font-size: 32px; font-weight: 700; color: #6366f1; margin-bottom: 4px;">{total_sentences}</div>
246
+ <div style="color: #6b7280; font-size: 14px;">ประโยคทั้งหมด</div>
247
+ </div>
248
+ <div style="background: white; padding: 20px; border-radius: 12px; text-align: center; box-shadow: 0 2px 8px rgba(0,0,0,0.05);">
249
+ <div style="font-size: 32px; font-weight: 700; color: #10b981; margin-bottom: 4px;">{avg_confidence*100:.0f}%</div>
250
+ <div style="color: #6b7280; font-size: 14px;">ความมั่นใจเฉลี่ย</div>
251
+ </div>
252
+ </div>
253
+
254
+ <div style="display: grid; gap: 8px;">
255
+ {"".join(chart_items)}
256
+ </div>
257
+ </div>
258
+ """)
259
 
260
+ html_parts.append("</div>")
261
 
262
+ return "".join(html_parts)
263
 
264
+ # Modern CSS with glassmorphism and smooth animations
265
  CUSTOM_CSS = """
266
+ @import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&display=swap');
267
+
268
+ * {
269
+ font-family: 'Inter', 'Noto Sans Thai', sans-serif !important;
270
  }
271
+
272
  .gradio-container {
273
+ max-width: 1200px !important;
274
+ margin: 0 auto !important;
275
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%) !important;
276
+ min-height: 100vh;
277
+ padding: 20px;
 
 
278
  }
279
+
280
+ .main-content {
281
+ background: rgba(255, 255, 255, 0.95) !important;
282
+ backdrop-filter: blur(20px) !important;
283
+ border-radius: 24px !important;
284
+ box-shadow: 0 8px 32px rgba(31, 38, 135, 0.37) !important;
285
+ border: 1px solid rgba(255, 255, 255, 0.18) !important;
286
+ overflow: hidden;
 
287
  }
288
+
289
+ .glass-card {
290
+ background: rgba(255, 255, 255, 0.9) !important;
291
+ backdrop-filter: blur(10px) !important;
292
+ border-radius: 16px !important;
293
+ border: 1px solid rgba(255, 255, 255, 0.2) !important;
294
+ box-shadow: 0 4px 16px rgba(0, 0, 0, 0.1) !important;
295
+ transition: all 0.3s ease !important;
296
  }
297
+
298
+ .glass-card:hover {
299
+ transform: translateY(-2px) !important;
300
+ box-shadow: 0 8px 24px rgba(0, 0, 0, 0.15) !important;
 
301
  }
302
+
303
  .gr-button {
304
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%) !important;
305
+ border: none !important;
306
+ border-radius: 12px !important;
307
+ padding: 12px 24px !important;
308
+ font-weight: 600 !important;
309
+ text-transform: uppercase !important;
310
+ letter-spacing: 0.5px !important;
311
+ transition: all 0.3s ease !important;
312
+ box-shadow: 0 4px 15px rgba(102, 126, 234, 0.4) !important;
 
 
 
 
 
313
  }
314
+
315
  .gr-button:hover {
316
+ transform: translateY(-2px) !important;
317
+ box-shadow: 0 6px 20px rgba(102, 126, 234, 0.6) !important;
318
+ }
319
+
320
+ .gr-button.secondary {
321
+ background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%) !important;
322
+ box-shadow: 0 4px 15px rgba(240, 147, 251, 0.4) !important;
323
+ }
324
+
325
+ .gr-button.secondary:hover {
326
+ box-shadow: 0 6px 20px rgba(240, 147, 251, 0.6) !important;
327
+ }
328
+
329
+ .gr-textbox textarea, .gr-textbox input {
330
+ border-radius: 12px !important;
331
+ border: 2px solid rgba(102, 126, 234, 0.2) !important;
332
+ font-size: 16px !important;
333
+ padding: 16px !important;
334
+ transition: all 0.3s ease !important;
335
+ background: rgba(255, 255, 255, 0.9) !important;
336
+ }
337
+
338
+ .gr-textbox textarea:focus, .gr-textbox input:focus {
339
+ border-color: #667eea !important;
340
+ box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1) !important;
341
+ transform: scale(1.01) !important;
342
+ }
343
+
344
+ .gr-dropdown > div {
345
+ border-radius: 12px !important;
346
+ border: 2px solid rgba(102, 126, 234, 0.2) !important;
347
+ background: rgba(255, 255, 255, 0.9) !important;
348
+ }
349
+
350
+ .gr-dropdown > div:focus-within {
351
+ border-color: #667eea !important;
352
+ box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1) !important;
353
  }
354
+
355
+ .gr-panel {
356
+ border-radius: 16px !important;
357
+ border: none !important;
358
+ background: transparent !important;
359
+ }
360
+
361
+ .gr-form {
362
+ background: transparent !important;
363
+ border: none !important;
364
+ }
365
+
366
+ .gr-box {
367
+ border: none !important;
368
+ background: transparent !important;
369
+ }
370
+
371
+ /* Custom scrollbar */
372
+ ::-webkit-scrollbar {
373
+ width: 8px;
374
+ }
375
+
376
+ ::-webkit-scrollbar-track {
377
+ background: rgba(255, 255, 255, 0.1);
378
+ border-radius: 4px;
379
+ }
380
+
381
+ ::-webkit-scrollbar-thumb {
382
+ background: rgba(102, 126, 234, 0.6);
383
+ border-radius: 4px;
384
+ }
385
+
386
+ ::-webkit-scrollbar-thumb:hover {
387
+ background: rgba(102, 126, 234, 0.8);
388
  }
389
+
390
+ /* Animation for content */
391
+ @keyframes slideIn {
392
+ from {
393
+ opacity: 0;
394
+ transform: translateY(20px);
395
+ }
396
+ to {
397
+ opacity: 1;
398
+ transform: translateY(0);
399
+ }
400
  }
401
+
402
+ .gr-column, .gr-row {
403
+ animation: slideIn 0.6s ease-out !important;
404
  }
405
+
406
+ /* Enhanced examples styling */
407
+ .gr-examples {
408
+ border-radius: 16px !important;
409
+ overflow: hidden !important;
410
  }
411
+
412
+ .gr-examples .gr-button {
413
+ background: rgba(255, 255, 255, 0.1) !important;
414
+ border: 1px solid rgba(255, 255, 255, 0.2) !important;
415
+ margin: 4px !important;
416
+ font-size: 14px !important;
417
+ text-transform: none !important;
418
+ letter-spacing: normal !important;
419
  }
420
+
421
+ .gr-examples .gr-button:hover {
422
+ background: rgba(255, 255, 255, 0.2) !important;
423
+ transform: scale(1.02) !important;
424
  }
425
  """
426
 
427
+ # Create the modern Gradio interface
428
  with gr.Blocks(
429
+ theme=gr.themes.Glass(
430
+ primary_hue="blue",
431
+ secondary_hue="purple",
432
+ neutral_hue="slate",
433
+ font=["Inter", "Noto Sans Thai", "sans-serif"]
434
+ ),
435
  css=CUSTOM_CSS,
436
+ title="🧠 AI Thai Sentiment Analyzer - วิเคราะห์ความรู้สึกภาษาไทย"
437
  ) as demo:
438
 
439
+ # Header with modern design
440
+ gr.HTML("""
441
+ <div style="text-align: center; padding: 40px 0 30px 0;">
442
+ <div style="display: inline-block; background: rgba(255, 255, 255, 0.15); backdrop-filter: blur(10px); padding: 20px 40px; border-radius: 20px; border: 1px solid rgba(255, 255, 255, 0.2); margin-bottom: 20px;">
443
+ <h1 style="font-size: 3.5em; margin: 0; color: white; font-weight: 800; text-shadow: 0 4px 8px rgba(0,0,0,0.3); display: flex; align-items: center; justify-content: center; gap: 20px;">
444
+ <span style="font-size: 1.2em;">🧠</span>
445
+ Thai Sentiment AI
446
+ </h1>
447
+ <p style="font-size: 1.4em; color: rgba(255,255,255,0.9); margin: 10px 0 0 0; font-weight: 300; text-shadow: 0 2px 4px rgba(0,0,0,0.2);">
448
+ ระบบวิเคราะห์ความรู้สึกภาษาไทยด้วย AI ที่ทันสมัยและแม่นยำ
449
+ </p>
450
  </div>
451
  </div>
452
  """)
453
 
454
+ with gr.Row(equal_height=False):
455
+ # Left Column - Controls
456
  with gr.Column(scale=1):
457
+ # Model Selection Card
458
+ with gr.Group():
459
+ gr.HTML("""
460
+ <div style="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; padding: 20px; border-radius: 16px 16px 0 0; margin: -20px -20px 20px -20px;">
461
+ <h3 style="margin: 0; font-size: 18px; font-weight: 700; display: flex; align-items: center; gap: 8px;">
462
+ <span style="font-size: 20px;">🤖</span>
463
+ เลือกโมเดล AI
464
+ </h3>
465
+ <p style="margin: 8px 0 0 0; opacity: 0.9; font-size: 14px;">เลือกโมเดลที่ต้องการใช้ในการวิเคราะห์</p>
466
+ </div>
467
+ """)
468
+
469
+ model_dropdown = gr.Dropdown(
470
+ choices=[choice[1] for choice in MODEL_LIST],
471
+ value="🏆 MultiSent E5 Pro - แนะนำ (ความแม่นยำสูงสุด)",
472
+ label="",
473
+ show_label=False,
474
+ container=False
475
+ )
 
 
 
 
 
 
476
 
477
+ # Tips Card
478
+ with gr.Group():
479
+ gr.HTML("""
480
+ <div style="background: linear-gradient(135deg, #ffecd2 0%, #fcb69f 100%); padding: 20px; border-radius: 16px;">
481
+ <h4 style="color: #8b4513; margin: 0 0 16px 0; font-size: 16px; font-weight: 700; display: flex; align-items: center; gap: 8px;">
482
+ <span style="font-size: 18px;">💡</span>
483
+ เคล็ดลับการใช้งาน
484
+ </h4>
485
+ <div style="color: #8b4513; font-size: 14px; line-height: 1.6;">
486
+ <div style="margin-bottom: 12px; display: flex; align-items: flex-start; gap: 8px;">
487
+ <span style="color: #ff6b35; font-weight: 600;">•</span>
488
+ <span>พิมพ์ข้อความภาษาไทยที่ต้องการวิเคราะห์</span>
489
+ </div>
490
+ <div style="margin-bottom: 12px; display: flex; align-items: flex-start; gap: 8px;">
491
+ <span style="color: #ff6b35; font-weight: 600;">•</span>
492
+ <span>แยกประโยคด้วยจุด (.) หรือขึ้นบรรทัดใหม่</span>
493
+ </div>
494
+ <div style="margin-bottom: 12px; display: flex; align-items: flex-start; gap: 8px;">
495
+ <span style="color: #ff6b35; font-weight: 600;">•</span>
496
+ <span>สามารถวิเคราะห์หลายประโยคพร้อมกันได้</span>
497
+ </div>
498
+ <div style="display: flex; align-items: flex-start; gap: 8px;">
499
+ <span style="color: #ff6b35; font-weight: 600;">•</span>
500
+ <span>ผลลัพธ์จะแสดงความมั่นใจและสรุปภาพรวม</span>
501
+ </div>
502
+ </div>
503
+ </div>
504
+ """)
505
+
506
+ # Right Column - Main Interface
507
  with gr.Column(scale=2):
508
+ # Input Section
509
+ with gr.Group():
510
+ gr.HTML("""
511
+ <div style="background: linear-gradient(135deg, #a8edea 0%, #fed6e3 100%); padding: 20px; border-radius: 16px 16px 0 0; margin: -20px -20px 20px -20px;">
512
+ <h3 style="color: #2d3748; margin: 0; font-size: 18px; font-weight: 700; display: flex; align-items: center; gap: 8px;">
513
+ <span style="font-size: 20px;">✍️</span>
514
+ ข้อความที่ต้องการวิเคราะห์
515
+ </h3>
516
+ </div>
517
+ """)
518
+
519
+ text_input = gr.Textbox(
520
+ lines=6,
521
+ placeholder="💭 ลองพิมพ์ข้อความที่ต้องการวิเคราะห์...\n\nตัวอย่าง:\nวันนี้อากาศดีมาก ฉันรู้สึกมีความสุข ☀️\nแต่การจราจรติดมาก น่าเบื่อจริงๆ 😤\nโดยรวมแล้วก็โอเคนะ 😊",
522
+ label="",
523
+ show_label=False,
524
+ container=False
525
+ )
526
+
527
+ with gr.Row():
528
+ analyze_btn = gr.Button(
529
+ "🔍 วิเคราะห์ข้อความ",
530
+ variant="primary",
531
+ size="lg",
532
+ scale=2
533
+ )
534
+ clear_btn = gr.Button(
535
+ "🗑️ ล้าง",
536
+ variant="secondary",
537
+ scale=1
538
+ )
539
+
540
+ # Output Section
541
+ with gr.Row():
542
+ with gr.Column():
543
+ output_box = gr.HTML(
544
+ label="📊 ผลการวิเคราะห์",
545
+ value="""
546
+ <div style="padding: 60px 20px; text-align: center; color: #6b7280; background: rgba(255, 255, 255, 0.5); border-radius: 16px; border: 2px dashed #d1d5db;">
547
+ <div style="font-size: 48px; margin-bottom: 16px;">🤖</div>
548
+ <h3 style="color: #374151; margin: 0 0 8px 0;">พร้อมวิเคราะห์ความรู้สึก</h3>
549
+ <p style="margin: 0; font-size: 14px;">ใส่ข้อความด้านบนแล้วกดปุ่ม "วิเคราะห์ข้อความ"</p>
550
+ </div>
551
+ """
552
  )
553
 
554
+ # Examples Section
555
+ gr.HTML("""
556
+ <div style="margin: 40px 0 20px 0;">
557
+ <h3 style="color: white; text-align: center; font-size: 20px; font-weight: 700; margin-bottom: 20px; text-shadow: 0 2px 4px rgba(0,0,0,0.3);">
558
+ ✨ ตัวอย่างการใช้งาน
559
+ </h3>
560
+ </div>
561
+ """)
562
+
563
  examples = gr.Examples(
564
  examples=[
565
+ ["วันนี้อากาศดีมาก ฉันรู้สึกมีความสุขมาก ☀️ สีฟ้าสวยจริงๆ น่ามองจัง"],
566
+ ["ฉันไม่ชอบอาหารนี้เลย 😤 รสชาติแปลกมาก เค็มเกินไป ไม่อร่อยเลย"],
567
+ ["วันนี้เป็นยังไงบ้าง? 🤔\nเรียนหนังสือกันไหม? 📚\nมีงานอะไรให้ช่วยไหม? 💪"],
568
+ ["บริการดีมาก 👍 พนักงานใจดีและเป็นกันเอง\nแต่ของมีราคาแพงไปหน่อย 💸\nโดยรวมแล้วพอใจครับ แนะนำให้เพื่อนมาลอง"],
569
+ ["เมื่อไหร่จะได้เจอกันอีก 😢 คิดถึงมากเลย\nแต่ตอนนี้ต้องทำงานหนักก่อน 💪 เพื่ออนาคตที่ดี"]
570
  ],
571
  inputs=[text_input],
572
+ label="",
573
+ examples_per_page=5
574
  )
575
 
576
+ # Legend Section
577
+ gr.HTML("""
578
+ <div style="margin: 40px 0 20px 0; background: rgba(255, 255, 255, 0.15); backdrop-filter: blur(10px); border-radius: 20px; padding: 30px; border: 1px solid rgba(255, 255, 255, 0.2);">
579
+ <h3 style="color: white; text-align: center; font-size: 22px; font-weight: 700; margin-bottom: 25px; text-shadow: 0 2px 4px rgba(0,0,0,0.3);">
580
+ 🎯 คำอธิบายผลการวิเคราะห์
581
+ </h3>
582
+ <div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 20px;">
583
+ <div style="background: rgba(16, 185, 129, 0.15); backdrop-filter: blur(10px); border-radius: 16px; padding: 20px; text-align: center; border: 1px solid rgba(16, 185, 129, 0.3); transition: all 0.3s ease;" onmouseover="this.style.transform='translateY(-5px)'; this.style.boxShadow='0 10px 25px rgba(16, 185, 129, 0.2)'" onmouseout="this.style.transform='translateY(0)'; this.style.boxShadow='none'">
584
+ <div style="font-size: 36px; margin-bottom: 12px;">😊</div>
585
+ <h4 style="color: #10b981; margin: 0 0 8px 0; font-size: 16px; font-weight: 700;">POSITIVE</h4>
586
+ <p style="color: rgba(255,255,255,0.8); margin: 0; font-size: 13px; line-height: 1.4;">ความรู้สึกเชิงบวก<br>ดี, สุข, ชอบ, ยินดี</p>
587
  </div>
588
+ <div style="background: rgba(239, 68, 68, 0.15); backdrop-filter: blur(10px); border-radius: 16px; padding: 20px; text-align: center; border: 1px solid rgba(239, 68, 68, 0.3); transition: all 0.3s ease;" onmouseover="this.style.transform='translateY(-5px)'; this.style.boxShadow='0 10px 25px rgba(239, 68, 68, 0.2)'" onmouseout="this.style.transform='translateY(0)'; this.style.boxShadow='none'">
589
+ <div style="font-size: 36px; margin-bottom: 12px;">😢</div>
590
+ <h4 style="color: #ef4444; margin: 0 0 8px 0; font-size: 16px; font-weight: 700;">NEGATIVE</h4>
591
+ <p style="color: rgba(255,255,255,0.8); margin: 0; font-size: 13px; line-height: 1.4;">ความรู้สึกเชิงลบ<br>เศร้า, โกรธ, ไม่ชอบ</p>
592
  </div>
593
+ <div style="background: rgba(245, 158, 11, 0.15); backdrop-filter: blur(10px); border-radius: 16px; padding: 20px; text-align: center; border: 1px solid rgba(245, 158, 11, 0.3); transition: all 0.3s ease;" onmouseover="this.style.transform='translateY(-5px)'; this.style.boxShadow='0 10px 25px rgba(245, 158, 11, 0.2)'" onmouseout="this.style.transform='translateY(0)'; this.style.boxShadow='none'">
594
+ <div style="font-size: 36px; margin-bottom: 12px;">😐</div>
595
+ <h4 style="color: #f59e0b; margin: 0 0 8px 0; font-size: 16px; font-weight: 700;">NEUTRAL</h4>
596
+ <p style="color: rgba(255,255,255,0.8); margin: 0; font-size: 13px; line-height: 1.4;">ความรู้สึกเป็นกลาง<br>ปกติ, พอใช้ได้, ธรรมดา</p>
597
  </div>
598
+ <div style="background: rgba(99, 102, 241, 0.15); backdrop-filter: blur(10px); border-radius: 16px; padding: 20px; text-align: center; border: 1px solid rgba(99, 102, 241, 0.3); transition: all 0.3s ease;" onmouseover="this.style.transform='translateY(-5px)'; this.style.boxShadow='0 10px 25px rgba(99, 102, 241, 0.2)'" onmouseout="this.style.transform='translateY(0)'; this.style.boxShadow='none'">
599
+ <div style="font-size: 36px; margin-bottom: 12px;">🤔</div>
600
+ <h4 style="color: #6366f1; margin: 0 0 8px 0; font-size: 16px; font-weight: 700;">QUESTION</h4>
601
+ <p style="color: rgba(255,255,255,0.8); margin: 0; font-size: 13px; line-height: 1.4;">ประโยคคำถาม<br>อะไร, ไหน, เมื่อไหร่</p>
602
  </div>
603
  </div>
604
  </div>
605
  """)
606
+
607
+ # Footer
608
+ gr.HTML("""
609
+ <div style="text-align: center; padding: 30px 0 10px 0;">
610
+ <p style="color: rgba(255,255,255,0.7); font-size: 14px; margin: 0; text-shadow: 0 1px 2px rgba(0,0,0,0.3);">
611
+ พัฒนาด้วย ❤️ โดยใช้ Transformers และ Gradio | รองรับโมเดล AI หลากหลาย
612
+ </p>
613
+ </div>
614
+ """)
615
 
616
+ # Event handlers with enhanced functionality
617
+ def analyze_wrapper(text, model_display_name):
618
  if not text.strip():
619
+ return """
620
+ <div style="padding: 60px 20px; text-align: center; color: #6b7280; background: rgba(255, 255, 255, 0.5); border-radius: 16px; border: 2px dashed #d1d5db;">
621
+ <div style="font-size: 48px; margin-bottom: 16px;">⚠️</div>
622
+ <h3 style="color: #ef4444; margin: 0 0 8px 0;">กรุณาใส่ข้อความ</h3>
623
+ <p style="margin: 0; font-size: 14px;">ใส่ข้อความที่ต้องการวิเคราะห์ในช่องด้านบน</p>
624
+ </div>
625
+ """
626
+
627
+ # Find actual model name from display name
628
+ model_name = None
629
+ for model_tuple in MODEL_LIST:
630
+ if model_tuple[1] == model_display_name:
631
+ model_name = model_tuple[0]
632
+ break
633
+
634
+ if not model_name:
635
+ model_name = "ZombitX64/MultiSent-E5-Pro" # fallback
636
+
637
  return analyze_text(text, model_name)
638
 
639
  def clear_text():
640
+ return "", """
641
+ <div style="padding: 60px 20px; text-align: center; color: #6b7280; background: rgba(255, 255, 255, 0.5); border-radius: 16px; border: 2px dashed #d1d5db;">
642
+ <div style="font-size: 48px; margin-bottom: 16px;">🤖</div>
643
+ <h3 style="color: #374151; margin: 0 0 8px 0;">พร้อมวิเคราะห์ความรู้สึก</h3>
644
+ <p style="margin: 0; font-size: 14px;">ใส่ข้อความด้านบนแล้วกดปุ่ม "วิเคราะห์ข้อความ"</p>
645
+ </div>
646
+ """
647
+
648
+ def auto_analyze(text, model_display_name):
649
+ if text.strip():
650
+ return analyze_wrapper(text, model_display_name)
651
+ return """
652
+ <div style="padding: 60px 20px; text-align: center; color: #6b7280; background: rgba(255, 255, 255, 0.5); border-radius: 16px; border: 2px dashed #d1d5db;">
653
+ <div style="font-size: 48px; margin-bottom: 16px;">🤖</div>
654
+ <h3 style="color: #374151; margin: 0 0 8px 0;">พร้อมวิเคราะห์ความรู้สึก</h3>
655
+ <p style="margin: 0; font-size: 14px;">ใส่ข้อความด้านบนแล้วกดปุ่ม "วิเคราะห์ข้อความ"</p>
656
+ </div>
657
+ """
658
 
659
  # Connect events
660
+ analyze_btn.click(
661
+ analyze_wrapper,
662
+ inputs=[text_input, model_dropdown],
663
+ outputs=output_box,
664
+ show_progress=True
665
+ )
666
+
667
+ text_input.submit(
668
+ analyze_wrapper,
669
+ inputs=[text_input, model_dropdown],
670
+ outputs=output_box,
671
+ show_progress=True
672
+ )
673
+
674
+ model_dropdown.change(
675
+ auto_analyze,
676
+ inputs=[text_input, model_dropdown],
677
+ outputs=output_box
678
+ )
679
+
680
+ clear_btn.click(
681
+ clear_text,
682
+ outputs=[text_input, output_box]
683
+ )
684
 
685
+ # Launch configuration
686
  if __name__ == "__main__":
687
+ demo.queue(
688
+ max_size=50,
689
+ default_concurrency_limit=10
690
+ ).launch(
691
  server_name="0.0.0.0",
692
  server_port=7860,
693
+ share=True, # Set to True for public sharing
694
  show_error=True,
695
+ show_api=False,
696
+ quiet=False,
697
+ favicon_path=None,
698
+ ssl_verify=False,
699
+ app_kwargs={
700
+ "docs_url": None,
701
+ "redoc_url": None,
702
+ }
703
+ )