Summary
I have Streamlit UI with a FastAPI backend.
Streamlit is used to add long-running jobs for the backend and keep track of their progress live.
The backend implements a background task that picks new tasks from a queue, processes them, and reports progress back via Server-Sent Events (SSE) by pushing โupdateโ messages every time there is progress. The update messages is supposed to trigger the Streamlit app to re-run and query the backend for the job status:
# <Task-making UI that submits the new jobs to the backend>
@st.cache_data(ttl=30, show_spinner=True)
def get_job_status() -> List[RecJob]:
return # <fetches a list of jobs with their status from the server>
# <Progress displaying UI that displays the jobs in different state of progress>
# Wait for an "update" message.
# Clear the cache for get_job_status
# Trigger a re-run
from sseclient import SSEClient
print("Listening for updates...")
if "messages" in st.session_state:
print("Closing old connection")
st.session_state["messages"].resp.close()
st.session_state["messages"] = SSEClient(env.api_base + "/heartbeat", headers={"Authorization": f"Bearer {env.api_token}"})
for msg in st.session_state["messages"]:
if not st.session_state:
print("The client disconnected")
break
if msg.event == "update":
print(msg.dump())
print("Reloading page")
get_job_status.clear()
# jobs_records = get_job_status()
st.experimental_rerun()
This almost works as it should, the reload on the update events is consistent and smooth.
The problem is, any other action results in a re-render and Iโm getting some random ghosted elements after it:
Am I missing some small details, or am I just trying to use Streamlit in a way that is fundamentally wrong? I understand that the way I do it, the script does not reach the end before the next re-run starts, and I guess thatโs the reason Streamlit behaves weirdly.
Is there a better way to implement what Iโm trying to achieve here?
Debug info
- Streamlit version: 1.25.0
- OS version: Ubuntu 22.04
- Browser version: Fresh Google Chrome