Adding a Simple Web UI to Your Python Chatbot
Part 2's chatbot works, but only in a terminal — no real Bright Leaf Coffee visitor could use it. This part turns it into a real, working web application using Flask, a genuinely minimal Python web framework, before this series moves to Django starting in part 4.
Real, practical setup
pip install flaskThe real, complete Flask application
# app.py
from flask import Flask, request, session, render_template
from shared.ai_client import get_client
app = Flask(__name__)
app.secret_key = "a-real-random-secret-key-here"
PLAN_DATA = """
- Gift Subscription: $18/mo, 1 bag, free shipping
- Monthly Subscription: $16/mo, 1 bag, free shipping
"""
SYSTEM_PROMPT = f"""You are a customer support assistant for Bright
Leaf Coffee. Real, current plan data:\n{PLAN_DATA}\nOnly answer using
this real data. Keep responses to 2-3 sentences."""
@app.route("/", methods=["GET", "POST"])
def chat():
if "history" not in session:
session["history"] = []
if request.method == "POST":
user_message = request.form["message"]
session["history"].append({"role": "user", "content": user_message})
client = get_client()
response = client.messages.create(
model="claude-sonnet-5",
max_tokens=1024,
system=SYSTEM_PROMPT,
messages=session["history"],
)
answer = response.content[0].text
session["history"].append({"role": "assistant", "content": answer})
session.modified = True
return render_template("chat.html", history=session.get("history", []))
if __name__ == "__main__":
app.run(debug=True)The real, minimal template
<!-- templates/chat.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Bright Leaf Coffee Support</title>
</head>
<body>
<h1>Ask us anything</h1>
<div>
{% for message in history %}
<p><strong>{{ message.role }}:</strong> {{ message.content }}</p>
{% endfor %}
</div>
<form method="POST">
<input type="text" name="message" required />
<button type="submit">Send</button>
</form>
</body>
</html>This is directly the HTML series' own real forms guidance applied here — a genuine, semantic <form> with a real, associated input, submitting to the same route that renders the page.
Why session — not a global variable — holds the real conversation
A global Python variable: shared across EVERY real visitor at once —
one person's conversation would leak into another's
Flask's session: a real, per-visitor cookie-backed store — each real
browser gets its own, separate conversation historyThis is a genuinely important, real correctness detail specific to moving from a single-user terminal script to a real, multi-visitor web application — the exact same history list from part 2's chatbot now has to be scoped per real visitor, not shared globally across everyone hitting the server.
This session-scoping requirement is the real, first genuine difference between "a script that works" and "a real, deployable web feature" — it's a structural concern that has nothing to do with the AI logic itself, and it's exactly the kind of detail this series will keep surfacing as each project moves from a standalone script toward something genuinely production-shaped.
A real, honest limitation: Flask's session isn't built for this at scale
Flask's default session store keeps data in a real, signed browser
cookie — genuinely fine for a small real demo, but a real,
growing conversation history (part 9's context-window problem)
eventually exceeds a practical real cookie size limitA real, production chat feature would store conversation history server-side (a real database or cache), keyed by a session ID, rather than in the cookie itself — a genuine, deliberate simplification for this part's teaching purpose, revisited properly once this series reaches Django's real database layer in part 5.
Next: integrating an LLM API into a real Django project — the foundational pattern part 5's actual Django chatbot builds on.