mkhorasani / Streamlit-Authenticator

A secure authentication module to validate user credentials in a Streamlit application.
Apache License 2.0
1.38k stars 229 forks source link

Authentication is causing chat functionality to fail on the streamlit cloud #99

Open sjondavey opened 7 months ago

sjondavey commented 7 months ago

I created a minimum wrapper for the openai chat completion api. It runs locally and on the Streamlit cloud without issue. When I add authentication, the local version still works fine but if I deploy to the streamlit community cloud, the chat stops working after one or two user inputs. Below is the code that includes the authentication.

import openai
import streamlit as st
import streamlit_authenticator as stauth

st.title("ChatGPT-like clone")

openai.api_key = st.secrets["OPENAI_API_KEY"]

authenticator = stauth.Authenticate(
    dict(st.secrets['credentials']),
    st.secrets['cookie']['name'],
    st.secrets['cookie']['key'],
    st.secrets['cookie']['expiry_days'],
    st.secrets['preauthorized']
)

name, authentication_status, username = authenticator.login('Login', 'sidebar') # location is 'sidebar' or 'main'

if authentication_status:

    if "openai_model" not in st.session_state:
        st.session_state["openai_model"] = "gpt-3.5-turbo"

    if "messages" not in st.session_state:
        st.session_state.messages = []

    for message in st.session_state.messages:
        with st.chat_message(message["role"]):
            st.markdown(message["content"])

    if prompt := st.chat_input("What is up?"):
        st.session_state.messages.append({"role": "user", "content": prompt})
        with st.chat_message("user"):
            st.markdown(prompt)

        with st.chat_message("assistant"):
            message_placeholder = st.empty()
            full_response = ""
            response = openai.chat.completions.create(
                model=st.session_state["openai_model"],
                messages=st.session_state.messages,
                stream=True,
            )
            for part in response:
                full_response += (part.choices[0].delta.content or "")
                message_placeholder.markdown(full_response + "▌")
            message_placeholder.markdown(full_response)
        st.session_state.messages.append({"role": "assistant", "content": full_response})

elif authentication_status == False:
    st.error('Username/password is incorrect')
elif authentication_status == None:
    st.warning('Please enter your username and password')

If I comment out the authentication, I can deploy to the Streamlit cloud and it works fine. See below

import openai
import streamlit as st
import streamlit_authenticator as stauth

st.title("ChatGPT-like clone")

openai.api_key = st.secrets["OPENAI_API_KEY"]

# authenticator = stauth.Authenticate(
#     dict(st.secrets['credentials']),
#     st.secrets['cookie']['name'],
#     st.secrets['cookie']['key'],
#     st.secrets['cookie']['expiry_days'],
#     st.secrets['preauthorized']
# )

# name, authentication_status, username = authenticator.login('Login', 'sidebar') # location is 'sidebar' or 'main'

# if authentication_status:

if "openai_model" not in st.session_state:
    st.session_state["openai_model"] = "gpt-3.5-turbo"

if "messages" not in st.session_state:
    st.session_state.messages = []

for message in st.session_state.messages:
    with st.chat_message(message["role"]):
        st.markdown(message["content"])

if prompt := st.chat_input("What is up?"):
    st.session_state.messages.append({"role": "user", "content": prompt})
    with st.chat_message("user"):
        st.markdown(prompt)

    with st.chat_message("assistant"):
        message_placeholder = st.empty()
        full_response = ""
        response = openai.chat.completions.create(
            model=st.session_state["openai_model"],
            messages=st.session_state.messages,
            stream=True,
        )
        for part in response:
            full_response += (part.choices[0].delta.content or "")
            message_placeholder.markdown(full_response + "▌")
        message_placeholder.markdown(full_response)
    st.session_state.messages.append({"role": "assistant", "content": full_response})

# elif authentication_status == False:
#     st.error('Username/password is incorrect')
# elif authentication_status == None:
#     st.warning('Please enter your username and password')

I have tried a few variations. As soon as the code

# authenticator = stauth.Authenticate(
#     dict(st.secrets['credentials']),
#     st.secrets['cookie']['name'],
#     st.secrets['cookie']['key'],
#     st.secrets['cookie']['expiry_days'],
#     st.secrets['preauthorized']
# )

is uncommented (even if I don't check the authentication status), chat stops working after one or two user inputs

Jerry-Master commented 7 months ago

The problem is in the cookie manager, I was having similar issue and commented all the cookie logic of the library and it worked. The problem now is, well, you dont have the cookie management.

sjondavey commented 7 months ago

Thanks @Jerry-Master , I raised this with the Extra-Streamlit-Components project here who seem to handle the cookie library. I hope we can get this resolved because having working authentication really helps to demo working prototypes.

broepke commented 5 months ago

I'm having the same issues - glad i'm not going nuts trying to figure out the root cause!

mkhorasani commented 5 months ago

Hello everyone, apologies for the inconvenience this issue is causing, I promise to delve deeper into this issue.

broepke commented 5 months ago

Thank you @mkhorasani for the great work! we appreciate it!

Zephyr69 commented 2 months ago

I'm seemingly having the same issue and I'm glad it is being investigated. I will just put out observations of mine: This issue seems to be related specifically to using "Markdown" to update the chat messages. If "Markdown" is used, the stauth will cause the input prompt to only work every other time (e.g. 1st prompt: OK; 2nd prompt: nothing happens; 3rd prompt: OK; 4th prompt: nothing happens...). If we use .ai_say to update the message only by text strings, everything is fine (but passing Markdown as one of the elements to be updated by .ai_say IS NOT FINE); but we lose a lot of functionalities of the chatbox which would be unacceptable.

Update: I tried commenting out all cookie functionalities, and it does resolve the problem. It seems the cookie function is causing the page to rerun multiple times at every prompt input, regardless of the authentication status, which breaks the functionality of some st elements.