File size: 24,153 Bytes
bf64382
 
8d8f4b0
00430c0
 
8d8f4b0
e51a5b0
8d8f4b0
 
b09c08f
8d8f4b0
 
 
00430c0
8d8f4b0
 
 
 
 
 
 
 
 
 
 
 
 
 
fb114fa
8d8f4b0
 
 
 
 
e51a5b0
8d8f4b0
c3f15f3
8d8f4b0
 
 
 
bf64382
b09c08f
 
 
00430c0
 
515db3b
 
00430c0
 
 
 
 
 
515db3b
ad80532
515db3b
 
00430c0
 
 
 
 
 
 
ad80532
 
 
 
 
 
00430c0
 
 
 
 
515db3b
 
 
 
 
 
00430c0
 
 
 
 
 
 
 
 
515db3b
 
 
 
ad80532
515db3b
 
00430c0
 
 
 
 
515db3b
 
 
 
 
 
00430c0
515db3b
00430c0
 
 
 
 
 
 
 
8d8f4b0
fb114fa
8d8f4b0
 
 
c56e31d
8d8f4b0
c56e31d
 
8d8f4b0
e51a5b0
c56e31d
 
8d8f4b0
 
c56e31d
 
8d8f4b0
 
 
 
c56e31d
8d8f4b0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
c56e31d
 
d4f4893
 
 
 
 
 
 
00430c0
d4f4893
 
 
 
 
 
 
 
 
 
 
00430c0
d4f4893
 
 
 
 
b09c08f
 
 
 
 
 
 
00430c0
8d8f4b0
 
 
00430c0
 
 
 
 
 
 
 
8d8f4b0
00430c0
 
 
 
 
 
 
 
 
 
515db3b
8d8f4b0
00430c0
8d8f4b0
00430c0
 
 
 
 
 
 
 
8d8f4b0
00430c0
8d8f4b0
 
 
 
 
 
e51a5b0
8d8f4b0
 
 
 
 
 
 
 
00430c0
8d8f4b0
 
 
 
 
e51a5b0
00430c0
 
 
 
b09c08f
8d8f4b0
 
 
 
 
 
 
d4f4893
b09c08f
00430c0
d4f4893
8d8f4b0
00430c0
8d8f4b0
e51a5b0
8d8f4b0
d4f4893
 
 
b09c08f
 
 
00430c0
 
 
b09c08f
 
 
e51a5b0
 
8d8f4b0
00430c0
e51a5b0
 
8d8f4b0
e51a5b0
c56e31d
00430c0
8d8f4b0
 
 
e51a5b0
 
00430c0
e51a5b0
 
8d8f4b0
e51a5b0
8d8f4b0
 
 
 
 
 
 
00430c0
8d8f4b0
 
00430c0
d4f4893
5121e98
 
 
 
 
 
 
 
 
 
 
 
 
d4f4893
5121e98
00430c0
 
d4f4893
00430c0
b09c08f
 
 
 
 
00430c0
 
 
 
 
 
 
e51a5b0
8d8f4b0
 
 
 
 
 
00430c0
8d8f4b0
 
 
 
 
 
 
 
00430c0
e51a5b0
8d8f4b0
c56e31d
8d8f4b0
b04fed6
515db3b
00430c0
515db3b
00430c0
515db3b
 
 
00430c0
e7bbd13
8d8f4b0
 
 
515db3b
8d8f4b0
 
 
 
 
 
 
 
 
d4f4893
 
00430c0
515db3b
 
00430c0
a1acd02
 
 
8d8f4b0
 
00430c0
d4f4893
 
 
 
 
b09c08f
 
00430c0
 
d4f4893
 
 
 
00430c0
8d8f4b0
 
 
 
 
00430c0
8d8f4b0
 
 
 
 
 
00430c0
8d8f4b0
a1acd02
 
 
 
 
 
 
8d8f4b0
 
 
 
 
 
 
 
 
d4f4893
b09c08f
8d8f4b0
bf64382
d4f4893
 
 
b09c08f
d4f4893
 
b09c08f
d4f4893
 
b09c08f
 
d4f4893
 
b09c08f
 
 
 
 
 
 
d4f4893
 
8d8f4b0
ad80532
515db3b
 
ad80532
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
fb114fa
ad80532
fb114fa
ad80532
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
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
import os
from threading import Thread
from typing import Iterator, List, Tuple, Dict, Any
import uuid
import json

import gradio as gr
import spaces
import torch
from transformers import AutoModelForCausalLM, AutoTokenizer, TextIteratorStreamer, pipeline
from bs4 import BeautifulSoup
import requests
from functools import lru_cache
from datasets import load_dataset, DatasetDict, Dataset, concatenate_datasets

# ---------------------------- Cấu Hình ---------------------------- #

DESCRIPTION = """\
# Llama 3.2 3B Instruct với Chức Năng Nâng Cao

Llama 3.2 3B là phiên bản mới nhất của Meta về các mô hình ngôn ngữ mở.
Demo này giới thiệu [`meta-llama/Llama-3.2-3B-Instruct`](https://huggingface.co/meta-llama/Llama-3.2-3B-Instruct), được tinh chỉnh để theo dõi hướng dẫn.
Để biết thêm chi tiết, vui lòng xem [bài viết của chúng tôi](https://huggingface.co/blog/llama32).
"""

MAX_MAX_NEW_TOKENS = 2048  # Số token tối đa có thể tạo ra
DEFAULT_MAX_NEW_TOKENS = 1024  # Số token tạo ra mặc định
MAX_INPUT_TOKEN_LENGTH = int(os.getenv("MAX_INPUT_TOKEN_LENGTH", "4096"))  # Độ dài token tối đa cho đầu vào

# Xác định thiết bị sử dụng (GPU nếu có, ngược lại CPU)
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")

model_id = "nltpt/Llama-3.2-3B-Instruct"  # ID mô hình, đảm bảo đây là ID mô hình đúng
tokenizer = AutoTokenizer.from_pretrained(model_id)  # Tải tokenizer từ Hugging Face
model = AutoModelForCausalLM.from_pretrained(
    model_id,
    device_map="auto",
    torch_dtype=torch.bfloat16,  # Sử dụng dtype phù hợp để tiết kiệm bộ nhớ
)
model.to(device)  # Di chuyển mô hình tới thiết bị đã chọn
model.eval()  # Đặt mô hình ở chế độ đánh giá

# Khởi tạo pipeline phân tích tâm lý
sentiment_pipeline = pipeline("sentiment-analysis", model="nlptown/bert-base-multilingual-uncased-sentiment")

# ---------------------------- Thiết lập Bộ nhớ Sử dụng Huggingface Datasets ---------------------------- #

HF_TOKEN = os.getenv("HF_TOKEN")  # Đảm bảo bạn đã set biến môi trường này
HF_DATASET = os.getenv("HF_DATASET")  # "your_username/chat_memory" Thay đổi theo tên của bạn

def initialize_dataset():
    """
    Khởi tạo Dataset trên Huggingface Hub nếu chưa tồn tại.
    """
    try:
        dataset = load_dataset(HF_DATASET, use_auth_token=HF_TOKEN)
        print("Dataset đã tồn tại trên Huggingface Hub.")
    except Exception as e:
        print(f"Dataset chưa tồn tại. Tạo mới Dataset: {e}")
        # Tạo Dataset mới nếu chưa tồn tại
        dataset = DatasetDict({
            "conversations": Dataset.from_dict({
                "user_id": [],
                "messages": []
            })
        })
        try:
            dataset.push_to_hub(HF_DATASET, private=True, token=HF_TOKEN)
            print("Dataset mới đã được tạo và đẩy lên Huggingface Hub.")
        except Exception as push_e:
            print(f"Lỗi khi đẩy Dataset lên Hub: {push_e}")
    return

def save_conversation(user_id: str, messages: List[Tuple[str, str]]):
    """
    Lưu cuộc hội thoại của người dùng vào Dataset.
    """
    try:
        dataset = load_dataset(HF_DATASET, split="conversations", use_auth_token=HF_TOKEN)
    except Exception as e:
        print(f"Lỗi khi tải Dataset: {e}")
        return
    
    # Chuyển đổi cuộc hội thoại thành định dạng JSON
    messages_json = json.dumps(messages)
    new_entry = {
        "user_id": user_id,
        "messages": messages_json
    }
    # Tạo Dataset từ entry mới
    new_dataset = Dataset.from_dict(new_entry)
    # Kết hợp với Dataset hiện tại
    try:
        updated_dataset = concatenate_datasets([dataset, new_dataset])
        # Đẩy lên Hub
        updated_dataset.push_to_hub(HF_DATASET, split="conversations", token=HF_TOKEN)
        print(f"Cuộc hội thoại của user_id {user_id} đã được lưu.")
    except Exception as e:
        print(f"Lỗi khi đẩy Dataset lên Hub: {e}")

def load_conversation(user_id: str) -> List[Tuple[str, str]]:
    """
    Truy xuất cuộc hội thoại của người dùng từ Dataset.
    """
    try:
        dataset = load_dataset(HF_DATASET, split="conversations", use_auth_token=HF_TOKEN)
    except Exception as e:
        print(f"Lỗi khi tải Dataset: {e}")
        return []
    
    # Tìm entry theo user_id
    user_data = dataset.filter(lambda x: x["user_id"] == user_id)
    if len(user_data) == 0:
        return []
    messages_json = user_data["messages"][0]
    return json.loads(messages_json)

# Khởi tạo Dataset
initialize_dataset()

# ---------------------------- Định Nghĩa Hàm ---------------------------- #

@lru_cache(maxsize=128)
def extract_text_from_webpage(html_content: str) -> str:
    """Trích xuất văn bản hiển thị từ nội dung HTML sử dụng BeautifulSoup."""
    soup = BeautifulSoup(html_content, "html.parser")
    # Loại bỏ các thẻ không hiển thị như script, style, header, footer, nav, form, svg
    for tag in soup(["script", "style", "header", "footer", "nav", "form", "svg"]):
        tag.extract()
    # Trích xuất văn bản hiển thị, tách bằng dấu cách và loại bỏ khoảng trắng thừa
    visible_text = soup.get_text(separator=' ', strip=True)
    return visible_text

def search(query: str) -> List[Dict[str, Any]]:
    """Thực hiện tìm kiếm trên Google và trả về kết quả."""
    term = query
    all_results = []
    max_chars_per_page = 8000  # Số ký tự tối đa mỗi trang
    headers = {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/111.0"
    }
    with requests.Session() as session:
        try:
            resp = session.get(
                url="https://www.google.com/search",
                headers=headers,
                params={"q": term, "num": 4},  # Tìm kiếm với 4 kết quả mỗi trang
                timeout=5,
                verify=False,  # Bỏ qua xác minh SSL
            )
            resp.raise_for_status()  # Kiểm tra phản hồi HTTP
            soup = BeautifulSoup(resp.text, "html.parser")
            result_blocks = soup.find_all("div", attrs={"class": "g"})  # Tìm tất cả các khối kết quả
            for result in result_blocks:
                link_tag = result.find("a", href=True)  # Tìm thẻ liên kết
                if link_tag and 'href' in link_tag.attrs:
                    link = link_tag["href"]
                    try:
                        webpage = session.get(
                            link,
                            headers=headers,
                            timeout=5,
                            verify=False
                        )
                        webpage.raise_for_status()
                        visible_text = extract_text_from_webpage(webpage.text)
                        if len(visible_text) > max_chars_per_page:
                            visible_text = visible_text[:max_chars_per_page]  # Cắt văn bản nếu quá dài
                        all_results.append({"link": link, "text": visible_text})
                    except requests.exceptions.RequestException:
                        all_results.append({"link": link, "text": "Không thể lấy nội dung."})
        except requests.exceptions.RequestException as e:
            all_results.append({"link": "N/A", "text": "Không thể thực hiện tìm kiếm."})
    return all_results

def summarize_text(text: str, max_length: int = 150) -> str:
    """Tóm tắt văn bản sử dụng mô hình Llama."""
    conversation = [
        {"role": "user", "content": f"Hãy tóm tắt đoạn văn sau: {text}"}
    ]
    input_ids = tokenizer.apply_chat_template(conversation, add_generation_prompt=True, return_tensors="pt")
    input_ids = input_ids.to(device)

    summary_streamer = TextIteratorStreamer(tokenizer, timeout=20.0, skip_prompt=True, skip_special_tokens=True)
    summary_kwargs = {
        "input_ids": input_ids,
        "streamer": summary_streamer,
        "max_new_tokens": max_length,
        "do_sample": True,
        "top_p": 0.95,
        "temperature": 0.7,
    }
    t = Thread(target=model.generate, kwargs=summary_kwargs)
    t.start()

    summary = ""
    for new_text in summary_streamer:
        summary += new_text
    return summary

def analyze_sentiment(text: str) -> str:
    """Phân tích tâm lý của văn bản sử dụng mô hình."""
    result = sentiment_pipeline(text)
    sentiment = result[0]['label']
    score = result[0]['score']
    return f"🟢 **Tâm lý**: {sentiment} (Điểm: {score:.2f})"

def generate_response(prompt: str, chat_history: List[Tuple[str, str]], user_id: str, max_new_tokens: int, temperature: float, top_p: float, top_k: int, repetition_penalty: float) -> Iterator[str]:
    """
    Tạo phản hồi sử dụng mô hình Llama cục bộ theo chế độ streaming.
    """
    # Lấy lịch sử từ Dataset
    conversation = load_conversation(user_id)
    # Chuyển đổi lịch sử thành định dạng mà mô hình hiểu
    conversation_formatted = []
    for user_msg, assistant_msg in conversation:
        conversation_formatted.extend([
            {"role": "user", "content": user_msg},
            {"role": "assistant", "content": assistant_msg},
        ])
    conversation_formatted.append({"role": "user", "content": prompt})  # Thêm tin nhắn của người dùng

    # Kiểm tra độ dài và sử dụng bản tóm tắt nếu cần
    if len(conversation_formatted) > 50:  # Giới hạn số lượng tin nhắn, điều chỉnh tùy nhu cầu
        summary = summarize_text(" ".join([msg["content"] for msg in conversation_formatted]))
        # Lưu bản tóm tắt vào Dataset
        new_messages = [("system", summary)]
        save_conversation(user_id, new_messages)
        # Giữ lại phần mới nhất
        conversation_formatted = [{"role": "system", "content": summary}] + conversation_formatted[-25:]

    # Chuẩn bị input_ids từ tokenizer
    input_ids = tokenizer.apply_chat_template(conversation_formatted, add_generation_prompt=True, return_tensors="pt")
    if input_ids.shape[1] > MAX_INPUT_TOKEN_LENGTH:
        # Sử dụng bản tóm tắt từ bộ nhớ
        summary = summarize_text(" ".join([msg["content"] for msg in conversation_formatted]))
        conversation_formatted = [{"role": "system", "content": summary}] + conversation_formatted[-(MAX_INPUT_TOKEN_LENGTH // 2):]
        input_ids = tokenizer.apply_chat_template(conversation_formatted, add_generation_prompt=True, return_tensors="pt")
        # Lưu lại bản tóm tắt
        new_messages = [("system", summary)]
        save_conversation(user_id, new_messages)

    input_ids = input_ids.to(device)  # Di chuyển input tới thiết bị

    # Khởi tạo streamer để nhận văn bản được tạo ra theo thời gian thực
    streamer = TextIteratorStreamer(tokenizer, timeout=20.0, skip_prompt=True, skip_special_tokens=True)
    generate_kwargs = {
        "input_ids": input_ids,
        "streamer": streamer,
        "max_new_tokens": max_new_tokens,
        "do_sample": True,
        "top_p": top_p,
        "top_k": top_k,
        "temperature": temperature,
        "num_beams": 1,
        "repetition_penalty": repetition_penalty,
    }
    t = Thread(target=model.generate, kwargs=generate_kwargs)  # Tạo luồng để sinh văn bản
    t.start()

    # Stream văn bản được tạo ra
    outputs = []
    for text in streamer:
        outputs.append(text)
        yield "".join(outputs)

    # Lưu phản hồi vào Dataset
    response = "".join(outputs)
    save_conversation(user_id, [(prompt, response)])

@lru_cache(maxsize=128)
def process_query(query: str) -> Dict[str, Any]:
    """
    Xác định hàm nào sẽ được gọi dựa trên truy vấn của người dùng.
    """
    # Định nghĩa các từ khóa hoặc mẫu để xác định hàm
    web_search_keywords = ["tìm kiếm", "tìm", "tra cứu", "google", "lookup"]
    general_query_keywords = ["giải thích", "mô tả", "nói cho tôi biết về", "cái gì là", "cách nào"]
    summarize_keywords = ["tóm tắt", "tóm lại", "khái quát", "ngắn gọn"]
    sentiment_keywords = ["cảm xúc", "tâm trạng", "tâm lý", "phân tích cảm xúc"]
    topic_keywords = ["chủ đề", "bàn về", "về"]

    query_lower = query.lower()  # Chuyển truy vấn thành chữ thường để so sánh

    if any(keyword in query_lower for keyword in web_search_keywords):
        function_name = "web_search"
        arguments = {"query": query}
    elif any(keyword in query_lower for keyword in summarize_keywords):
        function_name = "summarize_query"
        arguments = {"prompt": query}
    elif any(keyword in query_lower for keyword in sentiment_keywords):
        function_name = "sentiment_analysis"
        arguments = {"prompt": query}
    elif any(keyword in query_lower for keyword in topic_keywords):
        function_name = "new_topic"
        arguments = {"topic": query}
    elif any(keyword in query_lower for keyword in general_query_keywords):
        function_name = "general_query"
        arguments = {"prompt": query}
    else:
        function_name = "hard_query"
        arguments = {"prompt": query}

    return {
        "name": function_name,
        "arguments": arguments
    }

def handle_functions(function_call: Dict[str, Any], prompt: str, chat_history: List[Tuple[str, str]], user_id: str, max_new_tokens: int, temperature: float, top_p: float, top_k: int, repetition_penalty: float) -> Iterator[str]:
    """
    Thực thi hàm phù hợp dựa trên lời gọi hàm.
    """
    function_name = function_call["name"]
    arguments = function_call["arguments"]

    if function_name == "web_search":
        query = arguments["query"]
        yield "🔍 Đang thực hiện tìm kiếm trên web..."
        web_results = search(query)
        if not web_results:
            yield "⚠️ Không tìm thấy kết quả."
            return
        # Tóm tắt kết quả tìm kiếm
        web_summary = '\n\n'.join([f"🔗 **Liên kết**: {res['link']}\n📝 **Mô tả**: {res['text']}" for res in web_results if res["text"] != "Không thể lấy nội dung."])
        if not web_summary:
            web_summary = "⚠️ Không thể lấy nội dung từ kết quả tìm kiếm."

        # Trả về kết quả tìm kiếm cho người dùng
        yield "📄 **Kết quả tìm kiếm:**\n" + web_summary

    elif function_name == "summarize_query":
        # Khi người dùng yêu cầu tóm tắt, hệ thống sẽ thực hiện tìm kiếm và sau đó tóm tắt kết quả
        query = arguments["prompt"]
        yield "🔍 Đang thực hiện tìm kiếm để tóm tắt..."
        web_results = search(query)
        if not web_results:
            yield "⚠️ Không tìm thấy kết quả để tóm tắt."
            return
        # Lấy nội dung từ kết quả tìm kiếm để tóm tắt
        combined_text = ' '.join([res['text'] for res in web_results if res['text'] != "Không thể lấy nội dung."])
        if not combined_text:
            yield "⚠️ Không có nội dung để tóm tắt."
            return
        # Tóm tắt nội dung đã lấy
        yield "📝 Đang tóm tắt thông tin..."
        summary = summarize_text(combined_text)
        # Lưu tóm tắt vào Dataset
        save_conversation(user_id, [("tóm tắt", summary)])
        yield "📄 **Tóm tắt:**\n" + summary

    elif function_name == "sentiment_analysis":
        prompt_text = arguments["prompt"]
        yield "📊 Đang phân tích tâm lý..."
        sentiment = analyze_sentiment(prompt_text)
        yield sentiment

    elif function_name == "new_topic":
        topic = arguments["topic"]
        # Lưu chủ đề mới vào Dataset
        save_conversation(user_id, [("chủ đề", f"Chủ đề mới: {topic}")])
        yield f"🆕 Đã chuyển sang chủ đề mới: {topic}"

    elif function_name in ["general_query", "hard_query"]:
        prompt_text = arguments["prompt"]
        yield "🤖 Đang tạo phản hồi..."
        # Tạo phản hồi sử dụng mô hình Llama
        response_generator = generate_response(
            prompt=prompt_text,
            chat_history=chat_history,
            user_id=user_id,
            max_new_tokens=max_new_tokens,
            temperature=temperature,
            top_p=top_p,
            top_k=top_k,
            repetition_penalty=repetition_penalty
        )
        for response in response_generator:
            yield response

    else:
        yield "⚠️ Lời gọi hàm không được nhận dạng."

# ---------------------------- Giao Diện Gradio ---------------------------- #

def get_user_id(user_state: gr.State) -> str:
    """
    Tạo hoặc lấy user_id từ trạng thái của Gradio.
    """
    if user_state.value is None:
        user_state.value = str(uuid.uuid4())
    return user_state.value

@spaces.GPU(duration=15, queue=False)
def generate(
    message: str,
    chat_history: List[Tuple[str, str]],
    user_state: gr.State,  # Trạng thái để lưu trữ user_id
    max_new_tokens: int = DEFAULT_MAX_NEW_TOKENS,
    temperature: float = 0.6,
    top_p: float = 0.9,
    top_k: int = 50,
    repetition_penalty: float = 1.2,
) -> Iterator[str]:
    """
    Hàm chính để xử lý đầu vào của người dùng và tạo phản hồi.
    """
    # Thông báo về việc phân tích đầu vào
    yield "🔍 Đang phân tích truy vấn của bạn..."

    # Lấy user_id từ trạng thái
    user_id = get_user_id(user_state)

     # Thêm tin nhắn của người dùng vào lịch sử cuộc trò chuyện
    chat_history.append((message, "")) 

    # Xác định hàm nào sẽ được gọi dựa trên tin nhắn của người dùng
    function_call = process_query(message)

    # Thông báo về hàm được chọn
    if function_call["name"] == "web_search":
        yield "🛠️ Đã chọn chức năng: Tìm kiếm trên web."
    elif function_call["name"] == "summarize_query":
        yield "🛠️ Đã chọn chức năng: Tóm tắt văn bản."
    elif function_call["name"] == "sentiment_analysis":
        yield "🛠️ Đã chọn chức năng: Phân tích tâm lý."
    elif function_call["name"] == "new_topic":
        yield "🛠️ Đã chọn chức năng: Chủ đề mới."
    elif function_call["name"] in ["general_query", "hard_query"]:
        yield "🛠️ Đã chọn chức năng: Trả lời câu hỏi."
    else:
        yield "⚠️ Không thể xác định chức năng phù hợp."

    # Xử lý lời gọi hàm và sinh phản hồi tương ứng
    response_iterator = handle_functions(
        function_call=function_call,
        prompt=message,
        chat_history=chat_history,
        user_id=user_id,  # Sử dụng user_id để quản lý dữ liệu theo người dùng
        max_new_tokens=max_new_tokens,
        temperature=temperature,
        top_p=top_p,
        top_k=top_k,
        repetition_penalty=repetition_penalty
    )

    for response in response_iterator:
        # Thay thế tin nhắn cuối cùng của người dùng với phản hồi của hệ thống
        chat_history[-1] = (message, response)  
        yield chat_history
    # Lưu phản hồi cuối cùng vào Dataset
    save_conversation(user_id, chat_history)
    yield chat_history
    
# Định nghĩa các ví dụ để hướng dẫn người dùng
EXAMPLES = [
    ["Xin chào! Bạn khỏe không?"],
    ["Bạn có thể giải thích ngắn gọn về ngôn ngữ lập trình Python không?"],
    ["Giải thích cốt truyện của Cô bé Lọ Lem trong một câu."],
    ["Một người đàn ông cần bao nhiêu giờ để ăn một chiếc máy bay trực thăng?"],
    ["Viết một bài báo 100 từ về 'Lợi ích của mã nguồn mở trong nghiên cứu AI'"],
    ["Tìm và cung cấp cho tôi tin tức mới nhất về năng lượng tái tạo."],
    ["Tìm thông tin về Rạn san hô Great Barrier Reef."],
    ["Tóm tắt nội dung về trí tuệ nhân tạo."],
    ["Phân tích tâm lý của đoạn văn sau: Tôi rất vui khi được gặp bạn hôm nay!"],
]

# Cấu hình giao diện trò chuyện của Gradio với giao diện đẹp mắt
with gr.Blocks(css="""
    .gradio-container {
        background-color: #f0f2f5;  /* Màu nền nhẹ nhàng */
    }
    .gradio-container h1 {
        color: #4a90e2;  /* Màu xanh dương cho tiêu đề */
    }
    .gradio-container .gr-button {
        background-color: #4a90e2;  /* Màu xanh dương cho nút */
        color: white;  /* Màu chữ trắng trên nút */
    }
    .gradio-container .gr-slider__label {
        color: #333333;  /* Màu chữ đen cho nhãn slider */
    }
    .gradio-container .gr-chatbot {
        border: 2px solid #4a90e2;  /* Viền xanh dương cho chatbot */
        border-radius: 10px;  /* Bo góc viền chatbot */
        padding: 10px;  /* Khoảng cách bên trong chatbot */
        background-color: #ffffff;  /* Màu nền trắng cho chatbot */
    }
""", fill_height=True) as demo:
    gr.Markdown(DESCRIPTION)  # Hiển thị mô tả
    
    # Define the state for user_id
    user_state = gr.State(None)
    
    # Nút nhân bản không gian
    gr.DuplicateButton(value="Nhân bản Không gian để sử dụng riêng tư", elem_id="duplicate-button")
    
    # Chat Interface
    with gr.Row():
        chatbot = gr.Chatbot()
    
    with gr.Row():
        with gr.Column():
            message = gr.Textbox(
                label="Bạn:",
                placeholder="Nhập tin nhắn của bạn tại đây...",
            )
            submit = gr.Button("Gửi")
        with gr.Column():
            # Các thanh trượt cho tham số
            max_new_tokens = gr.Slider(
                label="Số token mới tối đa",
                minimum=1,
                maximum=MAX_MAX_NEW_TOKENS,
                step=1,
                value=DEFAULT_MAX_NEW_TOKENS,
            )
            temperature = gr.Slider(
                label="Nhiệt độ",
                minimum=0.1,
                maximum=4.0,
                step=0.1,
                value=0.6,
            )
            top_p = gr.Slider(
                label="Top-p (nucleus sampling)",
                minimum=0.05,
                maximum=1.0,
                step=0.05,
                value=0.9,
            )
            top_k = gr.Slider(
                label="Top-k",
                minimum=1,
                maximum=1000,
                step=1,
                value=50,
            )
            repetition_penalty = gr.Slider(
                label="Hình phạt sự lặp lại",
                minimum=1.0,
                maximum=2.0,
                step=0.05,
                value=1.2,
            )
    
    # Kết nối nút gửi với hàm generate
    submit.click(
        generate,
        inputs=[
            message,
            chatbot,
            user_state,
            max_new_tokens,
            temperature,
            top_p,
            top_k,
            repetition_penalty,
        ],
        outputs=chatbot,
    )
    
    # Thêm các ví dụ
    gr.Examples(
        examples=EXAMPLES,
        inputs=[message],
        outputs=[chatbot],
        fn=lambda x: x,  # Function to populate the message box with the example
    )

# Khởi chạy ứng dụng Gradio
if __name__ == "__main__":
    demo.queue(max_size=20).launch(share=True)  # Đặt share=True để tạo liên kết công khai