SauLM is an AI-powered, persona-based legal assistant. Choose a distinct persona to get concise, law-focused guidance tailored to different styles:
- Harvey Specter
- Saul Goodman
- Matt Murdock
SauLM uses retrieval-augmented generation (RAG) to analyze uploaded legal documents and answer user questions. It combines a Next.js frontend with a Node/Express backend, Google Generative AI embeddings, and a Qdrant vector database. Background processing is handled via BullMQ with Redis-compatible Valkey.
- Frontend: Next.js app at the repo root
- Backend: Express API in
server/- Queue producer in
server/src/index.ts(enqueues file-processing jobs) - Worker in
server/src/worker.ts(extracts, chunks, embeds, and stores vectors)
- Queue producer in
- Vector DB: Qdrant
- Queue: BullMQ with Valkey (Redis-compatible)
- Embeddings/GenAI: Google Generative AI (Gemini)
- Persona-based legal assistance (Harvey, Saul, Matt)
- Upload PDFs or DOC/DOCX; background processing and vector storage in Qdrant
- General chat over the full vector collection
- File-scoped chat constrained to a specific uploaded document
- Node.js 18+
- npm (or pnpm/yarn)
- Docker (for Valkey and Qdrant via Docker Compose)
- Google API key for embeddings
# Neon Postgres
DATABASE_URL=postgresql://<user>:<password>@<host>/<db>?sslmode=require
# NextAuth
NEXTAUTH_URL=http://localhost:3000
NEXTAUTH_SECRET=your_nextauth_secret
# Google OAuth
GOOGLE_CLIENT_ID=your_google_client_id
GOOGLE_CLIENT_SECRET=your_google_client_secretNotes:
- Use your Neon connection string for
DATABASE_URL. - In production, set
NEXTAUTH_URLto your deployed URL and rotateNEXTAUTH_SECRET.
GOOGLE_API_KEY=your_google_api_key
QDRANT_URL=http://localhost:6333
QDRANT_COLLECTION=pdf-docs
# Optional (defaults used in code / docker compose)
# REDIS_HOST=127.0.0.1
# REDIS_PORT=6379# From repo root
npm install
npm run dev
# http://localhost:3000cd server
npm i
# Start dependencies (Valkey + Qdrant)
docker compose up -d
# Start API server
npm run dev
# http://localhost:5000Run the worker in a separate terminal to process uploads:
cd server
npm run workerGET /— Health checkPOST /pdf/upload— Upload a document (form-data field:pdf)- Enqueues a job; the worker extracts, chunks, embeds, and stores in Qdrant
POST /chat— Ask a general question over the entire collection- Body:
{ "query": string }
- Body:
POST /pdf/:fileId/chat— Ask a question limited to a specific file- Params:
fileId(your chosen identifier) - Body:
{ "query": string }
- Params:
Example upload:
curl -F "pdf=@/path/to/file.pdf" http://localhost:5000/pdf/uploadExample chat:
curl -X POST http://localhost:5000/chat \
-H "Content-Type: application/json" \
-d '{"query":"What are the key obligations?"}'server/docker-compose.ymlbrings up Valkey on 6379 and Qdrant on 6333.- Multer stores uploads to
uploads/on disk; ensure write permissions. - Keep the worker running to process uploads into the vector DB.
- Redis/Valkey connection refused: ensure
docker compose up -dis running underserver/. - Qdrant connection errors: confirm
QDRANT_URLand that the container is healthy. - Missing/invalid
GOOGLE_API_KEY: verify the key and project access for embeddings.
From repo root (frontend):
npm run devFrom server/ (backend):
npm run build
npm run dev
npm run workerWe welcome contributions! See CONTRIBUTING.md for setup, branching, commit style, testing, and pull request guidance.