1. Streamlit basics¶
Beginner · 8 min read
1.1 Your first app¶
Save as app.py:
import streamlit as st
st.title("Prompt playground")
st.write("Type a prompt and see how long it is.")
prompt = st.text_area("Prompt", "Explain RAG in one sentence.")
st.write(f"**{len(prompt.split())} words**, about {len(prompt) // 4} tokens")
Run it from a terminal:
Edit the file and save — the browser shows Rerun (or turn on Always rerun in the ☰ menu).
1.2 The rerun model — the one idea to understand¶
Every time the user touches a widget, Streamlit runs your whole script again, top to bottom. Each widget call returns its current value.
flowchart LR
U[User types / clicks] --> R[Script reruns from line 1]
R --> W[Widgets return current values]
W --> D[Page redrawn]
D --> U
That makes code simple (no callbacks, no event handlers) but has two consequences:
- Normal variables reset on every rerun. To remember something (chat history, a counter), use
st.session_state— see Layout, state & caching. - Slow work reruns too. Loading a model or embedding a PDF on every click is slow and costly — cache it.
import streamlit as st
count = 0 # resets to 0 on every rerun!
if st.button("Click me"):
count += 1
st.write("Plain variable:", count) # never goes above 1
if "clicks" not in st.session_state: # survives reruns
st.session_state.clicks = 0
if st.button("Click me too"):
st.session_state.clicks += 1
st.write("session_state:", st.session_state.clicks)
1.3 Showing text¶
import streamlit as st
st.title("Title")
st.header("Header")
st.subheader("Subheader")
st.markdown("**Markdown** with `code`, [links](https://streamlit.io) and lists:\n- one\n- two")
st.caption("Small grey text — model name, latency, cost")
st.code("from openai import OpenAI\nclient = OpenAI()", language="python")
st.latex(r"\text{cosine}(a, b) = \frac{a \cdot b}{\|a\|\|b\|}")
st.divider()
st.write("`st.write` accepts almost anything:", {"model": "gpt-4o-mini"}, [1, 2, 3])
st.write is the Swiss-army knife: strings become Markdown, dicts become JSON viewers, DataFrames become
tables.
1.4 Showing data¶
import pandas as pd
import streamlit as st
runs = pd.DataFrame({
"model": ["gpt-4o-mini", "claude-haiku", "llama-3-8b"],
"accuracy": [0.86, 0.84, 0.71],
"latency_s": [1.2, 0.9, 0.6],
"cost_per_1k": [0.15, 0.25, 0.0],
})
c1, c2, c3 = st.columns(3)
c1.metric("Best accuracy", "86%", "+2% vs last week")
c2.metric("Avg latency", "0.9 s", "-0.3 s", delta_color="inverse")
c3.metric("Eval questions", 250)
st.dataframe(runs, hide_index=True) # sortable, scrollable table
st.bar_chart(runs, x="model", y="accuracy")
st.json({"model": "gpt-4o-mini", "usage": {"prompt_tokens": 812, "completion_tokens": 96}})
st.dataframe is interactive; st.table is static; st.data_editor lets the user edit the table —
handy for labelling eval data.
1.5 Widgets — getting input¶
Every widget returns its value directly:
import streamlit as st
model = st.selectbox("Model", ["gpt-4o-mini", "claude-sonnet-5-5", "llama-3-70b"])
temperature = st.slider("Temperature", 0.0, 2.0, 0.2, step=0.1)
max_tokens = st.number_input("Max tokens", min_value=16, max_value=4096, value=512, step=16)
stream = st.toggle("Stream tokens", value=True)
tools = st.multiselect("Tools the agent may use", ["web_search", "calculator", "sql"], default=["calculator"])
system = st.text_area("System prompt", "You are a concise assistant.", height=100)
question = st.text_input("Question", placeholder="Ask something…")
if st.button("Run", type="primary", disabled=not question):
st.write({"model": model, "temperature": temperature, "max_tokens": max_tokens,
"stream": stream, "tools": tools, "question": question})
| Widget | Returns |
|---|---|
st.text_input, st.text_area |
str |
st.number_input, st.slider |
int / float (or a tuple for a range slider) |
st.selectbox, st.radio |
the chosen option |
st.multiselect, st.pills |
list of chosen options |
st.checkbox, st.toggle |
bool |
st.button |
True only on the rerun right after the click |
st.file_uploader |
an uploaded file object or None |
Buttons don't stay pressed
st.button is True for one rerun only. Anything you show inside if st.button(...) disappears on
the next interaction. Save results in st.session_state if they must stay on screen.
1.6 Status and feedback¶
import time
import streamlit as st
with st.spinner("Calling the LLM…"):
time.sleep(1) # stand-in for the API call
st.success("Answer ready")
st.info("Using the cached index")
st.warning("Context is 90% full — older messages will be dropped")
st.error("Rate limited by the provider. Try again in 20 s.")
with st.status("Agent working…", expanded=True) as status:
st.write("🔍 Searching the knowledge base")
time.sleep(0.5)
st.write("🧮 Calling the calculator tool")
time.sleep(0.5)
status.update(label="Agent finished", state="complete")
st.toast("Saved to history")
st.status is perfect for showing an agent's steps while it works.
1.7 Stopping early¶
import streamlit as st
api_key = st.text_input("OpenAI API key", type="password")
if not api_key:
st.info("Enter your API key to continue.")
st.stop() # nothing below runs until there is a key
st.write("Key received — ready to chat.")
Practice¶
- Build a "token counter" app: a text area plus metrics for characters, words and estimated tokens.
- Add a
selectboxof models and show the estimated cost using a price-per-1K-tokens dict.
Next: Layout, state & caching — sidebar settings, memory between reruns, and fast apps.