| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Name | Name | Last commit date | ||
|---|---|---|---|---|
A vectorless, reasoning-based RAG that runs fully on your machine. Drop in your own documents (PDF, Word, Markdown, HTML, text); RagIndex builds a hierarchical "table-of-contents" tree per document that a local LLM reasons over to answer your questions — no vector database, no chunk-embedding store, no API keys, no cloud. Every answer comes with cited sources and a 0–100 confidence score so you know when to trust it (and when the system abstains).
100% on-device. The LLM and the embeddings are served locally by Ollama with small Qwen models. Nothing you upload or ask ever leaves your computer. One setup command installs and configures everything; one run command opens the app.
📊 The benchmark story — same recall as a vector DB, but it knows when it's wrong.
In a controlled run (web-crawled Wikipedia science, 6 docs / 90 chunks, local Ollama), Turing Tree retrieved with the same embeddings as a classic vector database — accuracy@5 = 1.00, identical recall, 0.00 cross-contamination. The edge is a confidence layer a vector DB doesn't have: it abstained on 100 % of off-topic questions (the vector DB answered every one silently and wrongly), with a +79.5 / 100 confidence separation between trustworthy and confused retrievals and zero false-abstentions on real questions. Re-indexing unchanged content is 198× faster via the content-hash cache. → Full method & caveats
flowchart LR
DOC[("Your documents\nPDF · DOCX · MD · HTML · TXT")] --> UP[upload_socket]
UP -->|extracted text| PI[pageindex_socket]
PI --> TREE["reasoning tree\n(per document)"]
Q(("Your question")) --> RAG[rag_socket]
TREE --> RAG
RAG --> ANS["grounded answer\n+ sources + confidence"]
Two commands set everything up and open the app — no API keys, no cloud, no GPU.
git clone https://github.com/1ssb/TuringTree.git RagIndex && cd RagIndex
# 1) Install everything (venv + deps + PageIndex model + Ollama + local models)
# Windows ...... powershell -ExecutionPolicy Bypass -File scripts\setup.ps1
# macOS / Linux . bash scripts/setup.sh
# 2) Open the app (builds the UI once, then opens http://127.0.0.1:8765)
# Windows ...... run.bat
# macOS / Linux . ./run.shThen, in the app, do these three things:
What makes this different from a normal RAG chatbot:
No Python on the machine? A packaged, double-click Windows app (no Python needed) is described in docs/desktop.md. Hit a snag? See Troubleshooting.
| Tool | Version | Notes |
|---|---|---|
| git | any recent | clones the repo and the vendored PageIndex model |
| Python | 3.10+ (3.13 recommended) | runs the backend, sockets, and setup |
| Node.js | 18+ (with npm) | builds the web UI |
| Ollama | latest | local model runtime — setup installs it for you if missing |
You do not need any API keys or a GPU. Plan for roughly 3–4 GB of disk for the default local models (qwen2.5:3b-instruct ≈ 2 GB + qwen3-embedding:0.6b ≈ 0.6 GB) and 8 GB+ RAM for comfortable CPU inference.
# 1. Get the repository
git clone https://github.com/1ssb/TuringTree.git RagIndex
cd RagIndex
# 2. One command does everything (idempotent — safe to re-run):
# - creates .venv and installs Python deps (root + backend)
# - seeds .env from .env.example
# - clones the PageIndex model and pins it to a known-good commit
# - installs the frontend's Node deps (npm install)
# - installs Ollama, starts it, and pulls the pinned local models| Your OS | Command |
|---|---|
| macOS / Linux | bash scripts/setup.sh |
| Windows (PowerShell) | powershell -ExecutionPolicy Bypass -File scripts\setup.ps1 |
| Windows (cmd) | scripts\setup.bat |
| Any OS (Python on PATH) | python scripts/setup.py |
Useful setup flags (any OS): --skip-ollama (don't install/start Ollama), --skip-models (set up Ollama but skip the model pulls). Re-running setup never re-downloads what already exists.
After setup, launch the desktop app. It builds the web UI the first time, then serves the UI and the API from a single local process and opens your browser at http://127.0.0.1:8765.
| Your OS | Command |
|---|---|
| Windows | run.bat (or double-click it in Explorer) |
| macOS / Linux | ./run.sh |
| Any OS | python scripts/run.py |
Flags are passed straight through to the launcher:
python scripts/run.py --no-browser # serve without opening a browser (CI/servers)
python scripts/run.py --port 8765 # pin a specific portPress Ctrl+C to stop. That's the whole story: setup once, then run.
Everything is served by a local Ollama runtime, so the project works completely offline once set up. The exact models are pinned in ollama-models.txt so every machine runs the same thing:
| Role | Model | Used by | Why |
|---|---|---|---|
| Chat + index (default) | qwen2.5:3b-instruct | answering and building the document tree | Small and fast on CPU, follows instructions well, and emits clean JSON (we avoid "thinking" models, whose extra tags break PageIndex's JSON). Using one model for both keeps a single model resident — no costly swaps. |
| Embeddings | qwen3-embedding:0.6b | confidence scoring + branch search | Small, fast, retrieval-tuned — same Qwen family. |
| Optional — higher quality | qwen2.5:7b-instruct | answering, if you enable it | Stronger reasoning, but noticeably slower on CPU. Left out of the default install to keep it lean. |
PageIndex and the chat path reach the model through LiteLLM with the ollama_chat/... provider; embeddings use the ollama/... provider — both point at your local server (OLLAMA_HOST, default http://localhost:11434). No request ever leaves your machine.
Prefer higher-quality answers? Pull the 7B and point chat at it:
ollama pull qwen2.5:7b-instruct
# then run with:
RAGINDEX_CHAT_TAG=qwen2.5:7b-instruct python scripts/run.pyWant different models entirely? Change the tags in both ollama-models.txt and config.py (OLLAMA_CHAT_TAG / OLLAMA_EMBED_TAG), then re-run setup.
Every setting lives in config.py and can be overridden with an environment variable (or a git-ignored .env — copy .env.example). No API keys are ever required. The knobs you're most likely to touch:
| Variable | Default | What it controls |
|---|---|---|
| OLLAMA_HOST | http://localhost:11434 | Where the local Ollama server listens. |
| RAGINDEX_CHAT_TAG | qwen2.5:3b-instruct | Chat/answer and index model tag. |
| RAGINDEX_EMBED_TAG | qwen3-embedding:0.6b | Embedding model tag. |
| RAGINDEX_INDEX_TAG | qwen2.5:3b-instruct | Model tag for the indexing/summary step. |
| RAGINDEX_LLM_MODEL / RAGINDEX_INDEX_MODEL / RAGINDEX_EMBED_MODEL | derived | Fully-qualified LiteLLM names (win over the tags above). |
| RAGINDEX_ANSWER_MAX_TOKENS | 280 | Max tokens per answer (length vs latency). |
| RAGINDEX_SELECT_LLM_MIN_DOCS | 4 | Only use an LLM to pick sections above this many docs (smaller indexes use an instant keyword fallback). |
| RAGINDEX_INDEX_CONCURRENCY | 4 | Max in-flight summary calls while indexing. |
| RAGINDEX_INDEX_CACHE | 1 | Content-hash summary cache on/off. |
| RAGINDEX_LLM_KEEP_ALIVE | 30m | Keep the model resident between calls (avoids cold reloads). |
| RAGINDEX_LLM_TIMEOUT | 180 | Per-call timeout, seconds. |
| RAGINDEX_WARMUP | 1 | Warm the chat model at startup. |
| RAGINDEX_DATA_DIR | per-user (app) / ./data (dev) | Where the index, caches, and uploads live. |
| RAGINDEX_USE_LOCAL_DATASET | 1 | Prefer the bundled dataset sample (offline). |
RagIndex is designed so any machine reproduces the same environment and behaviour:
Idempotent setup — scripts/setup.* is safe to re-run; it only fetches what's missing.
Pinned model — the vendored PageIndex is cloned and pinned to a known-good commit (override with RAGINDEX_PAGEINDEX_REF), so the engine is identical everywhere.
Pinned local models — exact Qwen tags are listed in ollama-models.txt and pulled by setup.
Deterministic generation — all model calls run at temperature 0.
Offline by default — a bounded dataset sample is committed in-repo, so no network is needed at run time.
Tested — install the dev deps and run the suite:
pip install -r requirements-dev.txt
python -m pytest -q # full backend + sockets suite
python tests/test_score_api.py # confidence-scorer contract testsNothing you upload or ask ever leaves your computer — the models run locally and the API binds to 127.0.0.1 only. The index, caches, uploads, and audit log are written to a per-user data directory:
| OS | Default data directory |
|---|---|
| Windows | %LOCALAPPDATA%\RagIndex |
| macOS | ~/Library/Application Support/RagIndex |
| Linux | $XDG_DATA_HOME/RagIndex or ~/.local/share/RagIndex |
(Running from source without the launcher — e.g. scripts/dev.py — uses ./data instead.) Point RAGINDEX_DATA_DIR anywhere to relocate all of it at once. To wipe the index, use Settings → Reset index in the app (or POST /api/index/reset); deleting the data directory clears everything.
With the venv and frontend deps installed, run the backend and frontend together (Ctrl-C stops both):
python scripts/dev.py # backend :8000 (FastAPI, /docs) + frontend :5173 (Vite)The repo ships a full containerised stack — Ollama + FastAPI backend + nginx-served frontend. A one-shot ollama-pull service downloads the models automatically and healthchecks gate the startup order (ollama → models pulled → backend → frontend), so the first run is turnkey.
Step 1 — Install Docker (bundles Docker Compose v2):
| OS | Get it |
|---|---|
| Windows / macOS | Docker Desktop |
| Linux | Docker Engine + Compose plugin |
Verify with docker --version and docker compose version.
Step 2a — Run on CPU (works everywhere, no GPU needed):
docker compose up -d --build
# first run pulls ~3 GB of models, then open http://localhost:5173Step 2b — Run on GPU (NVIDIA, optional, much faster inference). Install these in order, then use the GPU override file:
docker compose -f docker-compose.yml -f docker-compose.gpu.yml up -d --build
docker compose exec ollama nvidia-smi # confirm the GPU is visible in-containerManage the stack:
docker compose logs -f # follow logs
docker compose ps # services + health status
docker compose down # stop (keeps the model/data volumes)
docker compose down -v # stop and delete the ollama + ragdata volumesConfigure ports and models with a .env file (copy from .env.example): FRONTEND_PORT, BACKEND_PORT, OLLAMA_PORT, RAGINDEX_CHAT_TAG, RAGINDEX_EMBED_TAG. Models live in the named ollama volume (pulled once); the index persists in the ragdata volume.
Produce a self-contained app (no Python needed) and a Windows installer:
pip install -r requirements-dev.txt
python scripts/build_desktop.py # → dist/RagIndex/ (bundles the built UI)Then package dist/RagIndex/ with Inno Setup using packaging/windows/ragindex.iss. Full build/install/data-location guide: docs/desktop.md.
Building a tree is ~100% local LLM inference (a per-heading summary fan-out plus one document-description call), so RagIndex makes it cheaper out of the box and tunable via env:
python scripts/try_pipeline.py # dataset → PageIndex demo (prints a tree)
python scripts/index_branches.py list # list origin/* branches of the model
python scripts/index_branches.py build # build the semantic branch index
python scripts/index_branches.py search "markdown tree" # ask in plain EnglishWhen the app is running, interactive docs are at /docs (Swagger UI). The core endpoints (all under /api):
| Method | Path | Purpose |
|---|---|---|
| GET | /api/health | Component status (Ollama, dataset, index). |
| GET | /api/index/status | Is the index built, and over which documents? |
| GET | /api/index/tree | The indexed corpus as a nested tree. |
| POST | /api/index/build | Build/rebuild from uploaded files (or the bundled dataset). |
| POST | /api/index/build_async | Start a background build; returns a job_id. |
| GET | /api/index/jobs/{id} | Poll a background build (status, progress, ETA). |
| POST | /api/index/reset | Clear the index (start empty). |
| GET / POST | /api/index/export · /api/index/import | Export / restore an index bundle. |
| POST | /api/chat | Ask a question → {answer, sources, confidence}. |
| POST | /api/ingest · GET /api/ingest/log | Ingest one document with provenance · audit trail. |
| GET / POST | /api/branches · /api/branches/build · /api/branches/search | Git-branch semantic index. |
| GET | /api/dataset/sample | Sample chunks from the bundled dataset. |
| Symptom | Fix |
|---|---|
| "Ollama not detected" at startup | Start it: ollama serve (or open the Ollama app), then re-run. Branch search still works offline. |
| Answers are slow | It's local CPU inference. The default qwen2.5:3b-instruct is the fast path; shorten answers with RAGINDEX_ANSWER_MAX_TOKENS. Only switch to the 7B if you want quality over speed. |
| Port 8765 already in use | python scripts/run.py --port 8770 (any free port). |
| npm not found / UI didn't build | Install Node.js 18+ and re-run setup, or build manually: npm --prefix frontend install && npm --prefix frontend run build. |
| Index looks stale / want to start over | Settings → Reset index in the app, or POST /api/index/reset. |
| A model is missing | Re-pull it: ollama pull qwen2.5:3b-instruct (and qwen3-embedding:0.6b). |
Like a wall socket lets you plug in any appliance without rewiring the house, each module in sockets/ is a thin connector with a simple interface, so the rest of the code never touches the messy details of litellm, PageIndex, or git.
| Socket | File | Job |
|---|---|---|
| Upload | sockets/upload_socket.py | Extract text from PDF/DOCX/HTML/MD/TXT uploads. |
| Model | sockets/pageindex_socket.py | Build a reasoning tree from a document's text. |
| RAG | sockets/rag_socket.py | Vectorless retrieval + grounded answer + confidence. |
| Ingest | sockets/ingest_socket.py | Fold an uploaded doc into the shared index with provenance. |
| Confidence | sockets/topo_confidence_socket.py · sockets/rag_metrics.py | The retrieval-confidence engine + score() facade. |
| Dataset | sockets/dataset_socket.py | Stream/regroup the bundled Wikipedia-science sample (demos/benchmarks). |
| Branch index | sockets/branch_index_socket.py | Semantically index origin/* branches of PageIndex. |
A small, embedding-agnostic scorer that says how much to trust a RAG retrieval — a 0–100 confidence KPI, four explainable drivers, and an actionable verdict (ANSWER / REVIEW / ABSTAIN / ESCALATE), with no extra LLM call and no labels. It analyses the shape of the relevance your query induces over the retrieved passages, plus an absolute topical-grounding check.
from sockets.rag_metrics import score
m = score(query_embedding, passage_embeddings, texts=passages)
m.verdict # "ANSWER" | "REVIEW" | "ABSTAIN" | "ESCALATE"
print(m.narrate()) # human-readable, range-based explanation
log(m.to_dict()) # flat JSON contract for dashboards / gatingTry it on real data, or run the broad verification sweep:
python scripts/ask.py "who funds and leads FasterCures?" # retrieved support + explained scores
python scripts/verify_metrics.py --docs 3 --chunks 500 # docs x query-types, PASS/FAIL
python tests/test_score_api.py # 25 contract/invariant/edge testsFull documentation: docs/confidence_scoring.md — the metrics, the topology behind them, the API + JSON contract, integration, calibration, and limitations.
How does vectorless, confidence‑scored RAG compare to a classic vector database? A reproducible harness (benchmarks/, scripts/benchmark_rag.py) runs both over web‑crawled Wikipedia‑science documents using the same local embeddings, so recall is identical and the comparison isolates the reliability layer.
Headline (6 docs / 90 chunks, top‑k = 5):
| Vector DB | RagIndex | |
|---|---|---|
| retrieval accuracy@5 | 1.00 | 1.00 (parity) |
| off‑topic queries answered (silently wrong) | 100 % | 0 % |
| off‑topic queries abstained | 0 % | 100 % |
| confidence separation (in‑corpus vs off‑topic) | — | +79.5 / 100 |
python scripts/benchmark_rag.py --docs 6 --per-doc 15 --k 5 # main comparison
python scripts/benchmark_rag.py --scaling 30 60 90 # per‑query latency scalingSame recall as a vector DB — but it knows when it's wrong. Full method, metrics (cross‑contamination, source entropy, Gini) and results: docs/benchmark.md.
Every push and pull request runs the CI pipeline (.github/workflows/ci.yml):
CodeQL scans Python and JavaScript/TypeScript for security issues and Dependabot keeps dependencies patched. Run the same checks locally:
pytest -q # backend
cd frontend && npm ci && npm run build && npm run test # frontendBuild note. npm run build type-checks the whole src/ tree, including the *.test.tsx files, so the dev dependencies must be present — always npm ci (not --omit=dev) before building.
For the scientific evaluation — same recall as a vector DB, but it abstains on 100% of off-topic questions (a +79.5 / 100 confidence separation) — see Benchmarks vs a vector-DB baseline and docs/benchmark.md.
For demos and the benchmark harness, a bounded sample of the Hugging Face dataset Laz4rz/wikipedia_science_chunked_small_rag_512 (default 30k rows ≈ 13 MB) lives in dataset/, committed directly with Git — small enough that it needs no Git LFS.
The dataset socket prefers this local file, so the pipeline runs offline. If it's missing (or RAGINDEX_USE_LOCAL_DATASET=0), it falls back to streaming from Hugging Face.
Regenerate or resize it any time:
python scripts/make_dataset_sample.py # uses the config default
RAGINDEX_SAMPLE_ROWS=100000 python scripts/make_dataset_sample.pyFor a much larger sample, track dataset/*.parquet with Git LFS (git lfs track "*.parquet"). The data derives from Wikipedia (CC BY-SA); keep that attribution if you redistribute it.
If Ollama is running it uses true semantic embeddings from qwen3-embedding:0.6b; otherwise it falls back to a dependency-free offline embedding. See the heavily-commented sockets/branch_index_socket.py.
RagIndex/ (the desktop app is branded "Turing Tree") ├── run.bat / run.sh # open the app in one step (after setup) ├── config.py # one control panel for every setting ├── ollama-models.txt # exact local Qwen models pulled by setup ├── requirements*.txt # runtime deps (+ requirements-dev.txt for tests/build) ├── .env.example # optional local overrides (NO API keys needed) ├── backend/app/ # FastAPI app (routers: health, chat/index, ingest, …) ├── frontend/ # React + Vite web UI (built to frontend/dist) ├── desktop/launcher.py # serve UI + API as one local app, open the browser ├── sockets/ # the integration layer (see "socket architecture") ├── scripts/ │ ├── setup.sh / .ps1 / .bat / .py # one-command, cross-platform setup │ ├── run.py # build-if-needed + launch the desktop app │ ├── dev.py # backend :8000 + frontend :5173 (hot reload) │ ├── build_desktop.py # PyInstaller build → dist/RagIndex/ │ ├── make_dataset_sample.py # (re)build the bundled dataset sample │ ├── index_branches.py # build / search the branch index │ ├── ask.py · try_pipeline.py · verify_metrics.py · benchmark_rag.py ├── packaging/ # PyInstaller spec + Windows Inno Setup installer ├── benchmarks/ # vector-DB baseline + comparison harness ├── samples/ # sample documents (incl. sample-docs.zip) to test uploads ├── docs/ # desktop.md · confidence_scoring.md · benchmark.md ├── dataset/ # bundled dataset SAMPLE (parquet, committed) ├── tests/ # pytest suite + test_score_api.py ├── vendor/PageIndex/ # the model, cloned & pinned by setup (NOT committed) └── data/ # index, caches, uploads (git-ignored, regenerable)
A team effort by the Turing Tree interns, developed as part of the Microsoft Global Intern Hackathon 2026:
| Back | FazBrowse Home | New Git URL |