SupportMind is a small full-stack demo of a retrieval-augmented generation (RAG) assistant: upload a PDF, ingest it into a vector store, then chat with answers grounded in that document—with source page citations, session-based chat memory, and a simple relevance guardrail for off-topic questions.
The UI is branded SupportMind in the app (frontend/app/page.tsx, layout.tsx).
SupportMind.mp4
| Layer | Technologies |
|---|---|
| Frontend | Next.js 16 (App Router), React 19, TypeScript, Tailwind CSS v4 |
| Backend | FastAPI, Uvicorn (typical runner), python-dotenv |
| RAG / LLM | LangChain (langchain-community, langchain-classic, langchain-core), Chroma (in-memory / ephemeral per process), Hugging Face embeddings (all-MiniLM-L6-v2), Groq via langchain-groq (llama-3.1-8b-instant) |
| Retrieval | Hybrid BM25 + semantic retriever with weighted ensemble |
- PDF upload & ingestion — chunks, embeds, and builds the retriever + conversational chain (
backend/rag_pipeline.py). - Chat over your doc — history-aware RAG with
create_retrieval_chain/create_history_aware_retriever. - Multi-chat sidebar — create, switch, and delete conversations from a left sidebar on the chat page (
frontend/app/page.tsx). On small screens the list opens from a header control with a backdrop. - Client-persisted transcripts — each thread’s messages and metadata are saved in the browser under the key
supportmind-chat-v1viafrontend/lib/chat-storage.ts(survives refresh; cleared if site data is cleared). - Per-conversation session memory — each thread uses its own UUID as
session_idwhen calling/chat, so the server keeps separate LangChain histories inpipeline.sessionsper conversation (in-memory until the backend process restarts). - Source citations — bot messages can show source page numbers from retrieved context.
- Guardrail — low similarity to the index yields a polite “only documentation” style refusal (
is_relevant). - API key protection — backend expects
X-API-Key; Next.js server routes proxy to FastAPI so the key stays server-side (frontend/app/api/rag/*). - Status indicator — UI shows whether a document is loaded (
/status→ready).
flowchart LR
Browser[Browser] --> NextUI[Next.js pages]
NextUI --> NextAPI[Next.js API routes /api/rag/*]
NextAPI --> FastAPI[FastAPI backend]
FastAPI --> RAG[RAGPipeline]
RAG --> Chroma[Chroma vector store]
RAG --> Groq[Groq LLM]
FAQ_RAG/
├── .gitignore
├── graphify-out/
│ └── GRAPH_REPORT.md # committed architecture summary (graph built locally)
├── backend/
│ ├── .cursor/
│ │ └── rules/
│ │ └── graphify.mdc # Cursor rule: use graphify before exploring code
│ ├── main.py # FastAPI app: /, /status, /upload, /chat
│ ├── auth.py # X-API-Key verification
│ └── rag_pipeline.py # Ingestion, hybrid retrieval, chat chain
└── frontend/
├── package.json
├── next.config.ts
├── postcss.config.mjs
├── eslint.config.mjs
├── app/
│ ├── layout.tsx
│ ├── globals.css
│ ├── page.tsx # Chat UI (home)
│ ├── upload/
│ │ └── page.tsx # PDF upload UI
│ └── api/
│ └── rag/
│ ├── chat/route.ts # POST → backend /chat
│ ├── upload/route.ts # POST → backend /upload
│ └── status/route.ts # GET → backend /status
└── lib/
├── api.ts # Client fetch helpers → /api/rag/*
└── chat-storage.ts # Thread list + messages in localStorage; thread id = session_id
- Node.js (LTS recommended) and npm (or pnpm/yarn/bun).
- Python 3.10+ recommended.
- A Groq API key (Groq Console).
- PDF files as the knowledge source (backend validates .pdf). First backend startup may download the embedding model (all-MiniLM-L6-v2); allow time and disk/network.
- Backend (
backend/.envor shell)
| Variable | Required | Description |
|---|---|---|
GROQ_API_KEY |
Yes | Groq API key for ChatGroq. |
API_KEY |
Yes | Shared secret; clients must send X-API-Key: <value>. |
ALLOWED_ORIGINS |
No | Comma-separated CORS origins, or * (default in code). |
- Frontend (
frontend/.env.local)
| Variable | Required | Description |
|---|---|---|
BACKEND_URL |
Yes | FastAPI base URL (no trailing slash), e.g. http://127.0.0.1:8000. |
RAG_API_KEY |
Yes | Must match backend API_KEY (sent from server routes only). |
-
Backend From backend/:
cd backend python -m venv .venv # Windows: .venv\Scripts\activate # macOS/Linux: source .venv/bin/activate pip install fastapi uvicorn python-dotenv pydantic # LangChain / RAG stack (install versions compatible with your environment): pip install langchain-community langchain-text-splitters langchain-classic langchain-core langchain-groq chromadb sentence-transformers pymupdfCreate backend/.env:
GROQ_API_KEY=your_groq_key API_KEY=choose_a_long_random_secret # Optional: # ALLOWED_ORIGINS=http://localhost:3000Run the api:
uvicorn main:app --reload --host 0.0.0.0 --port 8000 -
Frontend From frontend/:
cd frontend npm installCreate frontend/.env.local:
BACKEND_URL=http://127.0.0.1:8000 RAG_API_KEY=choose_a_long_random_secretUse the same secret as API_KEY in the backend.
npm run dev
| Endpoint | Method | Auth | Purpose |
|---|---|---|---|
/ |
GET |
No | Health check |
/status |
GET |
X-API-Key |
Returns status, e.g. { "ready": true } |
/upload |
POST |
X-API-Key |
Upload PDF using multipart/form-data field file |
/chat |
POST |
X-API-Key |
Send JSON: { "question": "...", "session_id": "..." } |
This repo uses graphify for a local knowledge graph of the codebase. Only two graphify-related files are committed (Option A):
| Committed | Purpose |
|---|---|
backend/.cursor/rules/graphify.mdc |
Tells Cursor to query the graph before exploring code |
graphify-out/GRAPH_REPORT.md |
Human-readable architecture summary |
Everything else under graphify-out/ (including graph.json, graph.html, and cache/) is gitignored and rebuilt locally.
From the repo root (FAQ_RAG/):
pip install graphify # or: pipx install graphify
graphify extract . --code-only
graphify cluster-only . --no-labelRun from the repo root:
graphify query "how does chat work"
graphify explain "RAGPipeline"
graphify path "chat()" "RAGPipeline"
graphify update . # after code changes (AST-only, no API cost)Open graphify-out/graph.html in a browser for the interactive graph (local only, not committed).
After meaningful code changes, run graphify update . and optionally refresh the committed report with graphify cluster-only . --no-label if you want to update GRAPH_REPORT.md for the team.