Build complete retrieval pipelines
Ingest files, URLs, office documents, archives, images, and media transcripts; synchronize durable sources; and search with lexical, vector, hybrid, transformed, and reranked retrieval.
@absolutejs/ragv0.22.1betaAICrawl selected sites, retain versioned evidence and search it through the AbsoluteJS research stack.
Own the evidence lifecycle for selected public sites: durable crawling, source versions, freshness, native PostgreSQL retrieval and reviewed company, person and event projections. The index exposes the same SearchProvider contract as external web search, so research, batches and monitoring use the same downstream workflow.
bun add @absolutejs/rag@0.22.1 @absolutejs/rag-postgres@0.1.0 elysia@2.0.0-beta.6Configure eligible origins, URL/page/byte/chunk/depth limits, recrawl cadence and provider admission. Robots rules, public-address checks, redirect validation, origin leases and crawl delays apply. A run can finish with pending work; the worker resumes when the origin becomes eligible.
Conditional ETag/Last-Modified requests avoid re-embedding an unchanged page. Heading and section context accompany passages. Publication dates come from explicit article metadata. Search checks active committed source versions and reports corpus statistics.
@absolutejs/rag-postgres uses pgvector for vector retrieval and GIN full-text indexes for native lexical retrieval. Supported tenant/domain/publication predicates apply before top-K; only selected results cross into the application. Hybrid retrieval and reranking use collection configuration.
webIndexPlugin requires authorization for every route and separates read, crawl and admin operations. For private corpora, resolve identity to a server-selected tenant/index runtime. Client requests cannot configure origins, credentials, providers or budgets.
createWebIndexProjector extracts and separately reviews one immutable source version. It publishes supported identity/facts with original passage references. Stale versions cannot commit; explicit validity intervals can expire fields.
Pass index.provider to createResearch. Embedding, reranking and generation models remain provider choices. An index generation fixes model identity, dimensions, origins and representation; rebuild and evaluate a new generation before activation.
Every binding calls the same authorized server plugin. Clients start idle and expose state and cancellation. Cancellation cannot undo committed work.
| Option | Entry point | Usage |
|---|---|---|
| React | @absolutejs/rag/react | useWebIndex(); index.call("stats", {}) |
| Vue | @absolutejs/rag/vue | useWebIndex() with reactive state |
| Svelte | @absolutejs/rag/svelte | createWebIndexStore() with readable state; dispose on teardown |
| Angular | @absolutejs/rag/angular | WebIndexService.connect("/web-index", destroyRef) |
| HTML | @absolutejs/rag/web-index/client | bindWebIndexSearchForm(form, output); dispose the binding |
| HTMX | GET /web-index/html | Load and refresh escaped index statistics |
Outcomes
Ingest files, URLs, office documents, archives, images, and media transcripts; synchronize durable sources; and search with lexical, vector, hybrid, transformed, and reranked retrieval.
Swap memory, PostgreSQL/pgvector, SQLite/vec0, and Pinecone stores behind one contract and expose retrieval through framework-neutral or framework-specific clients.
Hardening checklist
Follow in order
Requires PostgreSQL with pgvector, DATABASE_URL and OPENAI_API_KEY. Run schema initialization during migration. reserveEmbeddingWork and authorizeIndexOperation are host admission and authorization functions. Replace example.com with the approved corpus origins. Mount app in the server and await worker.stop() during shutdown before closing the database.
import { Elysia } from "elysia";
import { createRAGCollection, openaiEmbeddings } from "@absolutejs/rag";
import { createPostgresRAGStore } from "@absolutejs/rag-postgres";
import {
createWebIndex, createPostgresWebIndexStore, webIndexPostgresSchemaSql,
webIndexPlugin, startWebIndexWorker,
} from "@absolutejs/rag/web-index";
const db = new Bun.SQL(process.env.DATABASE_URL!);
// Run this schema initialization in your deployment's migration phase.
await db.unsafe(webIndexPostgresSchemaSql());
const collection = createRAGCollection({
store: createPostgresRAGStore({ sql: db, tableName: "public_web_chunks", dimensions: 1536 }),
embedding: openaiEmbeddings({
apiKey: process.env.OPENAI_API_KEY!,
defaultModel: "text-embedding-3-small", dimensions: 1536
}),
});
const index = createWebIndex({
tenant: "public", index: "partner-sites",
store: createPostgresWebIndexStore(db),
origins: ["https://example.com"],
generations: [{
id: "v1", collection,
embedding: { provider: "openai", model: "text-embedding-3-small", dimensions: 1536 },
retrieval: "hybrid",
}],
limits: { maxUrls: 1000, maxPagesPerRun: 20, maxBytesPerRun: 20_000_000, maxChunksPerPage: 100, maxDepth: 3 },
recrawlMs: 6 * 60 * 60 * 1000,
originDelayMs: 1000,
admit: async work => reserveEmbeddingWork(work),
});
const app = new Elysia().use(webIndexPlugin({
runtime: index,
authorize: async (request, operation) => authorizeIndexOperation(request, operation),
}));
await index.enqueue(["https://example.com/"]);
const worker = startWebIndexWorker({ runtime: index, onError: error => console.error(error) });
// On shutdown, await worker.stop() before closing the database.
const result = await index.search({ query: "partnership eligibility", count: 5 });
Continues from the configured index. Install @absolutejs/ai for a model adapter. configuredAIProvider, extractionModel, reviewModel, authorizeResearch and recordResearchAccounting are host configuration. project is a trusted server function; its extractor and policy are not selected by a browser.
import { createResearch, researchPlugin } from "@absolutejs/rag/research";
import { createWebIndexProjector } from "@absolutejs/rag/web-index";
const research = createResearch({
provider: configuredAIProvider, model: extractionModel,
reviewer: { provider: configuredAIProvider, model: reviewModel },
search: index.provider,
});
app.use(researchPlugin({ runtime: research, authorize: authorizeResearch }));
const result = await research.run({ query: "Which partners meet these requirements?" });
const extract = createWebIndexProjector({
kind: "company", instructions: "Identify the company and its partner eligibility requirements.",
provider: configuredAIProvider, model: extractionModel,
reviewer: { provider: configuredAIProvider, model: reviewModel },
onResult: result => recordResearchAccounting(result),
});
await index.project({ url: "https://example.com/", extract });
const companies = await index.projections("company");
The framework-free client uses the authenticated plugin and existing same-origin session. Read/crawl/admin permission checks remain server-side.
import { createWebIndexClient } from '@absolutejs/rag/web-index/client';
const index = createWebIndexClient({ path: '/web-index' });
const stats = await index.call('stats', {});
const hits = await index.call('search', {
query: 'partnership eligibility', count: 5
});
// index.cancel(); index.dispose() when the view is removed.Trusted server functions. A manual remove creates a scope-wide takedown, purges history/projections and removes chunks from configured generations. restore permits future crawling explicitly.
const versions = await index.history('https://example.com/');
await index.remove('https://example.com/');
await index.restore('https://example.com/');
await index.enqueue(['https://example.com/']);Configure v1 and v2 in generations first, with separate model identity and collection configuration. Enqueue the old corpus in pages, let workers crawl v2, and evaluate it. Activation requires readiness and an actual passing host report, and compares the expected active generation.
let after: string | undefined;
do {
const page = await index.rebuild('v1', 'v2', 100, after);
after = page.next;
} while (after);
// Run the v2 frontier and evaluate before this separate release step:
const activated = await index.activate('v2', 'v1', {
passed: true,
evidence: approvedEvaluationReportReference
});Serve HTMX through the application. The authorized endpoint returns escaped index statistics.
<section hx-get="/web-index/html" hx-trigger="load, every 30s" hx-swap="innerHTML"></section>