File size: 13,262 Bytes
31968ad
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
import streamlit as st
import os
import openai
import json
import bcrypt
import os
from groq import Groq
import re

import warnings
warnings.filterwarnings('ignore')

client = Groq(
    api_key=os.environ.get("GROQ_API_KEY"),
)

# hiding streamlit header and menus
hide_menu = """
                <style>
                #MainMenu {visibiliyt: hidden;}
                footer {visibility: hidden;}
                header {visibility: hidden;}
                </style>"""

st.markdown(hide_menu, unsafe_allow_html = True)

# function to remove the streamlit tag from webpage title
def set_page_title(title):
    st.markdown(unsafe_allow_html=True, body=f"""
        <iframe height=0 srcdoc="<script>
            const title = window.parent.document.querySelector('title') \
                
            const oldObserver = window.parent.titleObserver
            if (oldObserver) {{
                oldObserver.disconnect()
            }} \

            const newObserver = new MutationObserver(function(mutations) {{
                const target = mutations[0].target
                if (target.text !== '{title}') {{
                    target.text = '{title}'
                }}
            }}) \

            newObserver.observe(title, {{ childList: true }})
            window.parent.titleObserver = newObserver \

            title.text = '{title}'
        </script>" />
    """)




# Define a Streamlit key for storing whether the user is authenticated.
if 'authenticated' not in st.session_state:
    st.session_state.authenticated = False

# loading user logins from config
config_file_path = "config.json"  # Path to your configuration file

with open(config_file_path) as config_file:
    credentials = json.load(config_file)


# Function to verify credentials
def check_credentials(password):
    # Check if the entered credentials match the hashed passwords in the config file
    # if username in credentials and bcrypt.checkpw(password.encode("utf-8"), credentials[username].encode("utf-8")):

    if password=='123':
        return True
    return False


# Main login page
def login_page():
    
    set_page_title("TGPT")

    st.markdown("<h1 style='text-align: center;'>Welcome to Team-GPT</h1>", unsafe_allow_html=True)
    st.markdown('\n')


    # Login form
    with st.form(key='login_form'):
        username = st.text_input("Username")
        password = st.text_input("Password", type="password")
        
        # Add some CSS styling to center-align the button
        login_button = st.form_submit_button(label='Login', type = 'primary', use_container_width = True)
        
        st.session_state.username = username

        if login_button:
            if check_credentials(password):
                pattern = r"^[a-zA-Z]+$"
                if re.fullmatch(pattern, username):
                    st.session_state['authenticated'] = True  # Set the session state
                    st.rerun()  # Rerun the app to update the state
                else:
                    st.error("Username can't contain numbers or special characters!")
            else:
                st.error("The password is incorrect!")
    
    st.write('Password: 123')

    st.markdown('\n')
    st.markdown("<span style='color:#2180b9; text-align:left; font-weight:bold; font-size:20px;'>Instructions :</span>", unsafe_allow_html=True)
    st.markdown("""
    <div style='text-align: left; padding-left: 20px;'>
    <ul>
        <li>Username can't have numbers or special characters.</li>
        <li>By default, every new conversation is Private.</li>
        <li>To make the conversation Public, select 'Shared' before submitting first prompt.</li>
        <li style='color:red;'>Once the first prompt is submitted, you can't change the chat visibility.</li> 
    </ul>
    </div>
    """, unsafe_allow_html=True)
        


# function to create new chat
def create_new_chat(st):

    st.session_state.messages = []
    
    
# function to update chat
def update_chat(st):
    
    # getting file messages
    file_var = open(st.session_state.file_name, encoding='utf-8')
    file_content = file_var.read()

    file_messages = file_content.split('-end-\n')
    file_messages = [file_messages[i].replace('-end-','') for i in range(len(file_messages))]

    # getting session_messages
    session_messages = st.session_state.messages

    # computing the message difference
    diff = len(file_messages) - len(session_messages)

    # appending the messages to update session state
    new_messages = []
    
    if diff>0:
        for i in range(len(session_messages), len(file_messages)):
            session_messages.append(file_messages[i])
            new_messages.append(file_messages[i])
            
    # displaying new messages
    for message in new_messages:
        print(message)
        with st.chat_message(message["added_by"]):
            st.markdown(message["content"])


def run_chatbot(st):
    
    set_page_title("TGPT")

    # Initialize chat history
    if "messages" not in st.session_state:
        st.session_state.messages = []

    # default chat is New
    if "chat_type" not in st.session_state:
        st.session_state.chat_type = 'New'

    # replace current chat variable
    if "replace_current_chat" not in st.session_state:
        st.session_state.replace_current_chat = False

    # creating file name variable in session_state
    if "file_name" not in st.session_state:
        st.session_state.file_name = ''
        
    # initializing visibility as shared
    if "visibility" not in st.session_state:
        st.session_state.visibility = 'Shared👥'
        

    # sidebar
    with st.sidebar:
        if st.button("Logout", type = "secondary", key = 'logout'):
            st.session_state.clear()  # Clear all session state variables
            st.rerun()

        if st.button("New Chat ➕", type = "primary", key = 'new_chat'):
            st.session_state.messages = []
            st.session_state.file_name = ''
            st.session_state.chat_type = 'New'
            st.session_state.replace_current_chat = False
            st.session_state.visibility = 'Private🔒'
            

        # displaying chat history from history files
        st.header('Chat History')

        history_files = os.listdir('chat_history/')

        # Sort the files based on their creation time in descending order
        complete_files = sorted(history_files, key=lambda x: os.path.getctime(os.path.join('chat_history/', x)),
                              reverse=True)
        
        # removing the username and visibility from chat heading
        usersnames = [f.split('_')[0] for f in complete_files]
        visibility_status = [f.split('_')[1] for f in complete_files]
        files = [f.split('_')[2] for f in complete_files]
        

        # iterating over each history file to add as a button
        for i in range(len(complete_files)):
            
            add_chat = False
            
            # based on user who created file and visibility, deciding if chat should be visible to a user or not
            if (visibility_status[i]=='Private🔒') & (usersnames[i]==st.session_state.username):
                add_chat = True
            elif visibility_status[i]=='Shared👥':
                add_chat = True
                
            if add_chat:
            
                unique_key = f"{i}_{files[i][:-4]}"

                if st.button(files[i][:-4], key=unique_key):
                    # print('Loading : ', chat)

                    st.session_state.replace_current_chat = True
                    st.session_state.chat_type = 'Old'
                    st.session_state.file_name = 'chat_history/' + complete_files[i]
            
            else:
                continue
            


    # replacing current chat with old
    if st.session_state.replace_current_chat:

        st.session_state.messages = []
        st.session_state.visibility = st.session_state.file_name.split('_')[2]

        prev_file = open(st.session_state.file_name, encoding='utf-8')
        file_content = prev_file.read()

        msgs = file_content.split('-end-\n')

        for i in range(len(msgs)):

            msgs[i] = msgs[i].replace('-end-','')
            
            # text format -> user:username:msg (for AI, the user and username are same : assistant)
            # first word is reserved for the user type : user, assistant
            # second word is for username
            # third is text
            
            msg_content = msgs[i].split(':',2) # splitting the text into 3 parts [user, username, msg]
            user_type = msg_content[0]
            from_user = msg_content[1]
            msg_text = msg_content[2]
            
            st.session_state.messages.append({"role": user_type, "added_by" : from_user, "content": msg_text})
            
            
    # creating columns to seperate title and visibility buttons        
    header1, header2 = st.columns(2)        
    
    with header1:
        # Setting title for chat window
        st.markdown("<h4 style='font-family: Arial;'>Team-GPT v0.2</h4>", unsafe_allow_html=True)
       
    
    # Shared-private option through radio button   
    with header2:     
        
        # css for button positioning
        st.markdown(
            """
            <style>
            .stRadio > div {
                flex-direction: row;
                justify-content: flex-end;
            }
            .st-Radio input[type="radio"] + label::before {
                width: 8px;
                height: 8px;
                margin-right: 4px;
            }
            </style>
            """,
            unsafe_allow_html=True
        )
        
        # for the past chats, we just display the visibility status earlier selected
        if st.session_state.chat_type=='Old':
            option = st.radio(label = '', options = (st.session_state.visibility,))
        else:
            option = st.radio(label = '', options = ('Private🔒', 'Shared👥'))
            st.session_state.visibility = option
            
    
    # displaying all the messages
    for message in st.session_state.messages:
        with st.chat_message(message["added_by"]):
            st.markdown(message["content"])


    # user input
    prompt = st.chat_input("Type here...")

    if prompt:

        if st.session_state.chat_type=='New':
            # making sure all the characters are alpha-numeric
            file_name = "".join(c if c.isalnum() or c in {'_', ' ', '.'} else ' ' for c in prompt)
            file_name = " ".join(file_name.split()[:5])
            file_name = file_name.strip()  # removing any trailing spaces

            # naming file after user and its visibility
            file_name = "chat_history/" + st.session_state.username + '_' +  st.session_state.visibility + '_' + file_name + ".txt"    
            
            st.session_state.file_name = file_name


        
        with st.chat_message(st.session_state.username):
            st.markdown(prompt)

        # add user msg to chat history
        st.session_state.messages.append({"role":"user", "added_by":st.session_state.username, "content":prompt})

        # writing prompt to file
        # print("file : ", st.session_state.file_name)
        file = open(st.session_state.file_name, "a", encoding='utf-8')

        if st.session_state.chat_type=='New':
            file.write('user:' + st.session_state.username + ':' + prompt + "-end-")
            st.session_state.chat_type = 'Old'

        elif st.session_state.chat_type=='Old':
            file.write("\n" + 'user:' +  st.session_state.username + ':'  + prompt + "-end-")



        with st.chat_message("assistant"):

            message_placeholder = st.empty()
            full_response = ""


            response = client.chat.completions.create(
                messages = [{"role":m["role"], "content":m["content"]} for m in st.session_state.messages[-10:]],
                model= 'llama-3.1-8b-instant',
                temperature = 1, 
                max_tokens = 3000
            )

            full_response = response.choices[0].message.content

            # sending just last 10 messages with the prompt to create some context to prompt
            # for response in openai.ChatCompletion.create(model = "gpt-3.5-turbo",
            #                                              messages = [
            #                                                  {"role":m["role"], "content":m["content"]} for m in st.session_state.messages[-10:]
            #                                              ], stream = True):
            #     print(response,'\n\n')
            #     full_response += response.choices[0].delta.get("content", "")
            #     message_placeholder.markdown(full_response + "| ")

            message_placeholder.markdown(full_response)
            file.write("\n" + 'assistant:assistant:' + full_response + "-end-")

        st.session_state.messages.append({"role":"assistant", "added_by":"assistant", "content":full_response})
        print(st.session_state.messages,'\n')
        update_chat(st)

if not st.session_state['authenticated']:
    login_page()
        
else:
    run_chatbot(st)