Skip to content

Repository files navigation

DocuMind — RAG-Powered Document Assistant

Upload PDFs, TXT, or Markdown files and chat with them using retrieval-augmented generation. Every AI answer includes verified source citations pointing back to specific document chunks — not just a chatbot wrapper.

Next.js 16 TypeScript 5 Tailwind CSS 4 Prisma SQLite BM25


How It Works

┌─────────────┐    ┌──────────────┐    ┌──────────────┐    ┌──────────────┐    ┌──────────────┐
│   Upload    │───►│    Chunk     │───►│   BM25       │───►│     LLM      │───►│   Cited      │
│  PDF/TXT/MD │    │  800 char    │    │  Retrieval   │    │  Generation  │    │  Response    │
│             │    │  w/ overlap  │    │  top-K = 6   │    │  z-ai-sdk    │    │  [Source N]  │
└─────────────┘    └──────────────┘    └──────────────┘    └──────────────┘    └──────────────┘

RAG Pipeline

  1. Upload & Extract — Drag-and-drop files. PDFs are parsed with pdf-parse; TXT/MD read as UTF-8.
  2. Chunk — Text is split into ~800-character chunks at sentence boundaries with 200-character overlap for context continuity.
  3. Retrieve (BM25) — When you ask a question, a BM25 engine scores all chunks against the query (TF-IDF with k1=1.5, b=0.75) and returns the top 6 most relevant.
  4. Generate — Retrieved chunks are injected into a structured prompt with citation instructions. The LLM generates an answer with inline [Source N] references.
  5. Cite — The response includes an expandable source panel showing each cited chunk's content, filename, and chunk number.

Why BM25 Instead of Vector Embeddings?

The LLM SDK available in this environment provides chat completions but not an embeddings endpoint. Rather than introducing an external vector database dependency, BM25 was implemented from scratch — it's the same algorithm powering Elasticsearch and Lucide, and it demonstrates deep understanding of information retrieval fundamentals.


Features

Feature Details
Document Upload Drag-and-drop or click; supports PDF, TXT, MD up to 20 MB
Smart Chunking Sentence-boundary-aware; 800-char chunks with 200-char overlap
BM25 Retrieval Industry-standard TF-IDF scoring (k1=1.5, b=0.75, top-K=6)
LLM Generation Multi-turn conversation history via z-ai-web-dev-sdk
Source Citations Inline [Source N] in answers + expandable panel with chunk content
Conversation Management Create, switch between, and delete conversations; auto-titling
Document Management Upload, list, delete; live stats (document count, chunk count)
Responsive Design Mobile-first (375 px) through desktop (1920 px); collapsible sidebar
Markdown Rendering AI responses rendered with react-markdown for rich formatting

Tech Stack

Layer Technology
Framework Next.js 16 (App Router, Turbopack)
Language TypeScript 5
Styling Tailwind CSS 4 + shadcn/ui (New York)
Database SQLite via Prisma ORM
State Zustand (client)
Retrieval Custom BM25 engine (src/lib/bm25.ts)
Chunking Custom sentence-boundary chunker (src/lib/chunker.ts)
PDF Parsing pdf-parse
LLM z-ai-web-dev-sdk (chat completions)
Markdown react-markdown
Icons Lucide React

Project Structure

src/
├── app/
│   ├── layout.tsx                          # Root layout
│   ├── page.tsx                            # Main page (sidebar + chat)
│   └── api/
│       ├── chat/route.ts                   # RAG chat endpoint
│       ├── documents/
│       │   ├── upload/route.ts              # File upload + text extraction + chunking
│       │   ├── list/route.ts               # List all documents
│       │   └── delete/route.ts             # Delete a document
│       └── conversations/
│           ├── create/route.ts             # Create new conversation
│           ├── list/route.ts               # List all conversations
│           ├── delete/route.ts             # Delete a conversation
│           └── messages/route.ts           # Get/set messages + linked documents
├── components/
│   ├── chat/
│   │   ├── document-upload.tsx             # Drag-and-drop upload with file list
│   │   ├── conversation-list.tsx           # Conversation sidebar panel
│   │   ├── chat-message.tsx                # Message bubble with citations
│   │   └── empty-state.tsx                 # Contextual empty states
│   └── ui/                                 # shadcn/ui components
├── lib/
│   ├── bm25.ts                             # BM25 retrieval engine
│   ├── chunker.ts                          # Sentence-boundary text chunker
│   ├── db.ts                               # Prisma client singleton
│   └── utils.ts                            # Tailwind merge utility
└── store/
    └── use-app-store.ts                    # Zustand global state

Database Schema

Document           DocumentChunk         Conversation
┌──────────┐       ┌──────────────┐      ┌──────────────┐
│ id       │───┐   │ id           │      │ id           │
│ filename │   └──►│ documentId   │      │ title        │
│ fileType │       │ chunkIndex   │      │ createdAt    │
│ fileSize │       │ content      │      │ updatedAt    │
│ chunkCount│      │ startChar    │      └──────┬───────┘
│ status   │       │ endChar      │             │
└──────────┘       └──────────────┘             │
      │                    ▲                   │
      └────────────────────┤          ┌────────┴────────┐
                           │          │ Conversation    │
                           │          │   Document      │
                           │          ├─────────────────┤
                           └──────────│ documentId      │
                                      │ conversationId  │
                                      └─────────────────┘
                                               │
                                      ┌────────┴────────┐
                                      │    Message      │
                                      ├─────────────────┤
                                      │ role (user/ai)  │
                                      │ content         │
                                      │ sources (JSON)  │
                                      │ conversationId  │
                                      └─────────────────┘

Getting Started

Prerequisites

  • Node.js 18+ or Bun
  • A valid z-ai-web-dev-sdk configuration

Installation

# Install dependencies
bun install

# Set up the database
bun run db:push

# Start the development server
bun run dev

The app runs on http://localhost:3000.

Scripts

Command Description
bun run dev Start dev server with Turbopack
bun run lint Run ESLint
bun run db:push Push Prisma schema to SQLite
bun run db:generate Regenerate Prisma Client

API Endpoints

Method Endpoint Description
POST /api/documents/upload Upload and process a document (multipart form)
GET /api/documents/list List all documents
POST /api/documents/delete Delete a document by ID
POST /api/conversations/create Create a new conversation (link documents)
GET /api/conversations/list List all conversations with document info
POST /api/conversations/delete Delete a conversation by ID
GET /api/conversations/messages?conversationId= Get messages for a conversation
POST /api/conversations/messages Update linked documents for a conversation
POST /api/chat Send a message (RAG: retrieve → generate → cite)

Usage

  1. Upload documents — Drag PDF, TXT, or MD files onto the upload area in the sidebar.
  2. Start a chat — Click "New Chat" to create a conversation linked to all uploaded documents.
  3. Ask questions — Type natural language questions. The system retrieves relevant chunks via BM25, sends them to the LLM, and returns a cited answer.
  4. Inspect sources — Click "Source Citations" on any assistant message to see the exact chunks that informed the answer.

Key Design Decisions

  • BM25 over vector search — No external vector DB needed; BM25 is the gold standard for keyword-based retrieval and works well for structured documents.
  • Sentence-boundary chunking — Chunks split at natural sentence endings (not mid-sentence) for better retrieval accuracy.
  • Overlapping chunks — 200-character overlap ensures context isn't lost at chunk boundaries.
  • SQLite + Prisma — Zero-config database; perfect for single-instance deployments and demos.
  • Source citations as first-class citizens — Every answer shows where it came from, making the system auditable and trustworthy.

License

This project is for educational and portfolio demonstration purposes.


Let's Build More

Have an idea for the next AI-powered project? Want to collaborate on something ambitious, or just want to bounce around concepts like this? I'm always open to connecting with builders, thinkers, and anyone who gets excited about turning ideas into working software.

Reach out — let's make something great together.

About

RAG-powered document assistant — upload PDFs, chat with them using BM25 retrieval + LLM, and get answers with verified source citations. Built with Next.js 16, Prisma, and a custom retrieval pipeline.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages