Skip to content
vectorless-js
Esc
navigateopen⌘Jpreview
On this page

Introduction

Vectorless, reasoning-based RAG that runs in the browser via WASM.

Most retrieval systems split a document into chunks, embed them, and search for whatever looks closest to your question. This one doesn’t. It builds a table of contents, hands that to an LLM, and lets it pick which section to read, the way a person uses an index.

It runs wherever JS does: the browser through WASM, plus Bun, Node, and Cloudflare Workers.

In a browser the PDF is read and the tree is built on the user’s machine. The file is never uploaded. The only thing that leaves is the slice of text the model asked to read, and it goes to your LLM endpoint. See Browser for what that looks like.

import { buildIndex, ask } from "vectorless-js";
import { pdfToMarkdown } from "vectorless-js/parse";

const doc = buildIndex(await pdfToMarkdown(bytes), "report.pdf");
const { answer, citations } = await ask(doc, "What was Q1 revenue?", "/api/llm");

How it works

PDF
 |   liteparse, compiled to WASM. Runs on your machine.
 v
markdown
 |   buildIndex: headings become nodes.
 v   Deterministic, no LLM, free.
tree
 |   The only smart part. The model reads the table of
 v   contents and fetches the one section it picks.
answer + citations

A 28-page SEC filing comes to 66 KB of text. The LLM sees 1.6 KB of it: the section titles and the line numbers where they start. It picks a section off that list, then fetches only those lines.

Why vectorless

Chunk and embed This
chunk, embed, similarity search build a tree, let the LLM read it, fetch a section
chunk boundaries cut through context the document’s own sections stay intact
needs a vector database the tree is just JSON
finds text that looks similar finds the section that answers the question
similarity scores citations you can trace back

What it includes

buildIndex builds the tree from Markdown headings. No LLM call, so it is free.

Real documents are less tidy, and buildIndexAuto handles the two ways they go wrong. A PDF may give every heading the same rank, leaving the titles correct and the nesting missing, so an LLM assigns the depths. A contract may have no headings at all, so an LLM infers the outline from the text. Either way the same deterministic builder assembles the tree. How it works walks through both.

Three functions fill the tree out once it exists. addNodeSummaries writes a summary per node, generateDocDescription writes one line for the whole document, and thinTree merges nodes too small to earn their own entry.

ask runs an OpenAI-compatible tool-use loop and hands back an answer plus citations. With more than one document, routeDocuments reads the descriptions and decides which are worth opening, using the same reasoning approach as the rest of the pipeline.

Was this page helpful?