Documentation: Frontend (frontend.py)
This file defines the Streamlit-based frontend for the Indaba Retrieval-Augmented Generation (RAG) chatbot.
It provides the user interface, manages queries, retrieves relevant chunks from the FAISS index, and generates answers using the Groq LLM API.
It also applies a custom CSS theme for a modern UI.
Key Responsibilities
- Load the FAISS index and pre-processed text chunks.
- Take user input (questions) through a chat-like form.
- Retrieve the most relevant chunks using semantic search.
- Pass retrieved chunks into the Groq-powered LLM to generate answers.
- Display responses in a styled Streamlit interface.
- Provide a button to clear chat history.
Step-by-Step Breakdown
1. Imports and Setup
import streamlit as st
import pickle
import faiss
from sentence_transformers import SentenceTransformer
from groq import Groq
import os
# from dotenv import load_dotenv
# load_dotenv()
What is happening:
- streamlit→ UI framework.
- pickle→ loads pre-saved chunks.
- faiss → similarity search engine for embeddings.
- SentenceTransformer → embedding model (all-MiniLM-L6-v2).
- Groq → client to access LLM API.
- os → (optional) environment variable access.
Unlike local.env use, API keys are pulled from st.secrets for deployment safety. And that is why the dotenv variable is commented out.
2. Load FAISS Index and Chunks
INDEX_FILE = "faiss_index.bin"
CHUNKS_FILE = "chunks.pkl"
embedder = SentenceTransformer("all-MiniLM-L6-v2", device="cpu")
index = faiss.read_index(INDEX_FILE)
with open(CHUNKS_FILE, "rb") as f:
chunks = pickle.load(f)
- Loads FAISS index (vector database).
- Loads preprocessed document chunks (chunks.pkl).
- Ensures embeddings match the index by using the same model.
- Forces CPU for compatibility on Streamlit Cloud.
3. Initialize Groq Client
client = Groq(api_key=st.secrets["grok"]["api_key"])
- Retrieves API key from Streamlit secrets.
- Sets up Groq client for LLM queries.
4. Semantic Search Function
def search_index(query, k=10):
q_vec = embedder.encode([query])
D, I = index.search(q_vec, k)
return [chunks[i] for i in I[0]]
- Encodes the query into a vector.
- Searches FAISS for the top k most relevant chunks.
- Returns those chunks for answer generation.
5. LLM Answer Generation
def generate_answer(question, context_chunks):
context = "\n\n".join(context_chunks)
prompt = (
f"Answer the question based on the context provided. "
"If the question is not related to the context in any way, do NOT attempt to answer. "
"Instead, strictly reply: 'My knowledge base does not have information about this. Please contact the technical team.'\n\n"
f"Context: {context}\n\nQuestion: {question}\nAnswer:"
)
response = client.chat.completions.create(
messages=[{"role": "user", "content": prompt}],
model="llama-3.3-70b-versatile",
)
return response.choices[0].message.content.strip()
Builds a RAG prompt with:
- Retrieved context.
- Question.
Sends to Groq’s LLaMA-3.3-70B model.
Returns a clean answer.
Enforces that if no context exists → chatbot says:
“My knowledge base does not have information about this. Please contact the technical team.”
- Custom Chat UI (CSS)
st.markdown(
"""
<style>
...
</style>
""",
unsafe_allow_html=True,
)
- Dark theme with neon-blue highlights.
- Styles input box, buttons, and retrieved chunks.
- Enhances readability and adds a polished UI feel.
7. Streamlit UI: Title & Instructions
st.title("🤖 Indaba")
st.write("Ask questions based on Discrete Mathematics.")
- Displays app title.
- Provides short instructions to user.
8. Question Input Form
with st.form(key="chat_form", clear_on_submit=True):
question = st.text_input("Your question:", key="question_input")
submit_button = st.form_submit_button("Send")
- Input form for user questions.
- Clears text box on submit.
- Controlled by a Send button.
9. Main Chat Logic
if submit_button and question:
retrieved = search_index(question)
answer = generate_answer(question, retrieved)
st.markdown("### 🤖 Answer")
st.write(answer)
On submit:
- Retrieves relevant chunks.
- Generates answer from Groq LLM.
- Displays result under “🤖 Answer”.
# Clear Chat Button
if st.button("Clear Chat"):
st.session_state.messages = []
st.rerun()
- Resets chat state.
- Reruns app for a fresh session.
Workflow (Frontend)
- User submits a question.
- Query is embedded and searched in FAISS.
- Top chunks are retrieved.
- Chunks + question are passed into LLM.
- LLM generates an answer based only on retrieved knowledge.
- Answer is displayed in a styled interface.
- User can clear chat anytime.
Other Notes
- Frontend relies on the backend (index_docs.py) having run beforehand. It doesn’t rebuild the index.
- Chat memory is session-based only (clears on refresh).
- Environment variable GROQ_API_KEY must be set in when running locally in the:
- Local .env file. Otherwise, it is set in streamlit secrets as:
- Streamlit Secrets ["grok"]["api_key"] during deployment.
Top comments (0)