A local-first, offline-capable document RAG (Retrieval-Augmented Generation) desktop application. Point it at a folder of PDFs, Word documents, and images (including scanned/photographed pages), and chat with an AI that answers questions with citations back to the exact source file.
Everything runs on the user's machine — documents, embeddings, vector search, and OCR never leave the device. Only the optional LLM call (Ollama local, or Groq cloud) needs network access, and that's a deliberate choice per-deployment.
- For end users
- System requirements
- Privacy & data handling
- Screenshots
- Why this exists
- Feature overview
- Architecture
- Data flow
- Tech stack
- Project layout
- API reference
- Settings
- Getting started (development)
- Packaging a Windows installer
- How OCR is bundled (no separate install)
- Known limitations
- Troubleshooting
- Contributing
- License
- Acknowledgments
No coding knowledge needed — this section is for someone who just wants to use the app.
- Install: run
Judgment Finder AI Setup <version>.exe. Pick an install folder if prompted; everything else (OCR engine, AI models, Python runtime) is bundled inside, nothing else to download or install. - First launch: the app takes ~20–30 seconds to start the first time while it loads the AI model — you'll see an "App is starting..." spinner.
- Pick a folder: choose the folder containing your PDFs, Word documents, or images. The app scans it and shows live progress as each file is read (including OCR for scanned/photographed pages).
- Ask questions: open the chat, type a question in English or Urdu, and get an answer with clickable source chips — click one to open that exact file.
- Keep adding files: anything dropped into the same folder later is picked up automatically (if "Watch folder in real time" is on in Settings) — no need to re-pick the folder.
- Choose your AI provider: in Settings, use Ollama for a fully offline experience (requires Ollama installed separately with a model pulled), or Groq for faster cloud-based answers (requires a free API key from console.groq.com).
If something doesn't look right, check Troubleshooting below.
| Minimum | |
|---|---|
| OS | Windows 10/11, 64-bit |
| Disk space | ~2 GB free (installer + extracted runtime, models, and your indexed data) |
| RAM | 4 GB minimum, 8 GB+ recommended (more if also running Ollama locally) |
| Internet | Not required, except for Groq cloud answers or pulling Ollama models |
| Admin rights | Not required — installs to the current user's profile |
- Your documents are read and indexed entirely on your own machine. Text extraction, OCR, embeddings, and vector search all run locally — none of it is uploaded anywhere.
- All data (
settings.json, the SQLite tracking/chat database, and the vector index) is stored locally under%USERPROFILE%\.rag-desktop\. Uninstalling the app does not delete this folder — remove it manually if you want a clean wipe. - The only thing that can leave your machine is the question text and retrieved document snippets sent to an LLM, and only if you choose the Groq cloud provider in Settings. Choosing Ollama keeps the entire pipeline, including the chat model, fully offline.
- Your Groq API key is stored locally in
settings.jsonand is never sent anywhere except directly to Groq's API.
Dashboard — indexed files, status, and chunk counts:
Settings — LLM provider, OCR languages, Top-K, and watch/convert options:
Chat — multi-session sidebar, cited answers with clickable sources, and the AI disclaimer:
Searching through hundreds of legal judgments, scanned PDFs, and WhatsApp screenshot exports by hand doesn't scale. Judgment Finder AI indexes a folder once, keeps it in sync as files are added, and lets you ask plain English/Urdu questions like "what did the court decide about the wife's inheritance share?" and get back an answer with the exact file (and page, where applicable) it came from.
- Multi-format ingestion — PDF (text + OCR fallback for scans),
.docx, and images (.png/.jpg/.jpeg/etc.) via Tesseract OCR. - Multilingual semantic search — embeddings model supports English and Urdu (and other languages) so a question in one language can match content in another.
- Incremental indexing — a SQLite-backed file tracker only
re-processes files that are new or changed; a
watchdogfile-system watcher picks up additions in real time without a manual rescan. - ChatGPT-style chat — multi-session chat history persisted in SQLite, collapsible sidebar, session rename/delete.
- Accurate citations — only sources the model actually referenced in its answer are shown as clickable chips; clicking one opens the source file directly.
- Pluggable LLM backend — local Ollama (fully offline) or Groq cloud API, switchable from Settings.
- Self-contained Windows installer — ships its own Python runtime, ML models, and Tesseract OCR binaries. No Python, no Tesseract install, no admin rights required on the end-user machine.
flowchart TB
subgraph Electron["Electron Desktop Shell"]
Main["main.ts<br/>(process lifecycle, IPC, native dialogs)"]
Preload["preload.ts<br/>(contextBridge)"]
Renderer["React UI<br/>(Onboarding / Dashboard / Chat)"]
Main -- spawns --> Backend
Main <-- IPC --> Preload
Preload <-- window.electronAPI --> Renderer
end
subgraph Backend["FastAPI Sidecar (127.0.0.1:8756)"]
API["main.py<br/>REST + SSE endpoints"]
Indexer["indexer.py<br/>extract -> chunk -> embed -> store"]
Watcher["watcher.py<br/>watchdog file events"]
Tracking["tracking.py<br/>SQLite: files + chat history"]
Chat["chat.py<br/>retrieve -> prompt -> LLM -> cite"]
Extractors["extractors/<br/>pdf.py - docx_reader.py - image.py"]
OCR["ocr_setup.py<br/>bundled Tesseract resolver"]
Embed["embeddings.py<br/>sentence-transformers"]
VectorStore["vectorstore.py<br/>ChromaDB"]
LLM["llm/<br/>ollama_provider.py - groq_provider.py"]
API --> Indexer
API --> Chat
Watcher --> Indexer
Indexer --> Extractors
Extractors --> OCR
Indexer --> Tracking
Indexer --> Embed
Embed --> VectorStore
Chat --> VectorStore
Chat --> LLM
Chat --> Tracking
end
Renderer -- "HTTP / SSE" --> API
subgraph External["Local / External Services"]
Ollama["Ollama (localhost)"]
Groq["Groq Cloud API"]
end
LLM --> Ollama
LLM --> Groq
subgraph Storage["Disk (~/.rag-desktop)"]
SQLite["tracking.db<br/>(files, chat_sessions, chat_messages)"]
Chroma["chroma/<br/>(vector index)"]
SettingsJSON["settings.json"]
end
Tracking --> SQLite
VectorStore --> Chroma
API --> SettingsJSON
sequenceDiagram
participant U as User
participant R as React UI
participant E as Electron Main
participant API as FastAPI
participant Idx as Indexer
participant Ext as Extractor (PDF/DOCX/Image)
participant OCR as Tesseract (bundled)
participant Emb as Embedding Model
participant DB as ChromaDB + SQLite
U->>R: Choose folder
R->>E: selectFolder() (native dialog)
E-->>R: folder path
R->>API: POST /folder { path }
API->>Idx: scan folder
loop for each new/changed file
Idx->>Ext: extract(file)
alt scanned/image content
Ext->>OCR: run OCR
OCR-->>Ext: extracted text
else native text (PDF/DOCX)
Ext-->>Idx: extracted text
end
Idx->>Idx: chunk(text)
Idx->>Emb: embed(chunks)
Emb-->>Idx: vectors
Idx->>DB: upsert chunks + vectors + file row
end
API-->>R: SSE progress updates (/status/stream)
R-->>U: live indexing progress
sequenceDiagram
participant U as User
participant R as Chat UI
participant API as FastAPI
participant V as ChromaDB
participant L as LLM (Ollama/Groq)
participant DB as SQLite (chat history)
U->>R: Type question
R->>API: POST /chat { session_id, question }
API->>V: similarity search (top_k)
V-->>API: top-k chunks + source metadata
API->>L: prompt (question + retrieved context)
L-->>API: generated answer (with inline [source: ...] refs)
API->>API: keep only sources actually referenced in answer
API->>DB: save user + assistant messages
API-->>R: { answer, sources[] }
R-->>U: render answer + clickable source chips
U->>R: click a source chip
R->>API: POST /open-file { path }
API-->>U: opens file in default OS viewer
| Layer | Choice | Why |
|---|---|---|
| Desktop shell | Electron + TypeScript | Native folder picker, file opening, single packaged .exe |
| UI | React + Tailwind CSS v4 + Zustand | Small, fast, no heavyweight component library needed |
| Backend | FastAPI (Python), spawned as a sidecar process | REST + Server-Sent Events for live indexing progress |
| PDF text | PyMuPDF | Fast native text extraction |
| Word docs | python-docx | Native .docx parsing |
| OCR | Tesseract (bundled binaries) + pytesseract | Scanned PDFs and photographed/screenshot images |
| Embeddings | sentence-transformers (paraphrase-multilingual-MiniLM-L12-v2) |
One model handles English + Urdu (+ other languages) |
| Vector store | ChromaDB (persistent, local) | Embedded, no separate server process |
| File tracking / chat history | SQLite | Zero-config, single file, survives restarts |
| File watching | watchdog | Real-time incremental indexing on file add/change |
| LLM | Ollama (local) or Groq (cloud), pluggable | User chooses fully offline vs. faster cloud inference |
| Packaging | PyInstaller (backend) + electron-builder/NSIS (installer) | Single .exe installer, no Python/Tesseract setup for end users |
rag-desktop/
├── electron/ Electron main process (TypeScript)
│ ├── main.ts window lifecycle, IPC handlers, spawns backend
│ └── preload.ts contextBridge -> window.electronAPI
├── src/ React renderer
│ ├── pages/ Onboarding.tsx, Dashboard.tsx, Chat.tsx
│ ├── components/ IndexingStatus, MessageBubble, SourceChip, PoweredByFooter
│ ├── lib/api.ts typed fetch wrapper around the FastAPI sidecar
│ └── store.ts Zustand app state (settings, current view)
├── backend/ FastAPI sidecar (Python)
│ ├── main.py REST + SSE API surface
│ ├── indexer.py orchestrates extract -> chunk -> embed -> store
│ ├── watcher.py watchdog-based folder watcher
│ ├── chunker.py text chunking with overlap
│ ├── embeddings.py sentence-transformers wrapper
│ ├── vectorstore.py ChromaDB client wrapper
│ ├── tracking.py SQLite: file index + chat sessions/messages
│ ├── chat.py retrieval -> prompt -> LLM -> citation filtering
│ ├── config.py Settings model, persisted to settings.json
│ ├── ocr_setup.py resolves bundled vs. system Tesseract path
│ ├── pdf_to_word.py optional PDF -> DOCX conversion
│ ├── extractors/ pdf.py, docx_reader.py, image.py
│ ├── llm/ ollama_provider.py, groq_provider.py, base.py
│ └── build.spec PyInstaller spec for freezing the backend
├── vendor/tesseract/ Bundled Tesseract binary + DLLs + tessdata
├── build/icon.ico App icon (multi-resolution)
└── package.json electron-builder config + npm scripts
Runtime data lives outside the install directory, in
%USERPROFILE%\.rag-desktop\:
~/.rag-desktop/
├── settings.json user settings (provider, OCR languages, top-k, ...)
├── tracking.db SQLite: indexed files, chat sessions, chat messages
└── chroma/ persistent vector index
The FastAPI sidecar listens on http://127.0.0.1:8756.
| Method | Path | Purpose |
|---|---|---|
POST |
/folder |
Set the folder to index and start scanning |
GET |
/status |
One-shot indexing/watch status |
GET |
/status/stream |
SSE stream of indexing progress |
GET |
/files |
List indexed files with status and chunk counts |
POST |
/reindex |
Force a full re-scan of the current folder |
POST |
/chat |
Ask a question within a chat session |
POST |
/chat/sessions |
Create a new chat session |
GET |
/chat/sessions |
List chat sessions |
GET |
/chat/sessions/{id}/messages |
Get message history for a session |
DELETE |
/chat/sessions/{id} |
Delete a chat session |
POST |
/convert-pdf |
Convert a PDF to a .docx |
POST |
/open-file |
Open a file path in the OS default viewer |
GET / POST |
/settings |
Read/update persisted settings |
| Setting | Values | Notes |
|---|---|---|
llm_provider |
ollama | groq |
Switch between fully local and cloud inference |
ollama_model |
e.g. qwen2.5-coder:7b |
Must already be pulled locally |
groq_api_key |
string | Stored locally; masked as a password field in the UI |
ocr_languages |
eng, urd |
Tesseract language packs used for image/scanned-PDF OCR |
top_k |
1–15 | How many chunks are retrieved as context per question — higher catches more but adds noise and latency |
watch_realtime |
bool | Auto-index new files dropped into the folder without a manual rescan |
auto_convert_pdf_to_word |
bool | Also produce an editable .docx next to each indexed PDF |
Prerequisites: Node.js, Python 3.11+, and either Ollama running locally or a Groq API key.
npm install
cd backend
python -m venv venv
venv\Scripts\pip install -r requirements.txt
cd ..Tesseract is required for OCR in dev mode (packaged builds bundle their own
copy — see below). Install it via the
UB-Mannheim build and make
sure tesseract.exe is on PATH, or drop urd.traineddata etc. into
~/.rag-desktop/tessdata.
Run everything (Vite + Electron + backend) with hot reload:
npm run dev:allIf your shell has
ELECTRON_RUN_AS_NODE=1set, unset it first — Electron refuses to launch its GUI process with that flag set.
npm run packageThis runs, in order:
vite build— production React bundletsc -p electron— compile the Electron main/preload process- PyInstaller (
backend/build.spec) — freezes the FastAPI backend + all Python dependencies (torch, chromadb, onnxruntime, etc.) into a standalonebackend/dist/main/folder (~900MB; these libraries dominate the size) electron-builder— packages everything intorelease/Judgment Finder AI Setup <version>.exe(NSIS installer) plus an unpackedrelease/win-unpacked/folder for quick testing
Notes:
- A clean PyInstaller build takes 10–15 minutes.
- First launch of a packaged build takes ~20–30 seconds before
/statusresponds — it's loading the embedding model from inside the frozen bundle (and Windows Defender scanning a large new exe for the first time). The UI shows an "App is starting..." spinner during this window.
End users should never have to manually install Tesseract. To guarantee that:
vendor/tesseract/containstesseract.exe, its ~50 dependent DLLs, andtessdata/(eng,urd,osd) — trimmed from a full Tesseract install by dropping training tools and docs (176MB vs. 239MB).package.json'sextraResourcescopies that folder intoresources/tesseract/next to the packaged backend exe.backend/ocr_setup.pydetects whether it's running frozen (sys.frozen) and, if so, pointspytesseractdirectly atresources/tesseract/tesseract.exe— no PATH lookup, no registry, no admin rights needed. In dev mode it falls back to a system PATH /C:\Program Files\Tesseract-OCRinstall.
This was verified end-to-end: a fresh image dropped into a watched folder on a clean packaged build was OCR'd correctly and the extracted text was retrievable via chat with an accurate citation, with no Tesseract installed on the test machine outside of what the installer shipped.
- OCR quality on heavily stylized Urdu Nastaliq calligraphy (e.g. low-res phone screenshots) can be poor — this is a Tesseract model limitation, not a retrieval/embedding bug.
- Citation matching uses filename substring containment against the LLM's
answer text rather than strict structured citation parsing, since LLMs
don't always reproduce the exact
[source: ..., page N]format requested in the prompt. - Only one folder can be indexed at a time per app instance.
| Symptom | Cause | Fix |
|---|---|---|
| "Backend sidecar is not reachable" | Backend process failed to start or is still loading models | Wait for the startup spinner; check Electron logs for the spawned process's stderr |
| Electron window never opens | ELECTRON_RUN_AS_NODE=1 set in the shell |
unset ELECTRON_RUN_AS_NODE before launching |
| Rebuild fails with file-lock/EPERM errors | A previous app instance (or orphaned main.exe/Electron process) still holds files open |
Kill Judgment Finder AI.exe / main.exe / stray node.exe processes, then rebuild |
| OCR returns empty/garbage text | Source image is low quality or in a script not covered by the loaded language packs | Enable the relevant OCR language in Settings; quality of the source image is the ceiling |
| Groq answers fail | Invalid or missing API key | Re-enter the key in Settings (stored locally, masked in the UI) |
- Fork/branch off
main. - Follow the dev setup and run
npm run dev:all. - Keep changes scoped — one feature/fix per PR, with a short description of why.
- Before opening a PR, sanity-check:
npm run lintcd backend && venv\Scripts\python -m pytest(extractor tests)- The app still boots via
npm run dev:alland can index a test folder.
- Open a PR against
maindescribing the change and how you tested it.
Bug reports and feature requests are welcome via GitHub Issues.
All rights reserved. This project is currently closed-source/proprietary; no license is granted to copy, modify, or redistribute without explicit permission from the author.
- Tesseract OCR and the UB-Mannheim Windows builds
- sentence-transformers for multilingual embeddings
- ChromaDB for the embedded vector store
- Ollama and Groq for LLM inference
- PyMuPDF and python-docx for document parsing
Powered by Mavi


