Ask general questions with an LLM, or upload documents and get answers grounded in those files via RAG.
web (Next.js)
│ HTTP + SSE
▼
api/ worker/ bull-board/ shared/
API (NestJS) ──Redis──► ingestion microservice queue UI
│ enqueue job parse → chunk → embed
▼ ▼
OpenAI (chat) Qdrant
shared/ is the contract both services depend on: job payload, pub/sub event shapes, structure-aware chunking, and file text extraction. Each service owns its own Nest adapters (OpenAI, Qdrant, Redis).
- General mode sends the conversation to the LLM with no retrieval.
- Documents mode embeds the question, searches Qdrant, and answers from retrieved chunks. Child chunks (~400 tokens) are embedded for precise search; matching parent sections (~800–1500 tokens) are sent to the LLM.
- Uploads return immediately (
202). Identical file bytes (SHA-256) reuse the existing document and skip ingest. Otherwise the API saves the file, enqueues a job, and the worker indexes it and publishes status over SSE.
| Layer | Project |
|---|---|
| Web | web/ Next.js |
| API | api/ NestJS |
| Ingestion worker | worker/ NestJS |
| Queue dashboard | bull-board/ Express + Bull Board |
| Shared contract | shared/ |
| Queue + pub/sub | Redis |
| Vectors | Qdrant |
| LLM | OpenAI |
- Start Qdrant and Redis:
docker compose up -d- Configure env files:
cp api/.env.example api/.env
cp worker/.env.example worker/.env
cp bull-board/.env.example bull-board/.envSet the same OPENAI_API_KEY in both. Keep DATA_DIR in worker/.env pointing at the API data folder (../api/data).
- Build shared, then run the processes:
cd shared && npm install && npm run buildcd api && npm install && npm run start:devcd worker && npm install && npm run start:devcd bull-board && npm install && npm run start:devcd web && npm install && npm run devOpen http://localhost:3000 (or 3001 if 3000 is in use). The API listens on http://localhost:4000. Bull Board is at http://localhost:4100/queues — keep it local; it has no auth.
GET /healthGET /documentsGET /documents/events— SSE document status streamPOST /documents— multipart fieldfile; returns202while indexingDELETE /documents/:idPOST /chat— SSE stream