How to have chat before query not after?

Currently my app has UI elements in the following order:

  1. Query
  2. Agent’s thoughts
  3. Chat history
  4. Current dataframe

The problem with such a structure is that if a user asks a lot of questions, the chat history becomes too long, and to see each new answer, the user needs to scroll down and then go back to query. Is there a way to reorder it as:

  1. Chat history
  2. Query
  3. Agent’s thoughts
  4. Current dataframe

I tried to do it on my own, but I am having a bug. When I provide my first query, no chat is shown. When I provide a second query, chat appears with the first query. If I provide a third chat, it appears with the first and second queries, and so on. I’m not sure how to fix it. My code:

if not user_api_key:
    layout.show_api_key_missing()
else:
    st.session_state.setdefault("reset_chat", False)

    uploaded_file = utils.handle_upload(["csv", "xlsx"])

    if uploaded_file:
        sidebar.about_()
        
        uploaded_file_content = BytesIO(uploaded_file.getvalue())
        if uploaded_file.type == "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" or uploaded_file.type == "application/vnd.ms-excel":
            df = pd.read_excel(uploaded_file_content)
        else:
            df = pd.read_csv(uploaded_file_content)

        st.session_state.df = df

        if "chat_history" not in st.session_state:
            st.session_state["chat_history"] = []
        csv_agent = PandasAgent()

        form_submitted = False

        if st.session_state.df is not None:
            if form_submitted:
                result, captured_output = csv_agent.get_agent_response(df, query)
                cleaned_thoughts = csv_agent.process_agent_thoughts(captured_output)
                csv_agent.display_agent_thoughts(cleaned_thoughts)
                csv_agent.update_chat_history(query, result)
                csv_agent.display_chat_history()

        csv_agent.display_chat_history()

        with st.form(key="query"):
            query = st.text_input("", value="", type="default", 
                placeholder="e-g : How many rows ? "
                )
            submitted_query = st.form_submit_button("Submit")
            reset_chat_button = st.form_submit_button("Reset Chat")
            if reset_chat_button:
                st.session_state["chat_history"] = []
            if submitted_query:
                form_submitted = True

        if form_submitted:
            result, captured_output = csv_agent.get_agent_response(df, query)
            cleaned_thoughts = csv_agent.process_agent_thoughts(captured_output)
            csv_agent.display_agent_thoughts(cleaned_thoughts)
            csv_agent.update_chat_history(query, result)
            # csv_agent.display_chat_history()

        if st.session_state.df is not None:
            st.subheader("Current dataframe:")
            st.write(st.session_state.df)

where PandasAgent is:

class PandasAgent :

    @staticmethod
    def count_tokens_agent(agent, query):
        """
        Count the tokens used by the CSV Agent
        """

        from langchain.callbacks import get_openai_callback

        with get_openai_callback() as cb:
            result = agent(query)
            st.write(f'Spent a total of {cb.total_tokens} tokens')

        return result
    
    def __init__(self):
        pass

    def get_agent_response(self, uploaded_file_content, query):
        
        from pandasai import PandasAI
        # from pandasai.llm.openai import OpenAI
        from pandasai.llm.azure_openai import AzureOpenAI

        llm = AzureOpenAI(
                            api_token = os.environ["OPENAI_API_KEY"], 
                            api_base = os.environ["OPENAI_API_BASE"], 
                            deployment_name = os.environ["OPENAI_API_MODEL_NAME"], # os.environ["OPENAI_API_DEPLOYMENT_NAME"],
                            model_name = os.environ["OPENAI_API_MODEL_NAME"]
                            )
        pandas_ai = PandasAI(llm, verbose=True)
        old_stdout = sys.stdout
        sys.stdout = captured_output = StringIO()
        
        response = pandas_ai.run(data_frame = uploaded_file_content, prompt=query)
        fig = plt.gcf()
        if fig.get_axes():
                    # Adjust the figure size
            fig.set_size_inches(12, 6)

            # Adjust the layout tightness
            plt.tight_layout()
            buf = BytesIO()
            fig.savefig(buf, format="png")
            buf.seek(0)
            st.image(buf, caption="Generated Plot")
        
        sys.stdout = old_stdout
        return response, captured_output

    def process_agent_thoughts(self,captured_output):
        thoughts = captured_output.getvalue()
        cleaned_thoughts = re.sub(r'\x1b\[[0-9;]*[a-zA-Z]', '', thoughts)
        cleaned_thoughts = re.sub(r'\[1m>', '', cleaned_thoughts)
        return cleaned_thoughts

    def display_agent_thoughts(self,cleaned_thoughts):
        with st.expander("Display the agent's thoughts"):
            st.write(cleaned_thoughts)

    def update_chat_history(self,query, result):
        st.session_state.chat_history.append(("user", query))
        st.session_state.chat_history.append(("agent", result))

    def display_chat_history(self, ):
        for i, (sender, message_text) in enumerate(st.session_state.chat_history):
            if sender == "user":
                message(message_text, is_user=True, key=f"{i}_user", avatar_style="thumbs")
            else:
                message(message_text, key=f"{i}")

1 Like

Hi @al-yakubovich,

Thanks for your question!

So I haven’t tried your code, but it seems that the form_submitted flag should be saved in the session_state so that it persists across runs of the script.

Hopefully, this amended code should resolve the issue:

if not st.session_state.get('form_submitted'):
    st.session_state['form_submitted'] = False

with st.form(key="query"):
    query = st.text_input("", value="", type="default",
                         placeholder="e.g.: How many rows?")
    submitted_query = st.form_submit_button("Submit")
    reset_chat_button = st.form_submit_button("Reset Chat")
    if reset_chat_button:
        st.session_state["chat_history"] = []
    if submitted_query:
        st.session_state['form_submitted'] = True
        st.session_state['current_query'] = query

if st.session_state['form_submitted']:
    result, captured_output = csv_agent.get_agent_response(df, st.session_state['current_query'])
    cleaned_thoughts = csv_agent.process_agent_thoughts(captured_output)
    csv_agent.display_agent_thoughts(cleaned_thoughts)
    csv_agent.update_chat_history(st.session_state['current_query'], result)
    # Reset form_submitted state to False for the next query
    st.session_state['form_submitted'] = False

Please let me know if that helps.

Best,
Charly

Hi @Charly_Wargnier
Thank you for your comment.

I was trying to use your code but I am getting exactly the same behavior from the app as before.

It looks like all those form_submitted flags do not work for some reason. Maybe a more straightforward code snippet brings more clarity to my code and what I try to accomplish:


if not user_api_key:
    layout.show_api_key_missing()
else:
    st.session_state.setdefault("reset_chat", False)

    uploaded_file = utils.handle_upload(["csv", "xlsx"])

    if uploaded_file:
        sidebar.about_()
        
        uploaded_file_content = BytesIO(uploaded_file.getvalue())
        if uploaded_file.type == "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" or uploaded_file.type == "application/vnd.ms-excel":
            df = pd.read_excel(uploaded_file_content)
        else:
            df = pd.read_csv(uploaded_file_content)

        st.session_state.df = df

        if "chat_history" not in st.session_state:
            st.session_state["chat_history"] = []
        csv_agent = PandasAgent()

        with st.form(key="query"):
            query = st.text_input("", value="", type="default", 
                placeholder="e-g : How many rows ? "
                )
            submitted_query = st.form_submit_button("Submit")
            reset_chat_button = st.form_submit_button("Reset Chat")
            if reset_chat_button:
                st.session_state["chat_history"] = []
        if submitted_query:
            result, captured_output = csv_agent.get_agent_response(df, query)
            cleaned_thoughts = csv_agent.process_agent_thoughts(captured_output)
            csv_agent.display_agent_thoughts(cleaned_thoughts)
            csv_agent.update_chat_history(query, result)
            csv_agent.display_chat_history()
        if st.session_state.df is not None:
            st.subheader("Current dataframe:")
            st.write(st.session_state.df)

In this code, chat is always below the query, and I am looking for a way to have chat above the query. Another option where chat is “inverse” (new results show on top of chat) would also work, but it looks like there is no such functionality for a chat element.

Thanks for your prompt feedback, @al-yakubovich.

Maybe placing the csv_agent.display_chat_history() line before the st.form block will display the chat history above the query field?

I would need the full code to experiment. Could you please share it, along with the CSV examples you used in the code snippets, and I can take another look :slight_smile:

Thanks,
Charly

Hi @Charly_Wargnier,

I was tring to put csv_agent.display_chat_history() but chat appears above chat only after second query with delay on one query:


…

…

The full code is located here: Robby-chatbot/src/pages/2_📊 Robby-Sheet (beta).py at main · yvann-hub/Robby-chatbot · GitHub

Let me know if GitHub is ok for you or I need to share code here.

Thank you!

Thanks for your quick feedback!

Actually, I haven’t tried it yet, but you can try wrapping the necessary code snippet in an st.container():

https://docs.streamlit.io/library/api-reference/layout/st.container.

If that doesn’t work right now and you can wait a bit longer, we’re about to release something that should give you the flexibility you’re looking for! :slight_smile:

And, if you need a solution urgently, let me know, and we’ll dig deeper into your query to find an acceptable workaround!

Best wishes,
Charly

I have the same interest in interspersing dataframes in a chat conversation. I’ve created a framework which should lend itself to doing so. ChatGPT Style Example SOTA

1 Like