Retrieval & Context
One createRAGCollection() call wires embedding, query transforms, retrieval routing, hybrid lexical + vector fusion, and reranking over any backend that implements the RAGVectorStore contract — in-memory in core, Postgres (pgvector), SQLite (vec0), and Pinecone as published adapters.
#Collections
A collection binds a store to a retrieval pipeline. It exposes search / searchWithTrace (results plus a full retrieval trace), ingest, ingestSource / removeSource (tracked, deletable source sets), and pass-through clear / getStatus / getCapabilities. Free-function wrappers — searchDocuments, ingestRAGDocuments, ingestRAGSource, removeRAGSource — mirror the methods.
| Option | Type / default | Purpose |
|---|---|---|
store | RAGVectorStorerequired | The backend. In-memory ships in core; SQLite, Postgres, and Pinecone are separate packages. |
embedding | RAGEmbeddingProviderLike | A provider object or bare embed function. Falls back to the store embed() when omitted. |
defaultTopK | 6default | Result count when a search omits topK. |
defaultCandidateMultiplier | 4default | Candidate over-fetch factor (topK x multiplier) when reranking, transforms, or hybrid modes are active. |
defaultModel | string | Embedding model used when neither the call nor the provider names one. |
queryTransform | RAGQueryTransformProviderLike | Rewrites the query and adds variant queries before retrieval. |
retrievalStrategy | RAGRetrievalStrategyProviderLike | Routes each query to vector, lexical, or hybrid retrieval per decision. |
rerank | RAGRerankerProviderLike | Re-orders the fused candidate set before the final topK slice. |
import {
buildRAGContext,
createHeuristicRAGReranker,
createRAGCollection,
ingestRAGDocuments,
openaiEmbeddings,
searchDocuments
} from '@absolutejs/rag';
import { createSQLiteRAGStore } from '@absolutejs/rag-sqlite';
const collection = createRAGCollection({
embedding: openaiEmbeddings({
apiKey: process.env.OPENAI_API_KEY ?? '',
defaultModel: 'text-embedding-3-small',
dimensions: 1536
}),
rerank: createHeuristicRAGReranker(),
store: createSQLiteRAGStore({ dimensions: 1536, path: './rag.sqlite' })
});
await ingestRAGDocuments(collection, {
documents: [
{
id: 'release-notes',
source: 'docs/releases.md',
text: 'Hybrid retrieval fuses lexical and vector results...',
title: 'Release Notes'
}
]
});
// topK defaults to 6. Candidates over-fetch at topK * 4 whenever a
// reranker, query transform, or non-vector mode is active.
const hits = await searchDocuments(collection, {
query: 'What changed in the latest release?',
retrieval: 'hybrid',
topK: 5
});
// Numbered "[1] Release Notes (docs/releases.md)" context blocks
// plus citation guidance, ready to drop into a prompt.
const context = buildRAGContext(hits);buildRAGContext(hits) renders results into a prompt-ready block: numbered [1] Title (source) headers, location labels for pages, sheets, slides, and timestamps, provenance cues for OCR and transcripts, then citation guidance. An empty hit list returns an empty string.
#Hybrid Search
Every search accepts retrieval — a mode string or a full RAGHybridSearchOptions object. In hybrid mode the store's vector results and BM25-style lexical results (via queryLexical where the backend supports it) are fused, de-duplicated, diversity-filtered, and only then reranked.
| Option | Default | Behavior |
|---|---|---|
mode | 'vector'default | 'vector' | 'lexical' | 'hybrid' — a bare mode string is accepted anywhere RAGHybridSearchOptions is. |
fusion | 'rrf'default | Reciprocal rank fusion, or 'max' for weighted max-score. |
fusionConstant | 60default | The k in weight / (k + rank); higher flattens rank differences. |
lexicalWeight / vectorWeight | 2 / 1defaults | Per-signal weights applied during fusion. |
lexicalTopK | unset | Caps the lexical candidate list independently of topK. |
diversityStrategy | 'none'default | 'mmr' enables maximal-marginal-relevance de-duplication. |
mmrLambda | 0.7default | MMR relevance-vs-diversity balance, clamped to 0..1. |
sourceBalanceStrategy | 'cap'default | With maxResultsPerSource: 'cap' truncates per source, 'round_robin' interleaves sources. |
import { fuseRAGQueryResults, searchDocuments } from '@absolutejs/rag';
// A mode string picks the defaults; an options object tunes fusion.
const results = await searchDocuments(collection, {
query: 'quarterly revenue by region',
retrieval: {
diversityStrategy: 'mmr', // default 'none'
fusion: 'rrf', // 'rrf' | 'max'
fusionConstant: 60, // default 60
lexicalWeight: 2, // default 2
mmrLambda: 0.7, // default 0.7
mode: 'hybrid', // 'vector' | 'lexical' | 'hybrid'
sourceBalanceStrategy: 'round_robin', // default 'cap'
vectorWeight: 1 // default 1
},
topK: 8
});
// The fusion primitive is exported for custom pipelines : reciprocal
// rank fusion (weight / (constant + rank)) or weighted max-score.
const fused = fuseRAGQueryResults({
fusion: 'rrf',
lexical: lexicalResults,
lexicalWeight: 2,
vector: vectorResults,
vectorWeight: 1
});The pieces are exported for custom pipelines: buildRAGLexicalHaystack and scoreRAGLexicalMatch for lexical scoring, fuseRAGQueryResults for fusion, and resolveRAGHybridSearchOptions to expand a mode string into the fully-defaulted options object. Fused results carry their per-signal ranks in metadata.retrievalSignals.
#Reranking & Transforms
Rerankers receive the over-fetched candidate set (topK x defaultCandidateMultiplier) and return the final ordering. Query transforms run first — createHeuristicRAGQueryTransform() expands domain terms into variant queries, or wrap a model call with createRAGQueryTransform. A retrieval strategy (createHeuristicRAGRetrievalStrategy) can override the mode per query: scoped filters go straight to vector, support-style queries to lexical, exact phrases to hybrid.
| Reranker | Default model | Notes |
|---|---|---|
createHeuristicRAGReranker() | absolute-heuristic-reranker | No network calls — BM25-style lexical re-scoring. A solid default. |
createCohereRAGReranker({ apiKey }) | rerank-v3.5 | Cohere /v2/rerank cross-encoder. |
createVoyageRAGReranker({ apiKey }) | rerank-2 | Voyage /v1/rerank cross-encoder. |
createJinaRAGReranker({ apiKey }) | jina-reranker-v2-base-multilingual | Jina /v1/rerank cross-encoder. |
createRAGReranker({ rerank }) | yours | Wrap any (input) => results function — an LLM judge, a local cross-encoder, anything. |
import {
createCohereRAGReranker,
createHeuristicRAGQueryTransform,
createHeuristicRAGRetrievalStrategy,
createRAGCollection,
createVoyageRAGReranker
} from '@absolutejs/rag';
const collection = createRAGCollection({
embedding,
// Heuristic variant expansion runs before retrieval; bring your own
// model-backed transform via createRAGQueryTransform({ transform }).
queryTransform: createHeuristicRAGQueryTransform(),
// Hosted cross-encoders : Cohere rerank-v3.5, Voyage rerank-2, or
// Jina jina-reranker-v2-base-multilingual by default.
rerank: createCohereRAGReranker({
apiKey: process.env.COHERE_API_KEY ?? ''
}),
// Routes scoped queries to vector, support-style queries to
// lexical, and exact-phrase / source-native queries to hybrid.
retrievalStrategy: createHeuristicRAGRetrievalStrategy(),
store
});
// Per-search overrides win over the collection defaults.
await collection.search({
query: 'refund policy for enterprise contracts',
rerank: createVoyageRAGReranker({
apiKey: process.env.VOYAGE_API_KEY ?? ''
}),
topK: 6
});applyRAGReranking and resolveRAGReranker are exported for standalone use — no reranker configured means results pass through unchanged.
#The RAGVectorStore Contract
Three required methods make a working store; everything else is a capability the pipeline detects and uses when present — queryLexical unlocks backend-side lexical retrieval, delete / count unlock source removal, analyze and rebuildNativeIndex unlock maintenance actions. The @absolutejs/rag/adapter-kit subpath re-exports the contract types plus the vector helpers, metadata filter matcher, lexical ranker, and native query planners adapter packages build on.
// The contract every backend implements (@absolutejs/rag/adapter-kit).
// embed, query, and upsert are required; every optional member
// unlocks a capability when present.
type RAGVectorStore = {
embed: (input: RAGEmbeddingInput) => Promise<number[]>;
query: (input: RAGQueryInput) => Promise<RAGQueryResult[]>;
queryLexical?: (input: RAGLexicalQueryInput) => Promise<RAGQueryResult[]>;
count?: (input?: RAGVectorCountInput) => Promise<number>;
delete?: (input?: RAGVectorDeleteInput) => Promise<number>;
analyze?: () => Promise<void> | void;
rebuildNativeIndex?: () => Promise<void> | void;
upsert: (input: RAGUpsertInput) => Promise<void>;
clear?: () => Promise<void> | void;
close?: () => Promise<void> | void;
getStatus?: () => RAGVectorStoreStatus;
getCapabilities?: () => RAGBackendCapabilities;
};
// A minimal custom store built on the adapter-kit vector helpers.
import {
createRAGVector,
normalizeVector,
querySimilarity
} from '@absolutejs/rag/adapter-kit';
const memory = new Map<string, { embedding: number[]; text: string }>();
const demoStore: RAGVectorStore = {
embed: async ({ text }) => normalizeVector(createRAGVector(text, 24)),
query: async ({ queryVector, topK }) =>
[...memory.entries()]
.map(([chunkId, row]) => ({
chunkId,
chunkText: row.text,
score: querySimilarity(queryVector, row.embedding)
}))
.sort((left, right) => right.score - left.score)
.slice(0, topK),
upsert: async ({ chunks }) => {
for (const chunk of chunks) {
memory.set(chunk.chunkId, {
embedding: chunk.embedding ?? [],
text: chunk.text
});
}
}
};getStatus() reports the backend and its vector mode — in_memory, SQLite json_fallback / native_vec0, or Postgres native_pgvector — with native diagnostics (index type, row counts, last query plan) when available.#Stores & Adapters
Two stores ship in core and three adapters are published as standalone packages, so the heavy backend dependencies stay out of your bundle until you opt in. Each adapter also exports a create*RAGCollection and a bundled create*RAG (store + collection + status accessors).
@absolutejs/ragZero-dependency store for tests, demos, and small corpora. JS filtering and lexical ranking included.
@absolutejs/ragBacks retrieval with a live @absolutejs/sync engine collection — search results update reactively.
@absolutejs/rag-postgrespgvector-native store: vector(n) column, cosine / l2 / inner_product, hnsw or ivfflat indexes.
@absolutejs/rag-sqliteEmbedded store on bun:sqlite. JSON fallback everywhere; native vec0 acceleration via platform packages.
@absolutejs/rag-pineconeServerless Pinecone indexes with filter translation, batched upserts, and index provisioning helpers.
| Factory | Key default | Notes |
|---|---|---|
createPostgresRAG(options?) | rag_chunksdefault table | Connection from options.sql, connectionString, RAG_POSTGRES_URL, or DATABASE_URL. Status reports native_pgvector with index diagnostics. |
createSQLiteRAG(options?) | :memory:default path | native: { mode: "vec0" } loads the sqlite-vec extension resolved from @absolutejs/absolute-rag-sqlite-* platform packages; getNativeSupport() explains the outcome. |
createPineconeRAG({ vector }) | 1536default dims | cosine / euclidean / dotproduct metrics, $eq..$containsAll filters, 100-record upsert batches. ensurePineconeIndex() provisions serverless indexes. |
// Postgres (pgvector) : table rag_chunks, hnsw index by default
import { createPostgresRAG } from '@absolutejs/rag-postgres';
const { collection, store } = createPostgresRAG({
storeOptions: {
connectionString: process.env.DATABASE_URL,
dimensions: 1536,
distanceMetric: 'cosine', // 'cosine' | 'l2' | 'inner_product'
indexType: 'hnsw' // 'none' | 'hnsw' | 'ivfflat'
}
});
// SQLite : JSON fallback everywhere, native vec0 when available
import { createSQLiteRAG } from '@absolutejs/rag-sqlite';
const sqlite = createSQLiteRAG({
storeOptions: {
dimensions: 1536,
native: { mode: 'vec0' }, // loads the platform vec0 extension
path: './rag.sqlite'
}
});
sqlite.getNativeSupport(); // resolution, nativeActive, actionableMessage
// Pinecone : serverless index, provision + connect
import {
createPineconeRAG,
ensurePineconeIndex
} from '@absolutejs/rag-pinecone';
await ensurePineconeIndex({
dimensions: 1536,
indexName: 'docs',
waitUntilReady: true
});
const pinecone = createPineconeRAG({
indexName: 'docs', // apiKey falls back to PINECONE_API_KEY
namespace: 'production',
vector: { dimensions: 1536, distanceMetric: 'cosine', provider: 'pinecone' }
});