Chat front ends for LLMs and assistants are one of the most common things people build with Streamlit.
Streamlit has elements made for chat interfaces:
st.chat_input("Ask something")renders an input pinned to the bottom of the page and returns the submitted text (orNone) for the rerun after the user presses Enter. It can also accept file attachments withaccept_file=True.with st.chat_message("user"):(or"assistant", or any name with anavatar=) draws a message bubble; anything written inside appears in it.st.write_stream(generator)writes a stream of text chunks as they arrive, with a typewriter effect, and returns the full concatenated text when done. It accepts plain generators and the streaming responses of OpenAI-style clients.
Because the script reruns on every message, the conversation must live in session state. The standard pattern:
1. Initialize st.session_state.messages = []. 2. Re-display every stored message at the top of each run. 3. When chat_input returns text, display it, call the model, stream the reply, and append both messages to the history.
Cache the model client with @st.cache_resource, and read API keys from st.secrets.
st.status("Thinking...") shows expandable progress for multi-step agents, and st.feedback("thumbs") collects ratings. Long conversations cost tokens and memory: trim or summarize history before sending it to the model.
Example
import time
import streamlit as st
def fake_llm(prompt): # stand-in for a streaming LLM call
for word in f"You said: {prompt}. Nice!".split():
yield word + " "
time.sleep(0.05)
if "messages" not in st.session_state:
st.session_state.messages = [{"role": "assistant", "content": "Hi! Ask me anything."}]
for m in st.session_state.messages: # redraw history on every rerun
with st.chat_message(m["role"]):
st.markdown(m["content"])
if prompt := st.chat_input("Your message"):
st.session_state.messages.append({"role": "user", "content": prompt})
with st.chat_message("user"):
st.markdown(prompt)
with st.chat_message("assistant"):
reply = st.write_stream(fake_llm(prompt)) # returns the full text
st.session_state.messages.append({"role": "assistant", "content": reply})Remember
st.chat_inputreturns the submitted message;st.chat_messagedraws bubbles- Keep the conversation in session state and redraw it every run
st.write_streamdisplays streamed tokens and returns the full reply
Watch out for
- Not storing messages in session state, so the conversation disappears on the next message.