tRAGar

Client-side Retrieval-Augmented Generation in the browser. transformers.js embeddings in the browser. Zero server. Three lines of code. (C++23 → WebAssembly coming in v0.2)

Install

Load tRAGar as an ES module. No build step required — serve from any static host or use the CDN URL from GitHub Pages.

// Via GitHub Pages CDN
import { TRAGar } from 'https://charly-vibes.github.io/tRAGar/dist/js/tragar.js';

Or serve the file yourself. The bundle has a single peer dependency (@xenova/transformers) that it loads on first use via dynamic import.

Quick Start

Three calls: create, ingest, query.

import { TRAGar } from 'https://charly-vibes.github.io/tRAGar/dist/js/tragar.js';

// 1. Create an instance (model loads on first ingest)
const rag = await TRAGar.create({
  store: TRAGar.stores.memory(),
  embedder: TRAGar.embedders.transformers(),
});

// 2. Ingest a document (chunk → embed → store)
await rag.ingest({ source: 'readme', text: yourMarkdownText });

// 3. Query the corpus
const hits = await rag.query('what is tRAGar?');
hits.forEach(h => console.log(h.score, h.text));

// Always close when done
await rag.close();

Core Concepts

Instance

TRAGar.create(config) returns a TRAGarInstance — a handle to one corpus (namespace). All operations (ingest, query, stats, close) go through it. Call close() when done to release file handles.

Ingest

ingest({ source, text, meta? }) chunks the text into paragraphs, embeds each chunk, and writes to the store. source is a stable identifier (a path or URL) used to attribute hits. meta is stored with each chunk and reserved for future filtering — it is not yet surfaced on query results.

Query

query(text, { k? }) returns the top-k chunks ranked by cosine similarity. Use queryStream() for an async iterator that yields hits in descending score order.

Namespace

Each instance operates on its own namespace (default: "default"). Create multiple instances with different namespace values to keep corpora isolated.

Stores

Choose where vectors and chunks are persisted.

StoreDescriptionPersists across reloads?
TRAGar.stores.memory() In-memory only. Fast, ephemeral. Good for testing and single-session use. No
TRAGar.stores.opfs() Origin Private File System (IndexedDB fallback). Chunks survive page reloads. Emits a StoreFallback warning when IndexedDB is used. Yes
// Persistent — chunks survive page reloads
const rag = await TRAGar.create({
  store: TRAGar.stores.opfs(),
  embedder: TRAGar.embedders.transformers(),
  namespace: 'my-corpus',
  onWarn: (code, msg) => console.warn(code, msg),
});

Embedders

Embedders convert text chunks into float32 vectors. The embedder is loaded lazily on the first ingest() call.

EmbedderDescriptionDefault model
TRAGar.embedders.transformers() Lazy-loads a transformers.js model. No network call until first ingest. Model is cached in the browser after the first download. Xenova/all-MiniLM-L6-v2 (dim 384, ~23 MB)
TRAGar.embedders.custom(fn, dim) Bring your own embedder. Good for testing or swapping models.
// Custom model
const rag = await TRAGar.create({
  store: TRAGar.stores.memory(),
  embedder: TRAGar.embedders.transformers(
    'Xenova/all-MiniLM-L12-v2',
    384
  ),
});

// Custom embedder function
const myEmbedder = TRAGar.embedders.custom(
  async (batch) => batch.map(() => new Float32Array(128)),
  128,
  'my-test-embedder',
);

Streaming Results

queryStream() returns an async iterator that yields hits in descending score order. Break early without consuming all results.

for await (const hit of rag.queryStream('search term', { k: 5 })) {
  console.log(`[${hit.score.toFixed(3)}] ${hit.text.slice(0, 80)}`);
  if (hit.score < 0.3) break; // stop when relevance drops
}

Error Handling

All errors reject with a TRAGarError that has a typed code property.

import { TRAGar, TRAGarError } from './tragar.js';

try {
  await rag.ingest(doc);
} catch (err) {
  if (err instanceof TRAGarError) {
    console.error(err.code, err.message); // e.g. "EmbedderLoadFailed"
  }
}
CodeWhen
InstanceClosedMethod called after close()
InvalidConfigBad namespace, unsupported config option
EmbedderLoadFailedtransformers.js model or module failed to load
SchemaTooNewPersisted schema version exceeds library support
SchemaTooOldPersisted schema has no migration path
NamespaceLockedAnother tab holds the namespace lock

Next Steps