I am using streamlit app to build a csv chatbot along with langchain csv agent and chatgpt api. The chatbot is able to response with text, table, plots per user’s input question. But plots from old question in conversation history will disappear when users ask a new question. This only happens to plots/graph. Text or table can still exist. I wonder if it is related to st.seesion_state() I used.
Steps to reproduce
Code snippet:
from langchain.agents import AgentType
from langchain.agents import create_pandas_dataframe_agent
from langchain.callbacks import StreamlitCallbackHandler
from langchain.llms.openai import OpenAI
import streamlit as st
import pandas as pd
import matplotlib.pyplot as plt
from langchain.tools.python.tool import PythonREPLTool
import os
df = pd.read_cvs('https://raw.githubusercontent.com/a-mt/fcc-medical-data-visualizer/master/medical_examination.csv')
if "messages" not in st.session_state or st.sidebar.button("Clear conversation history"):
st.session_state["messages"] = [{"role": "assistant", "content": "How can I help you?"}]
for msg in st.session_state.messages:
st.chat_message(msg["role"]).write(msg["content"])
if prompt := st.chat_input(placeholder="What is this data about?"):
st.session_state.messages.append({"role": "user", "content": prompt})
st.chat_message("user").write(prompt)
llm = OpenAI(openai_api_key="YOUR_API_KEY")
pandas_df_agent = create_pandas_dataframe_agent(
llm,
df,
verbose=True,
agent_type=AgentType.OPENAI_FUNCTIONS,
handle_parsing_errors=True,
)
with st.chat_message("assistant"):
st_cb = StreamlitCallbackHandler(st.container(), expand_new_thoughts=True)
response = pandas_df_agent.run(st.session_state.messages, callbacks=[st_cb])
st.session_state.messages.append({"role": "assistant", "content": response})
st.write(response)
fig = plt.gcf()
if fig:
st.write(fig)
If applicable, please provide the steps we should take to reproduce the error or specified behavior.
Expected behavior:
All plots in conversation history will stay.
Actual behavior:
say question 1 "to plot distribution of age’, it will show a figure of age distribution. Then ask another question like ‘how many rows and columns are there’. It will return the number of rows and columns but the previous plot of age distribution is gone.
The issue you’re facing is maybe because streamlit is clearing your plots. So you have to save the history somewherel
You can do the following:
Create a list to store the plots and their associated messages.
Check if the message is from the assistant and contains a plot (i.e., a fig is not None). If so, append the message and the associated plot to your list.
When displaying the conversation history, iterate through this list and show the plots along with their corresponding messages.
You can try this code
import streamlit as st
import pandas as pd
import matplotlib.pyplot as plt
# Define a list to store the conversation history including plots
conversation_history = []
if "messages" not in st.session_state or st.sidebar.button("Clear conversation history"):
st.session_state["messages"] = [{"role": "assistant", "content": "How can I help you?"}]
for msg in st.session_state["messages"]:
st.chat_message(msg["role"]).write(msg["content"])
if prompt := st.chat_input(placeholder="What is this data about?"):
st.session_state["messages"].append({"role": "user", "content": prompt})
st.chat_message("user").write(prompt)
# Assuming your response generates a plot
response_message, fig = generate_response(prompt) # Modify this part accordingly
if fig:
conversation_history.append((response_message, fig))
# Display the conversation history with plots
for msg, fig in conversation_history:
st.chat_message("assistant").write(msg)
st.write(fig)
Histories (including human and ai messages) contain only strings unless the assistants API is used. So, what I do is to add a simple message containing the fig information as an ai message while maintaining the list of fig objects separately. This is not very tidy, but works. Here is what I do.
fig = plt.gcf()
if fig and fig.get_axes():
fig_index = len(st.session_state.fig)
st.session_state.message_history.add_ai_message(
f"Figure {fig_index + 1} generated by AI."
)
st.session_state.fig.append(fig)
...
for msg in st.session_state.message_history.messagees:
...
if re.match(
r"^Figure \d+ generated by AI\.$", msg.content
): # Check to see if the message points to a figure object
fig_number = re.search(r'\bFigure (\d+)\b', msg.content).group(1)
st.pyplot(st.session_state.fig[int(fig_number) - 1])
...
In the above, ‘fig_index’ is maintained appropriately such that the message containing the fig information is connected to the right figure.
Hope this helps. Just for your reference, my app using this method of keeping figures is located at https://langchain-llm-agent.streamlit.app/. Another app utilizing the Assistants API (eliminating the need to maintain a separate list) can be found at https://assistants.streamlit.app/. These scripts are both single long files that are very untidy. Any advice on better ways of splitting these into multiple files in a systematic way would be appreciated.
Hello. I have come up with a better way of keeping images as part of a chat message, which is to add ‘additional_kwargs’ to the chat message as follows:
Figure objects generated by matplotlib are converted to base64 encoded images and then placed into the (list) value of the item ‘image_urls’. My code introduced above has now been changed using this method.
Thanks for stopping by! We use cookies to help us understand how you interact with our website.
By clicking “Accept all”, you consent to our use of cookies. For more information, please see our privacy policy.
Cookie settings
Strictly necessary cookies
These cookies are necessary for the website to function and cannot be switched off. They are usually only set in response to actions made by you which amount to a request for services, such as setting your privacy preferences, logging in or filling in forms.
Performance cookies
These cookies allow us to count visits and traffic sources so we can measure and improve the performance of our site. They help us understand how visitors move around the site and which pages are most frequently visited.
Functional cookies
These cookies are used to record your choices and settings, maintain your preferences over time and recognize you when you return to our website. These cookies help us to personalize our content for you and remember your preferences.
Targeting cookies
These cookies may be deployed to our site by our advertising partners to build a profile of your interest and provide you with content that is relevant to you, including showing you relevant ads on other websites.