Chat with your PDFs — fully local. Upload a document, ask questions, get answers grounded in the text with source citations. No API keys. No cloud LLM.
Stack: NestJS · local embeddings (all-MiniLM-L6-v2) · Ollama (llama3.1:8b) · hybrid retrieval
Prerequisites
npm startThat single command will:
- Install npm dependencies (if needed)
- Start Ollama if it isn’t running
- Pull
llama3.1:8b(first run only) - Start the API + chat UI
Upload a PDF in the UI, then ask questions.
Copy the example env file and edit as needed:
cp .env.example .envPORT=3000
OLLAMA_URL=http://localhost:11434
OLLAMA_MODEL=llama3.1:8bExample with a smaller model (tighter RAM):
OLLAMA_MODEL=llama3.2:3b npm start| Surface | URL |
|---|---|
| Chat UI | http://localhost:3000 |
| Health | GET /health |
| Upload PDF | POST /ingestion/upload (form field file) |
| Ask | POST /query/ask { "question": "..." } |
| Status | GET /ingestion/status |
| Clear store | DELETE /ingestion/clear |
- Ingest — PDF text → overlapping chunks → local embeddings → persisted store (
data/store.json) - Ask — embed the question → hybrid search (vectors + keywords + neighbor chunks) → prompt Ollama with cited context → answer + sources
public/ # Docor chat UI
scripts/start.mjs # one-command launcher
src/
embeddings/ # MiniLM via @xenova/transformers
vector-store/ # in-memory + hybrid search + disk persistence
llm/ # Ollama client
ingestion/ # PDF upload → chunks → embeddings
query/ # retrieval + prompt + answer
npm run start:api # Nest watch mode, no Ollama bootstrap
npm run build && npm run start:prod- First embedding run downloads ~80MB (cached afterward).
- Re-uploading the same filename replaces its old chunks.
- If answers look wrong, check the sources under each reply — that shows what was retrieved.