A full-stack app to chat with your PDFs using retrieval-augmented generation (RAG).
- Frontend: React + Vite (no UI library, custom CSS)
- Backend: Python + FastAPI
- Database: PostgreSQL + pgvector extension
- AI: OpenAI
text-embedding-3-small+gpt-4o
Upload PDF → parse text → chunk into 500-token pieces
→ embed each chunk (OpenAI) → store in pgvector
Ask question → embed question → cosine similarity search
→ retrieve top 5 chunks → GPT-4o with context → stream response
- Docker + Docker Compose
- Python 3.11+
- Node 18+
- OpenAI API key
docker-compose up -dThis starts a PostgreSQL 16 instance with pgvector pre-installed on port 5432.
cd backend
cp .env.example .env
# Edit .env and add your OPENAI_API_KEY
# DATABASE_URL is already set for the docker-compose db
pip install -r requirements.txt
uvicorn main:app --reloadBackend runs at http://localhost:8000 Swagger docs at http://localhost:8000/docs
cd frontend
npm install
npm run devFrontend runs at http://localhost:5173
To avoid unauthorized use and protect your OpenAI credits, you can require an access code:
- In your
.env, setACCESS_CODEto a secret string (e.g.ACCESS_CODE=my-secret-code). - Restart the backend. The app will then show an “Enter access code” screen; only requests that send the correct code (in the
X-Access-Codeheader) can list documents, upload, delete, or chat. - Share the code only with people you want to have access. The code is stored in the browser session until they click Lock or close the tab.
If ACCESS_CODE is not set, the app works as before with no gate.
- Open http://localhost:5173 (enter the access code if you set
ACCESS_CODE). - Drag and drop a PDF into the sidebar (it will be parsed, chunked, and embedded — takes a few seconds)
- Ask questions in the chat box
- Optionally select specific documents from the sidebar to narrow the search scope
- Chunking: PDFs are split into ~500 token pieces with 50 token overlap so context isn't cut off at boundaries
- Embeddings: Each chunk is converted to a 1536-dim vector capturing its semantic meaning
- Cosine similarity: When you ask a question, its embedding is compared to all chunk embeddings — closest ones win
- RAG prompt: The top 5 matching chunks are injected into GPT-4o's context along with your question
- Streaming: The response is streamed token-by-token for a responsive feel
- Backend: Deploy to Railway or Render — both support Python and Postgres
- Frontend: Deploy to Vercel — just point it at the
frontendfolder - Database: Railway has a managed Postgres addon with pgvector support
Remember to update the CORS origin in main.py and the API URL in App.jsx when deploying.