thanhdov commited on
Commit
8ff7807
·
1 Parent(s): 2c6ab64

feat: replace sentiment analysis demo with Gemma 4 chat assistant application

Browse files
Files changed (4) hide show
  1. .gitignore +3 -0
  2. README.md +4 -4
  3. app.py +287 -23
  4. requirements.txt +3 -1
.gitignore ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ __pycache__/
2
+ *.pyc
3
+ .env
README.md CHANGED
@@ -1,8 +1,8 @@
1
  ---
2
- title: Code
3
- emoji: 😻
4
- colorFrom: yellow
5
- colorTo: indigo
6
  sdk: gradio
7
  sdk_version: 6.27.0
8
  python_version: '3.12'
 
1
  ---
2
+ title: Gemma 4 Chat Assistant
3
+ emoji: ✨
4
+ colorFrom: blue
5
+ colorTo: purple
6
  sdk: gradio
7
  sdk_version: 6.27.0
8
  python_version: '3.12'
app.py CHANGED
@@ -1,34 +1,298 @@
1
- import gradio as gr
2
  import spaces
3
- from transformers import pipeline
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4
 
5
- # Load pipeline mô hình (ví dụ: mô hình phân loại cảm xúc văn bản)
6
- classifier = pipeline(
7
- "sentiment-analysis",
8
- model="distilbert-base-uncased-finetuned-sst-2-english",
 
 
 
9
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
10
 
 
 
 
 
 
 
11
 
12
- @spaces.GPU
13
- def analyze_text(text):
14
- if not text.strip():
15
- return "Vui lòng nhập văn bản cần phân tích."
16
- result = classifier(text)[0]
17
- label = result["label"]
18
- score = round(result["score"], 4)
19
- return f"Kết quả: {label} (Độ tin cậy: {score})"
20
 
 
 
 
21
 
22
- # Tạo giao diện người dùng
23
- demo = gr.Interface(
24
- fn=analyze_text,
25
- inputs=gr.Textbox(
26
- lines=3, placeholder="Nhập câu tiếng Anh cần kiểm tra..."
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
27
  ),
28
- outputs="text",
29
- title="Sentiment Analysis Demo",
30
- description="Ứng dụng phân tích cảm xúc sử dụng mô hình Hugging Face Transformers.",
 
31
  )
32
 
33
  if __name__ == "__main__":
34
- demo.launch()
 
1
+ import os
2
  import spaces
3
+ import torch
4
+ from threading import Thread
5
+ from transformers import (
6
+ AutoModelForCausalLM,
7
+ AutoTokenizer,
8
+ TextIteratorStreamer,
9
+ BitsAndBytesConfig,
10
+ )
11
+ import gradio as gr
12
+
13
+ # ==============================================================================
14
+ # CẤU HÌNH MÔ HÌNH VÀ TOKEN
15
+ # ==============================================================================
16
+ # Bạn có thể đổi sang 'google/gemma-4-31B-it', 'google/gemma-2-9b-it' hoặc mô hình khác
17
+ # qua biến môi trường MODEL_ID trên Hugging Face Space Settings.
18
+ MODEL_ID = os.getenv("MODEL_ID", "google/gemma-4-31B-it")
19
+ HF_TOKEN = os.getenv("HF_TOKEN", None)
20
+
21
+ print(f"[*] Khởi tạo cấu hình cho mô hình: {MODEL_ID}")
22
+
23
+ # Cấu hình lượng tử hóa 4-bit (BitsAndBytes NF4) để tối ưu VRAM trên ZeroGPU
24
+ bnb_config = BitsAndBytesConfig(
25
+ load_in_4bit=True,
26
+ bnb_4bit_quant_type="nf4",
27
+ bnb_4bit_compute_dtype=torch.bfloat16,
28
+ bnb_4bit_use_double_quant=True,
29
+ )
30
+
31
+ # Tải Tokenizer và Model
32
+ print("[*] Đang tải Tokenizer...")
33
+ tokenizer = AutoTokenizer.from_pretrained(
34
+ MODEL_ID,
35
+ token=HF_TOKEN,
36
+ trust_remote_code=True,
37
+ )
38
 
39
+ print("[*] Đang tải Model với 4-bit Quantization...")
40
+ model = AutoModelForCausalLM.from_pretrained(
41
+ MODEL_ID,
42
+ quantization_config=bnb_config,
43
+ device_map="auto",
44
+ token=HF_TOKEN,
45
+ trust_remote_code=True,
46
  )
47
+ print("[+] Mô hình đã sẵn sàng hoạt động!")
48
+
49
+ # ==============================================================================
50
+ # HÀM SUY LUẬN & STREAMING KẾT QUẢ VỚI ZERO-GPU
51
+ # ==============================================================================
52
+ @spaces.GPU(duration=120)
53
+ def chat_response(
54
+ message,
55
+ history,
56
+ system_prompt,
57
+ temperature,
58
+ max_new_tokens,
59
+ top_p,
60
+ ):
61
+ if not message or not message.strip():
62
+ yield ""
63
+ return
64
+
65
+ # Xây dựng danh sách tin nhắn theo chuẩn hội thoại
66
+ conversation = []
67
+
68
+ # 1. Thêm system prompt nếu có
69
+ if system_prompt and system_prompt.strip():
70
+ conversation.append({"role": "system", "content": system_prompt.strip()})
71
+
72
+ # 2. Đọc lại lịch sử chat (tương thích cả dict messages và tuple)
73
+ for item in history:
74
+ if isinstance(item, dict):
75
+ role = item.get("role", "user")
76
+ # Gemma chat template quy ước role là 'user' và 'model'
77
+ if role == "assistant":
78
+ role = "model"
79
+ conversation.append({"role": role, "content": item.get("content", "")})
80
+ elif isinstance(item, (list, tuple)) and len(item) == 2:
81
+ user_text, bot_text = item
82
+ if user_text:
83
+ conversation.append({"role": "user", "content": str(user_text)})
84
+ if bot_text:
85
+ conversation.append({"role": "model", "content": str(bot_text)})
86
+
87
+ # 3. Thêm tin nhắn hiện tại của người dùng
88
+ conversation.append({"role": "user", "content": message.strip()})
89
+
90
+ # 4. Tạo prompt qua chat template của Gemma
91
+ try:
92
+ prompt = tokenizer.apply_chat_template(
93
+ conversation,
94
+ tokenize=False,
95
+ add_generation_prompt=True,
96
+ )
97
+ except Exception:
98
+ # Fallback nếu tokenizer không hỗ trợ role 'system' riêng rẽ
99
+ fallback_conv = []
100
+ for idx, item in enumerate(conversation):
101
+ if item["role"] == "system":
102
+ continue
103
+ content = item["content"]
104
+ if idx == (1 if system_prompt else 0):
105
+ content = f"[Hướng dẫn hệ thống: {system_prompt}]\n\n{content}"
106
+ fallback_conv.append({"role": item["role"], "content": content})
107
+ prompt = tokenizer.apply_chat_template(
108
+ fallback_conv,
109
+ tokenize=False,
110
+ add_generation_prompt=True,
111
+ )
112
+
113
+ # 5. Tokenize và đưa vào thiết bị của model
114
+ model_inputs = tokenizer([prompt], return_tensors="pt").to(model.device)
115
 
116
+ # 6. Thiết lập Streamer để bắn từng token ra ngoài màn hình
117
+ streamer = TextIteratorStreamer(
118
+ tokenizer,
119
+ skip_prompt=True,
120
+ skip_special_tokens=True,
121
+ )
122
 
123
+ generation_kwargs = dict(
124
+ model_inputs,
125
+ streamer=streamer,
126
+ max_new_tokens=int(max_new_tokens),
127
+ temperature=float(max(temperature, 0.01)),
128
+ top_p=float(top_p),
129
+ do_sample=True if temperature > 0.0 else False,
130
+ )
131
 
132
+ # Chạy model.generate trên luồng nền (background thread)
133
+ thread = Thread(target=model.generate, kwargs=generation_kwargs)
134
+ thread.start()
135
 
136
+ # 7. Nhận từng token và yield về giao diện theo thời gian thực
137
+ partial_text = ""
138
+ for token in streamer:
139
+ partial_text += token
140
+ yield partial_text
141
+
142
+
143
+ # ==============================================================================
144
+ # TÙY BIẾN GIAO DIỆN THEME GOOGLE GEMINI
145
+ # ==============================================================================
146
+ CUSTOM_CSS = """
147
+ @import url('https://fonts.googleapis.com/css2?family=Google+Sans:wght@400;500;700&family=Inter:wght@400;500;600&display=swap');
148
+
149
+ :root {
150
+ --bg-primary: #131314;
151
+ --bg-secondary: #1e1f20;
152
+ --bg-bubble-user: #282a2c;
153
+ --text-primary: #e3e3e3;
154
+ --text-muted: #9aa0a6;
155
+ --border-color: #3c4043;
156
+ }
157
+
158
+ body, .gradio-container {
159
+ background-color: var(--bg-primary) !important;
160
+ color: var(--text-primary) !important;
161
+ font-family: 'Google Sans', 'Inter', -apple-system, BlinkMacSystemFont, sans-serif !important;
162
+ }
163
+
164
+ /* Tiêu đề phong cách Gemini Sparkle */
165
+ .gemini-title {
166
+ text-align: center;
167
+ margin: 1.5rem 0 0.5rem 0;
168
+ font-size: 2.2rem;
169
+ font-weight: 700;
170
+ background: linear-gradient(135deg, #4285f4 0%, #9b72cb 50%, #d96570 100%);
171
+ -webkit-background-clip: text;
172
+ -webkit-text-fill-color: transparent;
173
+ letter-spacing: -0.5px;
174
+ }
175
+
176
+ .gemini-subtitle {
177
+ text-align: center;
178
+ color: var(--text-muted);
179
+ font-size: 0.95rem;
180
+ margin-bottom: 1.5rem;
181
+ }
182
+
183
+ /* Chat container & Message styling */
184
+ .bubble-wrap {
185
+ background-color: var(--bg-primary) !important;
186
+ }
187
+
188
+ .message.user, [data-testid="user"] {
189
+ background-color: var(--bg-bubble-user) !important;
190
+ border: 1px solid var(--border-color) !important;
191
+ border-radius: 20px 20px 4px 20px !important;
192
+ color: #f1f3f4 !important;
193
+ padding: 12px 18px !important;
194
+ font-size: 15px !important;
195
+ }
196
+
197
+ .message.bot, [data-testid="bot"] {
198
+ background-color: transparent !important;
199
+ border: none !important;
200
+ border-radius: 20px 20px 20px 4px !important;
201
+ color: var(--text-primary) !important;
202
+ padding: 12px 18px !important;
203
+ font-size: 15px !important;
204
+ line-height: 1.65 !important;
205
+ }
206
+
207
+ /* Khối nhập liệu bo tròn mềm mại */
208
+ input[type="text"], textarea {
209
+ background-color: var(--bg-secondary) !important;
210
+ border: 1px solid var(--border-color) !important;
211
+ border-radius: 16px !important;
212
+ color: #ffffff !important;
213
+ }
214
+
215
+ input[type="text"]:focus, textarea:focus {
216
+ border-color: #4285f4 !important;
217
+ box-shadow: 0 0 0 2px rgba(66, 133, 244, 0.25) !important;
218
+ }
219
+
220
+ /* Accordion cài đặt */
221
+ .gr-accordion {
222
+ background-color: var(--bg-secondary) !important;
223
+ border: 1px solid var(--border-color) !important;
224
+ border-radius: 12px !important;
225
+ }
226
+
227
+ footer {
228
+ display: none !important;
229
+ }
230
+ """
231
+
232
+ # ==============================================================================
233
+ # KHỞI TẠO GIAO DIỆN GRADIO CHAT INTERFACE
234
+ # ==============================================================================
235
+ demo = gr.ChatInterface(
236
+ fn=chat_response,
237
+ type="messages",
238
+ title=None,
239
+ textbox=gr.Textbox(
240
+ placeholder="Hỏi Gemma bất kỳ điều gì (nhấn Enter để gửi)...",
241
+ lines=1,
242
+ max_lines=6,
243
+ container=False,
244
+ scale=7,
245
+ ),
246
+ additional_inputs=[
247
+ gr.Textbox(
248
+ label="Chỉ dẫn hệ thống (System Prompt)",
249
+ value="Bạn là Gemma, một trợ lý trí tuệ nhân tạo thông minh, am hiểu và thân thiện. Hãy trả lời súc tích, chính xác, định dạng câu trả lời bằng Markdown đẹp mắt.",
250
+ lines=2,
251
+ ),
252
+ gr.Slider(
253
+ minimum=0.0,
254
+ maximum=1.0,
255
+ value=0.7,
256
+ step=0.05,
257
+ label="Độ sáng tạo (Temperature)",
258
+ info="Giá trị thấp trả lời chuẩn xác hơn; giá trị cao trả lời phong phú và sáng tạo hơn.",
259
+ ),
260
+ gr.Slider(
261
+ minimum=128,
262
+ maximum=4096,
263
+ value=1536,
264
+ step=128,
265
+ label="Độ dài tối đa (Max New Tokens)",
266
+ info="Số lượng token tối đa mô hình tạo ra trong mỗi lượt trả lời.",
267
+ ),
268
+ gr.Slider(
269
+ minimum=0.1,
270
+ maximum=1.0,
271
+ value=0.9,
272
+ step=0.05,
273
+ label="Top-P (Nucleus Sampling)",
274
+ ),
275
+ ],
276
+ additional_inputs_accordion=gr.Accordion(
277
+ label="⚙️ Cài đặt tham số mô hình & System Prompt",
278
+ open=False,
279
+ ),
280
+ examples=[
281
+ "Giải thích ngắn gọn cách hoạt động của cơ chế Multi-Head Attention trong mô hình Transformer.",
282
+ "Viết một kịch bản ngắn giới thiệu vẻ đẹp thiên nhiên và văn hóa cồng chiêng của vùng đất Gia Lai.",
283
+ "Tạo một hàm Python đọc tệp CSV, làm sạch dữ liệu thiếu và vẽ biểu đồ xu hướng bằng Matplotlib.",
284
+ "So sánh ưu điểm và nhược điểm giữa cấu trúc Dense Model và Mixture-of-Experts (MoE).",
285
+ ],
286
+ css=CUSTOM_CSS,
287
+ theme=gr.themes.Base(
288
+ primary_hue="blue",
289
+ neutral_hue="neutral",
290
  ),
291
+ head="""
292
+ <div class="gemini-title">✨ Google Gemma Assistant</div>
293
+ <div class="gemini-subtitle">Trải nghiệm đối thoại AI phong cách Gemini • Tải trực tiếp mô hình Gemma 4 31B (4-bit ZeroGPU)</div>
294
+ """,
295
  )
296
 
297
  if __name__ == "__main__":
298
+ demo.queue().launch()
requirements.txt CHANGED
@@ -1,2 +1,4 @@
1
- transformers
2
  torch
 
 
 
1
+ transformers>=4.45.0
2
  torch
3
+ accelerate>=0.34.0
4
+ bitsandbytes>=0.43.0