Temporarily disable Streamlit app rerun

Continuing the discussion from Is that a way to block the streamlit app re-run before the previous run finished?:

Is there a way to temporarily disable streamlit app rerun?

I have a function that takes a couple minutes to finish (waiting for OpenAI API). The function is nested in st.spinner, but whenever a user clicks something in the UI while waiting for the function to complete (edit), the app is rerun and the function is triggered again, causing multiple identical requests to OpenAI API.

I have read the attached post but the answer does not solve my use case (try adding the button above the spinner).

It would be nice if st.spinner has an option disable_rerun, or am I missing something? :slight_smile:

Hello,
Unfortunately, in my knowledge there isn’t a built-in way to completely disable Streamlit’s automatic reruns. However, we can use session state to control when the function should run and prevent multiple identical requests.

Are you using a st.session_state ? Session State - Streamlit Docs

You can add a st.button to trigger it manually.

        def YOURAPIREQUEST():
            # Simulate a long-running process
            time.sleep(10)
            return "Function completed!"

        # Initialize session state
        if 'function_running' not in st.session_state:
            st.session_state.function_running = False
        if 'result' not in st.session_state:
            st.session_state.result = None

        # Button to trigger the function
        if st.button("Run Function"):
            st.session_state.function_running = True

        # Display the spinner and run the function only when needed
        if st.session_state.function_running:
            with st.spinner("Function is running..."):
                st.session_state.result = YOURAPIREQUEST()
                st.session_state.function_running = False

        # Display the result
        if st.session_state.result:
            st.write(st.session_state.result)

Yes, I am using session state. I had the same idea, but it does not work because the function is triggered again for every rerun. If you try the following code sample, you will see that the counter starts again for every random user action.

import time

import streamlit as st

def YOURAPIREQUEST():
    # Simulate a long-running process
    for i in range(10):
        i = st.write(i)
        time.sleep(1)
    return "Function completed!"

# Initialize session state
if 'function_running' not in st.session_state:
    st.session_state.function_running = False
if 'result' not in st.session_state:
    st.session_state.result = None
    
# Button to trigger the function
if st.button("Run Function"):
    st.session_state.function_running = True

# Random user action
if st.button("Random user action"):
    st.write("Random user action")
    
# Display the spinner and run the function only when needed
if st.session_state.function_running:
    with st.spinner("Function is running..."):
        st.session_state.result = YOURAPIREQUEST()
        st.session_state.function_running = False

# Display the result
if st.session_state.result:
    st.write(st.session_state.result)

I cannot find a way to avoid retriggering the long function but still register its output, something like:

if not st.session_state.function_running:
    st.session_state.result = YOURAPIREQUEST()
else:
    # wait for the original request

My bad,


# Initialize session state
if 'function_running' not in st.session_state:
    st.session_state.function_running = False

Should be only :

st.session_state.function_running = False

To set the session_state False until you trigger the button :slight_smile:

If you do that, you are right you will not retrigger YOURAPIREQUEST at a random user action, but the spinner disappears and the function does not continue (at least in the discussed example case)

Are you trying to achieve this ? request Api once, and got the api store in the session_state.

Havetest - Google Chrome 2024-07-18 13-44-20

No, pressing the second button while the spinner is spinning instead of after :slight_smile:

I see that was not clear from my initial post. Edited it.

The idea is:

  • The user sends a request to openai (long natural language task, like report generation based on a dataframe). The request takes a couple minutes.
  • While waiting for the response, the user either
    • cannot press anything in the UI to not mess with this request,
    • the user can take actions in the UI while awaiting the response without retriggering the request (what we have been discussing)
    • or the user can explore other elements of the UI (expanders, data editors), but none of their actions trigger an app rerun until the response is retrieved

So, it would be nice if st.spinner has an option disable_rerun!

I understand it a bit better !
The best i can do if it’s nested in a button it freeze until it’s finish:

if st.button('Run Function'):
    if st.session_state.result is not None:
        with st.spinner("Function is running..."):
            st.session_state.result = YOURAPIREQUEST()
            st.success

Hope it’s working for you !

Will never enter the function with the way you initialize the session state ofcourse. Even if it did, I don’t see how it would work as st.session_state.result is only assigned when YOURAPIREQUEST() has finished.

But thanks for thinking along!

You need to add

Initialize session state

if ‘result’ not in st.session_state:
st.session_state.result = None