manhdo commited on
Commit
c48801f
1 Parent(s): 04626de
.gitignore ADDED
@@ -0,0 +1 @@
 
 
1
+ *.pyc
app.py ADDED
@@ -0,0 +1,205 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from pathlib import Path
2
+ import requests
3
+ import time
4
+ from random import randrange
5
+ from PIL import Image
6
+ import cv2
7
+ import numpy as np
8
+ from io import BytesIO
9
+
10
+ import streamlit as st
11
+ from streamlit_option_menu import option_menu
12
+
13
+ from src.styles.menu_styles import FOOTER_STYLES, HEADER_STYLES
14
+ from src.utils.lang import en, vi
15
+ from src.utils.ui import message_func
16
+ from src.utils.footer import show_info
17
+ from src.utils.helpers import get_random_img, get_files_in_dir
18
+
19
+
20
+ # --- PATH SETTINGS ---
21
+ current_dir: Path = Path(__file__).parent if "__file__" in locals() else Path.cwd()
22
+ css_file: Path = current_dir / "src/styles/.css"
23
+ assets_dir: Path = current_dir / "src/assets"
24
+ img_dir: Path = assets_dir / "img"
25
+
26
+ # --- GENERAL SETTINGS ---
27
+ PAGE_TITLE: str = "VNPT Chatbot"
28
+ PAGE_ICON: str = "🤖"
29
+ LANG_EN: str = "En"
30
+ LANG_VI: str = "Vi"
31
+ AI_MODEL_OPTIONS: list[str] = [
32
+ "v.0.0.1",
33
+ ]
34
+ API_ADDRESS: str = ""
35
+
36
+ st.set_page_config(page_title=PAGE_TITLE, page_icon=PAGE_ICON, layout="wide")
37
+
38
+ # --- LOAD CSS ---
39
+ with open(css_file) as f:
40
+ st.markdown(f"<style>{f.read()}</style>", unsafe_allow_html=True)
41
+
42
+ selected_lang = option_menu(
43
+ menu_title=None,
44
+ options=[LANG_EN, LANG_VI, ],
45
+ icons=["globe2", "translate"],
46
+ menu_icon="cast",
47
+ default_index=0,
48
+ orientation="horizontal",
49
+ styles=HEADER_STYLES
50
+ )
51
+
52
+ # Storing The Context
53
+ if "locale" not in st.session_state:
54
+ st.session_state.locale = en
55
+ if "seed" not in st.session_state:
56
+ st.session_state.seed = randrange(10**3)
57
+ if "user_text" not in st.session_state:
58
+ st.session_state.user_text = ""
59
+
60
+ def main(api_address,
61
+ conversation_path_param,
62
+ new_chat_path_param,
63
+ chat_append_path_param,
64
+ process_path_param,
65
+ guide_path_param):
66
+ st.sidebar.selectbox(label=st.session_state.locale.select_placeholder1, key="model", options=AI_MODEL_OPTIONS)
67
+
68
+ if st.sidebar.button(st.session_state.locale.reset_conversation):
69
+ response = requests.post(api_address + new_chat_path_param)
70
+ # Check if request was successful (status code 200)
71
+ assert response.status_code == 200, response.status_code
72
+
73
+ # Add guide
74
+ st.sidebar.write("------------------------------------")
75
+ response = requests.post(api_address + guide_path_param,
76
+ json={"language": st.session_state.locale.lang_code})
77
+ assert response.status_code == 200, response.status_code
78
+ guide_dict = response.json()["options_dict"]
79
+ guide_options = {}
80
+ option_is_selected = False
81
+ option_prompt = ""
82
+ for k, v in guide_dict.items():
83
+ guide_options[k] = st.sidebar.button(k, use_container_width=True)
84
+ if guide_options[k]:
85
+ option_is_selected = True
86
+ option_prompt = v
87
+
88
+ prompt = st.chat_input()
89
+ print(prompt)
90
+ if option_is_selected:
91
+ print("Appending ...")
92
+ response = requests.post(api_address + chat_append_path_param,
93
+ json={"role": "user", "content": option_prompt})
94
+ assert response.status_code == 200, response.status_code
95
+ if prompt:
96
+ print("Appending ...")
97
+ response = requests.post(api_address + chat_append_path_param,
98
+ json={"role": "user", "content": prompt})
99
+ assert response.status_code == 200, response.status_code
100
+
101
+ # Visual conversation
102
+ response = requests.get(api_address + conversation_path_param,
103
+ json={"language": st.session_state.locale.lang_code})
104
+ assert response.status_code == 200, response.status_code
105
+ messages = response.json()["conversation"]
106
+ for message in messages:
107
+ message_func(
108
+ message["content"],
109
+ role = message["role"],
110
+ )
111
+ urls = message.get("urls", [])
112
+ titles = message.get("titles", [])
113
+
114
+ if len(urls) and not isinstance(urls[0], list):
115
+ urls = [[url] for url in urls]
116
+ if len(titles) and not isinstance(titles[0], list):
117
+ titles = [[title] for title in titles]
118
+
119
+ for sub_urls, sub_titles in zip(urls, titles):
120
+ display_images(sub_urls, sub_titles)
121
+
122
+ # for url, title in zip(urls, titles):
123
+ # display_image_with_title_and_url(title, url)
124
+
125
+ # Process the conversation if prompt
126
+ if prompt or option_is_selected:
127
+ print("Processing ...")
128
+ start = time.time()
129
+ response = requests.post(api_address + process_path_param,
130
+ json={"language": st.session_state.locale.lang_code})
131
+ assert response.status_code == 200, response.status_code
132
+ logs = response.json()["logs"]
133
+ for data in logs:
134
+ urls = data["urls"]
135
+ titles = data["titles"]
136
+ end = time.time()
137
+ print("done")
138
+ print(data)
139
+ print(end - start)
140
+ message_func(
141
+ data["content"],
142
+ role=data["role"]
143
+ )
144
+ for url, title in zip(urls, titles):
145
+ display_image_with_title_and_url(title, url)
146
+ message_func(
147
+ "",
148
+ role="assistant"
149
+ )
150
+
151
+ # if st.session_state.user_text:
152
+ # show_conversation()
153
+ # st.session_state.user_text = ""
154
+ # get_user_input()
155
+ # show_chat_buttons()
156
+
157
+ def run_agi():
158
+ global API_ADDRESS
159
+ print(API_ADDRESS)
160
+ st.sidebar.text_input("API address", key="api_address")
161
+ api_address = st.session_state.api_address
162
+ ##TODO: Dynamic this
163
+ conversation_path_param = "/chatbot/"
164
+ new_chat_path_param = "/chatbot/new_chat"
165
+ chat_append_path_param = "/chatbot/append_text_from_user"
166
+ process_path_param = "/chatbot/process"
167
+ guide_path_param = "/chatbot/guide_options"
168
+
169
+ match selected_lang:
170
+ case "En":
171
+ st.session_state.locale = en
172
+ case "Vi":
173
+ st.session_state.locale = vi
174
+ case _:
175
+ st.session_state.locale = en
176
+ st.markdown(f"<h1 style='text-align: center;'>{st.session_state.locale.title}</h1>", unsafe_allow_html=True)
177
+ selected_footer = option_menu(
178
+ menu_title=None,
179
+ options=[
180
+ st.session_state.locale.footer_option1,
181
+ st.session_state.locale.footer_option0,
182
+ ],
183
+ icons=["info-circle", "chat-square-text"], # https://icons.getbootstrap.com/
184
+ menu_icon="cast",
185
+ default_index=0,
186
+ orientation="horizontal",
187
+ styles=FOOTER_STYLES
188
+ )
189
+
190
+ match selected_footer:
191
+ case st.session_state.locale.footer_option0:
192
+ main(api_address,
193
+ conversation_path_param,
194
+ new_chat_path_param,
195
+ chat_append_path_param,
196
+ process_path_param,
197
+ guide_path_param)
198
+ case st.session_state.locale.footer_option1:
199
+ show_info()
200
+ case _:
201
+ show_info()
202
+
203
+
204
+ if __name__ == "__main__":
205
+ run_agi()
requirements.txt ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ numpy
2
+ opencv-python
src/styles/.css ADDED
@@ -0,0 +1,52 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ a {
2
+ text-decoration: none;
3
+ font-weight: 500;
4
+ }
5
+
6
+ .block-container {
7
+ padding: 0 16px 96px 16px;
8
+ }
9
+
10
+ a:hover {
11
+ text-decoration: none;
12
+ color: #d33682 !important;
13
+ }
14
+
15
+ ul {
16
+ list-style-type: none;
17
+ }
18
+
19
+ hr {
20
+ margin-top: 0;
21
+ margin-bottom: 5%;
22
+ }
23
+
24
+ #MainMenu {
25
+ visibility: hidden;
26
+ }
27
+
28
+ footer {
29
+ visibility: hidden;
30
+ }
31
+
32
+ header {
33
+ visibility: hidden;
34
+ }
35
+
36
+ audio::-webkit-media-controls-panel,
37
+ audio::-webkit-media-controls-enclosure {
38
+ background-color: #326464;
39
+ max-height: 30px;
40
+ border-radius: 4px;
41
+ }
42
+
43
+ audio::-webkit-media-controls-time-remaining-display,
44
+ audio::-webkit-media-controls-current-time-display {
45
+ color: #CBE4DE;
46
+ text-shadow: none;
47
+ }
48
+
49
+ audio::-webkit-media-controls-timeline {
50
+ border-radius: 8px;
51
+ margin: 10px;
52
+ }
src/styles/menu_styles.py ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import Dict
2
+
3
+ HEADER_STYLES: Dict[str, Dict] = {
4
+ "container": {
5
+ "padding": "0px",
6
+ "display": "grid",
7
+ "margin": "0!important",
8
+ "background-color": "#2C3333"
9
+ },
10
+ "icon": {"color": "#CBE4DE", "font-size": "14px"},
11
+ "nav-link": {
12
+ "font-size": "14px",
13
+ "text-align": "center",
14
+ "margin": "auto",
15
+ "background-color": "#2C3333",
16
+ "height": "30px",
17
+ "width": "7rem",
18
+ "color": "#CBE4DE",
19
+ "border-radius": "5px"
20
+ },
21
+ "nav-link-selected": {
22
+ "background-color": "#2E4F4F",
23
+ "font-weight": "300",
24
+ "color": "#f5f5f5",
25
+ "border": "1px solid #0E8388"
26
+ }
27
+ }
28
+
29
+ FOOTER_STYLES: Dict[str, Dict] = {
30
+
31
+ }
src/utils/constants.py ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ AI_ROLE_OPTIONS_EN: list[str] = [
2
+ "fashion assistant",
3
+ ]
4
+
5
+ AI_ROLE_OPTIONS_VI: list[str] = [
6
+ "trợ lý thời trang ảo",
7
+ ]
src/utils/footer.py ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from pathlib import Path
2
+
3
+ import streamlit as st
4
+
5
+
6
+ def show_info():
7
+ st.divider()
8
+ st.markdown(f"<div style='text-align: justify;'>{st.session_state.locale.product_information}</div>",
9
+ unsafe_allow_html=True)
10
+ st.divider()
11
+ st.markdown("""[**Contact us**](https://www.linkedin.com/in/manh-do-quang-50823a255/)""")
12
+ st.markdown("""[**Buy Me A Coffee**](https://www.buymeacoffee.com/doquangmanp)""")
src/utils/helpers.py ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import base64
2
+ import os
3
+ import random
4
+ from pathlib import Path
5
+ from typing import List
6
+
7
+
8
+ def get_files_in_dir(path: Path) -> List[str]:
9
+ files = []
10
+ for file in os.listdir(path):
11
+ if os.path.isfile(os.path.join(path, file)):
12
+ files.append(file)
13
+ return files
14
+
15
+
16
+ def get_random_img(img_names: List[str]) -> str:
17
+ return random.choice(img_names)
src/utils/lang.py ADDED
@@ -0,0 +1,104 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from dataclasses import dataclass
2
+ from typing import List # NOQA: UP035
3
+
4
+ from .constants import AI_ROLE_OPTIONS_EN, AI_ROLE_OPTIONS_VI
5
+
6
+
7
+ @dataclass
8
+ class Locale:
9
+ ai_role_options: List[str]
10
+ ai_role_prefix: str
11
+ ai_role_postfix: str
12
+ title: str
13
+ language: str
14
+ lang_code: str
15
+ chat_placeholder: str
16
+ chat_run_btn: str
17
+ chat_clear_btn: str
18
+ chat_save_btn: str
19
+ fill_api_address: str
20
+ select_placeholder1: str
21
+ select_placeholder2: str
22
+ select_placeholder3: str
23
+ radio_placeholder: str
24
+ radio_text1: str
25
+ radio_text2: str
26
+ reset_conversation: str
27
+ stt_placeholder: str
28
+ footer_title: str
29
+ footer_option0: str
30
+ footer_option1: str
31
+ footer_chat: str
32
+ footer_channel: str
33
+ product_information: str
34
+ tokens_count: str
35
+ message_cost: str
36
+ total_cost: str
37
+
38
+
39
+ # --- LOCALE SETTINGS ---
40
+ en = Locale(
41
+ ai_role_options=AI_ROLE_OPTIONS_EN,
42
+ ai_role_prefix="You are a female",
43
+ ai_role_postfix="Answer as concisely as possible.",
44
+ title="HangerAI Chatbot Assistant",
45
+ language="English",
46
+ lang_code="en",
47
+ chat_placeholder="Start Your Conversation With AI:",
48
+ chat_run_btn="Ask",
49
+ chat_clear_btn="Clear",
50
+ chat_save_btn="Save",
51
+ fill_api_address="Enter api address",
52
+ select_placeholder1="Select Model",
53
+ select_placeholder2="Select Role",
54
+ select_placeholder3="Create Role",
55
+ radio_placeholder="Role Interaction",
56
+ radio_text1="Select",
57
+ radio_text2="Create",
58
+ reset_conversation="New chat",
59
+ stt_placeholder="To Hear The Voice Of AI Press Play",
60
+ footer_title="Support & Feedback",
61
+ footer_option0="Chat",
62
+ footer_option1="Info",
63
+ footer_chat="AI Talks Chat",
64
+ footer_channel="AI Talks Channel",
65
+ product_information="""HangerChatbot tailored for the retail industry, specifically focusing on the fashion sector. Our chatbot is designed to provide exceptional assistance to customers in their product selection, exploration, and search. But that's not all – our product goes a step further by offering personalized clothing recommendations based on user interactions, preferences, history, and inquiries.\n
66
+ Powered by cutting-edge and state-of-the-art artificial intelligence technologies, our chatbot is here to revolutionize the way customers engage with your fashion brand. Whether it's helping customers find the perfect outfit, suggesting the latest trends, or answering their fashion-related questions, our chatbot is the ultimate solution to enhance the shopping experience.\n
67
+ Don't miss out on this opportunity to take your retail business to the next level with our advanced and customer-centric chatbot solution. Experience the future of retail with our innovative product today!""",
68
+ tokens_count="Tokens count: ",
69
+ message_cost="Message cost: ",
70
+ total_cost="Total cost of conversation: ",
71
+ )
72
+
73
+ vi = Locale(
74
+ ai_role_options=AI_ROLE_OPTIONS_VI,
75
+ ai_role_prefix="You are a female",
76
+ ai_role_postfix="Answer as concisely as possible.",
77
+ title="Trợ lý ảo Hanger",
78
+ language="Tiếng Việt",
79
+ lang_code="vi",
80
+ chat_placeholder="Start Your Conversation With AI:",
81
+ chat_run_btn="Ask",
82
+ chat_clear_btn="Clear",
83
+ chat_save_btn="Save",
84
+ fill_api_address="Nhập địa chỉ API",
85
+ select_placeholder1="Chọn mô hình",
86
+ select_placeholder2="Lựa chọn vai trò",
87
+ select_placeholder3="Tạo vai trò",
88
+ radio_placeholder="Lựa chọn phiên bản của trợ lý ảo",
89
+ radio_text1="Lựa chọn",
90
+ radio_text2="Tạo mới",
91
+ reset_conversation="Hội thoại mới",
92
+ stt_placeholder="To Hear The Voice Of AI Press Play",
93
+ footer_title="Support & Feedback",
94
+ footer_option0="Nói chuyện",
95
+ footer_option1="Thông tin",
96
+ footer_chat="AI Talks Chat",
97
+ footer_channel="AI Talks Channel",
98
+ product_information="""HangerChatbot được tùy chỉnh dành cho ngành bán lẻ, đặc biệt là tập trung vào lĩnh vực thời trang. Chatbot của chúng tôi được thiết kế để cung cấp sự hỗ trợ xuất sắc cho khách hàng trong việc lựa chọn sản phẩm, khám phá và tìm kiếm. Nhưng điều đó chưa đủ - sản phẩm của chúng tôi còn đi xa hơn bằng cách cung cấp các gợi ý về trang phục cá nhân dựa trên tương tác của người dùng, sở thích, lịch sử và câu hỏi.\n
99
+ Được trang bị bởi các công nghệ trí tuệ nhân tạo tiên tiến và hiện đại, chatbot của chúng tôi sẽ cách mạng hóa cách khách hàng tương tác với thương hiệu thời trang của bạn. Cho dù đó là giúp khách hàng tìm trang phục hoàn hảo, đề xuất các xu hướng mới nhất hoặc trả lời các câu hỏi liên quan đến thời trang, chatbot của chúng tôi là giải pháp tối ưu để nâng cao trải nghiệm mua sắm.\n
100
+ Đừng bỏ lỡ cơ hội này để đưa doanh nghiệp bán lẻ của bạn lên tầm cao mới với giải pháp chatbot tiên tiến và tập trung vào khách hàng của chúng tôi. Trải nghiệm tương lai của ngành bán lẻ với sản phẩm độc đáo của chúng tôi ngay hôm nay!""",
101
+ tokens_count="Tokens count: ",
102
+ message_cost="Message cost: ",
103
+ total_cost="Total cost of conversation: ",
104
+ )
src/utils/ui.py ADDED
@@ -0,0 +1,70 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import html
2
+ import re
3
+ import streamlit as st
4
+
5
+
6
+ def format_message(text):
7
+ """
8
+ This function is used to format the messages in the chatbot UI.
9
+
10
+ Parameters:
11
+ text (str): The text to be formatted.
12
+ """
13
+ text_blocks = re.split(r"```[\s\S]*?```", text)
14
+ code_blocks = re.findall(r"```([\s\S]*?)```", text)
15
+
16
+ text_blocks = [html.escape(block) for block in text_blocks]
17
+
18
+ formatted_text = ""
19
+ for i in range(len(text_blocks)):
20
+ formatted_text += text_blocks[i].replace("\n", "<br>")
21
+ if i < len(code_blocks):
22
+ formatted_text += f'<pre style="white-space: pre-wrap; word-wrap: break-word;"><code>{html.escape(code_blocks[i])}</code></pre>'
23
+
24
+ return formatted_text
25
+
26
+
27
+ def message_func(text, role):
28
+ """
29
+ This function is used to display the messages in the chatbot UI.
30
+
31
+ Parameters:
32
+ text (str): The text to be displayed.
33
+ role (str): Role for the text.
34
+ """
35
+ if role == "user":
36
+ avatar_url = "https://avataaars.io/?avatarStyle=Transparent&topType=ShortHairShortFlat&accessoriesType=Prescription01&hairColor=Auburn&facialHairType=BeardLight&facialHairColor=Black&clotheType=Hoodie&clotheColor=PastelBlue&eyeType=Squint&eyebrowType=DefaultNatural&mouthType=Smile&skinColor=Tanned"
37
+ message_alignment = "flex-end"
38
+ message_bg_color = "linear-gradient(135deg, #00B2FF 0%, #006AFF 100%)"
39
+ avatar_class = "user-avatar"
40
+ st.write(
41
+ f"""
42
+ <div style="display: flex; align-items: center; margin-bottom: 10px; justify-content: {message_alignment};">
43
+ <div style="background: {message_bg_color}; color: white; border-radius: 20px; padding: 10px; margin-right: 5px; max-width: 75%; font-size: 14px;">
44
+ {text} \n </div>
45
+ <img src="{avatar_url}" class="{avatar_class}" alt="avatar" style="width: 50px; height: 50px;" />
46
+ </div>
47
+ """,
48
+ unsafe_allow_html=True,
49
+ )
50
+ elif role == "assistant":
51
+ avatar_url = "https://avataaars.io/?avatarStyle=Transparent&topType=WinterHat2&accessoriesType=Kurt&hatColor=Blue01&facialHairType=MoustacheMagnum&facialHairColor=Blonde&clotheType=Overall&clotheColor=Gray01&eyeType=WinkWacky&eyebrowType=SadConcernedNatural&mouthType=Sad&skinColor=Light"
52
+ message_alignment = "flex-start"
53
+ message_bg_color = "#71797E"
54
+ avatar_class = "bot-avatar"
55
+
56
+ text = format_message(text)
57
+
58
+ st.write(
59
+ f"""
60
+ <div style="display: flex; align-items: center; margin-bottom: 10px; justify-content: {message_alignment};">
61
+ <img src="{avatar_url}" class="{avatar_class}" alt="avatar" style="width: 50px; height: 50px;" />
62
+ <div style="background: {message_bg_color}; color: white; border-radius: 20px; padding: 10px; margin-right: 5px; max-width: 75%; font-size: 14px;">
63
+ {text} \n </div>
64
+ </div>
65
+ """,
66
+ unsafe_allow_html=True,
67
+ )
68
+
69
+ else:
70
+ raise