@absolutejs/rag 0.6.2 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +56 -3
- package/dist/adapter-kit/index.js +89 -50
- package/dist/adapter-kit/index.js.map +4 -4
- package/dist/index.js +284 -85
- package/dist/index.js.map +8 -8
- package/dist/src/index.d.ts +1 -1
- package/dist/src/retrieval/corpus.d.ts +2 -3
- package/dist/src/retrieval/embeddingBudget.d.ts +26 -1
- package/dist/types/engine.d.ts +8 -0
- package/dist/types/retrieval.d.ts +1 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,6 +1,59 @@
|
|
|
1
1
|
# @absolutejs/rag
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
A standalone RAG runtime for Bun and Elysia applications covering document ingestion, chunking, embeddings, hybrid retrieval, reranking, source synchronization, evaluation, client primitives, and framework bindings.
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```sh
|
|
8
|
+
bun add @absolutejs/rag
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Quick start
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
import {
|
|
15
|
+
createInMemoryRAGStore,
|
|
16
|
+
createRAGCollection,
|
|
17
|
+
ingestRAGDocuments,
|
|
18
|
+
openaiEmbeddings,
|
|
19
|
+
searchDocuments
|
|
20
|
+
} from '@absolutejs/rag';
|
|
21
|
+
|
|
22
|
+
const collection = createRAGCollection({
|
|
23
|
+
embedding: openaiEmbeddings({
|
|
24
|
+
apiKey: process.env.OPENAI_API_KEY ?? '',
|
|
25
|
+
defaultModel: 'text-embedding-3-small'
|
|
26
|
+
}),
|
|
27
|
+
store: createInMemoryRAGStore()
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
await ingestRAGDocuments(collection, {
|
|
31
|
+
documents: [{ id: 'intro', text: 'AbsoluteJS ships typed Bun primitives.' }]
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
const results = await searchDocuments(collection, {
|
|
35
|
+
query: 'What does AbsoluteJS ship?',
|
|
36
|
+
topK: 3
|
|
37
|
+
});
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Retrieval and storage
|
|
41
|
+
|
|
42
|
+
The built-in memory store supports development and tests. Published adapters provide PostgreSQL with pgvector, SQLite with optional vec0 acceleration, and Pinecone behind the same `RAGVectorStore` contract. Lexical and vector results can be fused, transformed, and reranked with provider or heuristic rerankers.
|
|
43
|
+
|
|
44
|
+
## Ingestion and source sync
|
|
45
|
+
|
|
46
|
+
The ingestion pipeline handles files, directories, uploads, URLs, PDFs, office documents, archives, images, and media transcripts. Scheduled connectors can keep collections synchronized from email, GitHub, sitemaps, feeds, directories, and S3-compatible storage.
|
|
47
|
+
|
|
48
|
+
## Quality and evaluation
|
|
49
|
+
|
|
50
|
+
`@absolutejs/rag/quality` evaluates retrieval relevance and answer grounding, compares strategies and rerankers, and records runs against a baseline so retrieval changes can be tested before release.
|
|
51
|
+
|
|
52
|
+
## Client and framework entry points
|
|
53
|
+
|
|
54
|
+
- `@absolutejs/rag/client` and `/client/ui` provide browser-side primitives.
|
|
55
|
+
- `@absolutejs/rag/react`, `/vue`, `/svelte`, and `/angular` provide framework bindings.
|
|
56
|
+
- `@absolutejs/rag/adapter-kit` exposes the contracts used by vector-store adapters.
|
|
57
|
+
- `@absolutejs/rag/ui` exposes presentation-neutral UI contracts.
|
|
58
|
+
|
|
59
|
+
Pair the retrieval runtime with `@absolutejs/ai` when retrieved context should feed a model or streaming assistant.
|
|
@@ -13403,6 +13403,7 @@ var resolveRAGEmbeddingProvider = (providerLike, fallbackEmbed, defaultModel) =>
|
|
|
13403
13403
|
}
|
|
13404
13404
|
const resolvedDefaultModel = provider.defaultModel ?? defaultModel;
|
|
13405
13405
|
return {
|
|
13406
|
+
cacheNamespace: provider.cacheNamespace,
|
|
13406
13407
|
defaultModel: resolvedDefaultModel,
|
|
13407
13408
|
dimensions: provider.dimensions,
|
|
13408
13409
|
embed: (input) => provider.embed({
|
|
@@ -20893,6 +20894,30 @@ var createRAGCollection = (options) => {
|
|
|
20893
20894
|
validateRAGEmbeddingDimensions(vector, getExpectedDimensions(), context);
|
|
20894
20895
|
return vector;
|
|
20895
20896
|
};
|
|
20897
|
+
const throwIfAborted = (signal) => {
|
|
20898
|
+
if (signal?.aborted) {
|
|
20899
|
+
throw signal.reason ?? new DOMException("The operation was aborted", "AbortError");
|
|
20900
|
+
}
|
|
20901
|
+
};
|
|
20902
|
+
const mapConcurrentSettled = async (values, concurrency, mapper) => {
|
|
20903
|
+
const results = new Array(values.length);
|
|
20904
|
+
let nextIndex = 0;
|
|
20905
|
+
const workers = Array.from({ length: Math.min(values.length, Math.max(1, Math.floor(concurrency))) }, async () => {
|
|
20906
|
+
while (nextIndex < values.length) {
|
|
20907
|
+
const index = nextIndex++;
|
|
20908
|
+
try {
|
|
20909
|
+
results[index] = {
|
|
20910
|
+
status: "fulfilled",
|
|
20911
|
+
value: await mapper(values[index], index)
|
|
20912
|
+
};
|
|
20913
|
+
} catch (reason) {
|
|
20914
|
+
results[index] = { reason, status: "rejected" };
|
|
20915
|
+
}
|
|
20916
|
+
}
|
|
20917
|
+
});
|
|
20918
|
+
await Promise.all(workers);
|
|
20919
|
+
return results;
|
|
20920
|
+
};
|
|
20896
20921
|
const searchWithTrace = async (input) => {
|
|
20897
20922
|
const model = input.model ?? options.defaultModel;
|
|
20898
20923
|
const topK = input.topK ?? defaultTopK;
|
|
@@ -20953,17 +20978,20 @@ var createRAGCollection = (options) => {
|
|
|
20953
20978
|
stage: "input"
|
|
20954
20979
|
}
|
|
20955
20980
|
];
|
|
20956
|
-
const
|
|
20957
|
-
|
|
20958
|
-
|
|
20959
|
-
|
|
20960
|
-
|
|
20981
|
+
const queryVectors = new Map;
|
|
20982
|
+
if (runVector) {
|
|
20983
|
+
for (const query of searchQueries) {
|
|
20984
|
+
queryVectors.set(query, embed({ model, signal: input.signal, text: query }, "query"));
|
|
20985
|
+
}
|
|
20986
|
+
}
|
|
20987
|
+
const primarySearchQuery = searchQueries[0] ?? input.query;
|
|
20988
|
+
const queryVector = runVector ? await queryVectors.get(primarySearchQuery) : [];
|
|
20961
20989
|
if (runVector) {
|
|
20962
20990
|
steps.push({
|
|
20963
20991
|
label: "Embedded primary query",
|
|
20964
20992
|
metadata: {
|
|
20965
20993
|
dimensions: queryVector.length,
|
|
20966
|
-
query:
|
|
20994
|
+
query: primarySearchQuery
|
|
20967
20995
|
},
|
|
20968
20996
|
stage: "embed"
|
|
20969
20997
|
});
|
|
@@ -21022,11 +21050,7 @@ var createRAGCollection = (options) => {
|
|
|
21022
21050
|
}
|
|
21023
21051
|
const resultGroups = await Promise.all(searchQueries.map(async (query, queryIndex) => {
|
|
21024
21052
|
const [vectorResults2, lexicalResults2] = await Promise.all([
|
|
21025
|
-
runVector ?
|
|
21026
|
-
model,
|
|
21027
|
-
signal: input.signal,
|
|
21028
|
-
text: query
|
|
21029
|
-
}, "query").then((nextQueryVector) => options.store.query({
|
|
21053
|
+
runVector ? queryVectors.get(query).then((nextQueryVector) => options.store.query({
|
|
21030
21054
|
filter: input.filter,
|
|
21031
21055
|
candidateLimit: input.nativeCandidateLimit ?? nativeQueryProfile?.candidateLimit,
|
|
21032
21056
|
fillPolicy: input.nativeFillPolicy ?? nativeQueryProfile?.fillPolicy,
|
|
@@ -21322,32 +21346,46 @@ var createRAGCollection = (options) => {
|
|
|
21322
21346
|
return result.results;
|
|
21323
21347
|
};
|
|
21324
21348
|
const ingest = async (input) => {
|
|
21325
|
-
const
|
|
21326
|
-
|
|
21327
|
-
|
|
21328
|
-
|
|
21329
|
-
|
|
21330
|
-
const
|
|
21331
|
-
|
|
21349
|
+
const batchSize = Math.max(1, Math.floor(input.upsertBatchSize ?? 32));
|
|
21350
|
+
const concurrency = Math.max(1, Math.floor(input.embeddingConcurrency ?? 4));
|
|
21351
|
+
for (let start = 0;start < input.chunks.length; start += batchSize) {
|
|
21352
|
+
throwIfAborted(input.signal);
|
|
21353
|
+
const batch = input.chunks.slice(start, start + batchSize);
|
|
21354
|
+
const settled = await mapConcurrentSettled(batch, concurrency, async (chunk) => {
|
|
21355
|
+
throwIfAborted(input.signal);
|
|
21356
|
+
const normalizedEmbedding = chunk.embedding ? (validateRAGEmbeddingDimensions(chunk.embedding, getExpectedDimensions(), "chunk"), chunk.embedding) : await embed({
|
|
21332
21357
|
model: options.defaultModel,
|
|
21333
|
-
|
|
21358
|
+
signal: input.signal,
|
|
21359
|
+
text: chunk.text
|
|
21334
21360
|
}, "chunk");
|
|
21335
|
-
|
|
21336
|
-
|
|
21337
|
-
|
|
21338
|
-
|
|
21339
|
-
|
|
21340
|
-
|
|
21341
|
-
|
|
21342
|
-
|
|
21343
|
-
|
|
21344
|
-
|
|
21345
|
-
|
|
21346
|
-
|
|
21347
|
-
|
|
21361
|
+
const normalizedVariants = chunk.embeddingVariants ? await Promise.all(chunk.embeddingVariants.map(async (variant) => {
|
|
21362
|
+
const embedding = variant.embedding ? (validateRAGEmbeddingDimensions(variant.embedding, getExpectedDimensions(), "chunk"), variant.embedding) : await embed({
|
|
21363
|
+
model: options.defaultModel,
|
|
21364
|
+
signal: input.signal,
|
|
21365
|
+
text: variant.text ?? chunk.text
|
|
21366
|
+
}, "chunk");
|
|
21367
|
+
return {
|
|
21368
|
+
...variant,
|
|
21369
|
+
embedding
|
|
21370
|
+
};
|
|
21371
|
+
})) : undefined;
|
|
21372
|
+
return expandChunkForMultivectorStorage({
|
|
21373
|
+
...chunk,
|
|
21374
|
+
embedding: normalizedEmbedding,
|
|
21375
|
+
embeddingVariants: normalizedVariants,
|
|
21376
|
+
metadata: {
|
|
21377
|
+
...chunk.metadata ?? {},
|
|
21378
|
+
[MULTIVECTOR_PRIMARY]: true
|
|
21379
|
+
}
|
|
21380
|
+
});
|
|
21348
21381
|
});
|
|
21349
|
-
|
|
21350
|
-
|
|
21382
|
+
const chunks = settled.filter((result) => result.status === "fulfilled").flatMap((result) => result.value);
|
|
21383
|
+
if (chunks.length > 0)
|
|
21384
|
+
await options.store.upsert({ chunks });
|
|
21385
|
+
const failed = settled.find((result) => result.status === "rejected");
|
|
21386
|
+
if (failed)
|
|
21387
|
+
throw failed.reason;
|
|
21388
|
+
}
|
|
21351
21389
|
};
|
|
21352
21390
|
const buildSourceUpsertInput = async (sourceId, input) => {
|
|
21353
21391
|
const sharedMetadata = input.metadata;
|
|
@@ -21425,33 +21463,34 @@ var createRAGCollection = (options) => {
|
|
|
21425
21463
|
if (!sourceId) {
|
|
21426
21464
|
throw new Error("ingestSource requires a non-empty sourceId.");
|
|
21427
21465
|
}
|
|
21428
|
-
if (input.replace !== false) {
|
|
21429
|
-
await removeSource({
|
|
21430
|
-
chunkCount: input.previousChunkCount,
|
|
21431
|
-
sourceId
|
|
21432
|
-
});
|
|
21433
|
-
}
|
|
21434
21466
|
const built = await buildSourceUpsertInput(sourceId, input);
|
|
21435
21467
|
const embedKind = input.embedKind ?? "passage";
|
|
21436
|
-
const chunks =
|
|
21468
|
+
const chunks = built.chunks.map((chunk, index) => {
|
|
21437
21469
|
const chunkId = `${sourceId}#${index}`;
|
|
21438
|
-
const embedding = chunk.embedding ?? await embed({
|
|
21439
|
-
kind: embedKind,
|
|
21440
|
-
model: options.defaultModel,
|
|
21441
|
-
text: chunk.text
|
|
21442
|
-
}, "chunk");
|
|
21443
21470
|
return {
|
|
21444
21471
|
...chunk,
|
|
21445
21472
|
chunkId,
|
|
21446
|
-
embedding,
|
|
21447
21473
|
metadata: {
|
|
21448
21474
|
...chunk.metadata ?? {},
|
|
21475
|
+
embeddingKind: embedKind,
|
|
21449
21476
|
sourceId
|
|
21450
21477
|
},
|
|
21451
21478
|
source: chunk.source ?? sourceId
|
|
21452
21479
|
};
|
|
21453
|
-
})
|
|
21454
|
-
await ingest({
|
|
21480
|
+
});
|
|
21481
|
+
await ingest({
|
|
21482
|
+
chunks,
|
|
21483
|
+
embeddingConcurrency: input.embeddingConcurrency,
|
|
21484
|
+
signal: input.signal,
|
|
21485
|
+
upsertBatchSize: input.upsertBatchSize
|
|
21486
|
+
});
|
|
21487
|
+
if (input.replace !== false && typeof input.previousChunkCount === "number" && input.previousChunkCount > chunks.length) {
|
|
21488
|
+
await removeSource({
|
|
21489
|
+
chunkIds: Array.from({ length: input.previousChunkCount - chunks.length }, (_unused, index) => `${sourceId}#${chunks.length + index}`),
|
|
21490
|
+
filterDelete: false,
|
|
21491
|
+
sourceId
|
|
21492
|
+
});
|
|
21493
|
+
}
|
|
21455
21494
|
return {
|
|
21456
21495
|
chunkCount: chunks.length,
|
|
21457
21496
|
chunkIds: chunks.map((chunk) => chunk.chunkId),
|
|
@@ -31365,5 +31404,5 @@ export {
|
|
|
31365
31404
|
RAG_NATIVE_QUERY_CANDIDATE_LIMIT
|
|
31366
31405
|
};
|
|
31367
31406
|
|
|
31368
|
-
//# debugId=
|
|
31407
|
+
//# debugId=F1E8AAD3FF52845464756E2164756E21
|
|
31369
31408
|
//# sourceMappingURL=index.js.map
|