lightrag
v1.1.3
Published
Lightweight RAG library with knowledge graph — runs in Node.js and browsers (Web Worker, no bundler), MIT licensed
Maintainers
Readme
LightRAG
Lightweight Retrieval-Augmented Generation library with Knowledge Graph.
Runs in Node.js (CommonJS) and browsers (Web Worker, no bundler required).
Install
npm install lightragUsage
Node.js
const { pipeline } = require('@huggingface/transformers');
const { LightRAG, Embedder } = require('lightrag');
(async () => {
const _pipe = await pipeline('feature-extraction', 'Xenova/multilingual-e5-small', { dtype: 'fp32' });
const _embedder = new Embedder(_pipe);
const _rag = new LightRAG({
embedder: _embedder,
tokenizer: _pipe.tokenizer,
llmFunc: async (prompt, opts = {}) => {
const _resp = await fetch('https://api.deepseek.com/v1/chat/completions', {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer YOUR_KEY' },
body: JSON.stringify({ model: 'deepseek-v4-flash', messages: [{ role: 'user', content: prompt }] }),
});
const _json = await _resp.json();
return _json.choices?.[0]?.message?.content || '';
},
});
await _rag.insert('Your document text here...');
const _answer = await _rag.query('What is this about?', { mode: 'hybrid' });
console.log(_answer);
})();Browser (Web Worker)
Load Transformers.js from CDN in your Worker, then create a LightRAG instance. The storage.js module provides IndexedDB persistence for saving/restoring RAG state across page reloads.
import { pipeline, env } from 'https://unpkg.com/@huggingface/[email protected]/dist/transformers.js';
env.allowLocalModels = false;
env.useBrowserCache = true;
const _pipe = await pipeline('feature-extraction', 'Xenova/multilingual-e5-small', { dtype: 'fp32' });
const _embedder = new Embedder(_pipe);
const _rag = new LightRAG({
embedder: _embedder,
tokenizer: _pipe.tokenizer,
llmFunc: async (prompt) => {
const _resp = await fetch('https://api.deepseek.com/v1/chat/completions', { ... });
const _json = await _resp.json();
return _json.choices?.[0]?.message?.content || '';
},
});
await _rag.insert(documentText);
const _context = await _rag.buildContext(question, 'hybrid');See the BlackboardLM project for a complete browser integration example using Web Workers, IndexedDB persistence, and streaming LLM responses.
API
new LightRAG(opts)
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| opts.llmFunc | async (prompt, options) => string | required | LLM call function |
| opts.embedder | Embedder | required | Transformers.js embedder instance |
| opts.tokenizer | tokenizer | — | Tokenizer for token-counting chunker (falls back to character-based) |
| opts.embeddingDim | number | 384 | Embedding vector dimension |
| opts.chunkSize | number | 480 | Max tokens per chunk (with tokenizer) or chars (without) |
| opts.chunkOverlap | number | 50 | Overlap tokens (or chars) between chunks |
| opts.maxEntityTokens | number | 96000 | Max characters sent to LLM for entity extraction per chunk |
rag.insert(text)
Inserts document text: tokenizes → chunks → embeds each chunk into vector DB → extracts entities & relations into knowledge graph.
rag.query(question, options)
Query with RAG. Calls llmFunc with the question, system prompt (including retrieved context), and conversation history.
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| options.mode | string | 'hybrid' | 'naive' | 'local' | 'global' | 'hybrid' | 'mix' |
| options.systemPrompt | string | '' | System prompt prepended to LLM context |
| options.history | array | [] | Conversation history [{role, content}] |
| options.stream | boolean | false | Passed through to llmFunc |
rag.buildContext(question, mode)
Returns the retrieved context string for a given question and mode, without calling the LLM. Useful when you want to handle the LLM call yourself (e.g., for streaming).
| Arg | Type | Description |
|-----|------|-------------|
| question | string | The query to build context for |
| mode | string | Retrieval mode ('local' / 'global' / 'hybrid' / 'mix') |
Returns '' for 'naive' mode or when no documents are indexed.
rag.getGraphData()
Returns { nodes, edges } — nodes with id, entity_type, description, degree; edges with source, target, keywords, weight.
rag.getProgress()
Returns { total, ready, isInserting, isReady } — insertion progress.
rag.vdbSize
Getter returning the number of vectors currently stored (read-only).
rag.toJSON() / LightRAG.fromJSON(data, opts)
Serialize/deserialize the entire RAG state (vector DB + knowledge graph). Use with storage.js for IndexedDB persistence in browsers, or with fs in Node.js.
Storage (Browser)
lightrag/src/storage.js provides a simple IndexedDB wrapper for persisting RAG state:
const { Storage } = require('lightrag/src/storage.js');
await Storage.save('rag_state', rag.toJSON());
const _data = await Storage.load('rag_state');
const _rag = LightRAG.fromJSON(_data, opts);
await Storage.clear();Embedder
new Embedder(pipeline)
Wraps a Transformers.js feature-extraction pipeline for embedding text.
| Method | Description |
|--------|-------------|
| embedder.embed(texts, context) | Embed an array of strings. context is 'document' (default, prefix 'passage: ') or 'query' (prefix 'query: '). Returns number[][]. |
| embedder.embedQuery(query) | Shorthand for embed([query], 'query') — returns number[]. |
| embedder.embedDocuments(docs) | Shorthand for embed(docs, 'document') — returns number[][]. |
TokenChunker
new TokenChunker(tokenizer, tokenLimit, overlapTokens)
Splits text into overlapping chunks. When tokenizer is provided (e.g., pipe.tokenizer), chunks are measured in tokens. Without a tokenizer, falls back to character-based chunking (3000 chars per chunk, 150 char overlap).
const { TokenChunker } = require('lightrag');
const _chunker = new TokenChunker(pipe.tokenizer, 480, 50);
const _chunks = _chunker.chunk('Long document text...');VectorDB
new VectorDB(dim)
In-memory cosine-similarity vector database.
| Method | Description |
|--------|-------------|
| vdb.upsert(entries) | Insert or update [{id, vector, text}] entries |
| vdb.query(vector, topK) | Return top-K similar entries as [{id, text, score}] |
| vdb.size | Getter — number of stored vectors |
| vdb.toJSON() | Serialize to plain object |
| VectorDB.fromJSON(data) | Static — deserialize |
KnowledgeGraph
new KnowledgeGraph()
In-memory entity-relation knowledge graph.
| Method | Description |
|--------|-------------|
| graph.upsertNode(id, data) | Add/update a node with {entity_type, description, source_id} |
| graph.upsertEdge(source, target, data) | Add an edge with {keywords, weight, source_id} |
| graph.getNeighbors(id, maxDegree) | Get neighbor nodes with edge data |
| graph.getNode(id) | Get a single node by ID |
| graph.getAllNodes() | Returns all nodes with {id, entity_type, description, degree} |
| graph.getAllEdges() | Returns all edges with {source, target, keywords, weight} |
| graph.size | Getter — number of nodes |
| graph.toJSON() | Serialize to plain object |
| KnowledgeGraph.fromJSON(data) | Static — deserialize |
Entity Extraction Prompt
const { ENTITY_EXTRACTION_PROMPT } = require('lightrag');The prompt template used by LightRAG._extractEntities(). Contains {text} placeholder replaced with each chunk's content at extraction time.
Server (Node.js CLI)
server.js provides a JSON-line IPC server — reads JSON requests from stdin, writes JSON responses to stdout. Supports these actions:
| Action | Description |
|--------|-------------|
| insert | {action:"insert", text:"..."} → chunk, embed, extract entities |
| query | {action:"query", question, mode, systemPrompt, history} → LLM response |
| graph | Returns {nodes, edges} |
| progress | Returns {total, ready, isInserting, isReady} |
| reset | Clear all data and storage |
| save | Persist to $LIGHTRAG_STORAGE (default /tmp/blackboardlm_rag.json) |
| load | Restore from saved file |
Environment variables: TRANSFORMERS_JS_MODEL, DEEPSEEK_API_KEY, DEEPSEEK_BASE_URL, LLM_MODEL, LIGHTRAG_STORAGE, LLM_MAX_TOKENS, LLM_THINKING, LLM_REASONING_EFFORT.
License
MIT
