PixelRAG converts source documents into a searchable vector index through four sequential stages — render, chunk, embed, and build-index — each of which can be run independently or orchestrated end-to-end by theDocumentation Index
Fetch the complete documentation index at: https://mintlify.com/StarTrail-org/PixelRAG/llms.txt
Use this file to discover all available pages before exploring further.
pixelrag index command. Once the index is built, a separate pixelrag serve process loads it and handles search queries via a FastAPI HTTP server.
Pipeline at a glance
Stage 1 — Render (pixelshot / render_url)
The render stage converts source documents into tiled JPEG screenshots. It is exposed as the standalone pixelshot CLI command (available after pip install pixelrag) and as the render_url / render_urls Python API.
How it works:
- A headless Chromium CDP backend captures each URL as a full-page screenshot. The CDP backend auto-selects a turbo capture path when a capable Chrome binary is present.
- PDF files are rendered via PyMuPDF (bundled) or pdf2image (poppler), selected automatically based on what is installed.
- The output for each source is a directory named
{output_dir}/{stem}.png.tiles/containing:tile_NNNN.jpg— JPEG tiles, each up to 8192px talltiles.json— a manifest recording the tile filenames, total page height, and viewport width
| Parameter | Default | Description |
|---|---|---|
tile_height | 8192 px | Maximum height of each output tile |
viewport_width | 875 px | Browser viewport width for URL rendering |
quality | 85 | JPEG quality (1–100) |
backend | cdp | Rendering backend (cdp or playwright) |
On Linux (x64), a bundled turbo
headless_shell binary is installed automatically. On Windows and macOS, pixelshot uses your system Chrome/Chromium (auto-detected from standard install locations). Set CHROME_PATH=/path/to/chrome if it is not found automatically.Stage 2 — Chunk (pixelrag chunk)
The chunk stage splits each 8192px tile into smaller strips that the embedding model can process efficiently.
How it works:
- Each tile is cut into 1024px-tall horizontal strips (chunks). For tiles narrower than the viewport width, this produces a single column of strips. For wider sources (e.g. landscape PDFs), the grid is also split along the width at
viewport_widthintervals. - Each chunk is written as a
chunk_XXXX_YY.pngfile alongside its tile, whereXXXXis the tile index andYYis the chunk index within that tile. - A
chunks.jsonmanifest is written to each article directory, recording each chunk’stile_index,chunk_index,x_offset,y_offset,width, andheight. MIN_CHUNK_HEIGHT = 28px — strips shorter than one Qwen3-VL patch are merged into the previous chunk rather than being written as their own file. This avoids producing chunks that would cause processor errors downstream.
Stage 3 — Embed (pixelrag embed)
The embed stage runs each chunk image through Qwen/Qwen3-VL-Embedding-2B to produce a float16 embedding vector for every chunk.
How it works:
- The model encodes each chunk image using last-token pooling followed by L2 normalization.
- Multi-GPU support is available via
--gpu-ids. Workers pull from a shared task queue so faster GPUs process more work automatically. - The output is a set of
.npzshards, each containing:
| Array | dtype | Shape | Description |
|---|---|---|---|
embeddings | float16 | [N, D] | Embedding vectors |
article_ids | int64 | [N] | Source article identifier |
tile_indices | int32 | [N] | Which 8192px tile (0-based) |
chunk_indices | int32 | [N] | Which 1024px strip within the tile |
y_offsets | int32 | [N] | Y position of chunk top edge in page (px) |
tile_heights | int32 | [N] | Actual chunk height (last chunk may be <1024px) |
page_heights | int32 | [N] | Full page height in pixels |
viewport_widths | int32 | [N] | Page render width in pixels (capped at 875) |
image_hashes | S32 | [N] | MD5 of source PNG (for deduplication and patching) |
tile_paths | S512 | [N] | Absolute path to the chunk PNG file |
shard_id | int32 | scalar | Shard number |
Stage 4 — Build Index (pixelrag build-index)
The build-index stage merges all shard .npz files into a single searchable vector index.
How it works:
- Merges all shard files into unified embedding and metadata arrays.
- Deduplicates by
(article_id, tile_index, chunk_index)tuple — duplicate vectors produced by incremental or parallel runs are dropped. - Builds the configured index backend:
- FAISS IndexIVFFlat (default) — fast build, good recall. Configurable
nlistandnprobeparameters. - Qdrant — supports quantization, disk-backed vectors, and payload filtering. Requires a running Qdrant server.
- FAISS IndexIVFFlat (default) — fast build, good recall. Configurable
| File | Description |
|---|---|
index.faiss | FAISS IndexIVFFlat binary |
metadata.npz | Article IDs, tile/chunk indices, Y offsets, tile heights |
summary.json | Index parameters (backend, nlist, nprobe, total vectors) |
articles.json | URL and title per article ID |
Orchestrator (pixelrag index)
The pixelrag index build command runs all four stages automatically from a single pixelrag.yaml configuration file. It supports local files, web URLs, PDFs, and kiwix ZIM archives as source types.
tiles.json manifest for each document, and automatically adjusts FAISS nlist based on the total vector count.
Serve (pixelrag serve)
The serve stage starts a FastAPI HTTP server that loads the built index and the Qwen3-VL model at startup, then handles incoming search requests.
Endpoints:
| Endpoint | Method | Description |
|---|---|---|
/search | POST | Batch text/image/embedding search against the index |
/status | GET | Index statistics (vector count, dimension, nprobe, model) |
/health | GET | Liveness check ({"status": "ok"}) |
/tile/{article_id}/{tile_index}/{chunk_index} | GET | Serve a chunk image by coordinate |
Set
PIXELRAG_INDEX_MMAP=1 to memory-map the FAISS index file, which is essential for multi-100 GB indexes that exceed available RAM.