Skip to content

Repository files navigation

DocsAI — Chat with your documents

A full-stack AI SaaS application that lets users upload PDF documents and chat with them using natural language. Built with Next.js, Supabase, and a fully free AI stack.


What it does

Users sign up, upload a PDF, and ask questions about it in plain English. The app finds the most relevant sections of the document and generates a grounded answer — it never makes things up because it only uses content from the uploaded file.

Example questions you can ask:

  • "What is this document about?"
  • "Summarize the key points"
  • "What are the important clauses in section 3?"
  • "Explain this in simple terms"

Tech stack

Layer Technology Cost
Frontend + Backend Next.js 16 (App Router) Free
Database + Auth + Storage Supabase Free tier
Embeddings (AI fingerprints) @xenova/transformers (runs locally) Free forever
Chat answers Groq API (Llama 3) Free tier
Vector search pgvector (built into Supabase) Free
Styling Tailwind CSS v4 Free

Total cost to run: $0


Architecture — how RAG works

This app uses RAG (Retrieval-Augmented Generation), the same architecture used in enterprise AI products.

User uploads PDF
       ↓
Extract text from PDF (pdf2json)
       ↓
Split text into overlapping chunks (~500 words each)
       ↓
Convert each chunk to an embedding (384 numbers) — runs locally
       ↓
Store chunks + embeddings in Supabase (pgvector)
       ↓
Document is marked "ready"
 
User asks a question
       ↓
Convert question to an embedding — runs locally
       ↓
Find the 5 most similar chunks using vector cosine similarity
       ↓
Send: question + relevant chunks + chat history → Groq (Llama 3)
       ↓
AI generates a grounded answer based only on the document
       ↓
Save question + answer to messages table
       ↓
Return answer to user

Features

User system

  • Email + password signup and login (Supabase Auth)
  • Each user sees only their own documents (Row Level Security)
  • Multi-user support — fully isolated data per user

Document management

  • Drag-and-drop file upload (PDF and .txt)
  • Real-time processing status: Pending → Processing → Ready
  • Delete documents (removes file, chunks, and chat history)
  • Document list dashboard with status badges

AI chat

  • ChatGPT-style chat interface per document
  • Answers grounded in document content — no hallucination
  • Conversation memory (last 6 messages sent as context)
  • Typing indicator with animated dots
  • Quick-start suggestion buttons on empty chat
  • Shift+Enter for new line, Enter to send

UI / UX

  • Landing page with feature highlights
  • Consistent design system across all pages
  • Loading states and error banners everywhere
  • Responsive layout

Project structure

docsai/
├── app/
│   ├── api/
│   │   ├── chat/
│   │   │   └── route.js          # RAG chat endpoint
│   │   ├── documents/
│   │   │   └── [id]/
│   │   │       └── route.js      # Delete document endpoint
│   │   ├── process/
│   │   │   └── route.js          # PDF processing pipeline
│   │   └── upload/
│   │       └── route.js          # File upload endpoint
│   ├── chat/
│   │   └── [id]/
│   │       └── page.js           # Chat UI
│   ├── dashboard/
│   │   └── page.js               # Document list
│   ├── login/
│   │   └── page.js               # Login page
│   ├── signup/
│   │   └── page.js               # Signup page
│   ├── upload/
│   │   └── page.js               # Upload UI
│   ├── layout.js                 # Root layout
│   └── page.js                   # Landing page
├── lib/
│   ├── ai.js                     # Groq chat (Llama 3)
│   ├── chunker.js                # Text splitting logic
│   ├── embeddings.js             # Local embeddings (@xenova)
│   └── supabase.js               # Supabase client
├── .env.local                    # Secret keys (never commit)
├── next.config.mjs
└── package.json

Database schema

documents

Column Type Description
id uuid Primary key
user_id uuid FK → auth.users
name text Original filename
file_path text Path in Supabase Storage
status text pending / processing / ready / error
created_at timestamptz Upload time

chunks

Column Type Description
id uuid Primary key
document_id uuid FK → documents (cascade delete)
content text Raw text of this chunk
embedding vector(384) AI embedding from @xenova
chunk_index int Order within document

messages

Column Type Description
id uuid Primary key
document_id uuid FK → documents (cascade delete)
user_id uuid FK → auth.users
role text 'user' or 'assistant'
content text Message text
created_at timestamptz Sent time

Row Level Security is enabled on all tables — users can only access their own data.


Getting started

Prerequisites

  • Node.js v18 or higher
  • A Supabase account (free at supabase.com)
  • A Groq API key (free at console.groq.com)

1. Clone the repo

git clone https://github.com/YOUR_USERNAME/docsai.git
cd docsai
npm install

2. Set up Supabase

  1. Create a new project at supabase.com
  2. Go to Database → Extensions and enable vector
  3. Open SQL Editor and run the following:
-- Enable pgvector
CREATE EXTENSION IF NOT EXISTS vector;
 
-- Documents table
CREATE TABLE documents (
  id          uuid PRIMARY KEY DEFAULT gen_random_uuid(),
  user_id     uuid REFERENCES auth.users(id) ON DELETE CASCADE,
  name        text NOT NULL,
  file_path   text,
  status      text DEFAULT 'pending',
  created_at  timestamptz DEFAULT now()
);
ALTER TABLE documents ENABLE ROW LEVEL SECURITY;
CREATE POLICY "users see own documents"
  ON documents FOR ALL USING (auth.uid() = user_id);
 
-- Chunks table
CREATE TABLE chunks (
  id            uuid PRIMARY KEY DEFAULT gen_random_uuid(),
  document_id   uuid REFERENCES documents(id) ON DELETE CASCADE,
  content       text NOT NULL,
  embedding     vector(384),
  chunk_index   int,
  created_at    timestamptz DEFAULT now()
);
CREATE INDEX ON chunks USING ivfflat (embedding vector_cosine_ops) WITH (lists = 100);
ALTER TABLE chunks ENABLE ROW LEVEL SECURITY;
CREATE POLICY "users see own chunks"
  ON chunks FOR ALL
  USING (document_id IN (SELECT id FROM documents WHERE user_id = auth.uid()));
 
-- Messages table
CREATE TABLE messages (
  id           uuid PRIMARY KEY DEFAULT gen_random_uuid(),
  document_id  uuid REFERENCES documents(id) ON DELETE CASCADE,
  user_id      uuid REFERENCES auth.users(id) ON DELETE CASCADE,
  role         text NOT NULL,
  content      text NOT NULL,
  created_at   timestamptz DEFAULT now()
);
ALTER TABLE messages ENABLE ROW LEVEL SECURITY;
CREATE POLICY "users see own messages"
  ON messages FOR ALL USING (auth.uid() = user_id);
 
-- Vector search function
CREATE OR REPLACE FUNCTION match_chunks(
  query_embedding  vector(384),
  match_count      int DEFAULT 5,
  filter_doc_id    uuid DEFAULT NULL
)
RETURNS TABLE (id uuid, content text, document_id uuid, similarity float)
LANGUAGE sql STABLE AS $$
  SELECT chunks.id, chunks.content, chunks.document_id,
    1 - (chunks.embedding <=> query_embedding) AS similarity
  FROM chunks
  WHERE (filter_doc_id IS NULL OR chunks.document_id = filter_doc_id)
  ORDER BY chunks.embedding <=> query_embedding
  LIMIT match_count;
$$;
  1. Go to Storage → create a bucket called documents (set to Private)
  2. Run these storage policies in SQL Editor:
CREATE POLICY "users upload own files"
  ON storage.objects FOR INSERT
  WITH CHECK (
    bucket_id = 'documents'
    AND auth.uid()::text = (storage.foldername(name))[1]
  );
 
CREATE POLICY "users read own files"
  ON storage.objects FOR SELECT
  USING (
    bucket_id = 'documents'
    AND auth.uid()::text = (storage.foldername(name))[1]
  );
  1. Go to Authentication → Providers and make sure Email is enabled. Turn off "Confirm email" for local development.

3. Get your API keys

Supabase keys — Project Settings → API:

  • Project URL
  • anon public key
  • service_role key (keep this secret) Groq API key — console.groq.com → API Keys → Create API Key

4. Create .env.local

NEXT_PUBLIC_SUPABASE_URL=https://your-project-id.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=your-anon-key
SUPABASE_SERVICE_KEY=your-service-role-key
GROQ_API_KEY=gsk_your-groq-key
NEXT_PUBLIC_APP_URL=http://localhost:3000

5. Run the app

npm run dev

Open http://localhost:3000.

Note: The first time you upload a PDF, the app will download the embedding model (~25MB) and cache it locally. This only happens once.


How chunking works

Documents are split into overlapping chunks to preserve context at boundaries:

chunkSize = 500 words
overlap   = 50 words
 
Chunk 1: words 1   → 500
Chunk 2: words 451 → 950
Chunk 3: words 901 → 1400
...

A 10-page PDF typically produces 20–50 chunks. Each chunk gets its own embedding (384 numbers representing its meaning). When a user asks a question, the question also gets an embedding and the 5 closest chunks are retrieved using cosine similarity.


Environment variables reference

Variable Where to find it Used in
NEXT_PUBLIC_SUPABASE_URL Supabase → Project Settings → API Client + Server
NEXT_PUBLIC_SUPABASE_ANON_KEY Supabase → Project Settings → API Client + Server
SUPABASE_SERVICE_KEY Supabase → Project Settings → API Server only
GROQ_API_KEY console.groq.com → API Keys Server only
NEXT_PUBLIC_APP_URL Your local or production URL Server only

Known limitations

  • File size: Large PDFs (50+ pages) take longer to process — the embedding step runs sequentially per chunk
  • Scanned PDFs: pdf2json extracts text only; scanned image-based PDFs will produce no text
  • Cold start: The first embedding call after a long idle period may take a few seconds as the model loads into memory
  • Groq rate limits: Free tier allows 30 requests/minute and 14,400 requests/day — more than enough for personal use

Possible improvements

  • Background job queue (Inngest / Trigger.dev) for async processing at scale
  • Support for scanned PDFs via OCR (Tesseract.js)
  • Multiple file upload in one session
  • Document renaming
  • Copy-to-clipboard button on AI answers
  • Export chat history as PDF or Markdown
  • Upgrade to OpenAI embeddings + GPT-4 for higher accuracy

What this project demonstrates

  • Full-stack development — Next.js App Router, API routes, server/client components
  • RAG architecture — the industry-standard pattern for grounded AI answers
  • Vector databases — pgvector, embeddings, cosine similarity search
  • Database security — Row Level Security, per-user data isolation
  • AI integration — local embedding models, LLM API calls, prompt engineering
  • Real product thinking — auth flows, file management, loading states, error handling

License

MIT — feel free to use this as a starting point for your own projects.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages