Spaces:
Sleeping
Sleeping
File size: 12,038 Bytes
f74347a 2b16f16 f74347a 4fc4e56 2b16f16 f74347a bdf6b40 2b16f16 4fc4e56 bdf6b40 f74347a 2b16f16 cade15c 2b16f16 f74347a 2b16f16 f74347a 2b16f16 c29cfbe f74347a 2b16f16 c29cfbe f74347a c29cfbe f74347a 2b16f16 f74347a 2b16f16 f74347a 2b16f16 f74347a 2b16f16 f74347a 2b16f16 f74347a 2b16f16 f74347a 4fc4e56 ca4a5bd f74347a 4fc4e56 f74347a 4fc4e56 f74347a 4fc4e56 f74347a 4fc4e56 f74347a 4fc4e56 97b4d70 f74347a 97b4d70 f74347a 4fc4e56 f74347a 4fc4e56 f74347a 4fc4e56 c29cfbe f74347a ca4a5bd f74347a ca4a5bd f74347a 4fc4e56 f74347a 4fc4e56 ca4a5bd 4fc4e56 f74347a ca4a5bd f74347a 4fc4e56 ca4a5bd ec46645 4fc4e56 2b16f16 c29cfbe 4fc4e56 c29cfbe 4fc4e56 f74347a ca4a5bd f74347a ca4a5bd ec46645 2b16f16 ec46645 f74347a 4fc4e56 f74347a ca4a5bd f74347a ca4a5bd f74347a ca4a5bd f74347a 2b16f16 ca4a5bd c29cfbe ec46645 2b16f16 f74347a ca4a5bd f74347a ca4a5bd f74347a ca4a5bd f74347a 2b16f16 f74347a 2b16f16 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 |
"""
Chainlit UI for AI Agent with Login/Registration - FIXED VERSION
"""
import chainlit as cl
import httpx # Thay thế requests bằng httpx cho async support
from typing import Optional, Dict
import json
import os
import asyncio
# API Configuration
API_BASE_URL = os.getenv("API_BASE_URL")
# Timeout settings (giây)
API_TIMEOUT = 300.0 # 5 phút cho chat API (có thể điều chỉnh)
AUTH_TIMEOUT = 150.0 # 30 giây cho login/register
# ============================================
# API HELPER FUNCTIONS - CONVERTED TO ASYNC
# ============================================
async def check_user_exists(user_id: str) -> bool:
"""Check if user exists in the system"""
try:
async with httpx.AsyncClient(timeout=AUTH_TIMEOUT) as client:
response = await client.get(f"{API_BASE_URL}/user-exists/{user_id}")
if response.status_code == 200:
return response.json().get("exists", False)
return False
except httpx.TimeoutException:
print(f"Timeout checking user: {user_id}")
return False
except Exception as e:
print(f"Error checking user: {e}")
return False
async def register_user(user_id: str) -> Dict:
"""Register a new user"""
try:
async with httpx.AsyncClient(timeout=AUTH_TIMEOUT) as client:
response = await client.post(
f"{API_BASE_URL}/register",
json={"user_id": user_id}
)
if response.status_code == 201:
return {"success": True, "message": response.json().get("message", "Đăng ký thành công!")}
else:
error_msg = response.json().get("detail", "Đăng ký thất bại")
return {"success": False, "message": error_msg}
except httpx.TimeoutException:
return {"success": False, "message": "Timeout: Server không phản hồi"}
except httpx.ConnectError:
return {"success": False, "message": "Không thể kết nối đến server"}
except Exception as e:
return {"success": False, "message": f"Lỗi: {str(e)}"}
async def send_chat_message(query: str, user_id: str) -> Dict:
"""Send chat message to API with streaming support"""
try:
# Sử dụng timeout dài hơn cho chat vì có thể xử lý lâu
async with httpx.AsyncClient(timeout=API_TIMEOUT) as client:
response = await client.post(
f"{API_BASE_URL}/chat",
json={"query": query, "user_id": user_id}
)
if response.status_code == 200:
data = response.json()
return {
"success": True,
"answer": data.get("answer", ""),
"references": data.get("references", "Không có tài liệu tham khảo")
}
else:
error_msg = response.json().get("detail", "Lỗi xử lý câu hỏi")
return {"success": False, "message": error_msg}
except httpx.TimeoutException:
return {
"success": False,
"message": f"⏱️ Server đang xử lý quá lâu (>{API_TIMEOUT}s). Vui lòng thử lại hoặc đặt câu hỏi đơn giản hơn."
}
except httpx.ConnectError:
return {"success": False, "message": "❌ Không thể kết nối đến server. Vui lòng kiểm tra API_BASE_URL."}
except Exception as e:
return {"success": False, "message": f"❌ Lỗi: {str(e)}"}
# ============================================
# CHAINLIT EVENT HANDLERS
# ============================================
@cl.on_chat_start
async def start():
"""Initialize chat session"""
# Check if user is already logged in
user_id = cl.user_session.get("user_id")
if not user_id:
await show_login_screen()
else:
await show_chat_interface(user_id)
async def show_login_screen():
"""Display login/registration screen"""
welcome_msg = """
### 🎼 Chào mừng đến với Kumiko v2! 📯
**Đăng nhập** hoặc **đăng ký** để bắt đầu trò chuyện với mình nhé!
---
#### 📝 Hướng dẫn:
- **Đăng nhập**: Gõ `/login <user_id>` (ví dụ: `/login john`)
- **Đăng ký**: Gõ `/register <user_id>` (ví dụ: `/register john`)
- User ID phải có ít nhất 3 ký tự
---
**Ví dụ:**
- `/login alice`
- `/register bob123`
"""
await cl.Message(content=welcome_msg, author="Kumiko").send()
cl.user_session.set("awaiting_auth", True)
async def show_chat_interface(user_id: str):
"""Display chat interface after successful login"""
welcome_msg = f"""#### ✅ Yayy, bạn đã đăng nhập thành công!
**User ID của bạn là:** `{user_id}`
---
Bạn có thể bắt đầu đặt câu hỏi. Mình sẽ trả lời và cung cấp tài liệu tham khảo nếu có!
💡 **Mẹo:** Sau mỗi câu trả lời, bạn có thể nhấn nút "𝄢 Tài liệu tham khảo" để check lại thông tin quan trọng!
---
Để đăng xuất, gõ `/logout`
"""
await cl.Message(content=welcome_msg, author="Kumiko").send()
cl.user_session.set("user_id", user_id)
cl.user_session.set("awaiting_auth", False)
@cl.on_message
async def main(message: cl.Message):
"""Handle incoming messages"""
user_id = cl.user_session.get("user_id")
awaiting_auth = cl.user_session.get("awaiting_auth", True)
content = message.content.strip()
# Handle authentication commands
if content.startswith("/"):
await handle_command(content)
return
# Check if user is logged in
if not user_id or awaiting_auth:
await cl.Message(
content="⚠️ Vui lòng đăng nhập trước khi chat.\n\nGõ `/login <user_id>` hoặc `/register <user_id>`",
author="Kumiko"
).send()
return
# Process chat message
await process_chat_message(content, user_id)
async def handle_command(command: str):
"""Handle special commands"""
parts = command.split(maxsplit=1)
cmd = parts[0].lower()
# Login command
if cmd == "/login":
if len(parts) < 2:
await cl.Message(content="❌ Sử dụng: `/login <user_id>`", author="Kumiko").send()
return
user_id = parts[1].strip()
if len(user_id) < 3:
await cl.Message(content="❌ User ID phải có ít nhất 3 ký tự!", author="Kumiko").send()
return
# Show loading message
loading_msg = cl.Message(content="🔍 Đang kiểm tra tài khoản...", author="Kumiko")
await loading_msg.send()
# Check if user exists (now async)
exists = await check_user_exists(user_id)
await loading_msg.remove()
if exists:
await show_chat_interface(user_id)
else:
await cl.Message(
content=f"❌ User ID `{user_id}` không tồn tại!\n\nVui lòng đăng ký bằng: `/register {user_id}`",
author="Kumiko"
).send()
# Register command
elif cmd == "/register":
if len(parts) < 2:
await cl.Message(content="❌ Sử dụng: `/register <user_id>`", author="Kumiko").send()
return
user_id = parts[1].strip()
if len(user_id) < 3:
await cl.Message(content="❌ User ID phải có ít nhất 3 ký tự!", author="Kumiko").send()
return
# Show loading message
loading_msg = cl.Message(content="📝 Đang đăng ký tài khoản...", author="Kumiko")
await loading_msg.send()
# Register user (now async)
result = await register_user(user_id)
await loading_msg.remove()
if result["success"]:
await cl.Message(content=f"✅ {result['message']}", author="Kumiko").send()
await show_chat_interface(user_id)
else:
await cl.Message(content=f"❌ {result['message']}", author="Kumiko").send()
# Logout command
elif cmd == "/logout":
cl.user_session.set("user_id", None)
cl.user_session.set("awaiting_auth", True)
await cl.Message(content="👋 Đã đăng xuất thành công!", author="Kumiko").send()
await show_login_screen()
# Help command
elif cmd == "/help":
help_msg = """
#### 📖 Hướng dẫn sử dụng
#### Lệnh đăng nhập/đăng ký:
- `/login <user_id>` - Đăng nhập với user_id có sẵn
- `/register <user_id>` - Đăng ký user_id mới
- `/logout` - Đăng xuất
#### Lệnh khác:
- `/help` - Hiển thị hướng dẫn này
#### Chat:
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.
"""
await cl.Message(content=help_msg, author="Kumiko").send()
else:
await cl.Message(content=f"❌ Lệnh không hợp lệ: `{cmd}`\n\nGõ `/help` để xem danh sách lệnh.", author="Kumiko").send()
async def process_chat_message(query: str, user_id: str):
"""Process chat message and display response with progress updates"""
# Show thinking message
thinking_msg = cl.Message(content="🤔 Đang suy nghĩ...", author="Kumiko")
await thinking_msg.send()
try:
# Tạo task để track progress
start_time = asyncio.get_event_loop().time()
# Send request to API (now async)
result = await send_chat_message(query, user_id)
# Remove thinking message
await thinking_msg.remove()
if not result["success"]:
await cl.Message(content=f"{result['message']}", author="Kumiko").send()
return
answer = result["answer"]
references = result["references"]
# Store references in session for the button
cl.user_session.set("last_references", references)
# Create message with action button
actions = [
cl.Action(
name="show_references",
value="show",
label="𝄢 Tài liệu tham khảo",
description="Xem các nguồn tham khảo",
payload={"references": references}
)
]
# Send answer with reference button
await cl.Message(
content=answer,
actions=actions,
author="Kumiko"
).send()
except Exception as e:
await thinking_msg.remove()
await cl.Message(
content=f"❌ Đã xảy ra lỗi không mong muốn: {str(e)}",
author="Kumiko"
).send()
@cl.action_callback("show_references")
async def on_show_references(action: cl.Action):
"""Handle reference button click"""
# Get references from action payload
references = action.payload.get("references", "Không có tài liệu tham khảo")
# Send references as a new message
await cl.Message(
content=references,
author="Kumiko"
).send()
# Optional: Remove the action button after clicking
await action.remove()
@cl.on_chat_end
async def end():
"""Handle chat end"""
print("Chat session ended")
# ============================================
# CONFIGURATION
# ============================================
@cl.set_starters
async def set_starters():
"""Set starter prompts for quick access"""
return [
cl.Starter(
label="Đăng nhập",
message="/login your_user_id",
icon="🔑",
),
cl.Starter(
label="Đăng ký",
message="/register your_user_id",
icon="✍🏻",
),
cl.Starter(
label="Trợ giúp",
message="/help",
),
] |