ntphuc149 commited on
Commit
93f16f0
·
verified ·
1 Parent(s): ec674f6

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +247 -401
app.py CHANGED
@@ -29,53 +29,6 @@ url_api_retrieval_model = f"{retrieval_module}/search"
29
  url_api_reranker_model = f"{reranker_module}/rerank"
30
  url_api_generation_model = f"{abs_QA_module}/answer"
31
 
32
- # # ========= FLASK APP ===============
33
- # flask_app = Flask(__name__)
34
-
35
- # # Dùng để lưu tin nhắn nhận được trong session
36
- # if "messages" not in st.session_state:
37
- # st.session_state.messages = []
38
-
39
- # # Gửi tin nhắn tới người dùng
40
- # def send_message(recipient_id, message):
41
- # url = f"{FB_API_URL}/me/messages?access_token={FB_PAGE_ACCESS_TOKEN}"
42
- # payload = {
43
- # "recipient": {"id": recipient_id},
44
- # "message": {"text": message}
45
- # }
46
- # response = requests.post(url, json=payload)
47
- # return response.ok
48
-
49
- # # Xử lý GET và POST từ Facebook Webhook
50
- # @flask_app.route("/webhook", methods=["GET", "POST"])
51
- # def webhook():
52
- # if request.method == "GET":
53
- # if request.args.get("hub.verify_token") == FB_VERIFY_TOKEN:
54
- # return request.args.get("hub.challenge")
55
- # return "Verification token mismatch", 403
56
-
57
- # if request.method == "POST":
58
- # data = request.get_json()
59
- # if "entry" in data:
60
- # for entry in data["entry"]:
61
- # for event in entry["messaging"]:
62
- # sender_id = event["sender"]["id"]
63
- # message_text = event.get("message", {}).get("text", "")
64
- # if message_text:
65
- # # Lưu vào session_state
66
- # st.session_state.messages.append(
67
- # {"sender_id": sender_id, "text": message_text}
68
- # )
69
- # # Gửi trả lời mặc định
70
- # send_message(sender_id, "Cảm ơn bạn đã nhắn tin!")
71
- # return "OK", 200
72
-
73
- # # Chạy Flask trong luồng riêng
74
- # def run_flask():
75
- # flask_app.run(host="0.0.0.0", port=5000)
76
-
77
- # threading.Thread(target=run_flask, daemon=True).start()
78
-
79
  # ========== STREAMLIT UI ==========
80
 
81
  with open("./static/styles.css") as f:
@@ -87,376 +40,269 @@ tab1, tab2 = st.tabs(["🤖 ViBidLQA Chatbot", "🔐 Facebook OAuth"])
87
  # =============================
88
  # TAB 1: VIBIDLQA CHATBOT
89
  # =============================
90
- with tab1:
91
- if 'messages' not in st.session_state:
92
- st.session_state.messages = [{'role': 'assistant', 'content': "Xin chào. Tôi là trợ lý AI văn bản luật Đấu thầu Việt Nam được phát triển bởi Nguyễn Trường Phúc và các cộng sự. Rất vui khi được hỗ trợ bạn trong các vấn đề pháp lý tại Việt Nam!"}]
93
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
94
  st.markdown(f"""
95
- <div class=logo_area>
96
- <img src="./app/static/ai.jpg"/>
 
97
  </div>
98
  """, unsafe_allow_html=True)
99
- st.markdown("<h2 style='text-align: center;'>ViBidLQA</h2>", unsafe_allow_html=True)
100
-
101
- def classify_question(question):
102
- data = {
103
- "question": question
104
- }
105
-
106
- response = requests.post(url_api_question_classify_model, json=data)
107
-
108
- if response.status_code == 200:
109
- print(response)
110
- return response
111
- else:
112
- return f"Lỗi: {response.status_code} - {response.text}"
113
-
114
- def introduce_system(question):
115
- data = {
116
- "question": question
117
- }
118
-
119
- response = requests.post(url_api_introduce_system_model, json=data, stream=True)
120
-
121
- if response.status_code == 200:
122
- return response
123
- else:
124
- return f"Lỗi: {response.status_code} - {response.text}"
125
-
126
- def response_unrelated_question(question):
127
- data = {
128
- "question": question
129
- }
130
-
131
- response = requests.post(url_api_unrelated_question_response_model, json=data, stream=True)
132
-
133
- if response.status_code == 200:
134
- return response
135
- else:
136
- return f"Lỗi: {response.status_code} - {response.text}"
137
-
138
- def retrieve_context(question, top_k=10):
139
- data = {
140
- "query": question,
141
- "top_k": top_k
142
- }
143
-
144
- response = requests.post(url_api_retrieval_model, json=data)
145
-
146
- if response.status_code == 200:
147
- results = response.json()["results"]
148
- return results
149
- else:
150
- return f"Lỗi tại Retrieval Module: {response.status_code} - {response.text}"
151
-
152
- def rerank_context(url_rerank_module, question, relevant_docs, top_k=5):
153
- data = {
154
- "question": question,
155
- "relevant_docs": relevant_docs,
156
- "top_k": top_k
157
- }
158
-
159
- response = requests.post(url_rerank_module, json=data)
160
-
161
- if response.status_code == 200:
162
- results = response.json()["reranked_docs"]
163
- return results
164
- else:
165
- return f"Lỗi tại Rerank module: {response.status_code} - {response.text}"
166
-
167
- def get_abstractive_answer(question):
168
- retrieved_context = retrieve_context(question=question)
169
- retrieved_context = [item['text'] for item in retrieved_context]
170
-
171
- reranked_context = rerank_context(url_rerank_module=url_api_reranker_model,
172
- question=question,
173
- relevant_docs=retrieved_context,
174
- top_k=5)[0]
175
-
176
- data = {
177
- "context": reranked_context,
178
- "question": question
179
- }
180
-
181
- response = requests.post(url_api_generation_model, json=data, stream=True)
182
-
183
- if response.status_code == 200:
184
- return response
185
- else:
186
- return f"Lỗi: {response.status_code} - {response.text}"
187
 
188
- def generate_text_effect(answer):
189
- words = answer.split()
190
- for i in range(len(words)):
191
- time.sleep(0.03)
192
- yield " ".join(words[:i+1])
193
 
194
- for message in st.session_state.messages:
195
- if message['role'] == 'assistant':
196
- avatar_class = "assistant-avatar"
197
- message_class = "assistant-message"
198
- avatar = './app/static/ai.jpg'
 
 
 
 
 
 
 
 
 
 
 
199
  else:
200
- avatar_class = ""
201
- message_class = "user-message"
202
- avatar = ''
203
- st.markdown(f"""
204
- <div class="{message_class}">
205
- <img src="{avatar}" class="{avatar_class}" />
206
- <div class="stMarkdown">{message['content']}</div>
207
- </div>
208
- """, unsafe_allow_html=True)
209
-
210
- if prompt := st.chat_input(placeholder='Tôi có thể giúp được gì cho bạn?'):
211
- st.markdown(f"""
212
- <div class="user-message">
213
- <div class="stMarkdown">{prompt}</div>
214
- </div>
215
- """, unsafe_allow_html=True)
216
- st.session_state.messages.append({'role': 'user', 'content': prompt})
217
-
218
- message_placeholder = st.empty()
219
-
220
- full_response = ""
221
- classify_result = classify_question(question=prompt).json()
222
-
223
- print(f"The type of user query: {classify_result}")
224
-
225
- if classify_result == "BIDDING_RELATED":
226
- abs_answer = get_abstractive_answer(question=prompt)
227
-
228
- if isinstance(abs_answer, str):
229
- full_response = abs_answer
230
- message_placeholder.markdown(f"""
231
- <div class="assistant-message">
232
- <img src="./app/static/ai.jpg" class="assistant-avatar" />
233
- <div class="stMarkdown">{full_response}</div>
234
- </div>
235
- """, unsafe_allow_html=True)
236
- else:
237
- full_response = ""
238
- for line in abs_answer.iter_lines():
239
- if line:
240
- line = line.decode('utf-8')
241
- if line.startswith('data: '):
242
- data_str = line[6:]
243
- if data_str == '[DONE]':
244
- break
245
 
246
- try:
247
- data = json.loads(data_str)
248
- token = data.get('token', '')
249
- full_response += token
250
-
251
- message_placeholder.markdown(f"""
252
- <div class="assistant-message">
253
- <img src="./app/static/ai.jpg" class="assistant-avatar" />
254
- <div class="stMarkdown">{full_response}●</div>
255
- </div>
256
- """, unsafe_allow_html=True)
257
-
258
- except json.JSONDecodeError:
259
- pass
260
-
261
- elif classify_result == "ABOUT_CHATBOT":
262
- answer = introduce_system(question=prompt)
263
-
264
- if isinstance(answer, str):
265
- full_response = answer
266
- message_placeholder.markdown(f"""
267
- <div class="assistant-message">
268
- <img src="./app/static/ai.jpg" class="assistant-avatar" />
269
- <div class="stMarkdown">{full_response}</div>
270
- </div>
271
- """, unsafe_allow_html=True)
272
- else:
273
- full_response = ""
274
- for line in answer.iter_lines():
275
- if line:
276
- line = line.decode('utf-8')
277
- if line.startswith('data: '):
278
- data_str = line[6:]
279
- if data_str == '[DONE]':
280
- break
281
 
282
- try:
283
- data = json.loads(data_str)
284
- token = data.get('token', '')
285
- full_response += token
286
-
287
- message_placeholder.markdown(f"""
288
- <div class="assistant-message">
289
- <img src="./app/static/ai.jpg" class="assistant-avatar" />
290
- <div class="stMarkdown">{full_response}●</div>
291
- </div>
292
- """, unsafe_allow_html=True)
293
-
294
- except json.JSONDecodeError:
295
- pass
296
-
297
  else:
298
- answer = response_unrelated_question(question=prompt)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
299
 
300
- if isinstance(answer, str):
301
- full_response = answer
302
- message_placeholder.markdown(f"""
303
- <div class="assistant-message">
304
- <img src="./app/static/ai.jpg" class="assistant-avatar" />
305
- <div class="stMarkdown">{full_response}</div>
306
- </div>
307
- """, unsafe_allow_html=True)
308
- else:
309
- full_response = ""
310
- for line in answer.iter_lines():
311
- if line:
312
- line = line.decode('utf-8')
313
- if line.startswith('data: '):
314
- data_str = line[6:]
315
- if data_str == '[DONE]':
316
- break
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
317
 
318
- try:
319
- data = json.loads(data_str)
320
- token = data.get('token', '')
321
- full_response += token
322
-
323
- message_placeholder.markdown(f"""
324
- <div class="assistant-message">
325
- <img src="./app/static/ai.jpg" class="assistant-avatar" />
326
- <div class="stMarkdown">{full_response}●</div>
327
- </div>
328
- """, unsafe_allow_html=True)
329
-
330
- except json.JSONDecodeError:
331
- pass
332
 
333
- message_placeholder.markdown(f"""
334
- <div class="assistant-message">
335
- <img src="./app/static/ai.jpg" class="assistant-avatar" />
336
- <div class="stMarkdown">
337
- {full_response}
338
- </div>
339
- </div>
340
- """, unsafe_allow_html=True)
341
-
342
- st.session_state.messages.append({'role': 'assistant', 'content': full_response})
343
 
344
  # =============================
345
  # TAB 2: FACEBOOK OAUTH
346
  # =============================
347
- with tab2:
348
- st.title("Đăng nhập Facebook để lấy Page Access Token")
349
 
350
- # Tạo link login
351
- login_url = f"{FB_BACKEND_URL}/facebook/login"
352
-
353
- st.markdown(f"[👉 Bấm vào đây để đăng nhập Facebook]({login_url})")
354
-
355
- st.info("Sau khi đăng nhập xong, bạn có thể quay lại ứng dụng này. Thông tin page đã được in ra ở backend.")
356
-
357
- # st.title("Facebook OAuth Integration")
358
-
359
- # # Định nghĩa hàm đăng ký webhook Facebook
360
- # def register_facebook_webhook(page_id: str, page_access_token: str):
361
- # try:
362
- # url = f"https://graph.facebook.com/v19.0/{page_id}/subscribed_apps"
363
- # params = {
364
- # "subscribed_fields": "messages,messaging_postbacks",
365
- # "access_token": page_access_token
366
- # }
367
-
368
- # response = requests.post(url, params=params)
369
- # response.raise_for_status()
370
- # data = response.json()
371
-
372
- # if data.get("success"):
373
- # return True, "Đăng ký webhook thành công."
374
- # else:
375
- # return False, f"Facebook trả về lỗi: {data}"
376
- # except requests.exceptions.RequestException as e:
377
- # return False, f"Lỗi khi gọi Facebook API: {e}"
378
-
379
- # if "token" not in st.session_state:
380
- # params = {
381
- # "client_id": FB_APP_ID,
382
- # "redirect_uri": FB_REDIRECT_URI,
383
- # "scope": "pages_show_list,pages_manage_metadata,pages_messaging",
384
- # }
385
- # auth_url = f"{FB_CLIENT_URL}/dialog/oauth?{urlencode(params)}"
386
- # st.markdown("### Step 1: Đăng nhập Facebook")
387
- # st.markdown(f"[Bấm vào đây để đăng nhập Facebook]({auth_url})")
388
-
389
- # query_params = st.query_params
390
 
391
- # if "code" in query_params:
392
- # code = query_params["code"]
393
-
394
- # try:
395
- # token_response = requests.get(f"{FB_API_URL}/oauth/access_token", params={
396
- # "client_id": FB_APP_ID,
397
- # "redirect_uri": FB_REDIRECT_URI,
398
- # "client_secret": FB_APP_SECRET,
399
- # "code": code,
400
- # })
401
-
402
- # token = token_response.json()["access_token"]
403
- # st.session_state.token = token
404
- # st.success("🎉 Lấy access token thành công!")
405
 
406
- # st.markdown("""
407
- # <script>
408
- # const url = new URL(window.location.href);
409
- # url.searchParams.delete("code");
410
- # window.location.href = url.pathname;
411
- # </script>
412
- # """, unsafe_allow_html=True)
413
-
414
- # # Lấy page
415
- # pages_response = requests.get(f"{FB_API_URL}/me/accounts", params={"access_token": token})
416
- # pages = pages_response.json().get("data", [])
417
- # st.session_state.pages = pages
418
-
419
- # st.markdown("### Danh sách các Page bạn quản lý:")
420
- # for page in pages:
421
- # st.json(page)
422
-
423
- # except Exception as e:
424
- # st.error(f"Lỗi khi trao đổi token: {e}")
425
-
426
- # if "pages" in st.session_state and st.session_state.pages:
427
- # st.markdown("### Step 3: Đăng ký Webhook cho các page")
428
- # selected_pages = st.multiselect(
429
- # "Chọn các page để đăng ký webhook:",
430
- # options=[f"{p['name']} ({p['id']})" for p in st.session_state.pages]
431
- # )
432
-
433
- # if st.button("Đăng ký Webhook"):
434
- # # selected_pages = st.session_state.selected_pages # Giả sử bạn có danh sách page đã chọn
435
- # for page in st.session_state.pages:
436
- # label = f"{page['name']} ({page['id']})"
437
- # if label in selected_pages:
438
- # page_id = page['id']
439
- # page_access_token = page['access_token']
440
-
441
- # response = requests.post(f"{FB_BACKEND_URL}/register-webhook", json={
442
- # "page_id": page_id,
443
- # "page_access_token": page_access_token
444
- # })
445
- # res_json = response.json()
446
-
447
- # if res_json["success"]:
448
- # st.success(f"✅ Đã đăng ký Webhook cho page: {page['name']}")
449
- # else:
450
- # st.warning(f"⚠️ Lỗi với page {page['name']}: {res_json['message']}")
451
-
452
- # if st.button("Hiển thị Thông tin Trang"):
453
- # for page in st.session_state.pages:
454
- # page_id = page['id']
455
- # page_name = page['name']
456
- # page_access_token = page['access_token']
457
-
458
- # # Hiển thị thông tin của từng page
459
- # st.write(f"**Page Name**: {page_name}")
460
- # st.write(f"**Page ID**: {page_id}")
461
- # st.write(f"**Page Access Token**: {page_access_token}")
462
- # st.write("---") # Dấu phân cách giữa các trang
 
29
  url_api_reranker_model = f"{reranker_module}/rerank"
30
  url_api_generation_model = f"{abs_QA_module}/answer"
31
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
32
  # ========== STREAMLIT UI ==========
33
 
34
  with open("./static/styles.css") as f:
 
40
  # =============================
41
  # TAB 1: VIBIDLQA CHATBOT
42
  # =============================
43
+ # with tab1:
44
+ if 'messages' not in st.session_state:
45
+ st.session_state.messages = [{'role': 'assistant', 'content': "Xin chào. Tôi là trợ lý AI văn bản luật Đấu thầu Việt Nam được phát triển bởi Nguyễn Trường Phúc và các cộng sự. Rất vui khi được hỗ trợ bạn trong các vấn đề pháp lý tại Việt Nam!"}]
46
+
47
+ st.markdown(f"""
48
+ <div class=logo_area>
49
+ <img src="./app/static/ai.jpg"/>
50
+ </div>
51
+ """, unsafe_allow_html=True)
52
+ st.markdown("<h2 style='text-align: center;'>ViBidLQA</h2>", unsafe_allow_html=True)
53
+
54
+ def classify_question(question):
55
+ data = {
56
+ "question": question
57
+ }
58
+
59
+ response = requests.post(url_api_question_classify_model, json=data)
60
+
61
+ if response.status_code == 200:
62
+ print(response)
63
+ return response
64
+ else:
65
+ return f"Lỗi: {response.status_code} - {response.text}"
66
+
67
+ def introduce_system(question):
68
+ data = {
69
+ "question": question
70
+ }
71
+
72
+ response = requests.post(url_api_introduce_system_model, json=data, stream=True)
73
+
74
+ if response.status_code == 200:
75
+ return response
76
+ else:
77
+ return f"Lỗi: {response.status_code} - {response.text}"
78
+
79
+ def response_unrelated_question(question):
80
+ data = {
81
+ "question": question
82
+ }
83
+
84
+ response = requests.post(url_api_unrelated_question_response_model, json=data, stream=True)
85
+
86
+ if response.status_code == 200:
87
+ return response
88
+ else:
89
+ return f"Lỗi: {response.status_code} - {response.text}"
90
+
91
+ def retrieve_context(question, top_k=10):
92
+ data = {
93
+ "query": question,
94
+ "top_k": top_k
95
+ }
96
+
97
+ response = requests.post(url_api_retrieval_model, json=data)
98
+
99
+ if response.status_code == 200:
100
+ results = response.json()["results"]
101
+ return results
102
+ else:
103
+ return f"Lỗi tại Retrieval Module: {response.status_code} - {response.text}"
104
+
105
+ def rerank_context(url_rerank_module, question, relevant_docs, top_k=5):
106
+ data = {
107
+ "question": question,
108
+ "relevant_docs": relevant_docs,
109
+ "top_k": top_k
110
+ }
111
+
112
+ response = requests.post(url_rerank_module, json=data)
113
+
114
+ if response.status_code == 200:
115
+ results = response.json()["reranked_docs"]
116
+ return results
117
+ else:
118
+ return f"Lỗi tại Rerank module: {response.status_code} - {response.text}"
119
+
120
+ def get_abstractive_answer(question):
121
+ retrieved_context = retrieve_context(question=question)
122
+ retrieved_context = [item['text'] for item in retrieved_context]
123
+
124
+ reranked_context = rerank_context(url_rerank_module=url_api_reranker_model,
125
+ question=question,
126
+ relevant_docs=retrieved_context,
127
+ top_k=5)[0]
128
+
129
+ data = {
130
+ "context": reranked_context,
131
+ "question": question
132
+ }
133
+
134
+ response = requests.post(url_api_generation_model, json=data, stream=True)
135
+
136
+ if response.status_code == 200:
137
+ return response
138
+ else:
139
+ return f"Lỗi: {response.status_code} - {response.text}"
140
+
141
+ def generate_text_effect(answer):
142
+ words = answer.split()
143
+ for i in range(len(words)):
144
+ time.sleep(0.03)
145
+ yield " ".join(words[:i+1])
146
+
147
+ for message in st.session_state.messages:
148
+ if message['role'] == 'assistant':
149
+ avatar_class = "assistant-avatar"
150
+ message_class = "assistant-message"
151
+ avatar = './app/static/ai.jpg'
152
+ else:
153
+ avatar_class = ""
154
+ message_class = "user-message"
155
+ avatar = ''
156
  st.markdown(f"""
157
+ <div class="{message_class}">
158
+ <img src="{avatar}" class="{avatar_class}" />
159
+ <div class="stMarkdown">{message['content']}</div>
160
  </div>
161
  """, unsafe_allow_html=True)
162
+
163
+ if prompt := st.chat_input(placeholder='Tôi có thể giúp được gì cho bạn?'):
164
+ st.markdown(f"""
165
+ <div class="user-message">
166
+ <div class="stMarkdown">{prompt}</div>
167
+ </div>
168
+ """, unsafe_allow_html=True)
169
+ st.session_state.messages.append({'role': 'user', 'content': prompt})
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
170
 
171
+ message_placeholder = st.empty()
 
 
 
 
172
 
173
+ full_response = ""
174
+ classify_result = classify_question(question=prompt).json()
175
+
176
+ print(f"The type of user query: {classify_result}")
177
+
178
+ if classify_result == "BIDDING_RELATED":
179
+ abs_answer = get_abstractive_answer(question=prompt)
180
+
181
+ if isinstance(abs_answer, str):
182
+ full_response = abs_answer
183
+ message_placeholder.markdown(f"""
184
+ <div class="assistant-message">
185
+ <img src="./app/static/ai.jpg" class="assistant-avatar" />
186
+ <div class="stMarkdown">{full_response}</div>
187
+ </div>
188
+ """, unsafe_allow_html=True)
189
  else:
190
+ full_response = ""
191
+ for line in abs_answer.iter_lines():
192
+ if line:
193
+ line = line.decode('utf-8')
194
+ if line.startswith('data: '):
195
+ data_str = line[6:]
196
+ if data_str == '[DONE]':
197
+ break
198
+
199
+ try:
200
+ data = json.loads(data_str)
201
+ token = data.get('token', '')
202
+ full_response += token
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
203
 
204
+ message_placeholder.markdown(f"""
205
+ <div class="assistant-message">
206
+ <img src="./app/static/ai.jpg" class="assistant-avatar" />
207
+ <div class="stMarkdown">{full_response}●</div>
208
+ </div>
209
+ """, unsafe_allow_html=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
210
 
211
+ except json.JSONDecodeError:
212
+ pass
213
+
214
+ elif classify_result == "ABOUT_CHATBOT":
215
+ answer = introduce_system(question=prompt)
216
+
217
+ if isinstance(answer, str):
218
+ full_response = answer
219
+ message_placeholder.markdown(f"""
220
+ <div class="assistant-message">
221
+ <img src="./app/static/ai.jpg" class="assistant-avatar" />
222
+ <div class="stMarkdown">{full_response}</div>
223
+ </div>
224
+ """, unsafe_allow_html=True)
 
225
  else:
226
+ full_response = ""
227
+ for line in answer.iter_lines():
228
+ if line:
229
+ line = line.decode('utf-8')
230
+ if line.startswith('data: '):
231
+ data_str = line[6:]
232
+ if data_str == '[DONE]':
233
+ break
234
+
235
+ try:
236
+ data = json.loads(data_str)
237
+ token = data.get('token', '')
238
+ full_response += token
239
+
240
+ message_placeholder.markdown(f"""
241
+ <div class="assistant-message">
242
+ <img src="./app/static/ai.jpg" class="assistant-avatar" />
243
+ <div class="stMarkdown">{full_response}●</div>
244
+ </div>
245
+ """, unsafe_allow_html=True)
246
+
247
+ except json.JSONDecodeError:
248
+ pass
249
 
250
+ else:
251
+ answer = response_unrelated_question(question=prompt)
252
+
253
+ if isinstance(answer, str):
254
+ full_response = answer
255
+ message_placeholder.markdown(f"""
256
+ <div class="assistant-message">
257
+ <img src="./app/static/ai.jpg" class="assistant-avatar" />
258
+ <div class="stMarkdown">{full_response}</div>
259
+ </div>
260
+ """, unsafe_allow_html=True)
261
+ else:
262
+ full_response = ""
263
+ for line in answer.iter_lines():
264
+ if line:
265
+ line = line.decode('utf-8')
266
+ if line.startswith('data: '):
267
+ data_str = line[6:]
268
+ if data_str == '[DONE]':
269
+ break
270
+
271
+ try:
272
+ data = json.loads(data_str)
273
+ token = data.get('token', '')
274
+ full_response += token
275
+
276
+ message_placeholder.markdown(f"""
277
+ <div class="assistant-message">
278
+ <img src="./app/static/ai.jpg" class="assistant-avatar" />
279
+ <div class="stMarkdown">{full_response}●</div>
280
+ </div>
281
+ """, unsafe_allow_html=True)
282
 
283
+ except json.JSONDecodeError:
284
+ pass
285
+
286
+ message_placeholder.markdown(f"""
287
+ <div class="assistant-message">
288
+ <img src="./app/static/ai.jpg" class="assistant-avatar" />
289
+ <div class="stMarkdown">
290
+ {full_response}
291
+ </div>
292
+ </div>
293
+ """, unsafe_allow_html=True)
 
 
 
294
 
295
+ st.session_state.messages.append({'role': 'assistant', 'content': full_response})
 
 
 
 
 
 
 
 
 
296
 
297
  # =============================
298
  # TAB 2: FACEBOOK OAUTH
299
  # =============================
300
+ # with tab2:
301
+ # st.title("Đăng nhập Facebook để lấy Page Access Token")
302
 
303
+ # # Tạo link login
304
+ # login_url = f"{FB_BACKEND_URL}/facebook/login"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
305
 
306
+ # st.markdown(f"[👉 Bấm vào đây để đăng nhập Facebook]({login_url})")
 
 
 
 
 
 
 
 
 
 
 
 
 
307
 
308
+ # st.info("Sau khi đăng nhập xong, bạn có thể quay lại ứng dụng này. Thông tin page đã được in ra ở backend.")