lamgamepro1 commited on
Commit
cca95b3
·
verified ·
1 Parent(s): ff9bbe0

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +184 -175
app.py CHANGED
@@ -3,23 +3,19 @@ from huggingface_hub import InferenceClient
3
  import os
4
  import uuid
5
  import json
6
- import hashlib
7
- from datetime import datetime
8
 
9
  # Import an toàn
 
 
10
  try: from gtts import gTTS
11
  except: gTTS = None
12
 
13
- # CẤU HÌNH
14
  HF_TOKEN = os.getenv("HUGGINGFACE_HUB_TOKEN")
15
  client = InferenceClient(model="Qwen/Qwen2.5-7B-Instruct", token=HF_TOKEN)
16
  DB_FILE = "nodeai_pro_memory.json"
17
 
18
- # HỆ THỐNG USER
19
- def get_user_id(username):
20
- if not username: return "guest"
21
- return hashlib.md5(username.encode()).hexdigest()[:8]
22
-
23
  def load_db():
24
  if os.path.exists(DB_FILE):
25
  try:
@@ -31,197 +27,210 @@ def save_db(data):
31
  with open(DB_FILE, "w", encoding="utf-8") as f:
32
  json.dump(data, f, ensure_ascii=False, indent=4)
33
 
34
- # CSS ĐƠN GIẢN
35
  custom_css = """
36
- .gradio-container { background: #0a0a0f !important; color: white !important; }
37
- .hamburger-btn {
38
- position: fixed !important; top: 20px !important; left: 20px !important;
39
- z-index: 9999 !important; width: 60px !important; height: 60px !important;
40
- background: linear-gradient(135deg, #FF6B6B, #FF8E53) !important;
41
- border: none !important; border-radius: 15px !important; color: white !important;
42
- font-size: 24px !important; box-shadow: 0 8px 25px rgba(255,107,107,0.4) !important;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
43
  }
44
- .sidebar {
45
- position: fixed !important; top: 0 !important; left: -350px !important;
46
- width: 350px !important; height: 100vh !important; z-index: 9998 !important;
47
- background: rgba(20,20,30,0.98) !important; backdrop-filter: blur(20px) !important;
48
- border-right: 2px solid #00f2fe !important; transition: left 0.4s ease !important;
49
- padding: 90px 25px 25px !important;
 
 
 
 
50
  }
51
- .sidebar.open { left: 0 !important; }
52
- .sidebar-btn {
53
- width: 100% !important; height: 50px !important; margin-bottom: 15px !important;
54
- border: none !important; border-radius: 12px !important; font-size: 16px !important;
55
- font-weight: 600 !important; color: white !important; cursor: pointer !important;
 
 
 
 
 
56
  }
57
- .main-chat { padding: 80px 20px 150px 20px !important; }
58
- .chatbot { background: rgba(20,20,30,0.8) !important; border-radius: 20px !important; }
59
- .input-container {
60
- position: fixed !important; bottom: 20px !important; left: 20px !important; right: 20px !important;
61
- z-index: 1000 !important; background: rgba(20,20,30,0.95) !important; border-radius: 25px !important;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
62
  }
63
- footer { display: none !important; }
64
  """
65
 
66
- # ✅ FIX CHAT FUNCTION - TRẢ VỀ ĐÚNG 5 VALUES
67
- def chat_fn(message, history, session_id, store, username):
68
- user_text = message.strip() if message else ""
69
- if not user_text:
70
- return "", history, session_id, store, ""
 
71
 
72
- user_id = get_user_id(username)
73
- if user_id not in store:
74
- store[user_id] = {"workspaces": {}}
75
 
76
- if session_id not in store[user_id]["workspaces"]:
77
- store[user_id]["workspaces"][session_id] = {
78
- "title": user_text[:30], "history": [], "created": datetime.now().isoformat()
79
- }
80
-
81
- messages = [{"role": "system", "content": f"Bạn là NodeAI Genesis. User: {username}"}]
82
- for msg in history:
83
- messages.append({"role": msg["role"], "content": msg["content"]})
84
- messages.append({"role": "user", "content": user_text})
85
 
86
  try:
87
- # NON-STREAMING để tránh lỗi generator
88
- response = client.chat_completion(
89
- messages, max_tokens=1000, temperature=0.7
90
- ).choices[0].message.content
91
-
92
- new_history = history + [{"role": "user", "content": user_text}]
93
- final_history = new_history + [{"role": "assistant", "content": response}]
94
 
95
- # Lưu vào store
96
- store[user_id]["workspaces"][session_id]["history"] = final_history
97
- store[user_id]["workspaces"][session_id]["title"] = user_text[:30]
98
  save_db(store)
99
 
100
- return "", final_history, session_id, store, response
101
-
102
  except Exception as e:
103
- error_msg = f"Lỗi: {str(e)}"
104
- new_history = history + [{"role": "assistant", "content": error_msg}]
105
- return "", new_history, session_id, store, ""
106
 
107
- def tts_fn(text):
108
- if not text or not gTTS: return None
109
  try:
110
- tts = gTTS(text=text[:200], lang='vi', slow=False)
111
- tts.save("response.mp3")
112
- return "response.mp3"
113
  except: return None
114
 
115
- def toggle_sidebar():
116
- return gr.update(visible=True, elem_classes="sidebar open")
117
-
118
- # ✅ FIX LOGIN - Không cần input
119
- def handle_login():
120
- # Mặc định guest - vì LoginButton chỉ hoạt động trong HF Spaces
121
- return """
122
- <div style='background: rgba(255,107,107,0.2); padding: 20px; border-radius: 16px; border: 1px solid rgba(255,107,107,0.5); text-align: center;'>
123
- <h3 style='color: #ff8e53;'>👤 Khách (Guest)</h3>
124
- <p>Đăng nhập HF Spaces để lưu lịch sử riêng</p>
125
- </div>
126
- """, "guest"
127
-
128
- def get_history(store, username):
129
- user_id = get_user_id(username)
130
- if user_id in store and "workspaces" in store[user_id]:
131
- html = ""
132
- for sid, data in sorted(store[user_id]["workspaces"].items(),
133
- key=lambda x: x[1].get("created", ""), reverse=True)[:10]:
134
- msg_count = len(data.get("history", [])) // 2
135
- html += f"""
136
- <div style='background: rgba(255,255,255,0.05); border: 1px solid rgba(255,255,255,0.1);
137
- color: #a0aec0; border-radius: 10px; padding: 15px; margin-bottom: 10px;
138
- cursor: pointer; text-align: left;'>
139
- 💬 {data['title']}
140
- <div style='font-size: 12px; opacity: 0.7;'>
141
- {msg_count} tin nhắn
142
- </div>
143
- </div>
144
- """
145
- return html or "<div>Chưa có lịch sử chat</div>"
146
- return "<div>Chưa có lịch sử chat</div>"
147
-
148
- # GIAO DIỆN
149
- with gr.Blocks(title="NodeAI Genesis") as demo:
150
- store = gr.State(load_db())
151
  session_id = gr.State(str(uuid.uuid4()))
152
- current_user = gr.State("guest")
153
- tts_buffer = gr.State("")
154
-
155
- # Header
156
- gr.HTML("<h1 style='text-align:center; color:#00f2fe; font-size:48px; margin:40px 0;'>🤖 NODEAI GENESIS</h1>")
157
-
158
- # Hamburger
159
- hamburger = gr.Button("☰ MENU", elem_classes="hamburger-btn")
160
-
161
- # Sidebar
162
- with gr.Column(elem_classes="sidebar", visible=False) as sidebar:
163
- gr.HTML("<div style='font-size:32px; text-align:center; margin-bottom:30px; background: linear-gradient(90deg, #00f2fe, #ff6b6b); -webkit-background-clip: text; -webkit-text-fill-color: transparent;'>NODEAI</div>")
164
-
165
- login_btn = gr.Button("🔐 ĐĂNG NHẬP", elem_classes="sidebar-btn", variant="primary")
166
- user_info = gr.HTML()
167
-
168
- new_chat = gr.Button("➕ CHAT MỚI", elem_classes="sidebar-btn", variant="secondary")
169
- history_list = gr.HTML(value="Chưa có lịch sử")
170
-
171
- # Main Chat
172
- with gr.Column(elem_classes="main-chat"):
173
- chatbot = gr.Chatbot(height=500, show_label=False, elem_classes="chatbot")
174
- audio_player = gr.Audio(visible=False)
175
-
176
- with gr.Row(elem_classes="input-container"):
177
- msg_input = gr.Textbox(placeholder="✨ Gõ tin nhắn...", scale=8, show_label=False)
178
- send_btn = gr.Button("GỬI", scale=1, variant="primary")
179
-
180
- # Events
181
- hamburger.click(toggle_sidebar, outputs=sidebar)
182
-
183
- # Login (demo)
184
- login_btn.click(handle_login, outputs=[user_info, current_user])
185
 
186
- # Update history sau login
187
- def update_after_login(store, user):
188
- return get_history(store, user)
189
-
190
- current_user.change(
191
- update_after_login,
192
- inputs=[store, current_user],
193
- outputs=history_list
194
- )
195
-
196
- # New chat
197
- new_chat.click(
198
- lambda: ([], str(uuid.uuid4())),
199
- outputs=[chatbot, session_id]
200
- )
201
-
202
- # Chat submit
 
 
 
 
 
 
 
 
 
 
203
  msg_input.submit(
204
- chat_fn,
205
- inputs=[msg_input, chatbot, session_id, store, current_user],
206
- outputs=[msg_input, chatbot, session_id, store, tts_buffer]
207
  ).then(
208
- tts_fn, tts_buffer, audio_player
209
  )
210
 
211
- send_btn.click(
212
- chat_fn,
213
- inputs=[msg_input, chatbot, session_id, store, current_user],
214
- outputs=[msg_input, chatbot, session_id, store, tts_buffer]
215
- ).then(
216
- tts_fn, tts_buffer, audio_player
217
- )
218
 
219
  if __name__ == "__main__":
220
- demo.launch(
221
- server_name="0.0.0.0",
222
- server_port=7860,
223
- theme=gr.themes.Base(),
224
- css=custom_css,
225
- share=False, # Tắt share cho HF Spaces
226
- show_error=True
227
- )
 
3
  import os
4
  import uuid
5
  import json
 
 
6
 
7
  # Import an toàn
8
+ try: from duckduckgo_search import DDGS
9
+ except: DDGS = None
10
  try: from gtts import gTTS
11
  except: gTTS = None
12
 
13
+ # 1. CẤU HÌNH HỆ THỐNG & MODEL
14
  HF_TOKEN = os.getenv("HUGGINGFACE_HUB_TOKEN")
15
  client = InferenceClient(model="Qwen/Qwen2.5-7B-Instruct", token=HF_TOKEN)
16
  DB_FILE = "nodeai_pro_memory.json"
17
 
18
+ # 2. HỆ THỐNG TRÍ NHỚ (MEMORY CACHE)
 
 
 
 
19
  def load_db():
20
  if os.path.exists(DB_FILE):
21
  try:
 
27
  with open(DB_FILE, "w", encoding="utf-8") as f:
28
  json.dump(data, f, ensure_ascii=False, indent=4)
29
 
30
+ # 3. GIAO DIỆN SIÊU ĐẸP (GLASSMORPHISM & ORANGE GLOW)
31
  custom_css = """
32
+ @import url('https://fonts.googleapis.com/css2?family=Orbitron:wght@800&family=Plus+Jakarta+Sans:wght@400;500;600&display=swap');
33
+
34
+ /* Nền vũ trụ tối */
35
+ .gradio-container {
36
+ background: radial-gradient(circle at 50% 0%, #151522 0%, #050508 100%) !important;
37
+ font-family: 'Plus Jakarta Sans', sans-serif !important;
38
+ color: #e2e8f0 !important;
39
+ overflow-x: hidden !important;
40
+ }
41
+
42
+ /* Sidebar Kính mờ (Glassmorphism) */
43
+ .sidebar-node {
44
+ position: fixed !important;
45
+ left: 0 !important; top: 0 !important;
46
+ width: 280px !important; height: 100vh !important;
47
+ background: rgba(15, 15, 20, 0.4) !important;
48
+ backdrop-filter: blur(20px) !important;
49
+ -webkit-backdrop-filter: blur(20px) !important;
50
+ border-right: 1px solid rgba(255, 255, 255, 0.05) !important;
51
+ z-index: 1000 !important;
52
+ padding: 25px 20px !important;
53
+ box-shadow: 5px 0 30px rgba(0,0,0,0.5) !important;
54
  }
55
+
56
+ /* Logo Cyberpunk */
57
+ .logo-text {
58
+ font-family: 'Orbitron', sans-serif !important;
59
+ background: linear-gradient(90deg, #00f2fe, #4facfe, #00f2fe);
60
+ background-size: 200% auto;
61
+ -webkit-background-clip: text;
62
+ -webkit-text-fill-color: transparent;
63
+ font-size: 26px; text-align: center; margin-bottom: 30px;
64
+ animation: shine 3s linear infinite;
65
  }
66
+ @keyframes shine { to { background-position: 200% center; } }
67
+
68
+ /* Nút New Workspace Màu Cam Rực Rỡ */
69
+ .orange-btn {
70
+ background: linear-gradient(135deg, #FF6B6B 0%, #FF8E53 100%) !important;
71
+ border: none !important; color: white !important; font-weight: 600 !important;
72
+ border-radius: 12px !important; padding: 10px !important;
73
+ transition: all 0.3s ease !important;
74
+ box-shadow: 0 4px 15px rgba(255, 107, 107, 0.3) !important;
75
+ margin-bottom: 20px !important;
76
  }
77
+ .orange-btn:hover {
78
+ transform: translateY(-2px) !important;
79
+ box-shadow: 0 6px 20px rgba(255, 107, 107, 0.5) !important;
80
+ }
81
+
82
+ /* Nút Lịch sử Chat */
83
+ .hist-btn {
84
+ background: transparent !important; border: 1px solid transparent !important;
85
+ color: #a0aec0 !important; text-align: left !important;
86
+ border-radius: 8px !important; transition: 0.2s !important;
87
+ }
88
+ .hist-btn:hover {
89
+ background: rgba(255, 255, 255, 0.05) !important;
90
+ border: 1px solid rgba(255, 255, 255, 0.1) !important;
91
+ color: #fff !important;
92
+ }
93
+
94
+ /* Khu vực Chat Chính */
95
+ .chat-main {
96
+ margin-left: 280px !important;
97
+ width: calc(100% - 280px) !important;
98
+ min-height: 100vh !important;
99
+ padding: 20px 0 !important;
100
+ display: flex !important;
101
+ flex-direction: column !important;
102
+ align-items: center !important;
103
+ }
104
+
105
+ /* Chatbot Box */
106
+ .gr-chatbot {
107
+ background: transparent !important;
108
+ border: none !important;
109
+ width: 100% !important;
110
+ max-width: 900px !important;
111
+ height: 78vh !important;
112
+ margin-bottom: 100px !important;
113
+ }
114
+
115
+ /* Thanh Nhập Liệu Cố Định Chống Tràn */
116
+ .input-fixed-bottom {
117
+ position: fixed !important;
118
+ bottom: 30px !important;
119
+ left: 50% !important;
120
+ transform: translateX(calc(-50% + 140px)) !important; /* Cân bằng với sidebar */
121
+ width: 100% !important;
122
+ max-width: 850px !important;
123
+ background: rgba(20, 20, 28, 0.8) !important;
124
+ backdrop-filter: blur(15px) !important;
125
+ border: 1px solid rgba(0, 242, 254, 0.2) !important;
126
+ border-radius: 20px !important;
127
+ padding: 8px 15px !important;
128
+ z-index: 2000 !important;
129
+ box-shadow: 0 10px 40px rgba(0,0,0,0.5) !important;
130
+ transition: 0.3s !important;
131
+ }
132
+ .input-fixed-bottom:focus-within {
133
+ border-color: rgba(0, 242, 254, 0.6) !important;
134
+ box-shadow: 0 10px 40px rgba(0, 242, 254, 0.15) !important;
135
+ }
136
+
137
+ /* Ẩn các thành phần thừa */
138
+ footer, .gr-button-secondary { display: none !important; }
139
+
140
+ /* Responsive Mobile */
141
+ @media screen and (max-width: 768px) {
142
+ .sidebar-node { display: none !important; }
143
+ .chat-main { margin-left: 0 !important; width: 100% !important; }
144
+ .input-fixed-bottom {
145
+ transform: translateX(-50%) !important;
146
+ width: 90% !important;
147
+ bottom: 15px !important;
148
+ }
149
  }
 
150
  """
151
 
152
+ # 4. CORE LOGIC CHAT
153
+ def chat_fn(message, history, session_id, store):
154
+ user_text = message.get("text", "")
155
+ if not user_text and not message.get("files", []):
156
+ yield {"text": ""}, history, session_id, store, ""
157
+ return
158
 
159
+ if session_id not in store:
160
+ store[session_id] = {"title": user_text[:25] if user_text else "Nova Workspace", "history": []}
 
161
 
162
+ api_messages = [{"role": "system", "content": "Bạn là NodeAI Genesis. Master: Trần Đình Phúc."}]
163
+ for msg in history: api_messages.append({"role": msg["role"], "content": msg["content"]})
164
+ api_messages.append({"role": "user", "content": user_text})
 
 
 
 
 
 
165
 
166
  try:
167
+ ans = ""
168
+ new_h = history + [{"role": "user", "content": user_text}]
169
+ for token in client.chat_completion(messages=api_messages, max_tokens=2000, stream=True):
170
+ if token.choices[0].delta.content:
171
+ ans += token.choices[0].delta.content
172
+ # Yield text rỗng cho tts_buffer trong lúc stream để tránh lỗi
173
+ yield {"text": ""}, new_h + [{"role": "assistant", "content": ans}], session_id, store, ""
174
 
175
+ final_h = new_h + [{"role": "assistant", "content": ans}]
176
+ store[session_id]["history"] = final_h
 
177
  save_db(store)
178
 
179
+ # Chỉ khi hoàn thành mới gửi ans vào tts_buffer
180
+ yield {"text": ""}, final_h, session_id, store, ans
181
  except Exception as e:
182
+ yield {"text": ""}, history + [{"role": "assistant", "content": str(e)}], session_id, store, ""
 
 
183
 
184
+ def tts_engine(text):
185
+ if not text or len(text) < 2 or not gTTS: return None
186
  try:
187
+ tts = gTTS(text=text[:300], lang='vi')
188
+ tts.save("voice.mp3")
189
+ return "voice.mp3"
190
  except: return None
191
 
192
+ # 5. DỰNG GIAO DIỆN
193
+ with gr.Blocks(title="NodeAI Genesis", theme=gr.themes.Base()) as demo:
194
+ chat_store = gr.State(load_db())
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
195
  session_id = gr.State(str(uuid.uuid4()))
196
+ tts_buffer = gr.Textbox(visible=False) # Chìa khóa chống lỗi Audio
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
197
 
198
+ with gr.Row():
199
+ # SIDEBAR
200
+ with gr.Column(elem_classes="sidebar-node"):
201
+ gr.HTML("<div class='logo-text'>NODEAI GENESIS</div>")
202
+ gr.LoginButton(value="Xác thực Hugging Face")
203
+ new_btn = gr.Button("+ TẠO KHÔNG GIAN MỚI", elem_classes="orange-btn")
204
+
205
+ with gr.Column():
206
+ @gr.render(inputs=chat_store)
207
+ def render_history(store):
208
+ for sid in reversed(list(store.keys())):
209
+ btn = gr.Button(f"💬 {store[sid]['title']}", elem_classes="hist-btn")
210
+ btn.click(lambda s=sid: (store[s]["history"], s), None, [chatbot, session_id])
211
+
212
+ # MAIN CHAT
213
+ with gr.Column(elem_classes="chat-main"):
214
+ chatbot = gr.Chatbot(show_label=False, avatar_images=(None, "https://cdn-icons-png.flaticon.com/512/4712/4712139.png"))
215
+ audio_out = gr.Audio(autoplay=True, visible=False)
216
+
217
+ with gr.Row(elem_classes="input-fixed-bottom"):
218
+ msg_input = gr.MultimodalTextbox(
219
+ show_label=False,
220
+ placeholder="Nhập lệnh cho NodeAI...",
221
+ container=False
222
+ )
223
+
224
+ # XỬ LÝ SỰ KIỆN KHÔNG LỖI
225
  msg_input.submit(
226
+ chat_fn,
227
+ [msg_input, chatbot, session_id, chat_store],
228
+ [msg_input, chatbot, session_id, chat_store, tts_buffer]
229
  ).then(
230
+ tts_engine, tts_buffer, audio_out
231
  )
232
 
233
+ new_btn.click(lambda: ([], str(uuid.uuid4())), None, [chatbot, session_id])
 
 
 
 
 
 
234
 
235
  if __name__ == "__main__":
236
+ demo.launch(css=custom_css)