@dudousxd/nestjs-agent-rag 0.7.0 → 0.8.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 +46 -2
- package/dist/index.cjs +37 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +25 -0
- package/dist/index.d.ts +25 -0
- package/dist/index.js +37 -0
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/dist/index.d.cts
CHANGED
|
@@ -5,6 +5,31 @@ interface ChunkOptions {
|
|
|
5
5
|
chunkSize?: number;
|
|
6
6
|
/** Characters of overlap carried from the end of one chunk into the next. Default 100. */
|
|
7
7
|
overlap?: number;
|
|
8
|
+
/**
|
|
9
|
+
* Treat the text as records joined by this string, and never split one across chunks.
|
|
10
|
+
*
|
|
11
|
+
* Default chunking is structure-blind: it breaks at the *latest* paragraph/sentence/word boundary
|
|
12
|
+
* in the window, which for record-shaped text means a cut lands mid-record routinely. That is fine
|
|
13
|
+
* for prose, where the two halves are still sentences. It is not fine for a table flattened to one
|
|
14
|
+
* record per row, because the half that keeps the row identifier and the half that keeps the value
|
|
15
|
+
* end up in different chunks, and neither can answer a question about that row on its own.
|
|
16
|
+
*
|
|
17
|
+
* With a separator set, whole records are packed greedily up to `chunkSize` and a record is never
|
|
18
|
+
* cut — except one longer than `chunkSize`, which has nowhere to go and is character-split as
|
|
19
|
+
* usual, since no chunker can avoid that.
|
|
20
|
+
*
|
|
21
|
+
* `overlap` is IGNORED in this mode, deliberately. Overlap exists to rescue a sentence that a
|
|
22
|
+
* boundary cut in half; a boundary that never falls inside a record has nothing to rescue, and
|
|
23
|
+
* carrying the tail of the previous chunk in would duplicate whole records into neighbouring chunks
|
|
24
|
+
* — paying for the same rows twice at embedding time and letting one row match from two places.
|
|
25
|
+
*
|
|
26
|
+
* Measured on a 200-row spreadsheet flattened to one field-labelled record per row (15 columns,
|
|
27
|
+
* ~800-char budget): blind chunking left 27% of records split, and on questions targeting a field
|
|
28
|
+
* on the far side of such a cut, BM25 answered 54/66 (MRR 0.818) against 66/66 (MRR 1.000) with
|
|
29
|
+
* records kept whole. The dense leg scored 7/66 either way — 200 near-identical rows produce
|
|
30
|
+
* near-identical vectors, so that leg cannot do row lookup at all and chunking does not change it.
|
|
31
|
+
*/
|
|
32
|
+
separator?: string;
|
|
8
33
|
}
|
|
9
34
|
/**
|
|
10
35
|
* Splits text into overlapping chunks for embedding. Greedy up to `chunkSize`, but prefers to break
|
package/dist/index.d.ts
CHANGED
|
@@ -5,6 +5,31 @@ interface ChunkOptions {
|
|
|
5
5
|
chunkSize?: number;
|
|
6
6
|
/** Characters of overlap carried from the end of one chunk into the next. Default 100. */
|
|
7
7
|
overlap?: number;
|
|
8
|
+
/**
|
|
9
|
+
* Treat the text as records joined by this string, and never split one across chunks.
|
|
10
|
+
*
|
|
11
|
+
* Default chunking is structure-blind: it breaks at the *latest* paragraph/sentence/word boundary
|
|
12
|
+
* in the window, which for record-shaped text means a cut lands mid-record routinely. That is fine
|
|
13
|
+
* for prose, where the two halves are still sentences. It is not fine for a table flattened to one
|
|
14
|
+
* record per row, because the half that keeps the row identifier and the half that keeps the value
|
|
15
|
+
* end up in different chunks, and neither can answer a question about that row on its own.
|
|
16
|
+
*
|
|
17
|
+
* With a separator set, whole records are packed greedily up to `chunkSize` and a record is never
|
|
18
|
+
* cut — except one longer than `chunkSize`, which has nowhere to go and is character-split as
|
|
19
|
+
* usual, since no chunker can avoid that.
|
|
20
|
+
*
|
|
21
|
+
* `overlap` is IGNORED in this mode, deliberately. Overlap exists to rescue a sentence that a
|
|
22
|
+
* boundary cut in half; a boundary that never falls inside a record has nothing to rescue, and
|
|
23
|
+
* carrying the tail of the previous chunk in would duplicate whole records into neighbouring chunks
|
|
24
|
+
* — paying for the same rows twice at embedding time and letting one row match from two places.
|
|
25
|
+
*
|
|
26
|
+
* Measured on a 200-row spreadsheet flattened to one field-labelled record per row (15 columns,
|
|
27
|
+
* ~800-char budget): blind chunking left 27% of records split, and on questions targeting a field
|
|
28
|
+
* on the far side of such a cut, BM25 answered 54/66 (MRR 0.818) against 66/66 (MRR 1.000) with
|
|
29
|
+
* records kept whole. The dense leg scored 7/66 either way — 200 near-identical rows produce
|
|
30
|
+
* near-identical vectors, so that leg cannot do row lookup at all and chunking does not change it.
|
|
31
|
+
*/
|
|
32
|
+
separator?: string;
|
|
8
33
|
}
|
|
9
34
|
/**
|
|
10
35
|
* Splits text into overlapping chunks for embedding. Greedy up to `chunkSize`, but prefers to break
|
package/dist/index.js
CHANGED
|
@@ -9,6 +9,9 @@ function chunkText(text, options = {}) {
|
|
|
9
9
|
if (normalized.length === 0) {
|
|
10
10
|
return [];
|
|
11
11
|
}
|
|
12
|
+
if (options.separator !== void 0 && options.separator.length > 0) {
|
|
13
|
+
return chunkByRecord(normalized, chunkSize, options.separator);
|
|
14
|
+
}
|
|
12
15
|
if (normalized.length <= chunkSize) {
|
|
13
16
|
return [
|
|
14
17
|
normalized
|
|
@@ -38,6 +41,40 @@ function chunkText(text, options = {}) {
|
|
|
38
41
|
return chunks;
|
|
39
42
|
}
|
|
40
43
|
__name(chunkText, "chunkText");
|
|
44
|
+
function chunkByRecord(text, chunkSize, separator) {
|
|
45
|
+
const chunks = [];
|
|
46
|
+
let current = "";
|
|
47
|
+
const flush = /* @__PURE__ */ __name(() => {
|
|
48
|
+
if (current.length > 0) {
|
|
49
|
+
chunks.push(current);
|
|
50
|
+
current = "";
|
|
51
|
+
}
|
|
52
|
+
}, "flush");
|
|
53
|
+
for (const record of text.split(separator)) {
|
|
54
|
+
const trimmed = record.trim();
|
|
55
|
+
if (trimmed.length === 0) {
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
if (trimmed.length > chunkSize) {
|
|
59
|
+
flush();
|
|
60
|
+
chunks.push(...chunkText(trimmed, {
|
|
61
|
+
chunkSize,
|
|
62
|
+
overlap: 0
|
|
63
|
+
}));
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
const candidate = current.length > 0 ? `${current}${separator}${trimmed}` : trimmed;
|
|
67
|
+
if (candidate.length > chunkSize) {
|
|
68
|
+
flush();
|
|
69
|
+
current = trimmed;
|
|
70
|
+
} else {
|
|
71
|
+
current = candidate;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
flush();
|
|
75
|
+
return chunks;
|
|
76
|
+
}
|
|
77
|
+
__name(chunkByRecord, "chunkByRecord");
|
|
41
78
|
|
|
42
79
|
// src/vector-store.ts
|
|
43
80
|
function isLexicalVectorStore(store) {
|