anhkhoiphan commited on
Commit
ca4a5bd
·
1 Parent(s): 5e9162a

Revert lại hoàn toàn phần image captioning

Browse files
Files changed (4) hide show
  1. README.md +3 -3
  2. app.py +45 -82
  3. image_captioning.py +59 -59
  4. requirements.txt +9 -9
README.md CHANGED
@@ -1,8 +1,8 @@
1
  ---
2
  title: Kumiko V2.0
3
- emoji: 🏃
4
- colorFrom: green
5
- colorTo: gray
6
  sdk: docker
7
  pinned: false
8
  license: mit
 
1
  ---
2
  title: Kumiko V2.0
3
+ emoji: 🏆
4
+ colorFrom: blue
5
+ colorTo: yellow
6
  sdk: docker
7
  pinned: false
8
  license: mit
app.py CHANGED
@@ -1,5 +1,5 @@
1
  """
2
- Chainlit UI for AI Agent with Login/Registration and Image Support
3
  """
4
 
5
  import chainlit as cl
@@ -7,8 +7,6 @@ import requests
7
  from typing import Optional, Dict
8
  import json
9
  import os
10
- import asyncio
11
- from image_captioning import image_captioning
12
 
13
  # API Configuration
14
  API_BASE_URL = os.getenv("API_BASE_URL")
@@ -39,7 +37,7 @@ def register_user(user_id: str) -> Dict:
39
  if response.status_code == 201:
40
  return {"success": True, "message": response.json().get("message", "Đăng ký thành công!")}
41
  else:
42
- error_msg = response.json().get("detail", "Đăng ký thất bại. Hãy thử user_id khác!")
43
  return {"success": False, "message": error_msg}
44
  except Exception as e:
45
  return {"success": False, "message": f"Lỗi kết nối: {str(e)}"}
@@ -50,8 +48,7 @@ def send_chat_message(query: str, user_id: str) -> Dict:
50
  try:
51
  response = requests.post(
52
  f"{API_BASE_URL}/chat",
53
- json={"query": query, "user_id": user_id},
54
- timeout=180
55
  )
56
  if response.status_code == 200:
57
  data = response.json()
@@ -63,27 +60,10 @@ def send_chat_message(query: str, user_id: str) -> Dict:
63
  else:
64
  error_msg = response.json().get("detail", "Lỗi xử lý câu hỏi")
65
  return {"success": False, "message": error_msg}
66
-
67
- except requests.exceptions.Timeout:
68
- return {"success": False, "message": "⏱️ Server đang khởi động (cold start), vui lòng thử lại sau 30 giây!"}
69
  except Exception as e:
70
  return {"success": False, "message": f"Lỗi kết nối API: {str(e)}"}
71
 
72
 
73
- # ============================================
74
- # IMAGE PROCESSING
75
- # ============================================
76
-
77
- async def process_image(image_path: str) -> str:
78
- """Process image and return caption"""
79
- try:
80
- loop = asyncio.get_event_loop()
81
- caption = await loop.run_in_executor(None, image_captioning, image_path)
82
- return caption
83
- except Exception as e:
84
- return f"(Lỗi khi xử lý ảnh: {str(e)})"
85
-
86
-
87
  # ============================================
88
  # CHAINLIT EVENT HANDLERS
89
  # ============================================
@@ -91,6 +71,7 @@ async def process_image(image_path: str) -> str:
91
  @cl.on_chat_start
92
  async def start():
93
  """Initialize chat session"""
 
94
  user_id = cl.user_session.get("user_id")
95
 
96
  if not user_id:
@@ -152,10 +133,12 @@ async def main(message: cl.Message):
152
 
153
  content = message.content.strip()
154
 
 
155
  if content.startswith("/"):
156
  await handle_command(content)
157
  return
158
 
 
159
  if not user_id or awaiting_auth:
160
  await cl.Message(
161
  content="⚠️ Vui lòng đăng nhập trước khi chat.\n\nGõ `/login <user_id>` hoặc `/register <user_id>`",
@@ -163,47 +146,8 @@ async def main(message: cl.Message):
163
  ).send()
164
  return
165
 
166
- await process_chat_message_with_image(message, user_id)
167
-
168
-
169
- async def process_chat_message_with_image(message: cl.Message, user_id: str):
170
- """Process chat message with optional image"""
171
- query = message.content.strip() if message.content else ""
172
-
173
- images = [file for file in message.elements if file.mime and file.mime.startswith("image/")]
174
-
175
- if images:
176
- processing_msg = cl.Message(content="🖼️ Đang xử lý ảnh...", author="Kumiko")
177
- await processing_msg.send()
178
-
179
- image_captions = []
180
-
181
- for img in images:
182
- try:
183
- caption = await process_image(img.path)
184
- image_captions.append(caption)
185
- except Exception as e:
186
- image_captions.append(f"(Lỗi khi xử lý ảnh: {str(e)})")
187
-
188
- await processing_msg.remove()
189
-
190
- combined_captions = "\n\n".join(image_captions)
191
-
192
- if query:
193
- final_query = f"{query}\n\n{combined_captions}"
194
- else:
195
- final_query = combined_captions
196
- else:
197
- final_query = query
198
-
199
- if not final_query:
200
- await cl.Message(
201
- content="⚠️ Vui lòng nhập câu hỏi hoặc gửi ảnh!",
202
- author="Kumiko"
203
- ).send()
204
- return
205
-
206
- await process_chat_message(final_query, user_id)
207
 
208
 
209
  async def handle_command(command: str):
@@ -211,6 +155,7 @@ async def handle_command(command: str):
211
  parts = command.split(maxsplit=1)
212
  cmd = parts[0].lower()
213
 
 
214
  if cmd == "/login":
215
  if len(parts) < 2:
216
  await cl.Message(content="❌ Sử dụng: `/login <user_id>`", author="Kumiko").send()
@@ -222,22 +167,22 @@ async def handle_command(command: str):
222
  await cl.Message(content="❌ User ID phải có ít nhất 3 ký tự!", author="Kumiko").send()
223
  return
224
 
 
225
  loading_msg = cl.Message(content="🔍 Đang kiểm tra tài khoản...", author="Kumiko")
226
  await loading_msg.send()
227
 
228
- loop = asyncio.get_event_loop()
229
- exists = await loop.run_in_executor(None, check_user_exists, user_id)
230
-
231
- await loading_msg.remove()
232
-
233
- if exists:
234
  await show_chat_interface(user_id)
235
  else:
 
236
  await cl.Message(
237
  content=f"❌ User ID `{user_id}` không tồn tại!\n\nVui lòng đăng ký bằng: `/register {user_id}`",
238
  author="Kumiko"
239
  ).send()
240
 
 
241
  elif cmd == "/register":
242
  if len(parts) < 2:
243
  await cl.Message(content="❌ Sử dụng: `/register <user_id>`", author="Kumiko").send()
@@ -249,12 +194,12 @@ async def handle_command(command: str):
249
  await cl.Message(content="❌ User ID phải có ít nhất 3 ký tự!", author="Kumiko").send()
250
  return
251
 
 
252
  loading_msg = cl.Message(content="📝 Đang đăng ký tài khoản...", author="Kumiko")
253
  await loading_msg.send()
254
 
255
- loop = asyncio.get_event_loop()
256
- result = await loop.run_in_executor(None, register_user, user_id)
257
-
258
  await loading_msg.remove()
259
 
260
  if result["success"]:
@@ -263,12 +208,14 @@ async def handle_command(command: str):
263
  else:
264
  await cl.Message(content=f"❌ {result['message']}", author="Kumiko").send()
265
 
 
266
  elif cmd == "/logout":
267
  cl.user_session.set("user_id", None)
268
  cl.user_session.set("awaiting_auth", True)
269
  await cl.Message(content="👋 Đã đăng xuất thành công!", author="Kumiko").send()
270
  await show_login_screen()
271
 
 
272
  elif cmd == "/help":
273
  help_msg = """
274
  #### 📖 Hướng dẫn sử dụng
@@ -282,12 +229,7 @@ async def handle_command(command: str):
282
  - `/help` - Hiển thị hướng dẫn này
283
 
284
  #### Chat:
285
- Sau khi đăng nhập, bạn có thể:
286
- - Chat bình thường với text
287
- - **Gửi ảnh** (kèm hoặc không kèm text) để mình phân tích
288
- - Gửi nhiều ảnh cùng lúc
289
-
290
- Hệ thống sẽ trả lời và cung cấp tài liệu tham khảo.
291
  """
292
  await cl.Message(content=help_msg, author="Kumiko").send()
293
 
@@ -297,12 +239,14 @@ Hệ thống sẽ trả lời và cung cấp tài liệu tham khảo.
297
 
298
  async def process_chat_message(query: str, user_id: str):
299
  """Process chat message and display response"""
 
300
  thinking_msg = cl.Message(content="🤔 Đang suy nghĩ...", author="Kumiko")
301
  await thinking_msg.send()
302
 
303
- loop = asyncio.get_event_loop()
304
- result = await loop.run_in_executor(None, send_chat_message, query, user_id)
305
 
 
306
  await thinking_msg.remove()
307
 
308
  if not result["success"]:
@@ -312,8 +256,10 @@ async def process_chat_message(query: str, user_id: str):
312
  answer = result["answer"]
313
  references = result["references"]
314
 
 
315
  cl.user_session.set("last_references", references)
316
 
 
317
  actions = [
318
  cl.Action(
319
  name="show_references",
@@ -324,6 +270,7 @@ async def process_chat_message(query: str, user_id: str):
324
  )
325
  ]
326
 
 
327
  await cl.Message(
328
  content=answer,
329
  actions=actions,
@@ -334,13 +281,16 @@ async def process_chat_message(query: str, user_id: str):
334
  @cl.action_callback("show_references")
335
  async def on_show_references(action: cl.Action):
336
  """Handle reference button click"""
 
337
  references = action.payload.get("references", "Không có tài liệu tham khảo")
338
 
 
339
  await cl.Message(
340
  content=references,
341
  author="Kumiko"
342
  ).send()
343
 
 
344
  await action.remove()
345
 
346
 
@@ -371,4 +321,17 @@ async def set_starters():
371
  label="Trợ giúp",
372
  message="/help",
373
  ),
374
- ]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  """
2
+ Chainlit UI for AI Agent with Login/Registration
3
  """
4
 
5
  import chainlit as cl
 
7
  from typing import Optional, Dict
8
  import json
9
  import os
 
 
10
 
11
  # API Configuration
12
  API_BASE_URL = os.getenv("API_BASE_URL")
 
37
  if response.status_code == 201:
38
  return {"success": True, "message": response.json().get("message", "Đăng ký thành công!")}
39
  else:
40
+ error_msg = response.json().get("detail", "Đăng ký thất bại")
41
  return {"success": False, "message": error_msg}
42
  except Exception as e:
43
  return {"success": False, "message": f"Lỗi kết nối: {str(e)}"}
 
48
  try:
49
  response = requests.post(
50
  f"{API_BASE_URL}/chat",
51
+ json={"query": query, "user_id": user_id}
 
52
  )
53
  if response.status_code == 200:
54
  data = response.json()
 
60
  else:
61
  error_msg = response.json().get("detail", "Lỗi xử lý câu hỏi")
62
  return {"success": False, "message": error_msg}
 
 
 
63
  except Exception as e:
64
  return {"success": False, "message": f"Lỗi kết nối API: {str(e)}"}
65
 
66
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
67
  # ============================================
68
  # CHAINLIT EVENT HANDLERS
69
  # ============================================
 
71
  @cl.on_chat_start
72
  async def start():
73
  """Initialize chat session"""
74
+ # Check if user is already logged in
75
  user_id = cl.user_session.get("user_id")
76
 
77
  if not user_id:
 
133
 
134
  content = message.content.strip()
135
 
136
+ # Handle authentication commands
137
  if content.startswith("/"):
138
  await handle_command(content)
139
  return
140
 
141
+ # Check if user is logged in
142
  if not user_id or awaiting_auth:
143
  await cl.Message(
144
  content="⚠️ Vui lòng đăng nhập trước khi chat.\n\nGõ `/login <user_id>` hoặc `/register <user_id>`",
 
146
  ).send()
147
  return
148
 
149
+ # Process chat message
150
+ await process_chat_message(content, user_id)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
151
 
152
 
153
  async def handle_command(command: str):
 
155
  parts = command.split(maxsplit=1)
156
  cmd = parts[0].lower()
157
 
158
+ # Login command
159
  if cmd == "/login":
160
  if len(parts) < 2:
161
  await cl.Message(content="❌ Sử dụng: `/login <user_id>`", author="Kumiko").send()
 
167
  await cl.Message(content="❌ User ID phải có ít nhất 3 ký tự!", author="Kumiko").send()
168
  return
169
 
170
+ # Show loading message
171
  loading_msg = cl.Message(content="🔍 Đang kiểm tra tài khoản...", author="Kumiko")
172
  await loading_msg.send()
173
 
174
+ # Check if user exists
175
+ if check_user_exists(user_id):
176
+ await loading_msg.remove()
 
 
 
177
  await show_chat_interface(user_id)
178
  else:
179
+ await loading_msg.remove()
180
  await cl.Message(
181
  content=f"❌ User ID `{user_id}` không tồn tại!\n\nVui lòng đăng ký bằng: `/register {user_id}`",
182
  author="Kumiko"
183
  ).send()
184
 
185
+ # Register command
186
  elif cmd == "/register":
187
  if len(parts) < 2:
188
  await cl.Message(content="❌ Sử dụng: `/register <user_id>`", author="Kumiko").send()
 
194
  await cl.Message(content="❌ User ID phải có ít nhất 3 ký tự!", author="Kumiko").send()
195
  return
196
 
197
+ # Show loading message
198
  loading_msg = cl.Message(content="📝 Đang đăng ký tài khoản...", author="Kumiko")
199
  await loading_msg.send()
200
 
201
+ # Register user
202
+ result = register_user(user_id)
 
203
  await loading_msg.remove()
204
 
205
  if result["success"]:
 
208
  else:
209
  await cl.Message(content=f"❌ {result['message']}", author="Kumiko").send()
210
 
211
+ # Logout command
212
  elif cmd == "/logout":
213
  cl.user_session.set("user_id", None)
214
  cl.user_session.set("awaiting_auth", True)
215
  await cl.Message(content="👋 Đã đăng xuất thành công!", author="Kumiko").send()
216
  await show_login_screen()
217
 
218
+ # Help command
219
  elif cmd == "/help":
220
  help_msg = """
221
  #### 📖 Hướng dẫn sử dụng
 
229
  - `/help` - Hiển thị hướng dẫn này
230
 
231
  #### Chat:
232
+ Sau khi đăng nhập, bạn có thể chat bình thường. Hệ thống sẽ trả lời và cung cấp tài liệu tham khảo.
 
 
 
 
 
233
  """
234
  await cl.Message(content=help_msg, author="Kumiko").send()
235
 
 
239
 
240
  async def process_chat_message(query: str, user_id: str):
241
  """Process chat message and display response"""
242
+ # Show thinking message
243
  thinking_msg = cl.Message(content="🤔 Đang suy nghĩ...", author="Kumiko")
244
  await thinking_msg.send()
245
 
246
+ # Send request to API
247
+ result = send_chat_message(query, user_id)
248
 
249
+ # Remove thinking message
250
  await thinking_msg.remove()
251
 
252
  if not result["success"]:
 
256
  answer = result["answer"]
257
  references = result["references"]
258
 
259
+ # Store references in session for the button
260
  cl.user_session.set("last_references", references)
261
 
262
+ # Create message with action button
263
  actions = [
264
  cl.Action(
265
  name="show_references",
 
270
  )
271
  ]
272
 
273
+ # Send answer with reference button
274
  await cl.Message(
275
  content=answer,
276
  actions=actions,
 
281
  @cl.action_callback("show_references")
282
  async def on_show_references(action: cl.Action):
283
  """Handle reference button click"""
284
+ # Get references from action payload
285
  references = action.payload.get("references", "Không có tài liệu tham khảo")
286
 
287
+ # Send references as a new message
288
  await cl.Message(
289
  content=references,
290
  author="Kumiko"
291
  ).send()
292
 
293
+ # Optional: Remove the action button after clicking
294
  await action.remove()
295
 
296
 
 
321
  label="Trợ giúp",
322
  message="/help",
323
  ),
324
+ ]
325
+
326
+
327
+ # Custom CSS (optional - save as .chainlit/config.toml)
328
+ # """
329
+ # [UI]
330
+ # name = "AI Agent Chat"
331
+ # default_collapse_content = true
332
+ # default_expand_messages = false
333
+ # hide_cot = false
334
+
335
+ # [UI.theme]
336
+ # primary_color = "#2563eb"
337
+ # """
image_captioning.py CHANGED
@@ -1,74 +1,74 @@
1
- def image_captioning(image_path, prompt=None):
2
- """
3
- Tạo caption tiếng Anh cho ảnh sử dụng SmolVLM2 500M
4
 
5
- Args:
6
- image_path: Đường dẫn đến file ảnh
7
- prompt: Custom prompt cho model
8
 
9
- Returns:
10
- String caption với format "(Người dùng gửi ảnh với nội dung: ...)"
11
- """
12
- from transformers import AutoProcessor, AutoModelForImageTextToText
13
- import torch
14
 
15
- if prompt is None:
16
- prompt = """Describe this image in detail, including:
17
- - People: count, appearance, clothing, actions
18
- - Famous individuals or celebrities (if any)
19
- - Objects and notable items
20
- - Location or famous landmarks (if recognizable)
21
- - Overall scene and atmosphere
22
- - Main focus/subject of the image"""
23
 
24
- model_name = "HuggingFaceTB/SmolVLM2-500M-Video-Instruct"
25
 
26
- # ✅ Phải có trust_remote_code=True
27
- processor = AutoProcessor.from_pretrained(model_name, trust_remote_code=True)
28
 
29
- device = "cuda" if torch.cuda.is_available() else "cpu"
30
 
31
- model = AutoModelForImageTextToText.from_pretrained(
32
- model_name,
33
- torch_dtype=torch.bfloat16 if device == "cuda" else torch.float32,
34
- _attn_implementation="sdpa", # ✅ Tương thích, không cần flash-attn
35
- trust_remote_code=True
36
- ).to(device)
37
 
38
- messages = [
39
- {
40
- "role": "user",
41
- "content": [
42
- {"type": "image", "path": image_path},
43
- {"type": "text", "text": prompt}
44
- ]
45
- }
46
- ]
47
 
48
- inputs = processor.apply_chat_template(
49
- messages,
50
- add_generation_prompt=True,
51
- tokenize=True,
52
- return_dict=True,
53
- return_tensors="pt",
54
- ).to(model.device, dtype=torch.bfloat16)
55
 
56
- generated_ids = model.generate(**inputs, do_sample=False, max_new_tokens=128)
57
 
58
- generated_texts = processor.batch_decode(
59
- generated_ids,
60
- skip_special_tokens=True,
61
- )
62
 
63
- raw_output = generated_texts[0].strip()
64
 
65
- # ✅ Cắt bỏ phần prompt "User: ... Assistant:" nếu có
66
- if "Assistant:" in raw_output:
67
- caption = raw_output.split("Assistant:")[-1].strip()
68
- else:
69
- caption = raw_output
70
 
71
- # 🪶 Chuẩn hóa xuống 1 dòng (loại bỏ \n thừa)
72
- caption = " ".join(caption.split())
73
 
74
- return f"(Người dùng gửi ảnh với nội dung: {caption})"
 
1
+ # def image_captioning(image_path, prompt=None):
2
+ # """
3
+ # Tạo caption tiếng Anh cho ảnh sử dụng SmolVLM2 500M
4
 
5
+ # Args:
6
+ # image_path: Đường dẫn đến file ảnh
7
+ # prompt: Custom prompt cho model
8
 
9
+ # Returns:
10
+ # String caption với format "(Người dùng gửi ảnh với nội dung: ...)"
11
+ # """
12
+ # from transformers import AutoProcessor, AutoModelForImageTextToText
13
+ # import torch
14
 
15
+ # if prompt is None:
16
+ # prompt = """Describe this image in detail, including:
17
+ # - People: count, appearance, clothing, actions
18
+ # - Famous individuals or celebrities (if any)
19
+ # - Objects and notable items
20
+ # - Location or famous landmarks (if recognizable)
21
+ # - Overall scene and atmosphere
22
+ # - Main focus/subject of the image"""
23
 
24
+ # model_name = "HuggingFaceTB/SmolVLM2-500M-Video-Instruct"
25
 
26
+ # # ✅ Phải có trust_remote_code=True
27
+ # processor = AutoProcessor.from_pretrained(model_name, trust_remote_code=True)
28
 
29
+ # device = "cuda" if torch.cuda.is_available() else "cpu"
30
 
31
+ # model = AutoModelForImageTextToText.from_pretrained(
32
+ # model_name,
33
+ # torch_dtype=torch.bfloat16 if device == "cuda" else torch.float32,
34
+ # _attn_implementation="sdpa", # ✅ Tương thích, không cần flash-attn
35
+ # trust_remote_code=True
36
+ # ).to(device)
37
 
38
+ # messages = [
39
+ # {
40
+ # "role": "user",
41
+ # "content": [
42
+ # {"type": "image", "path": image_path},
43
+ # {"type": "text", "text": prompt}
44
+ # ]
45
+ # }
46
+ # ]
47
 
48
+ # inputs = processor.apply_chat_template(
49
+ # messages,
50
+ # add_generation_prompt=True,
51
+ # tokenize=True,
52
+ # return_dict=True,
53
+ # return_tensors="pt",
54
+ # ).to(model.device, dtype=torch.bfloat16)
55
 
56
+ # generated_ids = model.generate(**inputs, do_sample=False, max_new_tokens=128)
57
 
58
+ # generated_texts = processor.batch_decode(
59
+ # generated_ids,
60
+ # skip_special_tokens=True,
61
+ # )
62
 
63
+ # raw_output = generated_texts[0].strip()
64
 
65
+ # # ✅ Cắt bỏ phần prompt "User: ... Assistant:" nếu có
66
+ # if "Assistant:" in raw_output:
67
+ # caption = raw_output.split("Assistant:")[-1].strip()
68
+ # else:
69
+ # caption = raw_output
70
 
71
+ # # 🪶 Chuẩn hóa xuống 1 dòng (loại bỏ \n thừa)
72
+ # caption = " ".join(caption.split())
73
 
74
+ # return f"(Người dùng gửi ảnh với nội dung: {caption})"
requirements.txt CHANGED
@@ -3,12 +3,12 @@ requests
3
  websockets
4
 
5
  # Dùng xử lý ảnh
6
- transformers>=4.45.0
7
- pillow
8
- torch
9
- accelerate
10
- sentencepiece
11
- protobuf
12
- timm
13
- decord
14
- num2words
 
3
  websockets
4
 
5
  # Dùng xử lý ảnh
6
+ # transformers>=4.45.0
7
+ # pillow
8
+ # torch
9
+ # accelerate
10
+ # sentencepiece
11
+ # protobuf
12
+ # timm
13
+ # decord
14
+ # num2words