Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

3 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

📚 JustClick AI — RAG-Powered Study Assistant

An intelligent academic assistant that lets students upload course materials and interact with them through Q&A, auto-generated quizzes, and chapter summaries — all powered by Retrieval-Augmented Generation (RAG).


📑 Table of Contents


Overview

JustClick AI is a full-stack study tool built for university students. Students upload their lecture slides, PDFs, or documents organised by semester and subject. They can then:

  • Ask questions and get answers grounded strictly in their own uploaded content
  • Generate 10-question multiple-choice quizzes from any chapter or the entire subject
  • Get structured chapter summaries with key concepts and takeaways
  • Chat naturally — the assistant remembers recent conversation history

The system never uses outside knowledge. Every answer, quiz, and summary is produced only from what the student uploaded.


Features

Feature Description
📁 File Upload Supports PDF, PPTX, DOCX, and plain TXT files
🔍 Semantic Q&A Vector similarity search finds the most relevant chunks before answering
📝 Quiz Generation Auto-generates 10 MCQs with a separated answer key (---ANSWER KEY---)
📄 Chapter Summary Structured summaries with overview, key concepts, and takeaways
🗂️ Semester & Subject Scoping Answers are strictly scoped to the selected semester and subject
💬 Chat History Last 6 messages are included for follow-up question context
🔄 Cross-Subject Hints If a topic isn't in the current subject, the bot hints which subject covers it
♻️ Re-index Utility Admin tool to re-extract and re-embed files after extraction logic updates

Tech Stack & Tool Choices

Backend

Python + Flask

Why Flask over Django / FastAPI? Flask was chosen for its minimal footprint and flexibility. The project doesn't need Django's admin panel or ORM migrations by default, and Flask integrates more cleanly with SQLAlchemy as a standalone extension. FastAPI would have been a valid choice for async performance, but since DeepSeek calls are synchronous and the workload is not high-concurrency, Flask's simplicity won out.

SQLAlchemy + SQLite (via Flask-SQLAlchemy)

Why SQLAlchemy? Used to track uploaded materials (Material table) and chat messages (ChatMessage table). SQLAlchemy gives clean ORM models without raw SQL. SQLite is used in development because there is no need for a separate database server — it lives in a single file. In production this can be swapped for PostgreSQL by changing one DATABASE_URL environment variable.

ChromaDB

Why ChromaDB over Pinecone / Weaviate / pgvector? ChromaDB runs entirely locally with zero infrastructure — no API key, no cloud account, no Docker container required. It persists vector data to a local chroma_db/ folder automatically. For a university project where the priority is simplicity and offline capability, ChromaDB is the best fit. Pinecone would be better for large-scale production with millions of vectors, but introduces cost and network dependency. pgvector would require a PostgreSQL setup. ChromaDB hits the sweet spot.

Sentence Transformers (all-MiniLM-L6-v2)

Why this embedder over OpenAI embeddings? sentence-transformers runs 100% locally and for free. OpenAI's text-embedding-ada-002 would cost money per token and require a network call for every chunk at index time and every query at search time. For a student tool indexing hundreds of lecture slides, local embeddings are far more practical. all-MiniLM-L6-v2 is small (80MB), fast, and well-tested for semantic similarity on English academic text.

DeepSeek (deepseek-chat)

Why DeepSeek over GPT-4 / Claude / Gemini? DeepSeek offers GPT-4-class reasoning at a fraction of the cost — often 10–20x cheaper per million tokens. For a study tool making many LLM calls (one per question, one per quiz, one per summary), cost matters. The OpenAI-compatible API means the integration is identical to using OpenAI — just a different base_url and api_key. Switching to GPT-4 or Claude requires changing only two lines in rag.py.

pypdf

Why pypdf over pdfminer / pdfplumber? pypdf is lightweight, actively maintained, and handles the standard PDF text extraction use case well. pdfplumber is more powerful for table extraction, but adds unnecessary complexity. pdfminer is lower-level and requires more boilerplate. For lecture slides and notes, pypdf's page.extract_text() is sufficient.

python-pptx

Why python-pptx? It is the only mature, maintained library for reading .pptx files in Python. The project extracts text shape-by-shape and paragraph-by-paragraph, preserving bullet point hierarchy — a deliberate choice to maintain slide structure in the vector index.

python-docx

Why python-docx? Same rationale — the de facto standard for .docx reading in Python. Paragraphs are extracted in order and joined with newlines.


Frontend

Next.js (App Router)

Why Next.js over plain React / Vite? Next.js gives file-based routing, server components, and a built-in API proxy layer out of the box. The App Router (app/ directory) provides layouts that wrap every page, making it trivial to apply a consistent shell (sidebar + main panel) across all routes. Plain React with Vite would require manually setting up routing (React Router), SSR, and build optimisation.

Tailwind CSS

Why Tailwind over CSS Modules / styled-components? Tailwind's utility classes mean zero context-switching between JS and CSS files. Chat bubble colours, sidebar widths, and panel layouts are all defined inline. For a project where one developer is building both logic and UI, Tailwind dramatically speeds up iteration.

Component Design

Component Role
ChatInput.js Text box + Send button; fires POST /api/chat on submit
MainPanel.js Centre panel; holds MessageList and ChatInput
MessageList.js Scrollable list of user/AI bubbles; renders quiz cards interactively
Sidebar.js Left sidebar; semester picker, subject tabs, file upload

Project Structure

project-root/
│
├── backend/                        # Flask application
│   ├── app.py                      # App factory: registers blueprints, creates DB tables
│   ├── config.py                   # Env vars: DEEPSEEK_API_KEY, TOP_K, DB path, etc.
│   ├── extensions.py               # Shared singletons: ChromaDB client, sentence embedder
│   ├── models.py                   # SQLAlchemy models: Material, ChatMessage
│   ├── routes.py                   # All API endpoints
│   ├── rag.py                      # RAG core: indexing, Q&A, quiz, summary logic
│   ├── manage_db.py                # CLI helpers: init DB, drop tables, seed data
│   ├── seed.py                     # Optional: pre-load sample materials for testing
│   ├── requirements.txt            # Python dependencies
│   ├── .env                        # Secret keys (never commit this file)
│   └── chroma_db/                  # ChromaDB vector store data (auto-created on first run)
│
└── app/                            # Next.js frontend (App Router)
    ├── globals.css                 # Global styles, Tailwind base, chat bubble colours
    ├── layout.js                   # Root layout: wraps every page, sets <html> meta tags
    ├── page.js                     # Home page: renders <MainPanel> inside the app shell
    │
    └── components/
        ├── submit/
        │   ├── ChatInput.js        # Text box + Send button; fires POST /api/chat on submit
        │   └── MainPanel.js        # Centre panel: holds <MessageList> + <ChatInput>
        ├── cards/
        │   └── MessageList.js      # Scrollable list of user/AI bubbles; renders quiz cards
        └── Sidebar.js              # Left sidebar: semester picker, subject tabs, file upload

How It Works — RAG Pipeline

1. File Indexing (process_and_index_file)

Upload file
    ↓
Compute SHA-256 hash → skip if already indexed
    ↓
Extract text (PDF / PPTX / DOCX / TXT)
    ↓
Split into overlapping word chunks (400 words, 80-word overlap)
    ↓
Filter out junk chunks (raw XML, ZIP manifests)
    ↓
Embed each chunk with sentence-transformers (all-MiniLM-L6-v2)
    ↓
Store in ChromaDB with metadata: { semester, subject, source, chapter, chunk_index }
    ↓
Save Material record to SQLite

Chapter detection happens automatically:

  • First checks if the first 3 lines of a chunk contain Chapter N / Section N
  • Falls back to the filename (e.g. chapter_2.pptxChapter 2)
  • Falls back to "General Content" if neither matches

Junk filtering removes chunks that are raw XML internal to PPTX/DOCX zip containers, detected by looking for patterns like <a:t>, .rels, ppt/slides/slide1.xml, etc.


2. Intent Detection (answer_question)

Every incoming question passes through three regex checks before the vector search runs:

Question received
    ↓
_RE_QUIZ  matched?  → _build_quiz()
    ↓ no
_RE_SUMMARY matched? → _build_summary()
    ↓ no
Normal RAG Q&A

Quiz triggers (case-insensitive): quiz, quizze, make quiz, create quiz, test me, quiz me, mcq, practice test, generate questions, questions and answers, make questions and answers, q&a, and common misspellings.

Summary triggers: summary, summarize, overview, recap, brief, explain chapter, chapter summary.

Chapter detection (_detect_chapter) extracts chapter N / section N / ch. N from the question using a regex that also handles word-form numbers (one, two … ten).


3. Normal Q&A (4-level fallback)

Level 1: Search current subject + semester
         → found relevant chunks? → answer with DeepSeek
             ↓ not found
Level 2: Search all subjects in current semester
         → found? → tell user which subject covers this topic
             ↓ not found
Level 3: Search all semesters
         → found? → tell user which semester/subject covers this
             ↓ not found
Level 4: "I don't have any information about this topic."

Relevance is determined by cosine distance threshold = 1.3 (0 = identical vectors, 2 = opposite). Chunks with distance ≥ 1.3 are discarded as irrelevant.


4. Quiz Generation (_build_quiz)

Chapter specified in question?
    ├── YES → fetch chunks for that chapter only
    └── NO  → fetch chunks from the entire subject (all chapters combined)

Build context from up to 15 chunks
    ↓
Send to DeepSeek with strict format prompt
    ↓
Returns: 10 MCQs + ---ANSWER KEY--- separator + Q1: B / Q2: A ... format
    ↓
Frontend splits on ---ANSWER KEY--- to render interactive quiz cards

5. Summary Generation (_build_summary)

Chapter specified?
    ├── YES → fetch that chapter's chunks
    └── NO  → ask user to pick a chapter (shows available list)

Build context from up to 15 chunks
    ↓
Send to DeepSeek with structured summary prompt
    ↓
Returns: ## Overview / ## Key Concepts / ## Key Takeaways

6. Chat History

The last 6 messages from the ChatMessage table (filtered by session_id) are appended to every Q&A prompt as CHAT HISTORY. This lets users ask follow-up questions like "explain the third point more" without repeating context. Quiz and summary requests do not include chat history — they use only the document content.


API Endpoints

All endpoints are defined in routes.py.

Method Path Description
POST /api/chat Main chat endpoint. Body: { session_id, semester, subject, message }
POST /api/upload Upload a file. Form data: file, semester, subject
GET /api/materials List all indexed materials. Query: ?semester=&subject=
DELETE /api/materials/<id> Delete a material record and its ChromaDB chunks
GET /api/subjects List subjects for a given semester. Query: ?semester=

Setup & Installation

Prerequisites

Backend

cd backend
python -m venv venv
source venv/bin/activate        # Windows: venv\Scripts\activate
pip install -r requirements.txt

# Initialise the database
python manage_db.py init

# Start the Flask server
flask run --port 5000

Frontend

cd app
npm install
npm run dev

Open http://localhost:3000.


Environment Variables

Create a .env file inside backend/:

DEEPSEEK_API_KEY=sk-xxxxxxxxxxxxxxxx
TOP_K=20
DATABASE_URL=sqlite:///justclick.db
CHROMA_PATH=./chroma_db
SECRET_KEY=your-flask-secret-key
Variable Description Default
DEEPSEEK_API_KEY DeepSeek API key (required)
TOP_K Number of chunks retrieved per vector query 20
DATABASE_URL SQLAlchemy DB URL sqlite:///justclick.db
CHROMA_PATH Path for ChromaDB persistence ./chroma_db
SECRET_KEY Flask session secret

Running the App

# Terminal 1 — Backend
cd backend && flask run --port 5000

# Terminal 2 — Frontend
cd app && npm run dev

Then open http://localhost:3000, pick a semester, select a subject, upload your lecture slides, and start chatting.


Re-indexing

If you update the text extraction logic and need to re-embed existing files:

# In Flask shell: flask shell
from rag import reindex_subject
result = reindex_subject("sem 1", "Data Structures")
print(result)
# → { "reindexed": ["chapter1.pptx", ...], "skipped": [], "deleted_chunks": 142 }

This deletes all ChromaDB vectors for the subject and re-extracts/re-embeds from the original files recorded in the SQL database.


Design Decisions Summary

Decision Choice Why
Vector DB ChromaDB Zero infrastructure, local, free
Embedder all-MiniLM-L6-v2 Local, free, fast, good quality
LLM DeepSeek GPT-4 quality at 10–20x lower cost
LLM SDK OpenAI Python SDK DeepSeek is OpenAI-API-compatible
Backend Flask Lightweight, flexible, easy SQLAlchemy integration
ORM SQLAlchemy Clean models, easy to swap DB backend
Frontend Next.js App Router Built-in routing, layouts, SSR
Styling Tailwind CSS Fast utility-first development
PDF reading pypdf Lightweight, sufficient for lecture slides
PPTX reading python-pptx Only mature option; shape-level extraction
DOCX reading python-docx Industry standard

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages