I run app in local. I use st.chat_input in chat page. My app has multi page and I use st.navigation to switch page. But when I click to Chat page and then move to other page, it get stuck in Chat page. if I want to move to other page, I have to double click to that page. If I remove st.chat_input, everything is ok.
For testing, I use function test()
, this function is ok. So I think the error is from using navigation and multi pages.
Lib version:
streamlit==1.36.0
python=3.11
My code:
import re
from openai import OpenAI
import streamlit as st
import django
import sys
import os
import json
import extra_streamlit_components as stx
project_path = os.path.dirname(os.path.abspath(__file__))
sys.path.append(project_path)
# Set the DJANGO_SETTINGS_MODULE environment variable
os.environ['DJANGO_SETTINGS_MODULE'] = 'ichallenge.settings'
django.setup()
from project_user.models import ProjectUser
from chatbot.models import Roleplay, GPTConfig, UserRolePlay, ChatHistory, Conversation
from ichallenge.settings import OPENAI_API_KEY
st.title("E-Challenge 2024 Assistant")
# Initialize OpenAI client
client = OpenAI(api_key=OPENAI_API_KEY)
# Get system config
system_config = GPTConfig.objects.first()
# Initialize session state variables
def get_manager():
return stx.CookieManager()
cookie_manager = get_manager()
def get_user(cookie):
if not cookie:
return False
email, password = cookie['email'], cookie['password']
user = ProjectUser.objects.filter(email=email).first()
if not user:
return False
elif user.check_password(password):
st.session_state.login = True
st.session_state.user = user
if current_roleplay := UserRolePlay.get_current_roleplay(user):
st.session_state.system_prompt = current_roleplay.prompt
else:
return False
def init_state():
get_user(cookie_manager.get('user'))
if "login" not in st.session_state:
st.session_state.login = False
if "user" not in st.session_state:
st.session_state.user = None
if st.session_state.login:
st.session_state.user = ProjectUser.objects.get(
pk=st.session_state.user.id)
if "messages" not in st.session_state:
st.session_state.messages = []
if "openai_model" not in st.session_state:
st.session_state.openai_model = system_config.model if system_config.model else 'gpt-3.5-turbo'
if "system_prompt" not in st.session_state:
st.session_state.system_prompt = Roleplay.objects.filter(
default=True).first().prompt
if "conversation" not in st.session_state:
st.session_state.conversation = None
# Set CSS config
st.markdown(
"""
<style>
.st-emotion-cache-janbn0 {
flex-direction: row-reverse;
text-align: right;
}
</style>
""",
unsafe_allow_html=True,
)
def login():
st.header("Log in")
email = st.text_input("Email")
password = st.text_input("Password", type="password")
if st.button("Log in"):
user = ProjectUser.objects.filter(email=email).first()
if not user:
st.error("Email is not found!")
elif user.check_password(password):
st.success("Logged in successfully!")
cookie_manager.set('user', json.dumps(dict(email=user.email,
password=password)), key='user_info')
st.session_state.login = True
st.session_state.user = user
if current_roleplay := UserRolePlay.get_current_roleplay(user):
st.session_state.system_prompt = current_roleplay.prompt
st.rerun()
else:
st.error("Incorrect password!")
def logout():
st.header("Log out")
if st.button("Log out"):
st.session_state.login = False
st.session_state.user = None
st.success("Logged out successfully!")
cookie_manager.delete('user')
st.rerun()
def chat():
st.title('Chat')
prompt = st.chat_input("Say something")
if prompt:
conversation = st.session_state.conversation
if not conversation:
conversation = Conversation.create_new(user=st.session_state.user)
else:
conversation = Conversation.objects.get(pk=conversation.id)
st.session_state.conversation = conversation
system_message = {"role": "system",
"content": st.session_state.system_prompt}
st.session_state.messages.append(system_message)
ChatHistory.save_history(
user=st.session_state.user, content=system_message, conversation=conversation)
user_message = {"role": "user", "content": prompt}
st.session_state.messages.append(user_message)
ChatHistory.save_history(
user=st.session_state.user, content=user_message, conversation=conversation)
with st.chat_message("user"):
st.markdown(prompt)
with st.chat_message("assistant"):
stream = client.chat.completions.create(
model=st.session_state["openai_model"],
messages=[
{"role": m["role"], "content": m["content"]}
for m in st.session_state.messages
],
stream=True,
)
response = st.write_stream(stream)
assistant_message = {"role": "assistant", "content": response}
st.session_state.messages.append(assistant_message)
ChatHistory.save_history(
user=st.session_state.user, content=assistant_message, conversation=conversation)
if not conversation.name:
conversation.name = create_title_for_conversation(prompt)
conversation.save()
def roleplay():
st.title("Roleplays")
roleplays = Roleplay.objects.filter(
status='public').prefetch_related('conversation_starters').all()
data = {item.name: item for item in roleplays}
selected_item = st.selectbox("Choose one: ", options=list(data.keys()))
if selected_item:
st.write("Conversation starters:")
conversation_starters = data[selected_item].conversation_starters.all()
for con in conversation_starters:
st.markdown(
f"""
<div style="
padding: 10px;
border: 1px solid #ddd;
border-radius: 5px;
margin-top: 0px;
margin-bottom: 10px;
">
{con.content}
</div>
""",
unsafe_allow_html=True
)
if st.button("Save"):
st.success(f"Activated roleplay {selected_item}")
st.session_state.system_prompt = data[selected_item].prompt
UserRolePlay.activate_roleplay(
user=st.session_state.user, roleplay=data[selected_item])
def history():
"""
Show history of a conversation and let user chat on this conversation
"""
conversation_uuid = extract_uuid(st.session_state.current_page)
if conversation_uuid:
conversation = Conversation.objects.filter(
uuid=conversation_uuid).first()
if conversation:
st.session_state.conversation = conversation
messages = ChatHistory.objects.filter(
user=st.session_state.user, conversation=conversation
).order_by('created_at')
st.session_state.messages = []
for item in messages:
if item.content["role"] != "system":
with st.chat_message(item.content["role"]):
st.markdown(item.content["content"])
st.session_state.messages.append(item.content)
#chat()
def show_conversations():
conversations = Conversation.list_all(user=st.session_state.user)
pages = []
for con in conversations:
pages.append(st.Page(history, title=con.name,
url_path=f"history/?conversation={con.uuid}"))
return pages
def create_title_for_conversation(first_question):
print('create_title_for_conversation')
prompt = f"""create short name for a conversation based on input, just return the result.
Do not use double quote.
Style: formal.
Input is: {first_question}
"""
response = client.chat.completions.create(
model=st.session_state["openai_model"],
messages=[
{"role": "user", "content": prompt}
]
)
return response.choices[0].message.content
def extract_uuid(url):
# Define the regex pattern for UUID
pattern = r'conversation=([a-f0-9\-]+)'
# Search for the pattern in the URL
match = re.search(pattern, url)
# If a match is found, return the UUID, otherwise return None
if match:
return match.group(1)
else:
return None
def main():
init_state()
logout_page = st.Page(logout, title="Log out", icon=":material/logout:")
chat_page = st.Page(test, title="New conversation", icon=":material/chat:", url_path='chat')
roleplay_page = st.Page(roleplay, title="Roleplays")
account_pages = [logout_page]
chat_pages = [chat_page, roleplay_page]
if st.session_state.login:
conversations = show_conversations()
page_dict = {
"Chat": chat_pages,
"History": conversations
}
else:
page_dict = {}
if page_dict:
pg = st.navigation({"Account": account_pages} | page_dict)
else:
pg = st.navigation([st.Page(login, title='Login')])
st.session_state.current_page = pg.url_path
print('Path: ', st.session_state.current_page)
if st.session_state.current_page == 'chat':
st.session_state.conversation = None
pg.run()
def test():
st.title("Echo Bot")
# Initialize chat history
if "messages" not in st.session_state:
st.session_state.messages = []
# Display chat messages from history on app rerun
for message in st.session_state.messages:
with st.chat_message(message["role"]):
st.markdown(message["content"])
# React to user input
if prompt := st.chat_input("What is up?"):
# Display user message in chat message container
st.chat_message("user").markdown(prompt)
# Add user message to chat history
st.session_state.messages.append({"role": "user", "content": prompt})
response = f"Echo: {prompt}"
# Display assistant response in chat message container
with st.chat_message("assistant"):
st.markdown(response)
# Add assistant response to chat history
st.session_state.messages.append(
{"role": "assistant", "content": response})
if __name__ == "__main__":
main()