Introduction
Conversational AI has become one of the most exciting areas of modern software development. Whether you want to build a customer support bot, a personal study assistant, or a creative writing companion, large language models (LLMs) have made it easier than ever to create intelligent, context-aware applications.
In this guide you will build a fully featured AI chatbot using Mistral AI — one of the most powerful and open-minded LLM providers available today — and Streamlit, the fastest way to turn a Python script into a production-ready web app. By the end you will have a chatbot that supports multi-turn conversations, a configurable system prompt, chat history, and a clean UI.
If you are new to building AI-powered Streamlit apps, you might want to first read our guide on SANGAM AI: The Ultimate AI Toolkit, which covers the broader architecture of multi-modal Python AI projects.
Why Mistral AI?
Mistral AI was founded in 2023 by former Meta and Google DeepMind researchers. It quickly earned a reputation for releasing highly capable open-weight models that can be run locally or accessed via a fast cloud API. Here is a quick comparison of the available Mistral models:
| Model | Context Window | Best For | Speed |
|---|---|---|---|
mistral-tiny | 32k tokens | Quick, cost-effective responses | ⚡⚡⚡ |
mistral-small | 32k tokens | Balanced quality and speed | ⚡⚡ |
mistral-medium | 32k tokens | Complex reasoning, coding | ⚡ |
mistral-large-latest | 128k tokens | Best quality, long documents | ⚡ |
open-mistral-7b | 32k tokens | Open-weight, self-hostable | ⚡⚡⚡ |
open-mixtral-8x7b | 32k tokens | High quality open-weight | ⚡⚡ |
For this chatbot we will use mistral-small — it gives excellent conversational quality at a very reasonable API cost. You can swap it out for any other model by changing a single line of code.
Prerequisites
Before you begin, make sure you have the following set up:
pip install streamlit mistralai python-dotenv| Package | Version | Purpose |
|---|---|---|
streamlit | ≥ 1.32 | Web app framework |
mistralai | ≥ 0.4 | Official Mistral Python SDK |
python-dotenv | ≥ 1.0 | Load API keys from .env file |
Project Structure
Before writing code, here is the folder layout we will use:
chatbot/
├── chatbot_app.py ← Main Streamlit application
├── .env ← API key (never commit this!)
├── .gitignore ← Exclude .env from Git
└── requirements.txt ← Python dependenciesCreate a .env file and add your API key:
MISTRAL_API_KEY=your_actual_api_key_hereAnd a .gitignore to keep your key safe:
.env
__pycache__/
.streamlit/Step 1: Set Up the Streamlit Page
Create chatbot_app.py and start with the page configuration and imports:
import os
import streamlit as st
from mistralai.client import MistralClient
from mistralai.models.chat_completion import ChatMessage
from dotenv import load_dotenv
load_dotenv()
st.set_page_config(
page_title="AI Chatbot",
page_icon="🤖",
layout="centered"
)
st.title("🤖 Mistral AI Chatbot")
st.caption("Powered by Mistral AI — ask me anything!")Using load_dotenv() means your API key stays in the .env file and never appears in your source code.
Step 2: Initialize Session State
Streamlit reruns your script from top to bottom every time the user interacts with the page. st.session_state is how you persist data — like the conversation history — across those reruns:
# Initialize session state variables
if "messages" not in st.session_state:
st.session_state.messages = []
if "model" not in st.session_state:
st.session_state.model = "mistral-small"
if "system_prompt" not in st.session_state:
st.session_state.system_prompt = "You are a helpful, friendly AI assistant."The messages list will hold the full conversation in the format the Mistral API expects: a list of dictionaries with role (either "user" or "assistant") and content fields.
Step 3: Add the Sidebar Controls
A good chatbot UI lets users customise their experience. We will put the model selector, system prompt editor, and a clear button in the sidebar:
with st.sidebar:
st.header("⚙️ Settings")
st.session_state.model = st.selectbox(
"Model",
options=[
"mistral-tiny",
"mistral-small",
"mistral-medium",
"mistral-large-latest",
],
index=1,
)
st.session_state.system_prompt = st.text_area(
"System Prompt",
value=st.session_state.system_prompt,
height=150,
help="Define the chatbot's personality and behaviour here.",
)
if st.button("🗑️ Clear Chat", use_container_width=True):
st.session_state.messages = []
st.rerun()
st.divider()
st.caption(f"Messages in history: {len(st.session_state.messages)}")The system prompt is one of the most powerful tools available when working with LLMs. By changing it, you can turn your chatbot into a coding assistant, a Socratic tutor, a creative fiction writer, or a concise summariser — all without touching the underlying model.
Step 4: Display the Chat History
Streamlit's st.chat_message() component makes it trivial to render a WhatsApp-style chat thread:
# Render existing messages
for message in st.session_state.messages:
with st.chat_message(message["role"]):
st.markdown(message["content"])This loop runs every time the page loads and reconstructs the full conversation from session_state. Each message gets a distinct avatar — a person icon for the user and a robot icon for the assistant — automatically handled by Streamlit.
Step 5: Handle New User Input
Now for the main event — capturing user input and generating a response:
if prompt := st.chat_input("Type your message here..."):
# Add user message to history and display it
st.session_state.messages.append({"role": "user", "content": prompt})
with st.chat_message("user"):
st.markdown(prompt)
# Build the message list for the API call
api_messages = [
ChatMessage(role="system", content=st.session_state.system_prompt)
] + [
ChatMessage(role=m["role"], content=m["content"])
for m in st.session_state.messages
]
# Call Mistral API and stream the response
api_key = os.getenv("MISTRAL_API_KEY", "")
client = MistralClient(api_key=api_key)
with st.chat_message("assistant"):
response_placeholder = st.empty()
full_response = ""
for chunk in client.chat_stream(
model=st.session_state.model,
messages=api_messages,
):
delta = chunk.choices[0].delta.content or ""
full_response += delta
response_placeholder.markdown(full_response + "▌")
response_placeholder.markdown(full_response)
st.session_state.messages.append(
{"role": "assistant", "content": full_response}
)The streaming approach (client.chat_stream()) sends the response token-by-token, so users see words appearing as the model generates them — just like ChatGPT. The ▌ cursor gives a nice typing-in-progress effect.
Step 6: Run the App
streamlit run chatbot_app.pyOpen http://localhost:8501 and start chatting!
Complete Code
Here is the full chatbot_app.py file with all steps combined:
import os
import streamlit as st
from mistralai.client import MistralClient
from mistralai.models.chat_completion import ChatMessage
from dotenv import load_dotenv
load_dotenv()
st.set_page_config(page_title="AI Chatbot", page_icon="🤖", layout="centered")
st.title("🤖 Mistral AI Chatbot")
st.caption("Powered by Mistral AI — ask me anything!")
for key, default in [("messages", []), ("model", "mistral-small"),
("system_prompt", "You are a helpful, friendly AI assistant.")]:
if key not in st.session_state:
st.session_state[key] = default
with st.sidebar:
st.header("⚙️ Settings")
st.session_state.model = st.selectbox(
"Model",
["mistral-tiny", "mistral-small", "mistral-medium", "mistral-large-latest"],
index=1,
)
st.session_state.system_prompt = st.text_area(
"System Prompt", value=st.session_state.system_prompt, height=150
)
if st.button("🗑️ Clear Chat", use_container_width=True):
st.session_state.messages = []
st.rerun()
st.divider()
st.caption(f"Messages: {len(st.session_state.messages)}")
for message in st.session_state.messages:
with st.chat_message(message["role"]):
st.markdown(message["content"])
if prompt := st.chat_input("Type your message here..."):
st.session_state.messages.append({"role": "user", "content": prompt})
with st.chat_message("user"):
st.markdown(prompt)
api_messages = [
ChatMessage(role="system", content=st.session_state.system_prompt)
] + [ChatMessage(role=m["role"], content=m["content"])
for m in st.session_state.messages]
client = MistralClient(api_key=os.getenv("MISTRAL_API_KEY", ""))
with st.chat_message("assistant"):
placeholder = st.empty()
full_response = ""
for chunk in client.chat_stream(model=st.session_state.model, messages=api_messages):
full_response += chunk.choices[0].delta.content or ""
placeholder.markdown(full_response + "▌")
placeholder.markdown(full_response)
st.session_state.messages.append({"role": "assistant", "content": full_response})Understanding the Mistral API
Here is a summary of the key Mistral API concepts used in this project:
| Concept | What it Does | Example |
|---|---|---|
ChatMessage(role, content) | Wraps a single message | ChatMessage(role="user", content="Hello") |
system role | Sets the AI's personality | "You are a Python expert." |
user role | The human's message | "Explain list comprehensions" |
assistant role | The AI's previous reply | Used for multi-turn context |
client.chat() | Single blocking response | Good for short interactions |
client.chat_stream() | Token-by-token streaming | Best for chat UIs |
chunk.choices[0].delta.content | The next token in stream | "Hello", " world", "!" |
Customising the System Prompt
The system prompt is your most powerful tool. Here are some examples of how to specialise your chatbot:
| Persona | System Prompt |
|---|---|
| Python Tutor | "You are an expert Python instructor. Explain concepts clearly with code examples. Always suggest best practices." |
| Code Reviewer | "You are a senior software engineer doing code review. Be constructive and precise. Point out bugs and suggest improvements." |
| Socratic Teacher | "You are a Socratic tutor. Never give direct answers — guide the user to discover the answer through questions." |
| Recipe Assistant | "You are a professional chef. Suggest recipes based on ingredients the user provides. Always include cooking times and serving sizes." |
| English Corrector | "You are an English grammar expert. Rewrite the user's text with correct grammar and improved style. Explain each change briefly." |
Adding Memory: How Context Windows Work
When you send a message to Mistral, you send the entire conversation history with every request. This is how the model "remembers" previous messages — it can see everything that was said.
Here is the structure of a typical API call with history:
[
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": "What is Python?"},
{"role": "assistant", "content": "Python is a high-level programming language..."},
{"role": "user", "content": "What is it good for?"}, ← new message
]The model uses the full history to generate a contextually relevant answer to "What is it good for?" — it knows "it" refers to Python because the prior turns are included.
Important: As conversations grow, they consume more tokens. If the total message length exceeds the model's context window (32k or 128k tokens), older messages will need to be truncated. For long sessions you can implement a sliding window: keep only the last N messages in session_state.messages.
Deploying to Streamlit Cloud
Once your chatbot is working locally, you can share it with the world in minutes using Streamlit Community Cloud:
.env is in .gitignore).chatbot_app.py.MISTRAL_API_KEY — Streamlit Cloud stores it securely as a secret, so you do not need the .env file in production. Access it with st.secrets["MISTRAL_API_KEY"] instead of os.getenv().Related Projects
Now that you have a working chatbot, here are some related projects on this blog that complement what you have built:
Troubleshooting
| Problem | Likely Cause | Fix |
|---|---|---|
AuthenticationError | Wrong or missing API key | Check .env and os.getenv() call |
| Responses stop mid-sentence | Context window exceeded | Limit session_state.messages to last 20 |
| Very slow first response | Model cold start | Switch to mistral-tiny for faster responses |
ModuleNotFoundError: mistralai | Library not installed | Run pip install mistralai |
| Chat history lost on refresh | Session state cleared | This is expected in Streamlit — consider persisting to a file or database |
Conclusion
You have built a fully functional, streaming AI chatbot with Mistral AI and Streamlit. The app supports multi-turn conversations, a configurable system prompt, model switching, and a clean real-time streaming UI — all in under 60 lines of Python.
From here, the possibilities are endless: add document upload so users can chat with their PDFs, integrate with a database to persist chat history, or chain multiple API calls to build a fully autonomous AI agent.
Resources: