Streamlit Spinner and Chat Message odd interaction

I’m testing out Streamlit’s chatbot interface. I wanted to incorporate the Streamlit spinner. This is the code block that gets a pre-generated chart from a getChart function. The charts are randomly selected in the function.

# Accept user input
if prompt := st.chat_input("Enter your question here"):
    # Add user message to chat history
    st.session_state.messages[dataset.id].append({"role": "user", "content": prompt})
    # Display user message in chat message container
    with st.chat_message("user"):
        st.markdown(prompt)

    # Display assistant response in chat message container
    with st.chat_message("assistant"), st.spinner("Answering your question..."):
        data = getChart()
        st.write(data)
    # Add assistant response to chat history
    st.session_state.messages[dataset.id].append({"role": "assistant", "content": data})

When I enter in a second prompt, I get a weird effect when the second image is loading in. It looks like this:

The after image disappears after the new chart is selected.
Any ideas why this is happening?

Thanks!

The faded element is what we call “stale.” Streamlit removes stale elements when it knows they won’t be re-rendered. Streamilt keeps track of elements by the order they appear on the page.

Consider this very simplified example. If one script run renders:

  1. A title
  2. A header
  3. A chart
  4. A button

And the next script run renders:

  1. A title
  2. A chart
  3. A button

Streamlit will briefly have two charts at step 2 of the rerun (when it replaces the header with the new chart). Then, when the button renders, it replaces the chart from the previous run. Finally when the script run ends, the fourth element (the button from the first run) gets removed because Streamlit knows it’s the end and there are no more elements.

In your case, the spinner is replaced by the chart on the rerun and Streamlit won’t know to remove the stale chart until it “gets to the end.”

You can fix this by using empty. Something like this:

with st.chat_message("agent"), st.empty():
    with st.spinner("Getting chart..."):
        data = getChart()
    st.write(data)

Here’s a toy example to compare the two. If you need the message from the assistant to have more than one Streamlit element, you can insert a container after the empty so that the container gets swapped out instead of the single chart element.

import streamlit as st
import time
import random

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

add = st.button("Add")

def getChart():
    time.sleep(5)
    return random.choice(range(10))

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

if add:
    with st.chat_message("assistant"), st.empty():
        with st.spinner("Getting chart..."):
            data = getChart()
        st.write(data)
        st.session_state.history.append({"role":"assistant", "content":data})

# if add:
#     with st.chat_message("assistant"), st.spinner("Getting chart..."):
#         data = getChart()
#         st.write(data)
#         st.session_state.history.append({"role":"assistant", "content":data})

This topic was automatically closed 2 days after the last reply. New replies are no longer allowed.