Skip to main content

dirag

Search a folder of PDF books, read the page each result comes from, and get answers quoted from the books.

dirag indexes a library of PDFs once, then answers a query with ranked passages: book, chapter, page and a snippet. Each result opens the rendered page with the passage marked on it, or the whole book in the built-in reader at that page.

  • Passages carry their chapter, taken from each PDF's table of contents.
  • Three retrieval modes: lexical (BM25), semantic (embeddings), and hybrid (both, fused by rank). An optional rerank reads query and passage together: neural (a local cross-encoder) or an LLM.
  • Results are capped per book, so one long series does not fill the list.
  • The AI answer quotes the books: a language model picks the passages and the words, dirag checks every quote against the text and shows it in the book's own words, marked on its page.
  • The reader remembers the page you stopped on in every book.
  • Everything runs locally. No account, no network after the first downloads.

Start

uvx dirag

That needs only uv. The browser opens on the app: choose the library folder (the folder button at the top of the shelf) and press Index new. Subfolders are included.

dirag uses two things when they are there, with nothing to set:

When present Gives
An NVIDIA GPU indexing in minutes per hundred books instead of hours; a faster neural rerank
Ollama, running the LLM rerank and the AI answer; dirag pulls qwen2.5:7b-instruct through it on the first start

Without them, search, the neural rerank and the reader work as usual; the AI button and the LLM rerank are hidden. The terminal shows what was found:

dirag on http://127.0.0.1:8008
  GPU   NVIDIA GeForce RTX 3060
  AI    qwen2.5:7b-instruct via Ollama

The first start downloads the packages (about 2.5 GB on Linux and Windows, GPU libraries included), the embedding and rerank models (about 250 MB) and, with Ollama, the language model (about 4.7 GB). To keep dirag installed rather than run it through uvx: uv tool install dirag, then dirag.

Indexing

Indexing is long by design: every page is parsed and every passage embedded. The library view shows progress, a time estimate and a stop button. Stopping keeps every finished book, and the next run continues from there. Search works on the finished books while a run is going. Index new adds new and changed PDFs and drops deleted ones; Reindex all parses and embeds everything again.

Key Does
typing lists quick exact-word matches under the box; pick one (click, or arrows and Enter) to open its page
Enter, or the search button searches with the chosen mode and rerank
Ctrl+Enter, or the AI button answers from the books (see below)

The mode and rerank sit beside the search box:

Mode Finds
Hybrid both of the below, fused by rank (the default)
Lexical the exact words
Semantic passages close in meaning, without the words
Rerank Does
No rerank keeps the retrieval order
Neural scores the top 30 with a local cross-encoder; about a second on a CPU
LLM asks the language model to order the top 20 and leave out the ones that do not help; those stay in the list, faded

The line under the search box names what produced the results, with the count and the time. While a search runs, the current results fade under a moving bar.

Answer

Ctrl+Enter or the AI button runs the search, then asks the model which passages answer the question and which words to quote from each. Only what the model chose is shown, as one card marked with the AI icon:

  • its response, one to three sentences citing the quotes by number; citations to anything not quoted are removed, and a response left with none is dropped;
  • the quotes, as quotations in the books' own words, each with its source: the source opens the reader scrolled to the quote, marked on the page, and the page icon shows the page image in place.

Every quote is matched against its passage before it is shown; a quote that is not in the text is dropped. When no passage answers the question, the answer says so. The response is the model's reading, not the books; the quotes and their pages are what to check.

Terminal

dirag --port 9000 --no-browser   # serve on another port, without opening the browser
dirag index --library ~/Books    # choose the folder (remembered) and index it
dirag index                      # index new and changed PDFs, drop deleted ones
dirag index --reindex            # parse and embed every PDF again
dirag index --rechunk            # rebuild passages and vectors from cached pages
dirag find "martingale" --mode semantic --rerank neural   # passages
dirag where "instrumental variables"                      # chapters
dirag toc book.pdf               # the chapter map dirag reads from one PDF

Ctrl-C stops an index run the same way the stop button does; press it twice to stop at once.

Settings

All optional.

Variable Default Sets
DIRAG_HOME ~/.local/share/dirag indexes (one per library folder), models, the job record
DIRAG_STATE DIRAG_HOME config.json, positions.json, bookmarks.json, cards.json
DIRAG_LIBRARY chosen in the app fixes the library folder; the app then cannot change it
DIRAG_BROWSE_ROOT the home directory where the folder picker may browse
DIRAG_LLM_MODEL qwen2.5:7b-instruct the Ollama model
DIRAG_LLM_URL http://127.0.0.1:11434 the Ollama server, which can be another machine
DIRAG_RERANK_MODEL Xenova/ms-marco-MiniLM-L-12-v2 the cross-encoder; BAAI/bge-reranker-base is larger and slower
DIRAG_DEVICE detected cpu keeps the local models off the GPU

Books are stored by their path inside the library, so the folder can move as long as its contents keep their relative paths; a moved folder gets a new index unless its index file is renamed to match.

cards.json overrides how a book is shown on the shelf. Keys are paths relative to the library:

{"statistics/all-of-statistics.pdf": {"title": "All of Statistics", "subtitle": "A Concise Course",
  "authors": ["Larry Wasserman"], "year": 2004, "pages": 442}}

Running as a service

Install it with uv tool install dirag, then a systemd unit:

[Unit]
Description=dirag
After=network-online.target

[Service]
ExecStart=%h/.local/bin/dirag --no-browser
Restart=on-failure

[Install]
WantedBy=default.target

As a user unit (~/.config/systemd/user/dirag.service, then systemctl --user enable --now dirag). Stopping the service stops a running index job the same way the stop button does.

Security

The server binds 127.0.0.1 and has no login. Anyone who can reach the port can read every book in the library, choose another folder under the browse root and start indexing. Bind another address (--host) only on a network where every device is trusted, and set DIRAG_LIBRARY to fix the folder.

License

MIT. pdf.js (Apache-2.0) and Bootstrap Icons (MIT) are included; see src/dirag/static/vendor/LICENSE.pdfjs.

Metadata

Release files for dirag 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for dirag 0.1.0
File Size Uploaded
dirag-0.1.0.tar.gz 551.8 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for dirag 0.1.0
File Interpreter ABI Platform
dirag-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 1.1 MB

Release files / dirag-0.1.0.tar.gz

Download URL dirag-0.1.0.tar.gz
Size 551.8 kB
Tags Source
SHA-256 checksum
How to use checksums
74d98748967be8e0ee21b1d112bcfa99062039913e1a6734554c3e7b0225aabc
BLAKE2b-256 checksum
How to use checksums
4a256d68d8e777f29523f7e4fa3d21cf0c5f697d1c4c2473374f42913ceaa2b9
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.12.21 {"installer":{"name":"uv","version":"0.12.21","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Arch Linux","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Release files / dirag-0.1.0-py3-none-any.whl

Download URL dirag-0.1.0-py3-none-any.whl
Size 560.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
eeb6f786503d6152bea796713b4f1fab03c714c7e386c2ab7e8234fe1cd3dafe
BLAKE2b-256 checksum
How to use checksums
0239f63881ac65b97d09d25574847d3475856fe28c390cf4857bfaccebd2a020
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.12.21 {"installer":{"name":"uv","version":"0.12.21","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Arch Linux","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Release history Release notifications | RSS feed

0.2.0

2 release files

This release

0.1.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page