Is Asynchronous Chat Possible in Streamlit?

I am trying to write an asynchronous chat client in Streamlit. Synchronous chat clients (where prompts and responses occur sequentially on the same thread) are of course very easy to write. However, I have not been able to write an asynchronous one.

I am writing a multi-user chat application where clients can run in separate processes. I am using the NATS publish/subscribe framework for the backend. NATS uses asyncio. A client is notified when when a subject that it has subscribed to has new messages and displays them. The client can also publish messages entered in the GUI to the system.

I have not been able to write a Streamlit app that handles both receiving and sending. The best I’ve been able to do is write the receiving part.

import asyncio

import streamlit as st
from nats.aio.client import Client

async def main():
    st.set_page_config(page_title="NATS Listener")
    st.title("NATS Listener")
    chat_container = st.container(height=300)
    while True:
        for message in await subject_messages():
            with chat_container.chat_message("user"):
                st.write(message.data.decode())


async def subject_messages():
    client = Client()
    await client.connect()
    subscription = await client.subscribe("chat", max_msgs=1)
    messages = []
    async for msg in subscription.messages:
        messages.append(msg)
    return messages


if __name__ == "__main__":
    loop = asyncio.new_event_loop()
    asyncio.set_event_loop(loop)
    try:
        asyncio.run(main())
    except KeyboardInterrupt:
        pass
    finally:
        print("Closing Loop")
        loop.close()

If you launch this and then in a separate process use a NATS command line client to publish messages to the “chat” subject, you will see the following.

So far so good. Now I want to add a chat_input element for sending messages. The problem is that for this element to always be active it has to go inside the while True loop in main, which causes multiple instances to be created. I can’t figure out how to get around this.

I think the problem comes down to the fact that the client has to be listening for asynchronous notifications throughout its entire lifetime. To make this happen I have to do tricky things with asyncio loops that conflict with Streamlit’s run-the-script-once model. I feel like I’m forcing Streamlit to do something it doesn’t want to do.

From searching online it appears that I’m not the only one confused about how to make asyncio and Streamlit work together. Also the Native asyncio support issue makes me wonder if what I want to do is currently possible.

Is there a way to implement an asynchronous chat application like I have described? Or am I hitting a limitation of Streamlit?

Generally, you should not use while True loops in Streamlit for user input. Streamlit doesn’t “wait” at input widgets. The rerun structure of Streamlit replaces the while True loop you might use with console input. As soon as someone enters something into a widget, Streamlit reruns.

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