Docs menu
RAG pipeline — chunking, vector stores, GraphRAG
Deterministic chunking, durable vector stores (pgvector, Qdrant, Chroma, Weaviate) and similarity-graph (GraphRAG) retrieval; run inside createRagTool it's journaled → exactly-once / replayable
What it's for / when to use it#
@gnldev/ragturns a knowledge base into a tool the agent can call. The critical difference: when the RAG tool is used inside runDurable its result is journaled → on resume/replay the same documents come back, no new embed/query call is made (exactly-once RAG) — a retrieval that already ran is replayed from the journal rather than re-issued.
The pipeline has three parts: (1) chunking — splits raw text/documents into chunks; (2) vector store — in-memory, durable Postgres/pgvector, a Qdrant, Chroma or Weaviate server, or a similarity graph (GraphRAG); (3) createRagTool — packages embed + query + optional rerank into a single tool.
Chunking — chunkText / chunkDocuments#
chunkText splits raw text into chunks; three strategies: recursive (default — splits by preferring paragraph → line → sentence → word boundaries), markdown (sections by heading hierarchy; adds a heading breadcrumb metadata field to every chunk), and character (fixed window). Consecutive chunks overlap via overlap (context continuity).
chunkDocuments splits VectorDocs and produces a list ready for indexDocuments. The chunk id is <docId>#<i> — deterministic, so re-indexing upserts into the same ids instead of producing duplicates. A document that fits in a single chunk passes through unsplit (no pointless #0 derivation).
import { chunkDocuments, indexDocuments, InMemoryVectorStore } from '@gnldev/rag';
const chunks = chunkDocuments(docs, {
size: 1200, overlap: 120, strategy: 'markdown',
});
// chunks[i].id === 'guide#3', metadata: { source: 'guide', chunk: 3, heading: 'Kurulum > Docker' }
const store = new InMemoryVectorStore();
await indexDocuments(store, chunks, embed);Durable store — PostgresVectorStore (pgvector)#
PostgresVectorStoreand InMemoryVectorStore expose the same interface — Postgres backs it durably with pgvector: it takes an injectable pool, lazy-loads the pg driver, and supports HNSW or ivfflat indexes. This means the knowledge base persists across restarts in real customer projects.
import { PostgresVectorStore, createRagTool } from '@gnldev/rag';
import { Pool } from 'pg';
const store = new PostgresVectorStore({
pool: new Pool({ connectionString: process.env.DATABASE_URL }),
dimension: 1536,
index: 'hnsw',
});
await store.upsert(items); // the same signature as InMemoryVector servers — Qdrant, Chroma, Weaviate, Elasticsearch, OpenSearch#
If your vectors live in a dedicated vector database, five stores keep them there through the server's REST API, with no extra package: QdrantVectorStore, ChromaVectorStore (Chroma 1.x), WeaviateVectorStore (Weaviate 1.x), ElasticsearchVectorStore (Elasticsearch 9.x) and OpenSearchVectorStore (OpenSearch 3.x). Every call has a deadline (timeoutMs, 30 s by default). The collection is created on the first write with that write's vector length (or dimension); the API key goes in a header and never into an error message.
import { QdrantVectorStore, ChromaVectorStore, WeaviateVectorStore, ElasticsearchVectorStore, OpenSearchVectorStore } from '@gnldev/rag';
const qdrant = new QdrantVectorStore({ url: 'https://qdrant.internal:6333', apiKey: process.env.QDRANT_API_KEY });
const chroma = new ChromaVectorStore({ url: 'http://chroma.internal:8000', apiKey: process.env.CHROMA_TOKEN });
const weaviate = new WeaviateVectorStore({ url: 'https://weaviate.internal:8080', apiKey: process.env.WEAVIATE_API_KEY });
const es = new ElasticsearchVectorStore({ url: 'https://es.internal:9200', apiKey: process.env.ES_API_KEY });
const os = new OpenSearchVectorStore({ url: 'https://opensearch.internal:9200', auth: { username: 'gnl', password: process.env.OPENSEARCH_PASSWORD! } });
// each: createRagTool({ store: weaviate, embed })All of them keep the same rules as every other store, measured by the same 20-scenario vector kit: owner, namespace and sharing are part of each record's id, so a write cannot take over another owner's document, even when two processes race; a filter narrows before ranking; equal scores at the top-K cut are read whole and ordered by id. That last one is work on Chroma and Weaviate: their search is approximate and misses some copies of one vector (Weaviate: 2,974 of 3,000 found with a limit of 4,000), so the tie is read exactly.
GraphRAG — indirect-relevance retrieval#
GraphRagdoes retrieval over a similarity graph between chunks (the graph-retrieval pattern, over the same journal as everything else GraphRAG ). It catches what plain vector search misses: chunks that aren't directly similar to the query but are strongly connected to chunks that are (indirect relevance) also join the results. Since it implements the VectorStore interface, it's a drop-in for createRagTool.
Parameters: threshold (edge threshold, default 0.75), hops (neighbor-expansion depth, default 1; 0 = plain vector search), decay (per-hop score decay, default 0.7), and seeds (number of direct results seeding the expansion, default 4). If a node is reached by more than one path, the highest score is kept; ties break deterministically by id.
import { GraphRag, createRagTool } from '@gnldev/rag';
const graph = new GraphRag({ threshold: 0.75, hops: 1, decay: 0.7, seeds: 4 });
await graph.upsert(items); // edges are built incrementally
const ragTool = createRagTool({ store: graph, embed, topK: 6 });createRagTool — deterministic / exactly-once#
createRagTool packages embed + store.query + optional rerankinto a single tool. It works with any VectorStore (InMemory / Postgres / Qdrant / Chroma / Weaviate / GraphRag). Because runDurable journals the durableTool result, resume returns the same documents; no new embed/query/rerank call runs.
API reference#
chunkText(text, opts?) → Chunk[]; recursive/markdown/character strategy, size/overlap, markdown heading breadcrumb.
chunkDocuments(docs, opts?) → VectorDoc[]; deterministic <docId>#<i> chunk id → re-indexing upserts.
PostgresVectorStoreDurable pgvector store; injectable pool, lazy pg, HNSW/ivfflat — same interface as InMemory.
QdrantVectorStore / ChromaVectorStore / WeaviateVectorStore / ElasticsearchVectorStore / OpenSearchVectorStoreVector stores on a Qdrant, Chroma, Weaviate, Elasticsearch or OpenSearch server over REST; same interface as InMemory, same vector kit; no extra package.
GraphRagSimilarity-graph VectorStore; indirect-relevance retrieval via threshold/hops/decay/seeds, drop-in for createRagTool.
createRagTool({ store, embed, topK?, rerank?, ... }) → tool; journaled inside durable = exactly-once/replayable RAG.
InMemoryVectorStore / indexDocumentsIn-memory store + bulk index helper (test/prototype).