@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/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) {